feat(ble): 支持多种蓝牙服务UUID并优化调试日志
- 新增 FFF4 服务UUID兼容性支持,适配不同固件版本 - 重构服务发现逻辑,支持多候选UUID匹配 - 添加详细的蓝牙协议状态调试日志 - 优化WiFi配置模块的日志输出和预延时机制 - 更新设备发现过滤器以支持新UUID格式 - 添加AGENTS.md和CLAUDE.md项目指导文件 - 重构认证模块增加调试日志功能
This commit is contained in:
parent
bcf2285b65
commit
099349258b
|
|
@ -0,0 +1,67 @@
|
||||||
|
# Project Overview
|
||||||
|
|
||||||
|
This is a uni-app Vue3 smart home (智慧养老) application using uview-plus component library.
|
||||||
|
|
||||||
|
- Use the new features of es6
|
||||||
|
- Use the uniapp framework for small program development
|
||||||
|
- Added uview-plus component as UI component
|
||||||
|
|
||||||
|
# Build & Run Commands
|
||||||
|
|
||||||
|
Install dependencies with `npm install`.
|
||||||
|
|
||||||
|
This repository does not define npm scripts; builds and previews are normally run through HBuilderX. Use HBuilderX for the active target platform:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Typical local workflow:
|
||||||
|
- Open the project in HBuilderX and run to WeChat Mini Program or Android emulator/device.
|
||||||
|
- Use the built-in packaging flow for release builds.
|
||||||
|
|
||||||
|
# Architecture
|
||||||
|
|
||||||
|
## Module Organization
|
||||||
|
App entry files live at the repository root: `main.js`, `App.vue`, `pages.json`, and `manifest.json`. Feature pages are under `pages/` (`login`, `index`, `event`, `mine`, `device`). Shared business code is split across `api/`, `constants/`, `hooks/`, and `utils/`. Bluetooth logic is centralized in `utils/ble/`, with subfolders for `core/`, `modules/`, `parsers/`, `protocols/`, and `utils/`. Static assets go in `static/`; design and protocol notes live in `docs/`.
|
||||||
|
|
||||||
|
## BLE Module (`utils/ble/`)
|
||||||
|
If you are unclear about the functions of the BLE Module, please refer to the `docs/unified-ble-flow.md` file.
|
||||||
|
|
||||||
|
Unified Bluetooth Low Energy module for ED713/ED719 devices. Key files:
|
||||||
|
|
||||||
|
- `core/bleCore.js` - Bluetooth core capabilities (connect/discover/read/write/notify/reconnect)
|
||||||
|
- `core/controller.js` - Standard flow orchestration (scan→connect→discover→auth)
|
||||||
|
- `core/packet.js` - A7 packet fragmentation
|
||||||
|
- `modules/auth.js` - Authentication flow (read MSG + write KEY)
|
||||||
|
- `modules/wifi.js` - WiFi configuration
|
||||||
|
- `modules/radar.js` - Radar control (start/stop/params)
|
||||||
|
- `protocols/profiles.js` - ED713/ED719 device profile differences
|
||||||
|
|
||||||
|
The unified flow: `standardConnectFlow({ deviceId, key })` → `wifi.configureWifi(ssid, password)` → `radar.startRadarStream()`
|
||||||
|
|
||||||
|
Profile differences:
|
||||||
|
- ED713: radar signature `0x7C`, supports narrow mode
|
||||||
|
- ED719: radar signature `0x67`, supports fall param 67
|
||||||
|
|
||||||
|
If modifications are made to the BLE Module, they shall be added to the `docs/unified-ble-flow.md` file!!
|
||||||
|
|
||||||
|
## Authentication (`utils/auth.js`)
|
||||||
|
|
||||||
|
Route guard system with `setupAuthInterceptors()`. Currently `AUTH_ENABLED = false` for development. Routes are defined in `constants/routes.js`.
|
||||||
|
|
||||||
|
## Pages
|
||||||
|
|
||||||
|
- `pages/login/index.vue` - Login page
|
||||||
|
- `pages/index/index.vue` - Home (tab)
|
||||||
|
- `pages/event/index.vue` - Events (tab)
|
||||||
|
- `pages/mine/index.vue` - Profile (tab)
|
||||||
|
- `pages/device/add.vue` - Add device
|
||||||
|
- `pages/device/config.vue` - Device config with BLE search + WiFi panel
|
||||||
|
|
||||||
|
## Component Auto-import
|
||||||
|
|
||||||
|
uview-plus components auto-imported via `pages.json` easycom config: `^u-(.*)` → `uview-plus/components/u-$1/u-$1.vue`
|
||||||
|
|
||||||
|
## Coding Style & Naming Conventions
|
||||||
|
Follow the existing Vue SFC structure: `<template>`, `<script>`, `<style>`. JavaScript and Vue blocks use 2-space indentation; keep generated JSON files (`pages.json`, `manifest.json`) consistent with the existing HBuilderX formatting. Use `camelCase` for variables and functions, `UPPER_SNAKE_CASE` for constants, and keep page paths aligned with route names such as `pages/device/config.vue`. Prefer small reusable utilities in `utils/` or `hooks/` over duplicating BLE or navigation logic.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Commit & Pull Request Guidelines
|
||||||
|
Pull requests should include a short summary, impacted pages or modules, manual test steps, linked issues when available, and screenshots for UI changes. For BLE-related changes, call out protocol assumptions, permissions touched, and any platform-specific risk.
|
||||||
|
|
||||||
|
# Security & Configuration Tips
|
||||||
|
Do not commit production secrets, API tokens, or real device credentials. Review `manifest.json` permission changes carefully, since they affect Android packaging and device capabilities.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Testing Guidelines
|
||||||
|
No automated test framework is configured yet. Validate changes with manual testing on the affected target, especially BLE discovery, authentication, and Wi-Fi configuration flows in `pages/device/config.vue` and `utils/ble/`. When submitting changes, include clear reproduction and verification steps, plus device model notes if behavior differs between ED713 and ED719.
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Repository Guidelines
|
||||||
|
|
||||||
|
This file provides guidance to Codex when working with code in this repository.
|
||||||
|
|
||||||
|
@.claude/rules/frontend.md
|
||||||
|
@.claude/rules/git-workflow.md
|
||||||
|
@.claude/rules/test.md
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
@.claude/rules/frontend.md
|
||||||
|
@.claude/rules/git-workflow.md
|
||||||
|
@.claude/rules/test.md
|
||||||
|
|
@ -1,15 +1,23 @@
|
||||||
/**
|
/**
|
||||||
* 目标服务 UUID(用于优先识别特定设备)。
|
* 目标服务 UUID(用于优先识别特定设备)。
|
||||||
* 协议文档为 0x00F4,部分固件实现会使用 0xF400。
|
* 协议文档主服务是 0x00F4,部分固件会使用 0xF400 / 0xFFF4。
|
||||||
*/
|
*/
|
||||||
export const TARGET_SERVICE_UUID = '00F4'
|
export const TARGET_SERVICE_UUID = '00F4'
|
||||||
export const TARGET_SERVICE_UUID_FULL = '000000F4-0000-1000-8000-00805F9B34FB'
|
export const TARGET_SERVICE_UUID_FULL = '000000F4-0000-1000-8000-00805F9B34FB'
|
||||||
export const TARGET_SERVICE_UUID_ALT = 'F400'
|
export const TARGET_SERVICE_UUID_ALT = 'F400'
|
||||||
export const TARGET_SERVICE_UUID_ALT_FULL = '0000F400-0000-1000-8000-00805F9B34FB'
|
export const TARGET_SERVICE_UUID_ALT_FULL = '0000F400-0000-1000-8000-00805F9B34FB'
|
||||||
|
export const TARGET_SERVICE_UUID_COMPAT = 'FFF4'
|
||||||
|
export const TARGET_SERVICE_UUID_COMPAT_FULL = '0000FFF4-0000-1000-8000-00805F9B34FB'
|
||||||
|
|
||||||
export const TARGET_SHORT_UUID = '00F4'
|
export const TARGET_SHORT_UUID = TARGET_SERVICE_UUID
|
||||||
|
export const TARGET_SHORT_UUID_CANDIDATES = Object.freeze([
|
||||||
|
TARGET_SERVICE_UUID,
|
||||||
|
TARGET_SERVICE_UUID_ALT,
|
||||||
|
TARGET_SERVICE_UUID_COMPAT
|
||||||
|
])
|
||||||
|
|
||||||
export const DISCOVERY_FILTER_SERVICE_UUIDS = Object.freeze([
|
export const DISCOVERY_FILTER_SERVICE_UUIDS = Object.freeze([
|
||||||
|
TARGET_SERVICE_UUID_COMPAT_FULL,
|
||||||
TARGET_SERVICE_UUID_FULL,
|
TARGET_SERVICE_UUID_FULL,
|
||||||
TARGET_SERVICE_UUID_ALT_FULL
|
TARGET_SERVICE_UUID_ALT_FULL
|
||||||
])
|
])
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import {
|
||||||
DISCOVERY_FILTER_SERVICE_UUIDS,
|
DISCOVERY_FILTER_SERVICE_UUIDS,
|
||||||
TARGET_SERVICE_UUID_FULL,
|
TARGET_SERVICE_UUID_FULL,
|
||||||
TARGET_SERVICE_UUID_ALT_FULL,
|
TARGET_SERVICE_UUID_ALT_FULL,
|
||||||
TARGET_SHORT_UUID
|
TARGET_SERVICE_UUID_COMPAT_FULL,
|
||||||
|
TARGET_SHORT_UUID_CANDIDATES
|
||||||
} from '@/constants/bluetooth'
|
} from '@/constants/bluetooth'
|
||||||
import { DEVICE_PROFILE, createUnifiedBleController, shortUuid, uuidInList } from '@/utils/ble'
|
import { DEVICE_PROFILE, createUnifiedBleController, shortUuid, uuidInList } from '@/utils/ble'
|
||||||
import { showToast } from '@/utils/toast'
|
import { showToast } from '@/utils/toast'
|
||||||
|
|
@ -12,7 +13,7 @@ import { showToast } from '@/utils/toast'
|
||||||
const FILTER_SCAN_TIMEOUT_MS = 4000
|
const FILTER_SCAN_TIMEOUT_MS = 4000
|
||||||
const RESTART_SCAN_DELAY_MS = 120
|
const RESTART_SCAN_DELAY_MS = 120
|
||||||
const DISCOVERY_POLL_INTERVAL_MS = 1200
|
const DISCOVERY_POLL_INTERVAL_MS = 1200
|
||||||
const TARGET_NAME_KEYWORDS = Object.freeze(['ED713', '713WQ', 'ED719','EP832'])
|
const TARGET_NAME_KEYWORDS = Object.freeze(['ED713', '713WQ', 'ED719', 'EP832'])
|
||||||
|
|
||||||
function promisifyUniApi(apiName, params = {}) {
|
function promisifyUniApi(apiName, params = {}) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -121,7 +122,7 @@ export function createBluetoothDiscovery(options = {}) {
|
||||||
return DEVICE_PROFILE.ED719
|
return DEVICE_PROFILE.ED719
|
||||||
}
|
}
|
||||||
|
|
||||||
if (text.includes('713WQ')) {
|
if (text.includes('ED713') || text.includes('713WQ')) {
|
||||||
return DEVICE_PROFILE.ED713
|
return DEVICE_PROFILE.ED713
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,11 +177,13 @@ export function createBluetoothDiscovery(options = {}) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return uuidInList(TARGET_SERVICE_UUID_FULL, serviceList)
|
const serviceShortList = serviceList.map((item) => shortUuid(item))
|
||||||
|
|
||||||
|
return uuidInList(TARGET_SERVICE_UUID_COMPAT_FULL, serviceList)
|
||||||
|
|| uuidInList(TARGET_SERVICE_UUID_FULL, serviceList)
|
||||||
|| uuidInList(TARGET_SERVICE_UUID_ALT_FULL, serviceList)
|
|| uuidInList(TARGET_SERVICE_UUID_ALT_FULL, serviceList)
|
||||||
|| serviceList.some((item) => shortUuid(item) === TARGET_SHORT_UUID)
|
|| TARGET_SHORT_UUID_CANDIDATES.some((uuid) => serviceShortList.includes(uuid))
|
||||||
|| adShortUuids.includes(TARGET_SHORT_UUID)
|
|| TARGET_SHORT_UUID_CANDIDATES.some((uuid) => adShortUuids.includes(uuid))
|
||||||
|| adShortUuids.includes('F400')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function bufferToPrintableText(buffer) {
|
function bufferToPrintableText(buffer) {
|
||||||
|
|
@ -401,10 +404,12 @@ export function createBluetoothDiscovery(options = {}) {
|
||||||
})
|
})
|
||||||
|
|
||||||
offProtocolState = controller.ble.on('protocol:state', (payload) => {
|
offProtocolState = controller.ble.on('protocol:state', (payload) => {
|
||||||
|
try { console.debug('[BLE][STATE]', 'code=', payload?.stateCode, 'char=', payload?.characteristicId) } catch (e) {}
|
||||||
onProtocolState(payload)
|
onProtocolState(payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
offProtocolMsg = controller.ble.on('protocol:msg', (payload) => {
|
offProtocolMsg = controller.ble.on('protocol:msg', (payload) => {
|
||||||
|
try { console.debug('[BLE][MSG]', 'len=', payload?.value?.length || 0) } catch (e) {}
|
||||||
onProtocolMsg(payload)
|
onProtocolMsg(payload)
|
||||||
if (payload?.deviceInfo) {
|
if (payload?.deviceInfo) {
|
||||||
lastDeviceInfo = payload.deviceInfo
|
lastDeviceInfo = payload.deviceInfo
|
||||||
|
|
@ -421,6 +426,7 @@ export function createBluetoothDiscovery(options = {}) {
|
||||||
})
|
})
|
||||||
|
|
||||||
offError = controller.ble.on('error', (error) => {
|
offError = controller.ble.on('error', (error) => {
|
||||||
|
try { console.debug('[BLE][ERR]', error?.message || error?.errMsg || String(error)) } catch (e) {}
|
||||||
onError(error)
|
onError(error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -562,7 +568,7 @@ export function createBluetoothDiscovery(options = {}) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备搜索:
|
* 设备搜索:
|
||||||
* 优先按 0x00F4/0xF400 过滤;若无结果自动切到兜底扫描并轮询 getBluetoothDevices。
|
* 优先按 0xFFF4/0x00F4/0xF400 过滤;若无结果自动切到兜底扫描并轮询 getBluetoothDevices。
|
||||||
*/
|
*/
|
||||||
async function startSearch() {
|
async function startSearch() {
|
||||||
if (isSearching) {
|
if (isSearching) {
|
||||||
|
|
@ -751,7 +757,9 @@ export function createBluetoothDiscovery(options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function configureWifi(ssid, password, options = {}) {
|
async function configureWifi(ssid, password, options = {}) {
|
||||||
return controller.wifi.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 })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startRadar() {
|
async function startRadar() {
|
||||||
|
|
|
||||||
|
|
@ -338,8 +338,21 @@ export function createBleCore(options = {}) {
|
||||||
connectionStateHandler = null
|
connectionStateHandler = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getServiceCandidates() {
|
||||||
|
const profileService = currentProfile?.uuids?.service
|
||||||
|
const candidates = [
|
||||||
|
profileService,
|
||||||
|
toFullUuid('00F4'),
|
||||||
|
toFullUuid('F400'),
|
||||||
|
toFullUuid('FFF4')
|
||||||
|
]
|
||||||
|
|
||||||
|
return candidates.filter(Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发现并锁定目标服务(协议要求 0x00F4)。
|
* 发现并锁定目标服务。
|
||||||
|
* 文档主服务为 0x00F4,同时兼容部分固件 0xF400 / 0xFFF4。
|
||||||
*/
|
*/
|
||||||
async function discoverServices(deviceId = state.deviceId) {
|
async function discoverServices(deviceId = state.deviceId) {
|
||||||
if (!deviceId) {
|
if (!deviceId) {
|
||||||
|
|
@ -349,10 +362,11 @@ export function createBleCore(options = {}) {
|
||||||
try {
|
try {
|
||||||
const result = await promisifyUniApi('getBLEDeviceServices', { deviceId })
|
const result = await promisifyUniApi('getBLEDeviceServices', { deviceId })
|
||||||
const services = result.services || []
|
const services = result.services || []
|
||||||
const targetService = services.find((service) => uuidEquals(service.uuid, currentProfile.uuids.service))
|
const candidates = getServiceCandidates()
|
||||||
|
const targetService = services.find((service) => candidates.some((uuid) => uuidEquals(service.uuid, uuid)))
|
||||||
|
|
||||||
if (!targetService) {
|
if (!targetService) {
|
||||||
throw createBleError(10004, '设备未暴露目标服务 0x00F4')
|
throw createBleError(10004, '设备未暴露目标服务(00F4/F400/FFF4)')
|
||||||
}
|
}
|
||||||
|
|
||||||
patchState({ serviceId: targetService.uuid })
|
patchState({ serviceId: targetService.uuid })
|
||||||
|
|
|
||||||
|
|
@ -127,11 +127,11 @@ export function createUnifiedBleController(options = {}) {
|
||||||
const chars = await ble.discoverCharacteristics()
|
const chars = await ble.discoverCharacteristics()
|
||||||
|
|
||||||
if (chars.state && chars.state.uuid) {
|
if (chars.state && chars.state.uuid) {
|
||||||
await ble.notifyCharacteristic(chars.state.uuid, true)
|
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) {
|
if (chars.radar && chars.radar.uuid) {
|
||||||
await ble.notifyCharacteristic(chars.radar.uuid, true)
|
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
|
return chars
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ function generateRandomKey(length = 16) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const authLog = (...args) => { try { console.debug('[BLE][AUTH]', ...args) } catch (e) {} }
|
||||||
|
|
||||||
function normalizeMacForKey(macText = '') {
|
function normalizeMacForKey(macText = '') {
|
||||||
return String(macText || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
|
return String(macText || '').replace(/[^0-9a-fA-F]/g, '').toUpperCase()
|
||||||
}
|
}
|
||||||
|
|
@ -63,29 +65,21 @@ async function writePacketsToKeyCharacteristic(ble, keyText) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options = {}) {
|
async function writeKeyAndExpectStates(ble, keyText, expectedStateCodes, options = {}) {
|
||||||
const keyValue = await writePacketsToKeyCharacteristic(ble, keyText)
|
const waitPromise = waitForProtocolState(ble, {
|
||||||
|
|
||||||
const stateEvent = await waitForProtocolState(ble, {
|
|
||||||
allowedCodes: expectedStateCodes,
|
allowedCodes: expectedStateCodes,
|
||||||
rejectOnUnexpected: true,
|
rejectOnUnexpected: true,
|
||||||
timeout: options.timeout || 6000,
|
timeout: options.timeout || 6000,
|
||||||
timeoutMessage: '等待 UUID_STATE 超时',
|
timeoutMessage: '等待 UUID_STATE 超时',
|
||||||
unexpectedMessage: '密钥流程状态异常'
|
unexpectedMessage: '密钥流程状态异常'
|
||||||
})
|
})
|
||||||
|
authLog('STATE wait before writeKey, expect=', expectedStateCodes)
|
||||||
const stateCode = Number(stateEvent.stateCode)
|
const keyValue = await writePacketsToKeyCharacteristic(ble, keyText)
|
||||||
|
authLog('key written, length=', keyValue)
|
||||||
|
const stateCode = Number((await waitPromise).stateCode)
|
||||||
|
authLog('STATE received for KEY=', stateCode)
|
||||||
ble.setStage(BLE_STAGE.AUTHORIZED)
|
ble.setStage(BLE_STAGE.AUTHORIZED)
|
||||||
ble.setAuthState({
|
ble.setAuthState({ keyMatched: true, lastStateCode: stateCode })
|
||||||
keyMatched: true,
|
return { success: true, stateCode, key: keyValue }
|
||||||
lastStateCode: stateCode
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
stateCode,
|
|
||||||
key: keyValue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureAuthorizedWithDeviceInfo(ble, info, keyText, options = {}) {
|
async function ensureAuthorizedWithDeviceInfo(ble, info, keyText, options = {}) {
|
||||||
|
|
|
||||||
|
|
@ -39,44 +39,24 @@ function requireAuthorized(ble) {
|
||||||
* WiFi 配网模块:负责打包发送 ssid|password,并等待 STATE 回执。
|
* WiFi 配网模块:负责打包发送 ssid|password,并等待 STATE 回执。
|
||||||
*/
|
*/
|
||||||
export function createWifiModule(ble) {
|
export function createWifiModule(ble) {
|
||||||
|
function delay(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, Number(ms) || 0))
|
||||||
|
}
|
||||||
|
const log = (...args) => { try { console.debug('[BLE][WiFi]', ...args) } catch (e) {} }
|
||||||
|
|
||||||
async function configureWifi(ssid, password, options = {}) {
|
async function configureWifi(ssid, password, options = {}) {
|
||||||
const profile = ble.getProfile()
|
const profile = ble.getProfile()
|
||||||
const targetSsid = String(ssid || '').trim()
|
const s = String(ssid || '').trim(), p = String(password || '').trim()
|
||||||
const targetPass = String(password || '').trim()
|
validateWifi(profile, s, p); requireAuthorized(ble)
|
||||||
|
const { wifi } = ble.getState().characteristics; if (!wifi || !wifi.uuid) throw new Error('未发现 UUID_WIFI 特征值')
|
||||||
validateWifi(profile, targetSsid, targetPass)
|
const waitPromise = waitForProtocolState(ble, { allowedCodes: WIFI_FINAL_STATE_CODES, rejectOnUnexpected: false, timeout: options.timeout || 12000, timeoutMessage: '等待 WiFi 状态超时' })
|
||||||
requireAuthorized(ble)
|
const pre = options.preDelayMs ?? (ble.getProfile().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 { wifi } = ble.getState().characteristics
|
const sent = await writeA7Payload(ble, wifi.uuid, `${s}|${p}`, profile.packet, { packetInterval: Number(options.packetInterval) || 0 })
|
||||||
if (!wifi || !wifi.uuid) {
|
log('write WiFi done, packets=', sent)
|
||||||
throw new Error('未发现 UUID_WIFI 特征值')
|
const code = Number((await waitPromise).stateCode); log('STATE(final) received for WiFi=', code)
|
||||||
}
|
return { success: code === 3, stateCode: code }
|
||||||
|
|
||||||
const payload = `${targetSsid}|${targetPass}`
|
|
||||||
|
|
||||||
// 协议要求 WiFi 参数按 0xA7 分包发送到 UUID_WIFI(F401)。
|
|
||||||
await writeA7Payload(ble, wifi.uuid, payload, profile.packet, {
|
|
||||||
packetInterval: Number(options.packetInterval) || 0
|
|
||||||
})
|
|
||||||
|
|
||||||
const stateEvent = await waitForProtocolState(ble, {
|
|
||||||
// 仅等待最终态,避免在“连接中(4)”时过早返回导致页面提前结束流程。
|
|
||||||
allowedCodes: WIFI_FINAL_STATE_CODES,
|
|
||||||
rejectOnUnexpected: false,
|
|
||||||
timeout: options.timeout || 12000,
|
|
||||||
timeoutMessage: '等待 WiFi 状态超时'
|
|
||||||
})
|
|
||||||
|
|
||||||
const stateCode = Number(stateEvent.stateCode)
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: stateCode === 3,
|
|
||||||
stateCode
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return { configureWifi, requireAuthorized }
|
||||||
configureWifi,
|
|
||||||
requireAuthorized
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue