131 lines
4.9 KiB
JavaScript
131 lines
4.9 KiB
JavaScript
import { writeA7Payload } from '../core/packet'
|
||
import { waitForProtocolState } from '../core/stateWait'
|
||
|
||
const WIFI_FINAL_STATE_CODES = Object.freeze([1, 2, 3])
|
||
|
||
// STATE=4 为 WiFi 连接中中间态,收到后用于刷新等待窗口
|
||
const WIFI_CONNECTING_STATE_CODES = Object.freeze([4])
|
||
|
||
// 总等待上限:覆盖弱信号/5G/企业级路由真实 DHCP 时长
|
||
const WIFI_TOTAL_TIMEOUT_MS = 60000
|
||
// 收到 STATE=4 后的单次刷新窗口
|
||
const WIFI_REFRESH_TIMEOUT_MS = 30000
|
||
// ED713 分包写入间隔:连续 writeWithoutResponse 易丢包,固件需留处理时间
|
||
const ED713_PACKET_INTERVAL_MS = 50
|
||
|
||
/**
|
||
* 校验 WiFi 入参,规则来源于 profile.wifi。
|
||
*/
|
||
function validateWifi(profile, ssid, password) {
|
||
const errors = []
|
||
|
||
if (!ssid || ssid.length > profile.wifi.maxSsidLength) {
|
||
errors.push(`SSID 长度需在 1-${profile.wifi.maxSsidLength} 之间`)
|
||
}
|
||
|
||
if (password.length < profile.wifi.minPasswordLength || password.length > profile.wifi.maxPasswordLength) {
|
||
errors.push(`密码长度需在 ${profile.wifi.minPasswordLength}-${profile.wifi.maxPasswordLength} 之间`)
|
||
}
|
||
|
||
const invalidChars = profile.wifi.invalidChars || []
|
||
if (invalidChars.some((char) => ssid.includes(char))) {
|
||
errors.push(`SSID 不能包含特殊字符: ${invalidChars.join(' ')}`)
|
||
}
|
||
|
||
if (errors.length) {
|
||
throw new Error(errors.join(';'))
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 校验鉴权状态。
|
||
* 厂家确认密钥流程已不启用,此处仅记录未鉴权状态,不阻断 WiFi 配置。
|
||
*/
|
||
function requireAuthorized(ble) {
|
||
const state = ble.getState()
|
||
|
||
if (!state.auth || state.auth.keyMatched !== true) {
|
||
console.debug('[BLE][WiFi]', '密钥未匹配(密钥流程已废弃,继续配网)')
|
||
}
|
||
}
|
||
|
||
/**
|
||
* WiFi 配网模块:负责打包发送 ssid|password,并等待 STATE 回执。
|
||
* 使用前必须先完成密钥鉴权。
|
||
* @param {object} ble createBleCore 实例
|
||
*/
|
||
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) {} }
|
||
|
||
/**
|
||
* 发送WiFi配网指令。
|
||
* 将 ssid|password 按 A7 分包写入 UUID_WIFI,等待 STATE 回执判定结果。
|
||
* @param {string} ssid WiFi名称
|
||
* @param {string} password WiFi密码
|
||
* @param {object} options
|
||
* @param {number} options.timeout 等待状态回执总超时(ms),默认60000
|
||
* @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()
|
||
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 特征值')
|
||
}
|
||
|
||
const totalTimeout = options.timeout || WIFI_TOTAL_TIMEOUT_MS
|
||
// 先注册 STATE 等待,再写入,确保不漏回执
|
||
// 收到 STATE=4(连接中) 时刷新窗口,避免固定上限短于真实 DHCP 时长误判超时
|
||
const waitPromise = waitForProtocolState(ble, {
|
||
allowedCodes: WIFI_FINAL_STATE_CODES,
|
||
rejectOnUnexpected: false,
|
||
timeout: totalTimeout,
|
||
timeoutMessage: '等待 WiFi 状态超时',
|
||
refreshOnCodes: WIFI_CONNECTING_STATE_CODES,
|
||
refreshTimeout: WIFI_REFRESH_TIMEOUT_MS
|
||
})
|
||
|
||
// 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 packetInterval = Number(options.packetInterval) || (profile.id === 'ED713' ? ED713_PACKET_INTERVAL_MS : 0)
|
||
const sent = await writeA7Payload(ble, wifi.uuid, `${s}|${p}`, profile.packet, {
|
||
packetInterval
|
||
})
|
||
log('write WiFi done, packets=', sent, 'interval=', packetInterval)
|
||
|
||
let code
|
||
try {
|
||
code = Number((await waitPromise).stateCode)
|
||
} catch (error) {
|
||
// 超时时带上设备最后上报的 STATE 码,便于区分"设备未响应"与"设备回了非预期态"
|
||
const lastState = ble.getState().auth?.lastStateCode
|
||
const hint = (lastState === undefined || lastState === null)
|
||
? '(设备未上报任何 STATE,可能 WiFi 分包写入丢失或固件未处理)'
|
||
: `(设备最后上报 STATE=${lastState},未收到最终态 1/2/3)`
|
||
throw new Error(`${error.message}${hint}`)
|
||
}
|
||
log('STATE(final) received for WiFi=', code)
|
||
|
||
// STATE=3 表示 WiFi 连接成功
|
||
return { success: code === 3, stateCode: code }
|
||
}
|
||
|
||
return { configureWifi, requireAuthorized }
|
||
}
|