# 统一 BLE 连接与控制流程(ED713 / ED719) ## 1. 标准连接流程图(文字版) 1. 初始化阶段 - `openBluetoothAdapter` - 注册监听:`onBluetoothAdapterStateChange`、`onBLEConnectionStateChange`、`onBLECharacteristicValueChange` 2. 搜索阶段 - 优先过滤扫描:`startBluetoothDevicesDiscovery({ services:[0x00F4] })` - 超时无结果则无过滤兜底扫描 - 选择目标设备 `deviceId` 3. 连接与发现阶段 - `createBLEConnection(deviceId)` - `getBLEDeviceServices`,定位服务 `0x00F4` - `getBLEDeviceCharacteristics`,识别 `F401/F402/F403/F501/F301/F302` - 订阅通知:`notify(F301)=true`、`notify(F302)=true` 4. 标准业务阶段 - 读取设备信息:`read(F501)` - 鉴权:`write(F402, A7分包key)`,监听 `F301` 状态码 - 当前项目约定:**已知密钥 = 设备 MAC 去分隔符后 + `0000`**,例如 `10:52:1C:81:64:F4` → `10521C8164F40000` - 鉴权成功后进行功能操作: - WiFi 配网:`write(F401, A7分包ssid|pass)` - 控制命令:`write(F403, [cmd])` - 雷达数据:`cmd=0xA1` 开始、`cmd=0xA2` 停止,解析 `F302` 5. 收尾阶段 - 停止扫描 / 断开连接 / 关闭适配器 - 清理监听器与状态 ## 2. 目录结构 ```text utils/ble/ core/ bleCore.js # 蓝牙核心能力(连接/发现/读写/订阅/重连) controller.js # 标准流程编排(scan->connect->discover->auth) errors.js # 统一异常 eventBus.js # 回调分发 packet.js # A7 分包工具 retry.js # 重试工具 state.js # 状态模型 modules/ auth.js # 鉴权流程(读MSG + 写KEY) wifi.js # WiFi配置流程 radar.js # 雷达控制流程 parsers/ commonParser.js # MSG/STATE/雷达通用解析 index.js # 解析聚合出口 protocols/ profiles.js # ED713/ED719/UNKNOWN profile 差异配置 utils/ bytes.js # 字节/高低位工具 hex.js # hex/utf8 转换 uuid.js # UUID 标准化与比较 index.js # 统一出口 ``` ## 3. 公共方法列表 - 蓝牙核心 - `createBleCore(options)` - `openAdapter/getAdapterState/closeAdapter` - `startDiscovery/stopDiscovery` - `connect/disconnect` - `discoverServices/discoverCharacteristics` - `readCharacteristic/writeCharacteristic/notifyCharacteristic` - 流程编排 - `createUnifiedBleController(options)` - `standardConnectFlow({ deviceId, key })` - `scanAndPickDevice/connectAndDiscover/cleanup` - 业务模块 - `createAuthModule(ble).ensureAuthorized(key)` - `createAuthModule(ble).ensureAuthorizedByMac({ suffix: '0000' })` - `createWifiModule(ble).configureWifi(ssid, password)` - `createRadarModule(ble).startRadarStream()/stopRadarStream()/writeFallParams()` - 工具 - `buildA7Packets(payload, packetConfig)` - `bytesToHex/hexToBytes/utf8ToBytes/bytesToUtf8` - `splitToLowHigh/joinLowHigh/readUint16LE/readUint32LE` - `normalizeUuid/toFullUuid/uuidEquals` ## 4. 核心流程代码示例 ```js import { createUnifiedBleController, DEVICE_PROFILE } from '@/utils/ble' const bleController = createUnifiedBleController({ profileId: DEVICE_PROFILE.UNKNOWN, reconnect: { enabled: true, retries: 2, delay: 1000 } }) async function runStandardFlow({ key, ssid, password }) { try { // 1) 连接 + 服务发现 + 自动订阅F301/F302 + 鉴权 const session = await bleController.standardConnectFlow({ key }) // 2) 读取设备信息(如需要可再次读取) const info = await bleController.auth.readDeviceInfo() // 3) WiFi 配置 const wifiResult = await bleController.wifi.configureWifi(ssid, password) // 4) 雷达控制 await bleController.radar.startRadarStream() return { session, info, wifiResult } } finally { // 页面退出或流程结束时清理 await bleController.cleanup() } } ``` ## 5. ED713/ED719 兼容策略 - 相同 UUID 与鉴权主流程统一抽象。 - 差异通过 `profiles.js` 配置: - `radar.notifySignature`: ED713=`0x7c`,ED719=`0x67` - `cmd.supportsNarrowMode`: ED713=true - `cmd.supportsFallParam67`: ED719=true - 未知设备先用 `UNKNOWN`,收到 `F302` 数据后按签名字节自动识别 profile。 ## 6. 修改记录 ### 2026-04-30 对照协议文档第二轮修复 #### commonParser.js - `parseDeviceInfo(payload, profileId)`: 重写为按 ED713/ED719 分路解析 - ED713:MAC[0:6] + bindStatus[6] + 其它[7:10] + productId[10:12] + baseBoard[12] + coreBoard[13] + 其它[14:16] + deviceType[16] + wifiStatus[17] + cellularStatus[18] - ED719:布局与 ED713 一致(bindStatus/productId 偏移相同,文档仅省略标注),复用 `parseEd713DeviceInfo` - `parseEd719Radar`: 修正 `shieldNum` 从 bytes[27] → bytes[24](按文档结构体偏移累加 header[4]+event+people+num+nodata+height+mode+beeper+left+right+front+back+sensitive+nodata1+stateDelay=24) - 新增 `wallOption`、`peopleHaveSwitch`、`peopleActivitySwitch` 字段解析 #### radar.js - `setNarrowMode(enabled)`: 修正命令格式,从单字节 `0x7C`/`0x7D` 改为两字节 `[0x7C, flag]`,flag 0=关 1=开(符合协议文档 CMD=0x7C + 1byte参数) #### auth.js - `readDeviceInfo(options)`: 新增 `msgTimeout` 参数与 4 秒超时保护,防止 UUID_MSG 无回包时页面挂死 - `readDeviceInfo()`: 调用 `parseDeviceInfo` 时传入 `ble.getProfile().id` - `ensureAuthorized`/`ensureAuthorizedByMac`: 透传 `options` 到 `readDeviceInfo` - `writeKeyAndExpectStates`: `rejectOnUnexpected` 从 `true` 改为 `false`,避免设备中间态(-1/0)直接 reject 阻断鉴权 - `ensureAuthorizedWithDeviceInfo`: 移除 `bindStatus === null` 分支(ED719 实际布局与 ED713 一致,bindStatus 可正常读取) #### controller.js - 特征分发器 `createCharacteristicDispatcher` 中 `parseDeviceInfo` 调用传入 `profile.id` #### bleCore.js - `closeAdapter()`: 新增重置 `adapterStateHandler`/`connectionStateHandler`/`characteristicValueHandler`/`deviceFoundHandler` 为 `null`,确保 `openAdapter` 后 `init()` 可重新注册所有原生监听 #### pages/device/config.vue - `openWifiPanel`: `prepareWifiConfig` 超时从 `800000` 改为 `8000`,新增 `msgTimeout: 4000` - `wifiPanelVisible = true` 后加 `await this.$nextTick()` 确保 Vue DOM 更新后再调原生 toast #### hooks/useBluetoothDiscovery.js - 新增 `SCAN_DURATION_MS = 30000`,蓝牙搜索 30 秒后自动停止 - `startSearch`: 搜索开始前同步设置 `isSearching = true`(避免异步导致无法暂停);先 `stopDiscoverySilently()` 再 `resetDeviceMap()`(新搜索刷新数据);启动 30 秒自动停止定时器 - `startSearch`: `stopDiscoverySilently()` 后执行完整适配器重置(`unwatch*` 注销所有原生监听 → `closeAdapter` 关闭适配器 → `initialized = false`),由 `ensureInitialized` 重新打开并注册全部监听,确保每次搜索都从干净适配器状态开始,修复二次搜索找不到设备的问题 - `stopSearch`: 移除 `resetDeviceMap()` 调用,暂停搜索时保留已发现设备数据;清除自动停止定时器 ### 2026-04-29 对照协议文档修复 (初次修复,记录同上,已被 2026-04-30 条目覆盖)