import {bytesToHex, bytesToMac} from '../utils/hex' import {joinLowHigh, readUint16LE, toUint8Array} from '../utils/bytes' import {DEVICE_PROFILE} from '../protocols/profiles' /** * 解析 UUID_STATE 的状态字节,转为有符号 8bit。 */ export function parseStateCode(payload) { const bytes = toUint8Array(payload) if (!bytes.length) { return null } return bytes[0] > 127 ? bytes[0] - 256 : bytes[0] } /** * ED713 MSG 解析。 * 文档布局:MAC(6) + bindStatus(1) + 其它(3) + productId_H(1) + productId_L(1) * + baseBoard(1) + coreBoard(1) + 其它(2) + deviceType(1) * + wifiStatus(1) + cellularStatus(1) + 其它(1) */ function parseEd713DeviceInfo(bytes) { if (!bytes.length) { return { raw: bytes, mac: '', bindStatus: null, productId: null, deviceType: null, wifiStatus: null, cellularStatus: null } } return { raw: bytes, mac: bytesToMac(bytes.slice(0, 6)), bindStatus: bytes.length > 6 ? bytes[6] : null, productId: bytes.length > 11 ? joinLowHigh(bytes[11], bytes[10]) : null, baseBoardVersion: bytes.length > 12 ? bytes[12] : null, coreBoardVersion: bytes.length > 13 ? bytes[13] : null, deviceType: bytes.length > 16 ? bytes[16] : null, wifiStatus: bytes.length > 17 ? bytes[17] : null, cellularStatus: bytes.length > 18 ? bytes[18] : null } } /** * ED719 MSG 解析。 * 实际偏移与 ED713 一致(文档省略了 bindStatus/productId 但底板数据布局相同): * MAC(6) + bindStatus(1) + 其它(3) + productId_H(1) + productId_L(1) * + baseBoard(1) + coreBoard(1) + 其它(2) + deviceType(1) * + wifiStatus(1) + cellularStatus(1) + 其它(1) */ function parseEd719DeviceInfo(bytes) { return parseEd713DeviceInfo(bytes) } /** * 解析 UUID_MSG 设备信息(按 profile 区分 ED713/ED719)。 */ export function parseDeviceInfo(payload, profileId = DEVICE_PROFILE.UNKNOWN) { const bytes = toUint8Array(payload) if (!bytes.length) { return { raw: bytes, mac: '', bindStatus: null, productId: null, deviceType: null, wifiStatus: null, cellularStatus: null } } if (profileId === DEVICE_PROFILE.ED713) return parseEd713DeviceInfo(bytes) if (profileId === DEVICE_PROFILE.ED719) return parseEd719DeviceInfo(bytes) // UNKNOWN:按 ED713 布局兼容 return parseEd713DeviceInfo(bytes) } /** * ED713 雷达数据解析。 * 协议帧长度不足时返回 valid:false,交由上层决定是否忽略。 */ export function parseEd713Radar(payload) { const bytes = toUint8Array(payload) if (bytes.length < 60) { return { type: 'ED713', valid: false, reason: 'LENGTH_LT_60', raw: bytes } } return { type: 'ED713', valid: true, raw: bytes, peopleCount: bytes[24], peopleLocalDecimeter: bytes[25], peopleState: bytes[26], heartRate: bytes[28], respRate: bytes[29], activityState: bytes[30], moveIndex: bytes[35], waveNum: bytes[37], respWave: Array.from(bytes.slice(38, 58)), narrowFlag: bytes[59] } } /** * ED719 雷达数据解析。 * 按文档结构体定义: * header[4] + event(1) + people(1) + num(1) + nodata(1) * + height(2) + mode(1) + beeper(1) + left(2) + right(2) + front(2) + back(2) * + sensitive(1) + nodata1(1) + stateDelay(2) = 24 bytes * shield_num 在 offset 24,屏蔽区在 offset 25 * 屏蔽区之后:wallOption + peopleHaveSwitch + peopleActivitySwitch */ export function parseEd719Radar(payload) { const bytes = toUint8Array(payload) if (bytes.length < 24) { return { type: 'ED719', valid: false, reason: 'LENGTH_LT_24', raw: bytes } } const shieldNum = bytes[24] || 0 const shieldStart = 25 const shieldBlockLength = Math.min(Math.max(shieldNum, 0), 4) * 9 const wallOffset = shieldStart + shieldBlockLength return { type: 'ED719', valid: true, raw: bytes, eventState: bytes[4], peopleState: bytes[5], peopleNum: bytes[6], installHeight: readUint16LE(bytes, 8), installMode: bytes[10], beeperOn: bytes[11], leftDist: readUint16LE(bytes, 12), rightDist: readUint16LE(bytes, 14), frontDist: readUint16LE(bytes, 16), backDist: readUint16LE(bytes, 18), sensitive: bytes[20], stateDelay: readUint16LE(bytes, 22), shieldNum, shieldZoneRaw: Array.from(bytes.slice(shieldStart, shieldStart + shieldBlockLength)), wallOption: bytes[wallOffset] ?? null, peopleHaveSwitch: bytes[wallOffset + 1] ?? null, peopleActivitySwitch: bytes[wallOffset + 2] ?? null } }