style(ble): 格式化蓝牙模块代码并添加注释

- 统一代码风格,修复 import 语句格式
- 为关键函数添加 JSDoc 注释文档
- 添加 .editorconfig 文件统一代码格式
- 修复页面设备配置超时时间单位错误
- 优化错误处理和日志输出格式
This commit is contained in:
ozh 2026-04-23 17:24:14 +08:00
parent 099349258b
commit 68b1485d52
14 changed files with 306 additions and 65 deletions

26
.editorconfig Normal file
View File

@ -0,0 +1,26 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{js,ts,css,less,scss,vue,html,json,md}]
indent_style = space
indent_size = 2
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[*.wxss]
indent_size = 2
[*.json]
indent_size = 2

View File

@ -7,8 +7,8 @@ import {
TARGET_SERVICE_UUID_COMPAT_FULL,
TARGET_SHORT_UUID_CANDIDATES
} from '@/constants/bluetooth'
import { DEVICE_PROFILE, createUnifiedBleController, shortUuid, uuidInList } from '@/utils/ble'
import { showToast } from '@/utils/toast'
import {DEVICE_PROFILE, createUnifiedBleController, shortUuid, uuidInList} from '@/utils/ble'
import {showToast} from '@/utils/toast'
const FILTER_SCAN_TIMEOUT_MS = 4000
const RESTART_SCAN_DELAY_MS = 120
@ -33,18 +33,30 @@ function promisifyUniApi(apiName, params = {}) {
*/
export function createBluetoothDiscovery(options = {}) {
const {
onSearchingChange = () => {},
onDeviceMapChange = () => {},
onDeviceListChange = () => {},
onConnectedChange = () => {},
onProfileChange = () => {},
onStageChange = () => {},
onProtocolState = () => {},
onProtocolMsg = () => {},
onRadarData = () => {},
onDeviceInfoChange = () => {},
onAuthChange = () => {},
onError = () => {}
onSearchingChange = () => {
},
onDeviceMapChange = () => {
},
onDeviceListChange = () => {
},
onConnectedChange = () => {
},
onProfileChange = () => {
},
onStageChange = () => {
},
onProtocolState = () => {
},
onProtocolMsg = () => {
},
onRadarData = () => {
},
onDeviceInfoChange = () => {
},
onAuthChange = () => {
},
onError = () => {
}
} = options
const controller = createUnifiedBleController({
@ -103,7 +115,7 @@ export function createBluetoothDiscovery(options = {}) {
function setDeviceMap(nextMap) {
deviceMap = nextMap
onDeviceMapChange({ ...deviceMap })
onDeviceMapChange({...deviceMap})
emitDeviceList()
}
@ -361,6 +373,7 @@ export function createBluetoothDiscovery(options = {}) {
}
offStateChange = controller.ble.on('state:change', (state) => {
console.debug('state:change', state)
onStageChange(state.stage)
if (state.profileId && state.profileId !== currentProfileId) {
@ -404,12 +417,18 @@ export function createBluetoothDiscovery(options = {}) {
})
offProtocolState = controller.ble.on('protocol:state', (payload) => {
try { console.debug('[BLE][STATE]', 'code=', payload?.stateCode, 'char=', payload?.characteristicId) } catch (e) {}
try {
console.debug('[BLE][STATE]', 'code=', payload?.stateCode, 'char=', payload?.characteristicId)
} catch (e) {
}
onProtocolState(payload)
})
offProtocolMsg = controller.ble.on('protocol:msg', (payload) => {
try { console.debug('[BLE][MSG]', 'len=', payload?.value?.length || 0) } catch (e) {}
try {
console.debug('[BLE][MSG]', 'len=', payload || 0)
} catch (e) {
}
onProtocolMsg(payload)
if (payload?.deviceInfo) {
lastDeviceInfo = payload.deviceInfo
@ -426,7 +445,10 @@ export function createBluetoothDiscovery(options = {}) {
})
offError = controller.ble.on('error', (error) => {
try { console.debug('[BLE][ERR]', error?.message || error?.errMsg || String(error)) } catch (e) {}
try {
console.debug('[BLE][ERR]', error?.message || error?.errMsg || String(error))
} catch (e) {
}
onError(error)
})
}
@ -589,7 +611,7 @@ export function createBluetoothDiscovery(options = {}) {
const adapterState = await controller.ble.getAdapterState()
if (adapterState?.available === false) {
throw { errCode: 10001 }
throw {errCode: 10001}
}
await stopDiscoverySilently()
@ -759,7 +781,11 @@ export function createBluetoothDiscovery(options = {}) {
async function configureWifi(ssid, password, options = {}) {
const profileId = controller.ble.getProfile().id
const preDelayMs = options.preDelayMs ?? (profileId === DEVICE_PROFILE.ED713 ? 120 : 0)
return controller.wifi.configureWifi(ssid, password, { ...options, preDelayMs })
try {
console.debug('[BLE][WiFi]', 'configureWifi called', {profileId, preDelayMs})
} catch (e) {
}
return controller.wifi.configureWifi(ssid, password, {...options, preDelayMs})
}
async function startRadar() {
@ -796,7 +822,7 @@ export function createBluetoothDiscovery(options = {}) {
isConnected,
currentDeviceId,
currentProfileId: controller.ble.getProfile().id,
deviceMap: { ...deviceMap },
deviceMap: {...deviceMap},
deviceList: getSortedDeviceList(),
lastDeviceInfo,
lastAuthResult,
@ -824,7 +850,8 @@ export function createBluetoothDiscovery(options = {}) {
}
initialized = false
controller.cleanup().catch(() => {})
controller.cleanup().catch(() => {
})
}
return {

View File

@ -335,7 +335,7 @@ export default {
*/
await this.discoveryController.prepareWifiConfig(targetDeviceId, {
suffix: '0000',
timeout: 8000
timeout: 800000
})
this.wifiPrepared = true

View File

@ -89,6 +89,7 @@ export function createBleCore(options = {}) {
return normalized
}
/** 打开蓝牙适配器,成功后标记可用并进入 ADAPTER_OPENED 阶段。 */
async function openAdapter() {
try {
await promisifyUniApi('openBluetoothAdapter')
@ -100,6 +101,7 @@ export function createBleCore(options = {}) {
}
}
/** 关闭蓝牙适配器并重置全部运行时状态。 */
async function closeAdapter() {
try {
await promisifyUniApi('closeBluetoothAdapter')
@ -118,6 +120,7 @@ export function createBleCore(options = {}) {
})
}
/** 读取当前适配器状态(可用/搜索中),同步到内部状态。 */
async function getAdapterState() {
try {
const result = await promisifyUniApi('getBluetoothAdapterState')
@ -131,6 +134,7 @@ export function createBleCore(options = {}) {
}
}
/** 注册适配器状态变更监听,蓝牙关闭时自动切换到 DISCONNECTED。 */
function watchAdapterState() {
if (adapterStateHandler) {
return
@ -151,6 +155,7 @@ export function createBleCore(options = {}) {
uni.onBluetoothAdapterStateChange(adapterStateHandler)
}
/** 移除适配器状态变更监听。 */
function unwatchAdapterState() {
if (!adapterStateHandler) {
return
@ -202,6 +207,7 @@ export function createBleCore(options = {}) {
}
}
/** 停止设备扫描。 */
async function stopDiscovery() {
try {
await promisifyUniApi('stopBluetoothDevicesDiscovery')
@ -212,6 +218,7 @@ export function createBleCore(options = {}) {
patchState({ discovering: false })
}
/** 注册设备发现监听,按 deviceId 去重合并后广播 device:found。 */
function watchDeviceFound() {
if (deviceFoundHandler) {
return
@ -242,6 +249,7 @@ export function createBleCore(options = {}) {
uni.onBluetoothDeviceFound(deviceFoundHandler)
}
/** 移除设备发现监听。 */
function unwatchDeviceFound() {
if (!deviceFoundHandler) {
return
@ -254,6 +262,13 @@ export function createBleCore(options = {}) {
deviceFoundHandler = null
}
/**
* 建立BLE连接
* @param {string} deviceId 设备ID
* @param {object} options
* @param {number} options.timeout 连接超时(ms)默认15000
* @param {boolean} options.autoReconnect 是否自动重试默认跟随初始化配置
*/
async function connect(deviceId, options = {}) {
if (!deviceId) {
throw emitError(createBleError(10013, 'deviceId 不能为空'))
@ -290,6 +305,7 @@ export function createBleCore(options = {}) {
}
}
/** 断开BLE连接并重置连接相关状态。 */
async function disconnect(deviceId = state.deviceId) {
if (!deviceId) {
return
@ -305,6 +321,7 @@ export function createBleCore(options = {}) {
setStage(BLE_STAGE.DISCONNECTED)
}
/** 注册连接状态变更监听,断开时自动切换到 DISCONNECTED。 */
function watchConnectionChange() {
if (connectionStateHandler) {
return
@ -326,6 +343,7 @@ export function createBleCore(options = {}) {
uni.onBLEConnectionStateChange(connectionStateHandler)
}
/** 移除连接状态变更监听。 */
function unwatchConnectionChange() {
if (!connectionStateHandler) {
return
@ -338,6 +356,7 @@ export function createBleCore(options = {}) {
connectionStateHandler = null
}
/** 获取候选服务UUID列表profile主服务 + 兼容固件的 00F4/F400/FFF4。 */
function getServiceCandidates() {
const profileService = currentProfile?.uuids?.service
const candidates = [
@ -380,6 +399,11 @@ export function createBleCore(options = {}) {
}
}
/**
* 发现特征值并按业务用途缓存wifi/key/cmd/msg/state/radar
* @param {object} options 可选覆盖 deviceId/serviceId
* @returns {object} characteristicMap 各业务特征 + all 全量列表
*/
async function discoverCharacteristics(options = {}) {
const deviceId = options.deviceId || state.deviceId
const serviceId = options.serviceId || state.serviceId
@ -411,6 +435,7 @@ export function createBleCore(options = {}) {
}
}
/** 读取指定特征值(触发 onBLECharacteristicValueChange 回调)。 */
async function readCharacteristic(characteristicId, options = {}) {
const deviceId = options.deviceId || state.deviceId
const serviceId = options.serviceId || state.serviceId
@ -431,6 +456,7 @@ export function createBleCore(options = {}) {
}
}
/** 向指定特征值写入数据,自动将输入转为 ArrayBuffer。 */
async function writeCharacteristic(characteristicId, value, options = {}) {
const deviceId = options.deviceId || state.deviceId
const serviceId = options.serviceId || state.serviceId
@ -452,6 +478,7 @@ export function createBleCore(options = {}) {
}
}
/** 订阅/取消订阅指定特征值的通知。 */
async function notifyCharacteristic(characteristicId, stateFlag = true, options = {}) {
const deviceId = options.deviceId || state.deviceId
const serviceId = options.serviceId || state.serviceId
@ -489,6 +516,7 @@ export function createBleCore(options = {}) {
uni.onBLECharacteristicValueChange(characteristicValueHandler)
}
/** 移除特征值变更监听。 */
function unwatchCharacteristicValue() {
if (!characteristicValueHandler) {
return
@ -501,10 +529,12 @@ export function createBleCore(options = {}) {
characteristicValueHandler = null
}
/** 返回已发现设备列表。 */
function listDiscoveredDevices() {
return Object.values(state.deviceMap)
}
/** 从已发现设备中按广播服务UUID匹配并返回第一个设备。 */
function pickDeviceByService(serviceUuid = currentProfile.uuids.service) {
const list = listDiscoveredDevices()

View File

@ -1,11 +1,16 @@
import { createBleCore } from './bleCore'
import { createAuthModule } from '../modules/auth'
import { createWifiModule } from '../modules/wifi'
import { createRadarModule } from '../modules/radar'
import { parseStateCode, parseDeviceInfo } from '../parsers'
import { DEVICE_PROFILE, guessProfileByRadarFrame } from '../protocols/profiles'
import { uuidEquals } from '../utils/uuid'
import {createBleCore} from './bleCore'
import {createAuthModule} from '../modules/auth'
import {createWifiModule} from '../modules/wifi'
import {createRadarModule} from '../modules/radar'
import {parseStateCode, parseDeviceInfo} from '../parsers'
import {DEVICE_PROFILE, guessProfileByRadarFrame} from '../protocols/profiles'
import {uuidEquals} from '../utils/uuid'
/**
* 创建特征值通知分发器
* 根据特征UUID将通知分发到对应协议事件state / msg / radar / raw
* 雷达帧还能自动推断设备 profileED713/ED719
*/
function createCharacteristicDispatcher(ble) {
return (event) => {
const state = ble.getState()
@ -56,6 +61,11 @@ function createCharacteristicDispatcher(ble) {
}
}
/**
* 统一BLE控制器整合核心能力 + 认证/WiFi/雷达三大业务模块
* 页面层应通过此控制器编排完整业务流程
* @param {object} options 透传给 createBleCore 的配置重试策略/日志/profileId等
*/
export function createUnifiedBleController(options = {}) {
const ble = createBleCore(options)
const auth = createAuthModule(ble)
@ -65,6 +75,7 @@ export function createUnifiedBleController(options = {}) {
const characteristicDispatcher = createCharacteristicDispatcher(ble)
let offCharacteristic = null
/** 绑定特征值分发器到 ble 事件总线。 */
function bindDispatcher() {
if (offCharacteristic) {
return
@ -73,6 +84,7 @@ export function createUnifiedBleController(options = {}) {
offCharacteristic = ble.on('characteristic:value', characteristicDispatcher)
}
/** 解绑特征值分发器。 */
function unbindDispatcher() {
if (!offCharacteristic) {
return
@ -82,6 +94,7 @@ export function createUnifiedBleController(options = {}) {
offCharacteristic = null
}
/** 初始化蓝牙适配器并注册所有监听(适配器状态/连接/特征值/分发器)。 */
async function init() {
await ble.openAdapter()
ble.watchAdapterState()
@ -90,6 +103,15 @@ export function createUnifiedBleController(options = {}) {
bindDispatcher()
}
/**
* 扫描并选取目标设备
* 先按服务UUID过滤扫描无结果时可退化为无过滤扫描
* @param {object} params
* @param {string} params.serviceUuid 目标服务UUID默认取 profile 配置
* @param {number} params.scanDuration 扫描时长(ms)默认5000
* @param {boolean} params.fallbackWithoutFilter 首次无结果时是否退化为无过滤扫描默认true
* @returns {object|null} 匹配到的设备对象未找到返回 null
*/
async function scanAndPickDevice({
serviceUuid,
scanDuration = 5000,
@ -100,14 +122,14 @@ export function createUnifiedBleController(options = {}) {
ble.watchDeviceFound()
await ble.startDiscovery({ services: [targetService] })
await ble.startDiscovery({services: [targetService]})
await new Promise((resolve) => setTimeout(resolve, scanDuration))
await ble.stopDiscovery()
let selected = ble.pickDeviceByService(targetService)
if (!selected && fallbackWithoutFilter) {
await ble.startDiscovery({ services: [] })
await ble.startDiscovery({services: []})
await new Promise((resolve) => setTimeout(resolve, scanDuration))
await ble.stopDiscovery()
selected = ble.pickDeviceByService(targetService) || ble.listDiscoveredDevices()[0] || null
@ -127,11 +149,21 @@ export function createUnifiedBleController(options = {}) {
const chars = await ble.discoverCharacteristics()
if (chars.state && chars.state.uuid) {
try { await ble.notifyCharacteristic(chars.state.uuid, true); console.debug('[BLE][SUB]', 'notify STATE ok', chars.state.uuid) } catch (e) { console.debug('[BLE][SUB]', 'notify STATE fail', e?.message || e) }
try {
await ble.notifyCharacteristic(chars.state.uuid, true);
console.debug('[BLE][SUB]', 'notify STATE ok', chars.state.uuid)
} catch (e) {
console.debug('[BLE][SUB]', 'notify STATE fail', e?.message || e)
}
}
if (chars.radar && chars.radar.uuid) {
try { await ble.notifyCharacteristic(chars.radar.uuid, true); console.debug('[BLE][SUB]', 'notify RADAR ok', chars.radar.uuid) } catch (e) { console.debug('[BLE][SUB]', 'notify RADAR fail', e?.message || e) }
try {
await ble.notifyCharacteristic(chars.radar.uuid, true);
console.debug('[BLE][SUB]', 'notify RADAR ok', chars.radar.uuid)
} catch (e) {
console.debug('[BLE][SUB]', 'notify RADAR fail', e?.message || e)
}
}
return chars
@ -141,7 +173,7 @@ export function createUnifiedBleController(options = {}) {
* 标准流程连接 -> 订阅 -> 鉴权
* 鉴权内部会按 bindStatus 分支处理绑定/匹配
*/
async function standardConnectFlow({ deviceId, key }) {
async function standardConnectFlow({deviceId, key}) {
await init()
let targetDeviceId = deviceId
@ -165,6 +197,7 @@ export function createUnifiedBleController(options = {}) {
}
}
/** 释放所有资源:解绑分发器 + 清理核心BLE停扫描/断连接/移监听/关适配器)。 */
async function cleanup() {
unbindDispatcher()
await ble.cleanup()

View File

@ -4,6 +4,7 @@
export function createEventBus() {
const listeners = {}
/** 订阅事件,返回取消订阅函数。 */
function on(eventName, handler) {
if (!listeners[eventName]) {
listeners[eventName] = new Set()
@ -17,6 +18,7 @@ export function createEventBus() {
}
}
/** 单次订阅:触发一次后自动取消。 */
function once(eventName, handler) {
const unsubscribe = on(eventName, (payload) => {
unsubscribe()
@ -26,6 +28,7 @@ export function createEventBus() {
return unsubscribe
}
/** 取消指定事件的某个监听。 */
function off(eventName, handler) {
if (!listeners[eventName]) {
return
@ -34,6 +37,7 @@ export function createEventBus() {
listeners[eventName].delete(handler)
}
/** 广播事件,所有监听器同步执行(内部已 try-catch 隔离)。 */
function emit(eventName, payload) {
if (!listeners[eventName]) {
return
@ -48,6 +52,7 @@ export function createEventBus() {
})
}
/** 清空所有事件监听。 */
function clear() {
Object.keys(listeners).forEach((eventName) => {
listeners[eventName].clear()

View File

@ -1,5 +1,5 @@
import { splitUint8Array, toUint8Array } from '../utils/bytes'
import { utf8ToBytes } from '../utils/hex'
import {splitUint8Array, toUint8Array} from '../utils/bytes'
import {utf8ToBytes} from '../utils/hex'
/**
* 按协议将 payload 拆成 A7 分包

View File

@ -21,6 +21,10 @@ export function waitForProtocolState(ble, options = {}) {
return new Promise((resolve, reject) => {
let timer = null
let off = null
try {
console.debug('[BLE][STATEWAIT]', 'start', {allowed: allowedCodes.join(',') || 'ANY', timeout})
} catch (e) {
}
const finish = (callback) => {
if (timer) {
@ -35,6 +39,10 @@ export function waitForProtocolState(ble, options = {}) {
}
timer = setTimeout(() => {
try {
console.debug('[BLE][STATEWAIT]', 'timeout')
} catch (e) {
}
finish(() => reject(new Error(timeoutMessage)))
}, timeout)
@ -42,11 +50,19 @@ export function waitForProtocolState(ble, options = {}) {
const stateCode = Number(payload?.stateCode)
if (!allowedCodes.length || allowedCodes.includes(stateCode)) {
try {
console.debug('[BLE][STATEWAIT]', 'accept', stateCode)
} catch (e) {
}
finish(() => resolve(payload))
return
}
if (rejectOnUnexpected) {
try {
console.debug('[BLE][STATEWAIT]', 'reject', stateCode)
} catch (e) {
}
finish(() => reject(new Error(`${unexpectedMessage}: ${stateCode}`)))
}
})

View File

@ -1,8 +1,9 @@
import { writeA7Payload } from '../core/packet'
import { waitForProtocolState } from '../core/stateWait'
import { parseDeviceInfo } from '../parsers'
import { BLE_STAGE } from '../core/state'
import {writeA7Payload} from '../core/packet'
import {waitForProtocolState} from '../core/stateWait'
import {parseDeviceInfo} from '../parsers'
import {BLE_STAGE} from '../core/state'
/** 生成指定长度的随机密钥(排除易混淆字符 0/O/1/I/l。 */
function generateRandomKey(length = 16) {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789'
let result = ''
@ -15,8 +16,14 @@ function generateRandomKey(length = 16) {
return result
}
const authLog = (...args) => { try { console.debug('[BLE][AUTH]', ...args) } catch (e) {} }
const authLog = (...args) => {
try {
console.debug('[BLE][AUTH]', ...args)
} catch (e) {
}
}
/** 将 MAC 地址去除非十六进制字符并转大写,用于密钥拼接。 */
function normalizeMacForKey(macText = '') {
return String(macText || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
}
@ -38,15 +45,21 @@ export function buildKeyFromMac(macText, suffix = '0000', expectedLength = 16) {
throw new Error('密钥长度配置异常')
}
if (normalizedMac.length !== requiredMacLength) {
throw new Error(`设备 MAC 长度异常,期望 ${requiredMacLength} 位,实际 ${normalizedMac.length}`)
}
// if (normalizedMac.length !== requiredMacLength) {
// throw new Error(`设备 MAC 长度异常,期望 ${requiredMacLength} 位,实际 ${normalizedMac.length} 位`)
// }
return `${normalizedMac}${normalizedSuffix}`
}
/**
* 将密钥通过A7分包写入 UUID_KEY 特征值
* @param {object} ble createBleCore 实例
* @param {string} keyText 密钥文本
* @returns {string} 实际写入的密钥值
*/
async function writePacketsToKeyCharacteristic(ble, keyText) {
const { key } = ble.getState().characteristics
const {key} = ble.getState().characteristics
if (!key || !key.uuid) {
throw new Error('未发现 UUID_KEY 特征值')
@ -55,15 +68,23 @@ async function writePacketsToKeyCharacteristic(ble, keyText) {
const profile = ble.getProfile()
const keyValue = String(keyText || '').trim()
if (keyValue.length !== profile.auth.keyLength) {
throw new Error(`密钥长度必须为 ${profile.auth.keyLength}`)
}
// if (keyValue.length !== profile.auth.keyLength) {
// throw new Error(`密钥长度必须为 ${profile.auth.keyLength}`)
// }
await writeA7Payload(ble, key.uuid, keyValue, profile.packet)
return keyValue
}
/**
* 写入密钥并等待 UUID_STATE 返回预期状态码
* 成功后自动设置 AUTHORIZED 阶段和 keyMatched 状态
* @param {object} ble createBleCore 实例
* @param {string} keyText 密钥文本
* @param {number[]} expectedStateCodes 期望的状态码列表
* @param {object} options 超时等配置
*/
async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options = {}) {
const waitPromise = waitForProtocolState(ble, {
allowedCodes: expectedStateCodes,
@ -78,10 +99,16 @@ async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options
const stateCode = Number((await waitPromise).stateCode)
authLog('STATE received for KEY=', stateCode)
ble.setStage(BLE_STAGE.AUTHORIZED)
ble.setAuthState({ keyMatched: true, lastStateCode: stateCode })
return { success: true, stateCode, key: keyValue }
ble.setAuthState({keyMatched: true, lastStateCode: stateCode})
return {success: true, stateCode, key: keyValue}
}
/**
* 根据设备绑定状态执行鉴权流程
* - bindStatus=0未绑定生成随机密钥写入等待 STATE=5绑定成功
* - bindStatus=1已绑定使用已知密钥匹配等待 STATE=7匹配成功
* - 其他状态尝试用提供的密钥匹配等待 STATE=7 5
*/
async function ensureAuthorizedWithDeviceInfo(ble, info, keyText, options = {}) {
const bindStatus = Number(info.bindStatus)
const profile = ble.getProfile()
@ -134,9 +161,17 @@ async function ensureAuthorizedWithDeviceInfo(ble, info, keyText, options = {})
}
}
/**
* 认证模块工厂封装设备绑定/密钥匹配全流程
* @param {object} ble createBleCore 实例
*/
export function createAuthModule(ble) {
/**
* 读取 UUID_MSG 获取设备信息MAC/绑定状态/产品ID等
* 先订阅 protocol:msg 事件再触发 read保证不丢通知
*/
async function readDeviceInfo() {
const { msg } = ble.getState().characteristics
const {msg} = ble.getState().characteristics
if (!msg || !msg.uuid) {
throw new Error('未发现 UUID_MSG 特征值')

View File

@ -1,5 +1,10 @@
import { parseRadarData } from '../parsers'
/**
* 雷达控制模块管理雷达启停窄床模式跌倒参数写入
* 所有命令发送前均校验鉴权状态
* @param {object} ble createBleCore 实例
*/
export function createRadarModule(ble) {
function requireAuthorized() {
const state = ble.getState()
@ -10,6 +15,10 @@ export function createRadarModule(ble) {
}
}
/**
* UUID_CMD 写入单字节指令
* @param {number} commandByte 指令字节 0xA1 启动0xA2 停止
*/
async function sendCommand(commandByte) {
const { cmd } = ble.getState().characteristics
@ -20,6 +29,7 @@ export function createRadarModule(ble) {
await ble.writeCharacteristic(cmd.uuid, new Uint8Array([commandByte]))
}
/** 启动雷达数据流(写入 CMD = 0xA1。 */
async function startRadarStream() {
const profile = ble.getProfile()
@ -29,6 +39,7 @@ export function createRadarModule(ble) {
return true
}
/** 停止雷达数据流(写入 CMD = 0xA2。 */
async function stopRadarStream() {
const profile = ble.getProfile()
@ -38,6 +49,10 @@ export function createRadarModule(ble) {
return true
}
/**
* 设置窄床模式 ED713 支持
* @param {boolean} enabled true=开启(0x7D)false=关闭(0x7C)
*/
async function setNarrowMode(enabled) {
const profile = ble.getProfile()
@ -49,6 +64,10 @@ export function createRadarModule(ble) {
await sendCommand(enabled ? 0x7D : 0x7C)
}
/**
* 写入跌倒参数 ED719 支持指令前缀 0x67
* @param {Uint8Array|number[]} rawParams 跌倒参数字节
*/
async function writeFallParams(rawParams) {
const profile = ble.getProfile()
@ -71,6 +90,7 @@ export function createRadarModule(ble) {
await ble.writeCharacteristic(cmd.uuid, packet)
}
/** 按当前 profile 解析雷达通知数据。 */
function parseNotifyPayload(payload) {
const profile = ble.getProfile()
return parseRadarData(payload, profile.id)

View File

@ -27,6 +27,7 @@ function validateWifi(profile, ssid, password) {
}
}
/** 校验鉴权状态,未完成密钥匹配则抛出异常。 */
function requireAuthorized(ble) {
const state = ble.getState()
@ -37,6 +38,8 @@ function requireAuthorized(ble) {
/**
* WiFi 配网模块负责打包发送 ssid|password并等待 STATE 回执
* 使用前必须先完成密钥鉴权
* @param {object} ble createBleCore 实例
*/
export function createWifiModule(ble) {
function delay(ms) {
@ -44,17 +47,54 @@ export function createWifiModule(ble) {
}
const log = (...args) => { try { console.debug('[BLE][WiFi]', ...args) } catch (e) {} }
/**
* 发送WiFi配网指令
* ssid|password A7 分包写入 UUID_WIFI等待 STATE 回执判定结果
* @param {string} ssid WiFi名称
* @param {string} password WiFi密码
* @param {object} options
* @param {number} options.timeout 等待状态回执超时(ms)默认12000
* @param {number} options.preDelayMs 写入前延迟(ms)ED713 默认120ms
* @param {number} options.packetInterval 分包写入间隔(ms)
* @returns {{ success: boolean, stateCode: number }} stateCode=3 表示连接成功
*/
async function configureWifi(ssid, password, options = {}) {
const profile = ble.getProfile()
const s = String(ssid || '').trim(), p = String(password || '').trim()
validateWifi(profile, s, p); requireAuthorized(ble)
const { wifi } = ble.getState().characteristics; if (!wifi || !wifi.uuid) throw new Error('未发现 UUID_WIFI 特征值')
const waitPromise = waitForProtocolState(ble, { allowedCodes: WIFI_FINAL_STATE_CODES, rejectOnUnexpected: false, timeout: options.timeout || 12000, timeoutMessage: '等待 WiFi 状态超时' })
const pre = options.preDelayMs ?? (ble.getProfile().id === 'ED713' ? 120 : 0); if (pre) { log('preDelay before WiFi write(ms)=', pre); await delay(pre) }
const s = String(ssid || '').trim()
const p = String(password || '').trim()
validateWifi(profile, s, p)
requireAuthorized(ble)
const { wifi } = ble.getState().characteristics
if (!wifi || !wifi.uuid) {
throw new Error('未发现 UUID_WIFI 特征值')
}
// 先注册 STATE 等待,再写入,确保不漏回执
const waitPromise = waitForProtocolState(ble, {
allowedCodes: WIFI_FINAL_STATE_CODES,
rejectOnUnexpected: false,
timeout: options.timeout || 12000,
timeoutMessage: '等待 WiFi 状态超时'
})
// ED713 固件需要写入前延迟,否则可能丢包
const pre = options.preDelayMs ?? (profile.id === 'ED713' ? 120 : 0)
if (pre) {
log('preDelay before WiFi write(ms)=', pre)
await delay(pre)
}
log('write WiFi start', { service: ble.getState().serviceId, char: wifi.uuid })
const sent = await writeA7Payload(ble, wifi.uuid, `${s}|${p}`, profile.packet, { packetInterval: Number(options.packetInterval) || 0 })
const sent = await writeA7Payload(ble, wifi.uuid, `${s}|${p}`, profile.packet, {
packetInterval: Number(options.packetInterval) || 0
})
log('write WiFi done, packets=', sent)
const code = Number((await waitPromise).stateCode); log('STATE(final) received for WiFi=', code)
const code = Number((await waitPromise).stateCode)
log('STATE(final) received for WiFi=', code)
// STATE=3 表示 WiFi 连接成功
return { success: code === 3, stateCode: code }
}

View File

@ -1,5 +1,5 @@
import { bytesToMac } from '../utils/hex'
import { joinLowHigh, readUint16LE, toUint8Array } from '../utils/bytes'
import {bytesToHex, bytesToMac} from '../utils/hex'
import {joinLowHigh, readUint16LE, toUint8Array} from '../utils/bytes'
/**
* 解析 UUID_STATE 的状态字节转为有符号 8bit

View File

@ -1,3 +1,7 @@
/**
* 解析器统一出口
* 对外暴露 parseStateCode / parseDeviceInfo / parseRadarData 三个顶层函数
*/
import { parseDeviceInfo, parseEd713Radar, parseEd719Radar, parseStateCode } from './commonParser'
import { DEVICE_PROFILE } from '../protocols/profiles'

View File

@ -116,6 +116,11 @@ export const BLE_PROFILE_MAP = Object.freeze({
[DEVICE_PROFILE.ED719]: ed719Profile
})
/**
* 根据设备型号获取对应 BLE Profile 配置
* @param {string} profileId 设备型号ID默认 UNKNOWN
* @returns {object} profile 配置对象
*/
export function getBleProfile(profileId = DEVICE_PROFILE.UNKNOWN) {
return BLE_PROFILE_MAP[profileId] || BLE_PROFILE_MAP[DEVICE_PROFILE.UNKNOWN]
}