style(ble): 格式化蓝牙模块代码并添加注释
- 统一代码风格,修复 import 语句格式 - 为关键函数添加 JSDoc 注释文档 - 添加 .editorconfig 文件统一代码格式 - 修复页面设备配置超时时间单位错误 - 优化错误处理和日志输出格式
This commit is contained in:
parent
099349258b
commit
68b1485d52
|
|
@ -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
|
||||
|
|
@ -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({
|
||||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
@ -759,6 +781,10 @@ 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)
|
||||
try {
|
||||
console.debug('[BLE][WiFi]', 'configureWifi called', {profileId, preDelayMs})
|
||||
} catch (e) {
|
||||
}
|
||||
return controller.wifi.configureWifi(ssid, password, {...options, preDelayMs})
|
||||
}
|
||||
|
||||
|
|
@ -824,7 +850,8 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
}
|
||||
|
||||
initialized = false
|
||||
controller.cleanup().catch(() => {})
|
||||
controller.cleanup().catch(() => {
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -335,7 +335,7 @@ export default {
|
|||
*/
|
||||
await this.discoveryController.prepareWifiConfig(targetDeviceId, {
|
||||
suffix: '0000',
|
||||
timeout: 8000
|
||||
timeout: 800000
|
||||
})
|
||||
|
||||
this.wifiPrepared = true
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ import { parseStateCode, parseDeviceInfo } from '../parsers'
|
|||
import {DEVICE_PROFILE, guessProfileByRadarFrame} from '../protocols/profiles'
|
||||
import {uuidEquals} from '../utils/uuid'
|
||||
|
||||
/**
|
||||
* 创建特征值通知分发器。
|
||||
* 根据特征UUID将通知分发到对应协议事件:state / msg / radar / raw。
|
||||
* 雷达帧还能自动推断设备 profile(ED713/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,
|
||||
|
|
@ -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
|
||||
|
|
@ -165,6 +197,7 @@ export function createUnifiedBleController(options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
/** 释放所有资源:解绑分发器 + 清理核心BLE(停扫描/断连接/移监听/关适配器)。 */
|
||||
async function cleanup() {
|
||||
unbindDispatcher()
|
||||
await ble.cleanup()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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}`)))
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ 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,13 +45,19 @@ 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
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
@ -82,6 +103,12 @@ async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options
|
|||
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,7 +161,15 @@ 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { bytesToMac } from '../utils/hex'
|
||||
import {bytesToHex, bytesToMac} from '../utils/hex'
|
||||
import {joinLowHigh, readUint16LE, toUint8Array} from '../utils/bytes'
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
/**
|
||||
* 解析器统一出口。
|
||||
* 对外暴露 parseStateCode / parseDeviceInfo / parseRadarData 三个顶层函数。
|
||||
*/
|
||||
import { parseDeviceInfo, parseEd713Radar, parseEd719Radar, parseStateCode } from './commonParser'
|
||||
import { DEVICE_PROFILE } from '../protocols/profiles'
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue