import { buildA7Packets } from '../core/packet' import { parseDeviceInfo } from '../parsers' import { BLE_STAGE } from '../core/state' function waitForStateCode(ble, timeout = 6000) { return new Promise((resolve, reject) => { let timer = null let off = null const finish = (callback) => { if (timer) { clearTimeout(timer) } if (off) { off() } callback() } timer = setTimeout(() => { finish(() => reject(new Error('等待 UUID_STATE 超时'))) }, timeout) off = ble.on('protocol:state', (payload) => { finish(() => resolve(payload)) }) }) } function generateRandomKey(length = 16) { const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' let result = '' for (let index = 0; index < length; index += 1) { const random = Math.floor(Math.random() * chars.length) result += chars[random] } return result } async function writePacketsToKeyCharacteristic(ble, keyText) { const { key } = ble.getState().characteristics if (!key || !key.uuid) { throw new Error('未发现 UUID_KEY 特征值') } const profile = ble.getProfile() const keyValue = String(keyText || '').trim() if (keyValue.length !== profile.auth.keyLength) { throw new Error(`密钥长度必须为 ${profile.auth.keyLength}`) } const packets = buildA7Packets(keyValue, profile.packet) for (const packet of packets) { await ble.writeCharacteristic(key.uuid, packet) } return keyValue } async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options = {}) { const keyValue = await writePacketsToKeyCharacteristic(ble, keyText) const stateEvent = await waitForStateCode(ble, options.timeout || 6000) const stateCode = stateEvent.stateCode if (!expectedStateCodes.includes(stateCode)) { throw new Error(`密钥流程状态异常,期望=${expectedStateCodes.join('/')} 实际=${stateCode}`) } ble.setStage(BLE_STAGE.AUTHORIZED) ble.setAuthState({ keyMatched: true, lastStateCode: stateCode }) return { success: true, stateCode, key: keyValue } } export function createAuthModule(ble) { async function readDeviceInfo() { const { msg } = ble.getState().characteristics if (!msg || !msg.uuid) { throw new Error('未发现 UUID_MSG 特征值') } const valuePromise = new Promise((resolve) => { const off = ble.on('protocol:msg', (payload) => { off() resolve(payload) }) }) await ble.readCharacteristic(msg.uuid) const payload = await valuePromise return parseDeviceInfo(payload.value) } /** * 严格遵循协议1.2绑定流程: * 1) 先读取 UUID_MSG 获取 bindStatus; * 2) bindStatus=0 -> 写随机密钥并等待 UUID_STATE=5(绑定成功); * 3) bindStatus=1 -> 写已知密钥并等待 UUID_STATE=7(密钥匹配成功)。 * * 兼容说明:当某些固件 MSG 未明确返回 0/1 时,回退为“已知密钥匹配优先”, * 允许状态 7(匹配成功) 或 5(设备首次绑定成功)。 */ async function ensureAuthorized(keyText, options = {}) { ble.setStage(BLE_STAGE.AUTHORIZING) const info = await readDeviceInfo() const bindStatus = Number(info.bindStatus) ble.setAuthState({ bound: Number.isNaN(bindStatus) ? null : bindStatus, keyMatched: false }) // 未绑定:按协议执行绑定。 if (bindStatus === 0) { const keyToBind = String(keyText || '').trim() || generateRandomKey(ble.getProfile().auth.keyLength) const authResult = await writeKeyAndExpectStates(ble, keyToBind, [5], options) return { mode: 'BIND', deviceInfo: info, authResult, key: keyToBind } } // 已绑定:按协议执行密钥匹配。 if (bindStatus === 1) { const keyToMatch = String(keyText || '').trim() if (!keyToMatch) { throw new Error('设备已绑定,必须提供已绑定密钥用于匹配') } const authResult = await writeKeyAndExpectStates(ble, keyToMatch, [7], options) return { mode: 'MATCH', deviceInfo: info, authResult, key: keyToMatch } } // 绑定状态不明确时,执行兼容分支(优先匹配,允许绑定成功)。 const fallbackKey = String(keyText || '').trim() if (!fallbackKey) { throw new Error(`无法识别的绑定状态: ${info.bindStatus},且未提供密钥`) } const fallbackResult = await writeKeyAndExpectStates(ble, fallbackKey, [7, 5], options) return { mode: 'UNKNOWN_BIND_STATE_FALLBACK', deviceInfo: info, authResult: fallbackResult, key: fallbackKey } } return { readDeviceInfo, ensureAuthorized, generateRandomKey } }