Dokumentasi Protokol ADMS (Push Protocol) ZKTeco
Pendahuluan
ADMS (Attendance Data Management System) Push Protocol adalah sistem komunikasi berbasis HTTP
dimana mesin absen ZKTeco bertindak sebagai client yang secara aktif mengirimkan data ke server. Protokol
ini memungkinkan mesin untuk push data kehadiran secara real-time atau terjadwal tanpa polling dari server.
Arsitektur Sistem
[Mesin ZKTeco] --HTTP POST--> [Server ADMS]
(Client) (Server)
Karakteristik
Mesin absen sebagai HTTP Client
Server ADMS sebagai HTTP Server
Komunikasi menggunakan HTTP POST
Format data: Query String atau JSON
Port default: 80 (HTTP) atau 443 (HTTPS)
Konfigurasi Mesin
Parameter Koneksi
Konfigurasi pada menu mesin ZKTeco:
Parameter Deskripsi Contoh
Server IP/Domain Alamat server ADMS 192.168.1.100 atau adms.company.com
Server Port Port HTTP server 80 atau 8080
Push Interval Interval pengiriman (detik) 60 (1 menit)
Push Protocol Protokol yang digunakan ADMS
Stamp Signature/token untuk autentikasi ABC123XYZ
Menu Konfigurasi di Mesin
Communication → Cloud Server → ADMS
- Server Address: [IP/Domain]
- Port: [Port]
- Push Interval: [Detik]
- Stamp: [Token]
Request dari Mesin ke Server
1. Heartbeat / Ping Request
Mesin mengirim heartbeat secara periodik untuk memastikan koneksi aktif.
Endpoint: GET /iclock/getrequest
Query Parameters:
SN=[Serial Number]
Contoh Request:
http
GET /iclock/getrequest?SN=BAZR192360015 HTTP/1.1
Host: 192.168.1.100
User-Agent: ZK-Push/1.0
Response Server:
OK
2. Device Information (C Request)
Mesin mengirim informasi perangkat saat pertama kali connect atau setelah restart.
Endpoint: POST /iclock/cdata
Query Parameters:
SN=[Serial Number]
table=ATTPHOTO (optional)
Stamp=[Token]
POST Body (Content):
~SerialNumber=BAZR192360015
~IPAddress=192.168.1.201
~Platform=ZEM600
~Firmware=Ver 6.60
~PushVersion=2.4.1
~DeviceName=Main Gate
~MAC=00:17:61:12:34:56
Contoh Request:
http
POST /iclock/cdata?SN=BAZR192360015&Stamp=ABC123 HTTP/1.1
Host: 192.168.1.100
Content-Type: application/x-www-form-urlencoded
Content-Length: 156
~SerialNumber=BAZR192360015
~IPAddress=192.168.1.201
~Platform=ZEM600
~Firmware=Ver 6.60
~PushVersion=2.4.1
Response Server:
OK
3. Attendance Data Push
Mesin mengirim data kehadiran (transaksi absen) ke server.
Endpoint: POST /iclock/cdata
Query Parameters:
SN=[Serial Number]
table=ATTLOG
Stamp=[Token]
OpStamp=[Operation Stamp]
POST Body Format:
PIN\tDateTime\tStatus\tVerify\tWorkCode\tReserved
Field Description:
Field Deskripsi
PIN User ID / Employee ID
DateTime Format: YYYY-MM-DD HH:MM:SS
Status 0=Check In, 1=Check Out, 2-5=Break/OT
Verify Metode verifikasi (0-15)
WorkCode Kode pekerjaan (optional)
Reserved Field reserved (biasanya kosong)
Contoh Request:
http
POST /iclock/cdata?SN=BAZR192360015&table=ATTLOG&Stamp=ABC123&OpStamp=9999 HTTP/1.1
Host: 192.168.1.100
Content-Type: text/plain
Content-Length: 150
1001 2025-10-17 08:30:15 0 1 0
1002 2025-10-17 08:31:22 0 3 0
1003 2025-10-17 08:32:45 0 1 0
Response Server:
OK: 3
(Angka 3 menunjukkan jumlah record yang berhasil diterima)
4. User Photo Push
Mesin mengirim foto pengguna (jika ada).
Endpoint: POST /iclock/cdata
Query Parameters:
SN=[Serial Number]
table=ATTPHOTO
Stamp=[Token]
POST Body:
PIN=[User ID]&PhotoContent=[Base64 Encoded Image]&PhotoSize=[Size]
Contoh Request:
http
POST /iclock/cdata?SN=BAZR192360015&table=ATTPHOTO&Stamp=ABC123 HTTP/1.1
Host: 192.168.1.100
Content-Type: application/x-www-form-urlencoded
PIN=1001&PhotoContent=iVBORw0KGgoAAAANSUhEUgAA...&PhotoSize=5120
5. Operation Log Push
Mesin mengirim log operasional perangkat.
Endpoint: POST /iclock/cdata
Query Parameters:
SN=[Serial Number]
table=OPERLOG
Stamp=[Token]
POST Body:
OperTime\tOperator\tOperation\tObject
Contoh:
2025-10-17 08:00:00 1 PowerOn Device
2025-10-17 08:01:00 100 EnrollFP User:1001
Response dari Server ke Mesin
Response Code
Response Deskripsi
OK Data diterima dengan sukses
OK: [n] n record berhasil diterima
ERROR Terjadi error
REPEAT Data duplicate
Command dari Server (dalam response)
Server dapat mengirim command ke mesin melalui response body.
Format Command:
C:ID:Command:Content
Contoh Commands:
1. Update User
C:1:USER PIN=1001 Name=John Doe Privilege=0 Passwd= Card=1234567890 Grp=1 TZ=0000000100000000
2. Delete User
C:2:DELETE USER PIN=1001
3. Clear All Attendance
C:3:CLEAR DATA
4. Reboot Device
C:4:REBOOT
5. Update Time
C:5:CHECK
6. Get Device Info
C:6:INFO
Response Format dari Mesin (acknowledgment):
http
GET /iclock/devicecmd?SN=BAZR192360015&ID=1 HTTP/1.1
Kode Status & Verify Type
Status Code
Code Deskripsi
0 Check In
1 Check Out
2 Break Out
3 Break In
4 OT In
5 OT Out
Verify Type
Code Metode Verifikasi
0 Password
1 Fingerprint
2 Card (RFID)
3 Face
4 Fingerprint + Password
15 Face + Fingerprint
Implementasi Server ADMS
Contoh Server (Python Flask)
python
from flask import Flask, request, Response
from datetime import datetime
app = Flask(__name__)
# Storage untuk attendance data
attendance_logs = []
device_info = {}
@app.route('/iclock/getrequest', methods=['GET'])
def heartbeat():
"""Handle heartbeat/ping dari mesin"""
sn = request.args.get('SN')
print(f"Heartbeat from device: {sn} at {datetime.now()}")
# Server bisa mengirim command di sini
# Contoh: return "C:1:USER PIN=1001\tName=John Doe"
return Response("OK", status=200, mimetype='text/plain')
@app.route('/iclock/cdata', methods=['POST'])
def receive_data():
"""Handle data push dari mesin"""
sn = request.args.get('SN')
table = request.args.get('table')
stamp = request.args.get('Stamp')
# Validasi stamp/token
if stamp != 'ABC123':
return Response("ERROR: Invalid Stamp", status=401)
content = request.data.decode('utf-8')
if table == 'ATTLOG':
# Parse attendance data
lines = content.strip().split('\n')
count = 0
for line in lines:
if not line.strip():
continue
fields = line.split('\t')
if len(fields) >= 5:
attendance = {
'device_sn': sn,
'pin': fields[0],
'datetime': fields[1],
'status': fields[2],
'verify': fields[3],
'workcode': fields[4] if len(fields) > 4 else '',
'received_at': datetime.now().isoformat()
}
attendance_logs.append(attendance)
count += 1
# Simpan ke database di sini
print(f"Attendance: {attendance}")
return Response(f"OK: {count}", status=200, mimetype='text/plain')
elif table == 'ATTPHOTO':
# Handle foto
print(f"Received photo data: {content[:100]}...")
return Response("OK", status=200, mimetype='text/plain')
elif table == 'OPERLOG':
# Handle operation log
print(f"Received operation log: {content}")
return Response("OK", status=200, mimetype='text/plain')
else:
# Device info (C request)
print(f"Device info from {sn}:\n{content}")
device_info[sn] = content
return Response("OK", status=200, mimetype='text/plain')
@app.route('/iclock/devicecmd', methods=['GET'])
def device_command_ack():
"""Handle acknowledgment dari mesin setelah menerima command"""
sn = request.args.get('SN')
cmd_id = request.args.get('ID')
print(f"Command {cmd_id} acknowledged by device {sn}")
return Response("OK", status=200, mimetype='text/plain')
@app.route('/attendance/list', methods=['GET'])
def list_attendance():
"""API untuk melihat data attendance yang sudah diterima"""
return {
'total': len(attendance_logs),
'data': attendance_logs[-50:] # 50 data terakhir
}
if __name__ == '__main__':
app.run(host='0.0.0.0', port=80, debug=True)
Contoh Server (Node.js Express)
javascript
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const PORT = 80;
// Middleware
app.use(bodyParser.text({ type: '*/*' }));
// Storage
let attendanceLogs = [];
let deviceInfo = {};
// Heartbeat endpoint
app.get('/iclock/getrequest', (req, res) => {
const sn = req.query.SN;
console.log(`Heartbeat from device: ${sn} at ${new Date()}`);
// Bisa mengirim command di sini
res.send('OK');
} );
// Data push endpoint
app.post('/iclock/cdata', (req, res) => {
const { SN, table, Stamp } = req.query;
// Validasi stamp
if (Stamp !== 'ABC123') {
return res.status(401).send('ERROR: Invalid Stamp');
}
const content = req.body;
if (table === 'ATTLOG') {
// Parse attendance data
const lines = content.trim().split('\n');
let count = 0;
lines.forEach(line => {
if (!line.trim()) return;
const fields = line.split('\t');
if (fields.length >= 5) {
const attendance = {
device_sn: SN,
pin: fields[0],
datetime: fields[1],
status: fields[2],
verify: fields[3],
workcode: fields[4] || '',
received_at: new Date().toISOString()
};
attendanceLogs.push(attendance);
count++;
console.log('Attendance:', attendance);
}
} );
res.send(`OK: ${count}`);
} else if (table === 'ATTPHOTO') {
console.log('Received photo data');
res.send('OK');
} else if (table === 'OPERLOG') {
console.log('Received operation log:', content);
res.send('OK');
} else {
// Device info
console.log(`Device info from ${SN}:\n${content}`);
deviceInfo[SN] = content;
res.send('OK');
}
} );
// Command acknowledgment
app.get('/iclock/devicecmd', (req, res) => {
const { SN, ID } = req.query;
console.log(`Command ${ID} acknowledged by device ${SN}`);
res.send('OK');
} );
// API untuk melihat data
app.get('/attendance/list', (req, res) => {
res.json({
total: attendanceLogs.length,
data: attendanceLogs.slice(-50) // 50 data terakhir
} );
} );
app.listen(PORT, '0.0.0.0', () => {
console.log(`ADMS Server running on port ${PORT}`);
} );
Keamanan & Best Practices
1. Autentikasi
Gunakan Stamp (token) untuk validasi request
Implementasikan IP whitelist untuk mesin yang diizinkan
Gunakan HTTPS untuk enkripsi komunikasi
2. Validasi Data
python
def validate_attendance(data):
required_fields = ['pin', 'datetime', 'status', 'verify']
for field in required_fields:
if field not in data or not data[field]:
return False
# Validasi format datetime
try:
datetime.strptime(data['datetime'], '%Y-%m-%d %H:%M:%S')
except ValueError:
return False
return True
3. Duplicate Detection
python
def is_duplicate(new_log):
"""Check apakah log sudah pernah diterima"""
for log in attendance_logs:
if (log['pin'] == new_log['pin'] and
log['datetime'] == new_log['datetime'] and
log['device_sn'] == new_log['device_sn']):
return True
return False
4. Logging
Log semua request dari mesin
Simpan raw data untuk debugging
Monitor heartbeat untuk deteksi device offline
Troubleshooting
Device Tidak Push Data
Penyebab:
1. Konfigurasi server address salah
2. Port tidak terbuka/firewall blocking
3. Stamp/token tidak match
4. Network connectivity issue
Solusi:
Cek konfigurasi di menu Communication → Cloud Server
Test koneksi menggunakan ping dari mesin
Verifikasi firewall rules di server
Cek log server untuk error messages
Data Tidak Lengkap
Penyebab:
1. Network timeout saat transfer data besar
2. Buffer overflow pada server
Solusi:
Kurangi push interval
Tingkatkan kapasitas server
Implementasikan pagination
Duplicate Records
Penyebab:
1. Mesin retry karena tidak terima response OK
2. Network issue menyebabkan double send
Solusi:
Pastikan server selalu return response yang benar
Implementasikan duplicate detection di server
Gunakan unique identifier (PIN + DateTime + Device SN)
Testing
Test Heartbeat
bash
curl "http://192.168.1.100/iclock/getrequest?SN=BAZR192360015"
Test Attendance Push
bash
curl -X POST "http://192.168.1.100/iclock/cdata?SN=BAZR192360015&table=ATTLOG&Stamp=ABC123" \
-H "Content-Type: text/plain" \
-d "1001 2025-10-17 08:30:15 0 1 0 "
Simulasi Device dengan Script
python
import requests
import time
SERVER_URL = "http://192.168.1.100"
DEVICE_SN = "BAZR192360015"
STAMP = "ABC123"
def send_heartbeat():
response = requests.get(
f"{SERVER_URL}/iclock/getrequest",
params={'SN': DEVICE_SN}
)
print(f"Heartbeat: {response.text}")
def send_attendance(pin, status=0, verify=1):
now = time.strftime("%Y-%m-%d %H:%M:%S")
data = f"{pin}\t{now}\t{status}\t{verify}\t0\t"
response = requests.post(
f"{SERVER_URL}/iclock/cdata",
params={
'SN': DEVICE_SN,
'table': 'ATTLOG',
'Stamp': STAMP
},
data=data,
headers={'Content-Type': 'text/plain'}
)
print(f"Attendance: {response.text}")
# Test
send_heartbeat()
send_attendance('1001', status=0, verify=1)
Referensi
ZKTeco Push Protocol Specification
ADMS Server Development Guide
ZKTeco Device Communication Manual
Versi Dokumentasi: 2.0
Tanggal: Oktober 2025
Protokol: ADMS Push Protocol via HTTP