// 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 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) { 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) { var mask = regionMask || protocol.REGION.FULL_FACE return writeCommandWithRetry(protocol.CMD.START, [mask]) } function stopTreatment() { return writeCommandWithRetry(protocol.CMD.STOP, []) } function queryStatus() { return writeCommandWithRetry(protocol.CMD.QUERY_STATUS, []) } function bindDevice(userId, 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) { 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, writeCommandWithRetry: writeCommandWithRetry, readDeviceInfo: readDeviceInfo, setParams: setParams, startTreatment: startTreatment, stopTreatment: stopTreatment, queryStatus: queryStatus, bindDevice: bindDevice, unbindDevice: unbindDevice }