2026-03-17 01:14:40 +00:00
|
|
|
"""
|
|
|
|
|
KKS Bluetooth Badge Protocol Constants
|
|
|
|
|
|
|
|
|
|
Defines all protocol markers, protocol numbers, alarm types,
|
|
|
|
|
signal strength levels, data report modes, and related mappings.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from typing import Dict, FrozenSet
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Start / Stop Markers
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
START_MARKER_SHORT: bytes = b'\x78\x78' # 1-byte packet length field
|
|
|
|
|
START_MARKER_LONG: bytes = b'\x79\x79' # 2-byte packet length field
|
|
|
|
|
STOP_MARKER: bytes = b'\x0D\x0A'
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Protocol Numbers
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
PROTO_LOGIN: int = 0x01
|
|
|
|
|
PROTO_HEARTBEAT: int = 0x13
|
|
|
|
|
PROTO_LBS_ADDRESS_REQ: int = 0x17
|
|
|
|
|
PROTO_ADDRESS_QUERY: int = 0x1A
|
|
|
|
|
PROTO_TIME_SYNC: int = 0x1F
|
|
|
|
|
PROTO_GPS: int = 0x22
|
|
|
|
|
PROTO_LBS_MULTI: int = 0x28
|
|
|
|
|
PROTO_LBS_MULTI_REPLY: int = 0x2E
|
|
|
|
|
PROTO_WIFI: int = 0x2C
|
|
|
|
|
PROTO_HEARTBEAT_EXT: int = 0x36
|
|
|
|
|
PROTO_ONLINE_CMD: int = 0x80
|
|
|
|
|
PROTO_ONLINE_CMD_REPLY: int = 0x81
|
|
|
|
|
PROTO_MESSAGE: int = 0x82
|
|
|
|
|
PROTO_TIME_SYNC_2: int = 0x8A
|
|
|
|
|
PROTO_GENERAL_INFO: int = 0x94
|
|
|
|
|
PROTO_ADDRESS_REPLY_EN: int = 0x97
|
|
|
|
|
PROTO_GPS_4G: int = 0xA0
|
|
|
|
|
PROTO_LBS_4G: int = 0xA1
|
|
|
|
|
PROTO_WIFI_4G: int = 0xA2
|
|
|
|
|
PROTO_ALARM_SINGLE_FENCE: int = 0xA3
|
|
|
|
|
PROTO_ALARM_MULTI_FENCE: int = 0xA4
|
|
|
|
|
PROTO_ALARM_LBS_4G: int = 0xA5
|
|
|
|
|
PROTO_LBS_4G_ADDRESS_REQ: int = 0xA7
|
|
|
|
|
PROTO_ALARM_ACK: int = 0x26
|
|
|
|
|
PROTO_ALARM_WIFI: int = 0xA9
|
|
|
|
|
PROTO_ATTENDANCE: int = 0xB0
|
|
|
|
|
PROTO_ATTENDANCE_4G: int = 0xB1
|
|
|
|
|
PROTO_BT_PUNCH: int = 0xB2
|
|
|
|
|
PROTO_BT_LOCATION: int = 0xB3
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Alarm Types (bit-pattern -> name)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
ALARM_TYPES: Dict[int, str] = {
|
|
|
|
|
0x00: "normal",
|
|
|
|
|
0x01: "sos",
|
|
|
|
|
0x02: "power_cut",
|
|
|
|
|
0x03: "vibration",
|
|
|
|
|
0x04: "enter_fence",
|
|
|
|
|
0x05: "exit_fence",
|
|
|
|
|
0x06: "over_speed",
|
|
|
|
|
0x09: "displacement",
|
|
|
|
|
0x0A: "enter_gps_dead_zone",
|
|
|
|
|
0x0B: "exit_gps_dead_zone",
|
|
|
|
|
0x0C: "power_on",
|
|
|
|
|
0x0D: "gps_first_fix",
|
|
|
|
|
0x0E: "low_battery",
|
|
|
|
|
0x0F: "low_battery_protection",
|
|
|
|
|
0x10: "sim_change",
|
|
|
|
|
0x11: "power_off",
|
|
|
|
|
0x12: "airplane_mode",
|
|
|
|
|
0x13: "remove",
|
|
|
|
|
0x14: "door",
|
|
|
|
|
0x15: "shutdown",
|
|
|
|
|
0x16: "voice_alarm",
|
|
|
|
|
0x17: "fake_base_station",
|
|
|
|
|
0x18: "cover_open",
|
|
|
|
|
0x19: "internal_low_battery",
|
|
|
|
|
0xFE: "acc_on",
|
|
|
|
|
0xFF: "acc_off",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# GSM Signal Strength Levels
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
GSM_SIGNAL_LEVELS: Dict[int, str] = {
|
|
|
|
|
0x00: "No Signal",
|
|
|
|
|
0x01: "Very Weak",
|
|
|
|
|
0x02: "Weak",
|
|
|
|
|
0x03: "Good",
|
|
|
|
|
0x04: "Strong",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Data Report Mode (0x00 - 0x0F)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
DATA_REPORT_MODES: Dict[int, str] = {
|
|
|
|
|
0x00: "Timing Upload", # 定时上报
|
|
|
|
|
0x01: "Distance Upload", # 定距上报
|
|
|
|
|
0x02: "Turn Point Upload", # 拐点上传
|
|
|
|
|
0x03: "ACC Status Changed", # ACC状态改变上传
|
|
|
|
|
0x04: "Last Point After Stop", # 运动→静止补传最后定位点
|
|
|
|
|
0x05: "Reconnect Upload", # 断网重连上报最后有效点
|
|
|
|
|
0x06: "Ephemeris Force Upload", # 星历更新强制上传GPS点
|
|
|
|
|
0x07: "Button Upload", # 按键上传定位点
|
|
|
|
|
0x08: "Power On Upload", # 开机上报位置信息
|
|
|
|
|
0x09: "Unused", # 未使用
|
|
|
|
|
0x0A: "Static Update", # 设备静止后上报(时间更新)
|
|
|
|
|
0x0B: "WiFi Parsed Upload", # WIFI解析经纬度上传
|
|
|
|
|
0x0C: "LJDW Upload", # 立即定位指令上报
|
|
|
|
|
0x0D: "Static Last Point", # 设备静止后上报最后经纬度
|
|
|
|
|
0x0E: "GPSDUP Upload", # 静止状态定时上传
|
|
|
|
|
0x0F: "Exit Tracking Mode", # 退出追踪模式
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Protocol Numbers That Require a Server Response
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
PROTOCOLS_REQUIRING_RESPONSE: FrozenSet[int] = frozenset({
|
|
|
|
|
PROTO_LOGIN,
|
|
|
|
|
PROTO_HEARTBEAT,
|
|
|
|
|
PROTO_LBS_ADDRESS_REQ,
|
|
|
|
|
PROTO_ADDRESS_QUERY,
|
|
|
|
|
PROTO_TIME_SYNC,
|
Add WebSocket, multi API key, geocoding proxy, beacon map picker, and comprehensive bug fixes
- Multi API Key + permission system (read/write/admin) with SHA-256 hash
- WebSocket real-time push (location, alarm, device_status, attendance, bluetooth)
- Geocoding proxy endpoints for Amap POI search and reverse geocode
- Beacon modal map-based location picker with search and click-to-select
- GCJ-02 ↔ WGS-84 bidirectional coordinate conversion
- Data cleanup scheduler (configurable retention days)
- Fix GPS longitude sign inversion (course_status bit 11: 0=East, 1=West)
- Fix 2G CellID 2→3 bytes across all protocols (0x28, 0x2C, parser.py)
- Fix parser loop guards, alarm_source field length, CommandLog.sent_at
- Fix geocoding IMEI parameterization, require_admin import
- Improve API error messages for 422 validation errors
- Remove beacon floor/area fields (consolidated into name)
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
2026-03-24 05:10:05 +00:00
|
|
|
# Note: PROTO_LBS_MULTI (0x28) does NOT require response; only 0x2E does
|
2026-03-17 01:14:40 +00:00
|
|
|
PROTO_HEARTBEAT_EXT,
|
|
|
|
|
PROTO_TIME_SYNC_2,
|
|
|
|
|
# PROTO_GENERAL_INFO (0x94) does NOT require response per protocol doc
|
|
|
|
|
PROTO_ALARM_SINGLE_FENCE,
|
|
|
|
|
PROTO_ALARM_MULTI_FENCE,
|
|
|
|
|
PROTO_ALARM_LBS_4G,
|
|
|
|
|
PROTO_LBS_4G_ADDRESS_REQ,
|
|
|
|
|
PROTO_ALARM_WIFI,
|
|
|
|
|
PROTO_ATTENDANCE,
|
|
|
|
|
PROTO_ATTENDANCE_4G,
|
|
|
|
|
PROTO_BT_PUNCH,
|
|
|
|
|
# Note: PROTO_BT_LOCATION (0xB3) does NOT require a response per protocol spec
|
|
|
|
|
})
|
|
|
|
|
|
Add batch management APIs, API security, rate limiting, and optimizations
- Batch device CRUD: POST /api/devices/batch (create 500), PUT /api/devices/batch (update 500),
POST /api/devices/batch-delete (delete 100) with WHERE IN bulk queries
- Batch command: POST /api/commands/batch with model_validator mutual exclusion
- API key auth (X-API-Key header, secrets.compare_digest timing-safe)
- Rate limiting via SlowAPIMiddleware (60/min default, 30/min writes)
- Real client IP extraction (X-Forwarded-For / CF-Connecting-IP)
- Global exception handler (no stack trace leaks, passes HTTPException through)
- CORS with auto-disable credentials on wildcard origins
- Schema validation: IMEI pattern, lat/lon ranges, Literal enums, MAC/UUID patterns
- Heartbeats router, per-ID endpoints for locations/attendance/bluetooth
- Input dedup in batch create, result ordering preserved
- Baidu reverse geocoding, Gaode map tiles with WGS84→GCJ02 conversion
- Device detail panel with feature toggles and command controls
- Side panel for location/beacon pages with auto-select active device
via [HAPI](https://hapi.run)
Co-Authored-By: HAPI <noreply@hapi.run>
2026-03-20 09:18:43 +00:00
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Device Defaults
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
DEFAULT_DEVICE_TYPE: str = "P240"
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Language Codes (used in 0x80 / 0x82 packets)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
LANG_CHINESE: int = 0x0001
|
|
|
|
|
LANG_ENGLISH: int = 0x0002
|
|
|
|
|
DEFAULT_LANGUAGE: int = LANG_CHINESE
|
|
|
|
|
DEFAULT_LANGUAGE_BYTES: bytes = b"\x00\x01"
|
|
|
|
|
|
|
|
|
|
# Server flag placeholder (4 bytes, used in command/message packets)
|
|
|
|
|
SERVER_FLAG_BYTES: bytes = b"\x00\x00\x00\x00"
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Attendance Status (from terminal_info byte, bits[5:2])
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
ATTENDANCE_STATUS_SHIFT: int = 2
|
|
|
|
|
ATTENDANCE_STATUS_MASK: int = 0x0F
|
|
|
|
|
ATTENDANCE_TYPES: Dict[int, str] = {
|
|
|
|
|
0b0001: "clock_in",
|
|
|
|
|
0b0010: "clock_out",
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# GPS Course/Status Bit Fields
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
COURSE_BIT_REALTIME: int = 0x2000 # bit 13
|
|
|
|
|
COURSE_BIT_POSITIONED: int = 0x1000 # bit 12
|
|
|
|
|
COURSE_BIT_EAST: int = 0x0800 # bit 11
|
|
|
|
|
COURSE_BIT_NORTH: int = 0x0400 # bit 10
|
|
|
|
|
COURSE_MASK: int = 0x03FF # bits 9-0
|
|
|
|
|
|
|
|
|
|
# MCC high-bit flag: if set, MNC is 2 bytes instead of 1
|
|
|
|
|
MCC_MNC2_FLAG: int = 0x8000
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Voltage Levels (0x00-0x06)
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
VOLTAGE_LEVELS: Dict[int, str] = {
|
|
|
|
|
0x00: "shutdown",
|
|
|
|
|
0x01: "very_low",
|
|
|
|
|
0x02: "low",
|
|
|
|
|
0x03: "medium",
|
|
|
|
|
0x04: "good",
|
|
|
|
|
0x05: "high",
|
|
|
|
|
0x06: "full",
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 01:14:40 +00:00
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
# Protocol Number -> Human-Readable Name
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
PROTOCOL_NAMES: Dict[int, str] = {
|
|
|
|
|
PROTO_LOGIN: "Login",
|
|
|
|
|
PROTO_HEARTBEAT: "Heartbeat",
|
|
|
|
|
PROTO_LBS_ADDRESS_REQ: "LBS Address Request",
|
|
|
|
|
PROTO_ADDRESS_QUERY: "Address Query",
|
|
|
|
|
PROTO_TIME_SYNC: "Time Sync",
|
|
|
|
|
PROTO_ALARM_ACK: "Alarm ACK",
|
|
|
|
|
PROTO_GPS: "GPS",
|
|
|
|
|
PROTO_LBS_MULTI: "LBS Multi",
|
|
|
|
|
PROTO_LBS_MULTI_REPLY: "LBS Multi Reply",
|
|
|
|
|
PROTO_WIFI: "WIFI",
|
|
|
|
|
PROTO_HEARTBEAT_EXT: "Heartbeat Extended",
|
|
|
|
|
PROTO_ONLINE_CMD: "Online Command",
|
|
|
|
|
PROTO_ONLINE_CMD_REPLY: "Online Command Reply",
|
|
|
|
|
PROTO_MESSAGE: "Message",
|
|
|
|
|
PROTO_TIME_SYNC_2: "Time Sync 2",
|
|
|
|
|
PROTO_GENERAL_INFO: "General Info",
|
|
|
|
|
PROTO_ADDRESS_REPLY_EN: "Address Reply (EN)",
|
|
|
|
|
PROTO_GPS_4G: "GPS 4G",
|
|
|
|
|
PROTO_LBS_4G: "LBS 4G",
|
|
|
|
|
PROTO_WIFI_4G: "WIFI 4G",
|
|
|
|
|
PROTO_ALARM_SINGLE_FENCE: "Alarm Single Fence",
|
|
|
|
|
PROTO_ALARM_MULTI_FENCE: "Alarm Multi Fence",
|
|
|
|
|
PROTO_ALARM_LBS_4G: "Alarm LBS 4G",
|
|
|
|
|
PROTO_LBS_4G_ADDRESS_REQ: "LBS 4G Address Request",
|
|
|
|
|
PROTO_ALARM_WIFI: "Alarm WIFI",
|
|
|
|
|
PROTO_ATTENDANCE: "Attendance",
|
|
|
|
|
PROTO_ATTENDANCE_4G: "Attendance 4G",
|
|
|
|
|
PROTO_BT_PUNCH: "BT Punch",
|
|
|
|
|
PROTO_BT_LOCATION: "BT Location",
|
|
|
|
|
}
|