文件
jw-beauty/miniprogram/services/ble/commands.js
T
Guoguo ec5b8584ec refactor: remove 17-byte FFE4 and 33-byte FFE1 legacy compat code
- Remove parseVendorStatus 17-byte branch (FFE4 is now 19 bytes)
- Remove parseVendorStatus 33-byte branch (FFE1 is now 34 bytes)
- Remove buildVendorCommand33 deprecated alias
- Remove fitting/run_state !== -1 guards in connection.js
- Update commands.js to use buildVendorCommand directly
2026-06-24 05:13:36 -07:00

247 行
7.3 KiB
JavaScript

// BLE commands: writing commands and high-level device operations
var protocol = require('./protocol')
var connection = require('./connection')
var _cmdSeq = 0
var _pendingAcks = {}
function nextSeq() {
_cmdSeq = (_cmdSeq + 1) % 256
return _cmdSeq
}
function getPendingAcks() {
return _pendingAcks
}
function clearPendingAcks() {
_pendingAcks = {}
}
// --- low-level write ---
function writeCommand(type, payload) {
return new Promise(function (resolve, reject) {
if (!connection.isConnected()) {
reject({ error_code: 0x0C, error_msg: 'ERR_BLE_DISCONNECTED' })
return
}
var chars = connection.getChars()
if (!chars.command) {
reject({ error_code: 0xFF, error_msg: 'command characteristic not found' })
return
}
var seq = nextSeq()
var payloadWithSeq = (payload || []).concat([seq])
var buffer = protocol.buildFrame(type, payloadWithSeq)
_pendingAcks[seq] = { resolve: resolve, reject: reject }
setTimeout(function () {
if (_pendingAcks[seq]) {
_pendingAcks[seq].reject({ error_code: 0xFF, error_msg: 'ACK timeout' })
delete _pendingAcks[seq]
}
}, 5000)
var deviceId = connection.getDeviceId()
wx.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: chars.command.serviceId,
characteristicId: chars.command.uuid,
value: buffer,
success: function () {},
fail: function () {
delete _pendingAcks[seq]
reject({ error_code: 0x0C, error_msg: 'ERR_BLE_DISCONNECTED' })
}
})
})
}
function writeRawCommand(bytes) {
return new Promise(function (resolve, reject) {
if (!connection.isConnected()) {
reject({ error_code: 0x0C, error_msg: 'ERR_BLE_DISCONNECTED' })
return
}
var chars = connection.getChars()
if (!chars.command) {
reject({ error_code: 0xFF, error_msg: 'command characteristic not found' })
return
}
console.log('[BLE] FFE1 发送', bytes.length + '字节', '| hex:', protocol.bytesToHex(bytes))
wx.writeBLECharacteristicValue({
deviceId: connection.getDeviceId(),
serviceId: chars.command.serviceId,
characteristicId: chars.command.uuid,
value: protocol.bytesToBuffer(bytes),
success: function () {
resolve({ message: 'success', bytes: bytes })
},
fail: function (err) {
console.error('[BLE] FFE1 发送失败', err)
reject({ error_code: 0x0C, error_msg: 'ERR_BLE_DISCONNECTED' })
}
})
})
}
function writeCommandWithRetry(type, payload, maxRetries) {
if (maxRetries === undefined) maxRetries = 3
var attempt = 0
function tryOnce() {
attempt++
return writeCommand(type, payload).catch(function (err) {
if (err && err.error_code === 0x0C) {
return Promise.reject(err)
}
if (attempt >= maxRetries) {
return Promise.reject(err)
}
return new Promise(function (resolve) {
setTimeout(resolve, 500)
}).then(function () {
return tryOnce()
})
})
}
return tryOnce()
}
// --- high-level commands ---
function readDeviceInfo() {
return new Promise(function (resolve, reject) {
var chars = connection.getChars()
var deviceId = connection.getDeviceId()
if (!connection.isConnected() || !chars.deviceInfo) {
reject({ msg: '设备未连接或特征值未就绪' })
return
}
wx.readBLECharacteristicValue({
deviceId: deviceId,
serviceId: chars.deviceInfo.serviceId,
characteristicId: chars.deviceInfo.uuid,
success: function () {},
fail: function () { reject({ msg: '读取设备信息失败' }) }
})
var handler = function (res) {
if (res.characteristicId.toUpperCase().indexOf(protocol.CHAR.DEVICE_INFO) !== -1) {
wx.offBLECharacteristicValueChange(handler)
var bytes = protocol.bufferToBytes(res.value)
if (bytes.length >= 14) {
resolve({
hw_version: (bytes[0] << 8) | bytes[1],
fw_version: (bytes[2] << 8) | bytes[3],
device_type: (bytes[4] << 8) | bytes[5],
device_id: protocol.bytesToHex(bytes.slice(6, 14))
})
} else {
reject({ msg: '设备信息格式错误' })
}
}
}
wx.onBLECharacteristicValueChange(handler)
})
}
function setParams(options) {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
return writeRawCommand(protocol.buildVendorCommand(options || {}))
}
var regionMask = options.region_mask || protocol.REGION.FULL_FACE
var wavelength = options.wavelength || protocol.WAVELENGTH.R
var brightness = options.brightness || 200
var durationMs = options.duration_ms || 600000
var mode = options.mode !== undefined ? options.mode : protocol.TREAT_MODE.NORMAL
var payload = [
regionMask,
wavelength,
brightness,
protocol.uint32ToBytes(durationMs),
mode
].reduce(function (a, b) {
return a.concat(Array.isArray(b) ? b : [b])
}, [])
return writeCommandWithRetry(protocol.CMD.SET_PARAMS, payload)
}
function startTreatment(regionMask) {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
return Promise.resolve({ message: 'vendor_33 uses setParams write as start', region_mask: regionMask })
}
var mask = regionMask || protocol.REGION.FULL_FACE
return writeCommandWithRetry(protocol.CMD.START, [mask])
}
function stopTreatment() {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
return writeRawCommand(protocol.buildVendorCommand({
region_mask: 0,
wavelength: protocol.WAVELENGTH.R,
brightness: 0,
current_gain: 0,
hold_time: 0,
control: protocol.CONTROL.IDLE
}))
}
return writeCommandWithRetry(protocol.CMD.STOP, [])
}
function queryStatus() {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
return Promise.resolve({ message: 'query status not defined by vendor_33 protocol' })
}
return writeCommandWithRetry(protocol.CMD.QUERY_STATUS, [])
}
function bindDevice(userId, bindToken) {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
setTimeout(function () {
connection.emit('bind_result', { success: true, skipped_ble_bind: true })
}, 0)
return Promise.resolve({ message: 'BLE bind not defined by vendor_33 protocol', user_id: userId, bind_token: bindToken })
}
var userBytes = protocol.uint32ToBytes(parseInt(userId, 10))
var tokenBytes = protocol.hexToBytes(bindToken)
var ts = Math.floor(Date.now() / 1000)
var tsBytes = protocol.uint32ToBytes(ts)
var payload = [0x01].concat(userBytes).concat(tokenBytes).concat(tsBytes)
return writeCommandWithRetry(protocol.CMD.BIND, payload)
}
function unbindDevice(userId) {
if (protocol.PROTOCOL_MODE === 'vendor_33') {
return Promise.resolve({ message: 'BLE unbind not defined by vendor_33 protocol', user_id: userId })
}
var userBytes = protocol.uint32ToBytes(parseInt(userId, 10))
var payload = [0x02].concat(userBytes)
return writeCommandWithRetry(protocol.CMD.UNBIND, payload)
}
module.exports = {
getPendingAcks: getPendingAcks,
clearPendingAcks: clearPendingAcks,
writeCommand: writeCommand,
writeRawCommand: writeRawCommand,
writeCommandWithRetry: writeCommandWithRetry,
readDeviceInfo: readDeviceInfo,
setParams: setParams,
startTreatment: startTreatment,
stopTreatment: stopTreatment,
queryStatus: queryStatus,
bindDevice: bindDevice,
unbindDevice: unbindDevice
}