refactor(ble): 移除密钥鉴权流程,改为厂家确认的连接订阅流程
- controller/hook 删除 readMsgOnce 时序前置与 authorizeDeviceByMac 调用, 连接后仅做发现服务、订阅 F301/F302,写 KEY 后由 WiFi 配网走 STATE=3 - 设备操作按钮改为按型号注册表渲染(EP832 仅展示网络配置),其余型号 保留网络配置/参数配置/服务器配置 - 鉴权相关提示文案改为连接/设备未连接语义,与无鉴权流程对齐
This commit is contained in:
parent
388599e840
commit
d1681ee08c
|
|
@ -798,16 +798,10 @@ export function createBluetoothDiscovery(options = {}) {
|
|||
* WiFi配置专用流程:连接 -> 发现 -> 订阅 -> 按 MAC+0000 完成鉴权。
|
||||
*/
|
||||
async function prepareWifiConfig(deviceId, options = {}) {
|
||||
// 厂家确认:无需鉴权、无需读 MSG。
|
||||
// 流程:连接 → 发现服务特征 → 订阅 F301,完成后即可进入 WiFi 配置。
|
||||
await connectDevice(deviceId)
|
||||
|
||||
// 厂家确认:密钥流程已不启用,写 KEY 后设备不回 STATE,鉴权必然超时。
|
||||
// 这里尽力尝试鉴权,失败则忽略并继续,由 WiFi 配网直接走(写 F401 后等 STATE=3)。
|
||||
try {
|
||||
return await authorizeDeviceByMac(options)
|
||||
} catch (error) {
|
||||
console.debug('[BLE][AUTH]', '鉴权失败已忽略(密钥流程已废弃)', error?.message || error)
|
||||
return { mode: 'AUTH_SKIPPED', error: error?.message || String(error) }
|
||||
}
|
||||
return { mode: 'READY', deviceId }
|
||||
}
|
||||
|
||||
async function readDeviceInfo() {
|
||||
|
|
|
|||
|
|
@ -32,12 +32,15 @@
|
|||
</view>
|
||||
<text class="device-id">{{ device.deviceId }}</text>
|
||||
<view class="device-actions">
|
||||
<view class="action-btn" :class="{ disabled: isDevicePreparing(device.deviceId) }"
|
||||
@click.stop="openWifiPanel(device.deviceId)">
|
||||
{{ isDevicePreparing(device.deviceId) ? '鉴权中' : '网络配置' }}
|
||||
<view
|
||||
v-for="action in getDeviceActions(device)"
|
||||
:key="action.key"
|
||||
class="action-btn"
|
||||
:class="{ disabled: isActionBusy(action, device) }"
|
||||
@click.stop="invokeAction(action, device)"
|
||||
>
|
||||
{{ getActionLabel(action, device) }}
|
||||
</view>
|
||||
<view class="action-btn">参数配置</view>
|
||||
<view class="action-btn">服务器配置</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -117,6 +120,30 @@ const WIFI_STATE_MESSAGES = Object.freeze({
|
|||
4: '设备正在连接 WiFi,请稍候'
|
||||
})
|
||||
|
||||
// 设备型号 → 启用的按钮 key 列表。
|
||||
// 后续如需让其他型号裁剪按钮,只需新增映射条目。
|
||||
const DEVICE_ACTION_KEYS = Object.freeze({
|
||||
EP832: ['WIFI'],
|
||||
DEFAULT: ['WIFI', 'PARAMS', 'SERVER']
|
||||
})
|
||||
|
||||
// 按钮注册表:label / 忙态文案 / 点击方法名 / 忙态判定方法名。
|
||||
// 仅声明 key 即可被引用,未配置的方法名渲染为不可点击的占位项。
|
||||
const ACTION_REGISTRY = Object.freeze({
|
||||
WIFI: {
|
||||
label: '网络配置',
|
||||
busyLabel: '连接中',
|
||||
handler: 'openWifiPanel',
|
||||
isBusy: 'isDevicePreparing'
|
||||
},
|
||||
PARAMS: {
|
||||
label: '参数配置'
|
||||
},
|
||||
SERVER: {
|
||||
label: '服务器配置'
|
||||
}
|
||||
})
|
||||
|
||||
function getErrorMessage(error) {
|
||||
if (!error) {
|
||||
return '操作失败,请重试'
|
||||
|
|
@ -253,6 +280,47 @@ export default {
|
|||
return Boolean(this.devicePreparingMap[safeDeviceId])
|
||||
},
|
||||
|
||||
isEp832(device) {
|
||||
const name = String(device?.showName || device?.name || device?.localName || '').toUpperCase()
|
||||
return name.includes('EP832')
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据设备型号返回该设备可见的操作按钮列表。
|
||||
* 列表中的 action 对象包含 key、label、handler、isBusy 等元数据,
|
||||
* 模板里直接 v-for 渲染。
|
||||
*/
|
||||
getDeviceActions(device) {
|
||||
const identifier = this.isEp832(device) ? 'EP832' : 'DEFAULT'
|
||||
const keys = DEVICE_ACTION_KEYS[identifier] || DEVICE_ACTION_KEYS.DEFAULT
|
||||
return keys.map(key => ({ key, ...ACTION_REGISTRY[key] }))
|
||||
},
|
||||
|
||||
isActionBusy(action, device) {
|
||||
if (!action.isBusy) {
|
||||
return false
|
||||
}
|
||||
const checker = this[action.isBusy]
|
||||
return typeof checker === 'function' ? Boolean(checker.call(this, device.deviceId)) : false
|
||||
},
|
||||
|
||||
getActionLabel(action, device) {
|
||||
if (this.isActionBusy(action, device) && action.busyLabel) {
|
||||
return action.busyLabel
|
||||
}
|
||||
return action.label
|
||||
},
|
||||
|
||||
invokeAction(action, device) {
|
||||
if (!action.handler) {
|
||||
return
|
||||
}
|
||||
const handler = this[action.handler]
|
||||
if (typeof handler === 'function') {
|
||||
handler.call(this, device.deviceId)
|
||||
}
|
||||
},
|
||||
|
||||
getDeviceById(deviceId) {
|
||||
const safeDeviceId = String(deviceId || '').trim()
|
||||
if (!safeDeviceId) {
|
||||
|
|
@ -314,7 +382,7 @@ export default {
|
|||
}
|
||||
|
||||
if (this.activePreparingDeviceId && this.activePreparingDeviceId !== targetDeviceId) {
|
||||
showToast('正在为其他设备鉴权,请稍候')
|
||||
showToast('正在为其他设备准备,请稍候')
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -331,27 +399,21 @@ export default {
|
|||
this.setDevicePreparing(targetDeviceId, true)
|
||||
|
||||
uni.showLoading({
|
||||
title: '密钥鉴权中',
|
||||
title: '连接设备中',
|
||||
mask: true
|
||||
})
|
||||
|
||||
let authError = null
|
||||
let prepError = null
|
||||
try {
|
||||
/**
|
||||
* 先按项目固定已知密钥规则(MAC+0000)完成鉴权,成功后再允许进入 WiFi 弹层。
|
||||
*/
|
||||
await this.discoveryController.prepareWifiConfig(targetDeviceId, {
|
||||
suffix: '0000',
|
||||
timeout: 8000,
|
||||
msgTimeout: 4000
|
||||
})
|
||||
// 厂家确认流程:连接 → 发现服务特征 → 订阅 F301,无需鉴权/读MSG。
|
||||
await this.discoveryController.prepareWifiConfig(targetDeviceId)
|
||||
|
||||
this.wifiPrepared = true
|
||||
this.wifiPanelVisible = true
|
||||
// 确保 Vue 完成 DOM 更新后再调原生 toast,避免原生层覆盖 Vue 弹层
|
||||
await this.$nextTick()
|
||||
} catch (error) {
|
||||
authError = error
|
||||
prepError = error
|
||||
await this.safeDisconnect()
|
||||
} finally {
|
||||
this.setDevicePreparing(targetDeviceId, false)
|
||||
|
|
@ -362,16 +424,16 @@ export default {
|
|||
uni.hideLoading()
|
||||
}
|
||||
|
||||
if (authError) {
|
||||
// 鉴权失败用模态弹窗,需用户主动确认,确保错误诊断信息可见可读
|
||||
if (prepError) {
|
||||
// 连接/订阅失败用模态弹窗,需用户主动确认,确保错误诊断信息可见可读
|
||||
uni.showModal({
|
||||
title: '密钥鉴权失败',
|
||||
content: getErrorMessage(authError),
|
||||
title: '设备连接失败',
|
||||
content: getErrorMessage(prepError),
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
})
|
||||
} else if (this.wifiPanelVisible) {
|
||||
showToast('密钥鉴权成功,请填写WiFi信息')
|
||||
showToast('设备已连接,请填写WiFi信息')
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -463,7 +525,7 @@ export default {
|
|||
}
|
||||
|
||||
if (!this.wifiPrepared) {
|
||||
showToast('请先完成密钥鉴权')
|
||||
showToast('设备未连接,请重新配置')
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,42 +9,6 @@ import {
|
|||
} from '../protocols/profiles';
|
||||
import { uuidEquals } from '../utils/uuid';
|
||||
|
||||
/**
|
||||
* 订阅 F301 前先读一次 UUID_MSG(F501),满足厂家强制时序。
|
||||
* 先注册 protocol:msg 监听再触发读取,避免回调早于监听丢失数据。
|
||||
*/
|
||||
async function readMsgOnce(ble, chars, timeout = 4000) {
|
||||
if (!chars || !chars.msg || !chars.msg.uuid) {
|
||||
console.debug('[BLE][MSG]', 'readMsgOnce skipped: no MSG characteristic');
|
||||
return null;
|
||||
}
|
||||
|
||||
let timer = null;
|
||||
let off = null;
|
||||
try {
|
||||
const payload = await new Promise((resolve, reject) => {
|
||||
const finish = (cb) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
if (off) off();
|
||||
cb();
|
||||
};
|
||||
off = ble.on('protocol:msg', (data) => finish(() => resolve(data)));
|
||||
timer = setTimeout(
|
||||
() => finish(() => reject(new Error('读取 UUID_MSG 超时'))),
|
||||
timeout
|
||||
);
|
||||
ble.readCharacteristic(chars.msg.uuid).catch((e) => finish(() => reject(e)));
|
||||
});
|
||||
const info = parseDeviceInfo(payload.value, ble.getProfile().id);
|
||||
console.debug('[BLE][MSG]', 'readMsgOnce ok', JSON.stringify(info));
|
||||
return info;
|
||||
} catch (e) {
|
||||
// 读 MSG 失败不阻断后续(仅作时序前置),但记录日志便于排查
|
||||
console.debug('[BLE][MSG]', 'readMsgOnce failed', e?.message || e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建特征值通知分发器。
|
||||
* 根据特征UUID将通知分发到对应协议事件:state / msg / radar / raw。
|
||||
|
|
@ -193,22 +157,17 @@ export function createUnifiedBleController(options = {}) {
|
|||
}
|
||||
|
||||
/**
|
||||
* 连接后严格执行协议前置(厂家要求顺序):
|
||||
* 连接后执行协议前置(厂家确认流程):
|
||||
* 1) 发现服务和特征
|
||||
* 2) 先读取 UUID_MSG(F501) —— 必须在订阅 F301 之前
|
||||
* 3) 订阅 UUID_STATE(F301) / UUID_RADAR(F302)
|
||||
* 2) 订阅 UUID_STATE(F301) / UUID_RADAR(F302)
|
||||
*
|
||||
* 厂家说明:订阅 F301 前必须先读 F501,否则后续设备不回 STATE 通知,
|
||||
* 表现为写 KEY/WIFI 后 F301 零回执、等待超时。
|
||||
* 厂家确认:无需读 MSG、无需鉴权,订阅 F301 后直接写 F401 配网即可。
|
||||
*/
|
||||
async function connectAndDiscover(deviceId) {
|
||||
await ble.connect(deviceId);
|
||||
await ble.discoverServices(deviceId);
|
||||
const chars = await ble.discoverCharacteristics();
|
||||
|
||||
// 先读 UUID_MSG(F501),再订阅 F301 —— 厂家强制顺序
|
||||
await readMsgOnce(ble, chars);
|
||||
|
||||
if (chars.state && chars.state.uuid) {
|
||||
// 打印 STATE 特征属性,区分 notify / indicate,定位通知不投递问题
|
||||
console.debug('[BLE][SUB]', 'STATE props', JSON.stringify(chars.state.properties || {}));
|
||||
|
|
|
|||
Loading…
Reference in New Issue