/** * @description 蓝牙数据持久化 */ import { defineStore } from 'pinia' import crc from 'crc' import { sm4 } from 'sm-crypto' import { md5 } from 'js-md5' import { getServerDatetime } from '@/api/check' import { getUserNoListRequest, updateLockUserNoRequest } from '@/api/key' import { updateElectricQuantityRequest } from '@/api/room' import { reportOpenDoorRequest } from '@/api/lockRecords' import { updateTimezoneOffsetRequest } from '@/api/user' // 定时器 let timer // 特性值回调 let characteristicValueCallback = null // 搜索次数 let searchNumber = 10 // 搜索提示标志 let searchTipFlag = true // 命令ID const cmdIds = { // 获取公钥 getPublicKey: 0x3090, // 获取私钥 getCommKey: 0x3091, // 获取锁状态 getLockStatus: 0x3040, // 新增用户 addUser: 0x3001, // 开门 openDoor: 0x3005, // 重置设备 resetDevice: 0x3004, // 清理用户 cleanUser: 0x300C, // 扩展命令 expandCmd: 0x3030 } // 子命令ID const subCmdIds = { // 设置开锁密码 setLockPassword: 3, // 重置开锁密码 resetLockPassword: 19 } export const useBluetoothStore = defineStore('ble', { state() { return { /* * 蓝牙状态 * -1 未知(初始状态) * 0 正常 * 1 系统蓝牙未打开 * 2 小程序蓝牙功能被禁用 * 3 系统蓝牙未打开且小程序蓝牙功能被禁用 */ bluetoothStatus: -1, // 设备列表 deviceList: [], // 当前锁信息 currentLockInfo: {}, // 消息序号 messageCount: 1, // 是否初始化蓝牙 isInitBluetooth: false, // 服务器时间 serverTimestamp: 0, // 设备keyID keyId: '0' } }, actions: { // 更新keyId updateKeyId(keyId) { this.keyId = keyId }, // 二进制转字符串 uint8ArrayToString(uint8Array) { let str = '' for (let i = 0; i < uint8Array.length; i++) { if (uint8Array[i] !== 0) { str += String.fromCharCode(uint8Array[i]); } } return str }, // 更新服务端时间戳 async updateServerTimestamp() { const { code, data, message } = await getServerDatetime({}) if(code === 0) { this.serverTimestamp = parseInt(data.date / 1000) } return { code, data, message } }, // 初始化并监听 async initAndListenBluetooth(tipFlag) { // 初始化蓝牙 const initResult = await this.initBluetooth(tipFlag) if (initResult) { // 更新蓝牙初始化状态 this.updateInitBluetooth(true) // 监听蓝牙开关状态 this.onBluetoothState() // 监听蓝牙连接状态 this.onBluetoothConnectStatus() // 监听设备特征值变化 this.onBluetoothCharacteristicValueChange() return true } else { return false } }, // 更新是否初始化蓝牙字段 updateInitBluetooth(value) { this.isInitBluetooth = value }, // 初始化蓝牙模块 initBluetooth(tipFlag = true) { const that = this // 初始化蓝牙模块 return new Promise(resolve => { uni.openBluetoothAdapter({ success() { that.bluetoothStatus = 0 console.log('蓝牙初始化成功') resolve(true) }, fail(err) { console.log('蓝牙初始化失败', err) // 系统蓝牙未打开 if(err.errCode === 10001) { if(that.bluetoothStatus === 2) { that.bluetoothStatus = 3 } else { that.bluetoothStatus = 1 } } // 小程序蓝牙功能被禁用 if(err.errno === 103) { if(that.bluetoothStatus === 1) { that.bluetoothStatus = 3 } else { that.bluetoothStatus = 2 } } // 蓝牙已经初始化 if(err.errMsg === 'openBluetoothAdapter:fail already opened') { resolve(true) return } if(err.errno === 3 && tipFlag) { uni.showModal({ title: '提示', content: '蓝牙功能需要附近设备权限,请前往设置开启微信的附近设备权限后再试', showCancel: false, confirmText: '确定', }) } resolve(false) } }) }) }, // 监听蓝牙状态变化 onBluetoothState() { const that = this uni.onBluetoothAdapterStateChange((res) => { console.log('蓝牙状态改变', res) if(that.bluetoothStatus === 3 && res.available) { that.bluetoothStatus = 2 } else if (that.bluetoothStatus === 2 && !res.available) { that.bluetoothStatus = 3 } else if (that.bluetoothStatus === 1 && res.available) { that.bluetoothStatus = 0 } else if (that.bluetoothStatus === 0 && !res.available){ that.bluetoothStatus = 1 } else if (that.bluetoothStatus === -1) { if(res.available) { that.bluetoothStatus = 0 } else { that.bluetoothStatus = 1 } } }) }, // 监听蓝牙设备连接状态 onBluetoothConnectStatus() { const that = this uni.onBLEConnectionStateChange(function (res) { if(res.deviceId === that.currentLockInfo.deviceId) { console.log('设备连接状态改变', res) that.updateCurrentLockInfo({ ...that.currentLockInfo, connected: res.connected }) } }) }, // 解析特征值 parsingCharacteristicValue(binaryData) { const that = this // 0x20 明文 0x22 SM4(事先约定密钥) 0x23 SM4(设备指定密钥) if(binaryData[7] === 0x20) { if(binaryData[12] * 256 + binaryData[13] === cmdIds.getPublicKey) { if(binaryData[14] === 0) { that.updateCurrentLockInfo({ ...that.currentLockInfo, publicKey: [...binaryData.slice(15, 31)] }) } characteristicValueCallback({ code: binaryData[14] }) } } else if(binaryData[7] === 0x22) { // 截取入参 const cebBinaryData = binaryData.slice(12, binaryData.length - 2) // 解密 const key = new Uint8Array(16) for (let i = 0; i < that.currentLockInfo.name.length; i++) { key[i] = that.currentLockInfo.name.charCodeAt(i) } const decrypted = sm4.decrypt(cebBinaryData, key, { mode: 'ecb', output: 'array' }) console.log('ecb解密后的数据', decrypted) if(decrypted[0] * 256 + decrypted[1] === cmdIds.getCommKey) { if(decrypted[2] === 0) { that.updateCurrentLockInfo({ ...that.currentLockInfo, commKey: decrypted.slice(3, 19), signKey: decrypted.slice(19, 35), pwdTimestamp: this.arrayToTimestamp(decrypted.slice(35, 39)) * 1000 }) console.log('commKey', Array.from(that.currentLockInfo.commKey)) console.log('signKey', Array.from(that.currentLockInfo.signKey)) } characteristicValueCallback({ code: decrypted[2] }) } } else { const cebBinaryData = binaryData.slice(12, binaryData.length - 2) const decrypted = sm4.decrypt(cebBinaryData, that.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) console.log('ecb解密后的数据', decrypted) const cmdId = decrypted[0] * 256 + decrypted[1] switch (cmdId) { case cmdIds.getLockStatus: if (decrypted[2] === 0) { const lockConfig = { vendor: that.uint8ArrayToString(decrypted.slice(3, 23)), product: decrypted[23], model: that.uint8ArrayToString(decrypted.slice(24, 44)), fwVersion: that.uint8ArrayToString(decrypted.slice(44, 64)), hwVersion: that.uint8ArrayToString(decrypted.slice(64, 84)), serialNum0: that.uint8ArrayToString(decrypted.slice(84, 100)), serialNum1: that.uint8ArrayToString(decrypted.slice(100, 116)), btDeviceName: that.uint8ArrayToString(decrypted.slice(116, 132)), electricQuantity: decrypted[132], electricQuantityStandby: decrypted[133], restoreCount: decrypted[134] * 256 + decrypted[135], restoreDate: this.arrayToTimestamp(decrypted.slice(136, 140)), icPartNo: that.uint8ArrayToString(decrypted.slice(140, 150)), indate: this.arrayToTimestamp(decrypted.slice(150, 154)), mac: that.uint8ArrayToString(decrypted.slice(154, 174)), timezoneOffset: new Date().getTimezoneOffset() * 60 } that.updateCurrentLockInfo({ ...that.currentLockInfo, featureValue: that.uint8ArrayToString(decrypted.slice(175, 175 + decrypted[174])), featureSettingValue: that.uint8ArrayToString(decrypted.slice(176 + decrypted[174], 176 + decrypted[174] + decrypted[175 + decrypted[174]])), featureSettingParams: Array.from(decrypted.slice(176 + decrypted[174] + decrypted[175 + decrypted[174]])), lockConfig }) console.log('获取锁状态成功', that.currentLockInfo.lockConfig) } characteristicValueCallback({ code: decrypted[2] }) break case cmdIds.addUser: that.updateCurrentLockInfo({ ...that.currentLockInfo, token: decrypted.slice(42,46), lockUserNo: decrypted[47] * 256 + decrypted[48] }) console.log('添加用户结果', decrypted[46], that.currentLockInfo.token) characteristicValueCallback({ code: decrypted[46] }) break case cmdIds.expandCmd: const subCmdId = decrypted[3] switch (subCmdId) { case subCmdIds.resetLockPassword: that.updateCurrentLockInfo({ ...that.currentLockInfo, token: decrypted.slice(5,9) }) characteristicValueCallback({ code: decrypted[2] }) if(decrypted[2] === 0) { that.updateCurrentLockInfo({ ...that.currentLockInfo, encrpyKey: decrypted.slice(9, 17) }) } break case subCmdIds.setLockPassword: that.updateCurrentLockInfo({ ...that.currentLockInfo, token: decrypted.slice(5,9) }) characteristicValueCallback({ code: decrypted[2], data: { status: decrypted[11] } }) break } break case cmdIds.openDoor: that.updateCurrentLockInfo({ ...that.currentLockInfo, token: decrypted.slice(2,6) }) console.log('开门', decrypted[6], that.currentLockInfo.token) characteristicValueCallback({ code: decrypted[6] }) if(decrypted[6] === 0) { updateElectricQuantityRequest({ lockId: that.currentLockInfo.lockId, electricQuantity: decrypted[7], electricQuantityStandby: decrypted[9] }).then(res => { if(res.code === 0) { that.updateCurrentLockInfo({ ...that.currentLockInfo, electricQuantityDate: res.data.electricQuantityDate }) } }) reportOpenDoorRequest({ lockId: that.currentLockInfo.lockId, keyId: that.keyId, }).then(res => { console.log('上报开门结果', res) }) updateTimezoneOffsetRequest({ timezoneOffset: new Date().getTimezoneOffset() * 60 }).then(res => { console.log('上报时区结果', res) }) } default: that.updateCurrentLockInfo({ ...that.currentLockInfo, token: decrypted.slice(2,6) }) console.log('默认结果', decrypted[6], that.currentLockInfo.token) characteristicValueCallback({ code: decrypted[6] }) break } } }, // 监听蓝牙设备特征值改变 onBluetoothCharacteristicValueChange() { const that = this // 完整数据 let completeArray // 完整内容数据长度 let length console.log('开始监听特征值改变') uni.onBLECharacteristicValueChange(function (res) { if(res.deviceId === that.currentLockInfo.deviceId) { let binaryData = new Uint8Array(res.value) if(binaryData[0] === 0xEF && binaryData[1] === 0x01 && binaryData[2] === 0xEE && binaryData[3] === 0x02) { length = binaryData[8] * 256 + binaryData[9] if(length + 14 > binaryData.length) { completeArray = binaryData } else { that.parsingCharacteristicValue(binaryData) } } else { if(completeArray) { const combinedArray = new Uint8Array(completeArray.length + binaryData.length) combinedArray.set(completeArray, 0); combinedArray.set(binaryData, completeArray.length) completeArray = combinedArray if(length + 14 === completeArray.length) { that.parsingCharacteristicValue(completeArray) completeArray = null } } } } }) }, // 开始搜索蓝牙设备 getBluetoothDevices() { const that = this if(this.bluetoothStatus !== 0) { console.log('搜索未执行', this.bluetoothStatus) this.getBluetoothStatus() return } this.deviceList = [] uni.startBluetoothDevicesDiscovery({ success: function (res) { setTimeout(() => { that.searchBluetoothDevices() }, 300) }, fail: async function (res) { console.log('开始搜索失败', res) if(res.errno === 1509008) { uni.showModal({ title: '提示', content: '安卓手机蓝牙功能需要定位权限,请前往设置开启微信的定位权限后再试', showCancel: false, confirmText: '重试', success(res) { if (res.confirm) { that.getBluetoothDevices() } } }) } if(res.errCode === 10000) { // 重新初始化蓝牙适配器 await that.initBluetooth() that.getBluetoothDevices() } } }) }, // 定时查询蓝牙设备 searchBluetoothDevices() { const that = this if(this.bluetoothStatus !== 0) { console.log('搜索未执行', this.bluetoothStatus) this.getBluetoothStatus() return } timer = setInterval(() => { uni.getBluetoothDevices({ success(res) { searchNumber-- if(searchNumber === 0 && searchTipFlag) { uni.showModal({ title: '提示', content: '长时间未搜索到任何设备,请确认微信的附近设备权限开启后再试', showCancel: false, success() { uni.navigateBack() } }) } const deviceList = res.devices if(deviceList.length !== 0) { searchTipFlag = false } that.deviceList = [] for(let i = 0; i < deviceList.length; i++) { if(deviceList[i]?.advertisServiceUUIDs) { const uuid = deviceList[i]?.advertisServiceUUIDs[0] if(uuid && uuid.slice(2,8)==='758824' && uuid.slice(30,32)==='00') { that.deviceList.push(deviceList[i]) } } } console.log('设备列表', that.deviceList) }, fail: async function (res) { console.log('获取设备列表失败', res) if(res.errCode === 10000) { // 重新初始化蓝牙适配器 await that.initBluetooth() } } }) }, 1000) }, // 停止搜索蓝牙设备 stopGetBluetoothDevices() { searchNumber = 10 searchTipFlag = true clearInterval(timer) uni.stopBluetoothDevicesDiscovery() }, // 蓝牙状态提示 getBluetoothStatus() { if(this.bluetoothStatus === 1) { uni.showModal({ title: '提示', content: '蓝牙尚未打开,请先打开蓝牙', showCancel: false, confirmText: '确定', }) } else if(this.bluetoothStatus === 2 || this.bluetoothStatus === 3) { uni.showModal({ title: '提示', content: '小程序蓝牙功能被禁用,请打开小程序蓝牙权限', showCancel: false, confirmText: '去设置', success(res) { if(res.confirm) { uni.openSetting() } } }) } }, // 检查小程序设置 checkSetting() { const that = this return new Promise((resolve) => { uni.getSetting({ async success(res) { const bluetooth = res.authSetting['scope.bluetooth'] if(that.bluetoothStatus === -1) { if(bluetooth !== false) { that.bluetoothStatus = 0 } else { that.bluetoothStatus = 2 } } else if(that.bluetoothStatus === 0 && bluetooth === false) { that.bluetoothStatus = 2 } else if(that.bluetoothStatus === 1 && bluetooth === false) { that.bluetoothStatus = 3 } else if(that.bluetoothStatus === 2 && bluetooth !== false) { that.bluetoothStatus = 0 await that.initBluetooth() } else if(that.bluetoothStatus === 3 && bluetooth !== false) { that.bluetoothStatus = 1 await that.initBluetooth() that.onBluetoothState() } console.log('蓝牙权限', bluetooth, that.bluetoothStatus) resolve(bluetooth) }, fail() { resolve(false) } }) }) }, // 连接蓝牙设备+获取设备服务+获取设备特征值 connectBluetoothDevice() { const that = this return new Promise((resolve) => { if(that.bluetoothStatus !== 0) { console.log('连接未执行', that.bluetoothStatus) that.getBluetoothStatus() resolve(false) return } uni.createBLEConnection({ deviceId: that.currentLockInfo.deviceId, success(res) { console.log('连接成功', res) // 获取设备服务 uni.getBLEDeviceServices({ deviceId: that.currentLockInfo.deviceId, success (res) { let serviceId for(let i = 0; i < res.services.length; i++) { if(res.services[i].uuid.indexOf('FFF0') !== -1) { serviceId = res.services[i].uuid } } // 获取设备对应服务的特征值 uni.getBLEDeviceCharacteristics({ deviceId: that.currentLockInfo.deviceId, serviceId: serviceId, success (res) { let notifyCharacteristicId let writeCharacteristicId for(let i = 0; i < res.characteristics.length; i++) { const characteristic = res.characteristics[i] if(characteristic.properties.notify) { notifyCharacteristicId = characteristic.uuid } if(characteristic.properties.write) { writeCharacteristicId = characteristic.uuid } } that.updateCurrentLockInfo({ ...that.currentLockInfo, serviceId, notifyCharacteristicId, writeCharacteristicId }) that.notifyBluetoothCharacteristicValueChange() resolve(true) }, fail(res) { if(res.errCode === 10006) { uni.showToast({ title: '连接失败,请靠近设备并保持设备处于唤醒状态', icon: 'none' }) } console.log('获取设备特征值失败', res) resolve(false) } }) }, fail(res) { if(res.errCode === 10006) { uni.showToast({ title: '连接失败,请靠近设备并保持设备处于唤醒状态', icon: 'none' }) } console.log('获取设备服务失败', res) resolve(false) } }) }, fail(res) { if(res.errno === 1509007) { resolve(true) return } uni.showToast({ title: '连接失败,请靠近设备并保持设备处于唤醒状态', icon: 'none' }) console.log('连接失败', res) resolve(false) } }) }) }, // 更新当前锁信息 updateCurrentLockInfo(lockInfo) { console.log('更新当前锁信息', lockInfo) this.currentLockInfo = lockInfo }, // 订阅设备特征值改变 notifyBluetoothCharacteristicValueChange() { const that = this uni.notifyBLECharacteristicValueChange({ state: true, deviceId: that.currentLockInfo.deviceId, serviceId: that.currentLockInfo.serviceId, characteristicId: that.currentLockInfo.notifyCharacteristicId, success(res) { console.log('订阅成功', res) }, fail(res) { console.log('订阅失败', res) } }) }, // 断开蓝牙连接 closeBluetoothConnection() { const that = this uni.closeBLEConnection({ deviceId: that.currentLockInfo.deviceId, success(res) { console.log('断开连接成功', res) }, fail(res) { console.log('断开连接失败', res) } }) }, // 查找设备并连接 async searchAndConnectDevice() { const that = this let timer1 let timer2 return new Promise((resolve) => { uni.startBluetoothDevicesDiscovery({ success: function (res) { timer2 = setTimeout(() => { uni.stopBluetoothDevicesDiscovery() clearInterval(timer1) resolve(false) }, 10500) timer1 = setInterval(() => { uni.getBluetoothDevices({ success(res) { const deviceList = res.devices for(let i = 0; i < deviceList.length; i++) { if(deviceList[i]?.name === that.currentLockInfo.name) { const uuid = deviceList[i]?.advertisServiceUUIDs[0] if(uuid && uuid.slice(2,8)==='758824' && uuid.slice(30,32)==='01') { uni.stopBluetoothDevicesDiscovery() clearTimeout(timer2) clearInterval(timer1) resolve(deviceList[i].deviceId) break } } } }, fail: async function (res) { console.log('获取设备列表失败', res) if(res.errCode === 10000) { // 重新初始化蓝牙适配器 await that.initBluetooth() } } }) }, 1000) }, fail: async function (res) { console.log('开始搜索失败', res) if(res.errCode === 10000) { // 重新初始化蓝牙适配器 await that.initBluetooth() that.searchAndConnectDevice() } else { resolve(false) } } }) }) }, // 检查是否已添加为用户 async checkLockUser() { console.log('检查是否已添加为用户', this.currentLockInfo.lockUserNo) if(this.currentLockInfo.lockUserNo === 0) { const timestamp = parseInt(new Date().getTime() / 1000) const password = (Math.floor(Math.random() * 900000) + 100000).toString() console.log('用户未添加,开始添加用户') const { code: addUserCode } = await this.addLockUser({ name: this.currentLockInfo.name, keyId: this.keyId, authUid: this.currentLockInfo.senderUserId.toString(), uid: this.currentLockInfo.uid.toString(), openMode: 1, keyType: 0, startDate: this.currentLockInfo.startDate === 0 ? timestamp : this.currentLockInfo.startDate, expireDate: this.currentLockInfo.endDate === 0 ? 0xffffffff : this.currentLockInfo.endDate, useCountLimit: this.currentLockInfo.keyType === 3 ? 1 : 0xffff, isRound: 0, weekRound: 0, startHour: 0, startMin: 0, endHour: 0, endMin: 0, role: 0, password }) console.log('添加用户蓝牙结果', addUserCode) if(addUserCode === 0) { const { code } = await updateLockUserNoRequest({ keyId: this.keyId, lockUserNo: this.currentLockInfo.lockUserNo }) console.log('添加用户请求结果', code) return true } else if(addUserCode === 0x0c) { console.log('用户达上限,开始清理用户') const { code: requestCode, data: requestData } = await getUserNoListRequest({ lockId: this.currentLockInfo.lockId }) console.log('获取用户列表请求结果', requestCode, requestData) if(requestCode !== 0) return false const userNoList = requestData.userNos const { code: cleanCode } = await this.cleanLockUser({ name: this.currentLockInfo.name, keyId: this.keyId, authUid: this.currentLockInfo.senderUserId.toString(), uid: this.currentLockInfo.uid.toString(), userNoList: userNoList }) console.log('清理用户蓝牙结果', cleanCode) if(cleanCode === 0) { return await this.checkLockUser() } else { return false } } else if(addUserCode === 0x0f) { return true } else { return false } } else { return true } }, // 写入特征值 async writeBLECharacteristicValue(binaryData) { const that = this console.log('设备ID:', that.currentLockInfo.deviceId) console.log('设备名称:', that.currentLockInfo.name) console.log('设备主服务:', that.currentLockInfo.serviceId) console.log('设备写入特征值:', that.currentLockInfo.writeCharacteristicId) console.log('设备写入数据:', Array.from(binaryData)) // 次数 const count = Math.ceil(binaryData.length / 20) for(let i = 0; i < count; i++) { const writeData = binaryData.slice(i * 20, i === (count - 1) ? binaryData.length : (i + 1) * 20) uni.writeBLECharacteristicValue({ deviceId: that.currentLockInfo.deviceId, serviceId: that.currentLockInfo.serviceId, characteristicId: that.currentLockInfo.writeCharacteristicId, value: writeData.buffer, fail(res) { console.log('写入失败', res) } }) } }, /* * 生成包头 * encryptionType 加密类型 0:明文,1:AES128,2:SM4(事先约定密钥),3:SM4(设备指定密钥) * originalLength 原始数据长度 * */ createPackageHeader(encryptionType, originalLength) { // 头部数据 let headArray = new Uint8Array(12) // 固定包头 headArray[0] = 0xEF headArray[1] = 0x01 headArray[2] = 0xEE headArray[3] = 0x02 // 包类型 发送 headArray[4] = 0x01 // 包序号 headArray[5] = this.messageCount / 256 headArray[6] = this.messageCount % 256 this.messageCount++ // 包标识 if(encryptionType === 0) { headArray[7] = 0x20 } else if(encryptionType === 2) { headArray[7] = 0x22 } else { headArray[7] = 0x23 } // 数据长度 if(encryptionType === 0) { headArray[8] = originalLength / 256 headArray[9] = originalLength % 256 } else { const length = Math.ceil(originalLength / 16) * 16 headArray[8] = length / 256 headArray[9] = length % 256 } headArray[10] = originalLength / 256 headArray[11] = originalLength % 256 return headArray }, // 生成包尾 头部数据+内容数据 createPackageEnd(headArray,conentArray) { // 拼接头部和内容 let mergerArray = new Uint8Array(headArray.length + conentArray.length) mergerArray.set(headArray) mergerArray.set(conentArray, headArray.length) // crc加密 const crcResult = crc.crc16kermit(mergerArray) // 拼接crc let newArray = new Uint8Array(mergerArray.length + 2) newArray.set(mergerArray) newArray.set([crcResult / 256, crcResult % 256], mergerArray.length) return newArray }, // 获取公钥 async getPublicKey(name) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } const headArray = this.createPackageHeader(0, 42) const conentArray = new Uint8Array(42) conentArray[0] = cmdIds.getPublicKey / 256 conentArray[1] = cmdIds.getPublicKey % 256 for(let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } const packageArray = this.createPackageEnd(headArray, conentArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult() }, // 获取私钥 async getCommKey(name, keyId, authUid, nowTime) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } const length = 2 + 40 + 40 + 20 + 4 + 1 + 16 const headArray = this.createPackageHeader(2, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.getCommKey / 256 conentArray[1] = cmdIds.getCommKey % 256 for (let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } for(let i = 0; i < keyId.length; i++) { conentArray[i + 42] = keyId.charCodeAt(i) } for(let i = 0; i < authUid.length; i++) { conentArray[i + 82] = authUid.charCodeAt(i) } conentArray.set(this.timestampToArray(nowTime), 102) conentArray[106] = 16 const md5Array = this.md5Encrypte(authUid + keyId, conentArray.slice(102, 106), this.currentLockInfo.publicKey) conentArray.set(md5Array, 107) const cebArray = sm4.encrypt(conentArray, conentArray.slice(2, 18), { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult() }, // md5加密 md5Encrypte(text, token, key) { const length = text.length + 4 + 16 const md5Array = new Uint8Array(length) for (let i = 0; i < text.length; i++) { md5Array[i] = text.charCodeAt(i) } md5Array.set(token , text.length) md5Array.set(key, text.length + 4) const md5Text= md5(md5Array) return new Uint8Array(md5Text.match(/.{1,2}/g).map(byte => parseInt(byte, 16))) }, // 获取锁状态 async getLockStatus(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } const { name, uid, nowTime, localTime } = data const length = 2 + 40 + 20 + 4 + 4 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.getLockStatus / 256 conentArray[1] = cmdIds.getLockStatus % 256 for (let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } for (let i = 0; i < uid.length; i++) { conentArray[i + 42] = uid.charCodeAt(i) } conentArray.set(this.timestampToArray(nowTime), 62) conentArray.set(this.timestampToArray(localTime), 66) const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult(this.getLockStatus, data) }, // 时间戳转二进制 timestampToArray(timestamp) { const array = new Uint8Array(4) array[0] = (timestamp & 0xff000000) >> 24 array[1] = (timestamp & 0xff0000) >> 16 array[2] = (timestamp & 0xff00) >> 8 array[3] = (timestamp & 0xff) return array }, // 二进制转时间戳 arrayToTimestamp(array) { const timestamp = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3] return timestamp >>> 0 }, // 添加用户 async addLockUser(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } const { name, authUid, uid, keyId, openMode, keyType, startDate, expireDate, useCountLimit, isRound, weekRound, startHour, startMin, endHour, endMin, role, password } = data const length = 2 + 40 + 20 + 40 + 20 + 1 + 1 + 4 + 4 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 20 + 4 + 1 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.addUser / 256 conentArray[1] = cmdIds.addUser % 256 for(let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } for(let i = 0; i < authUid.length; i++) { conentArray[i + 42] = authUid.charCodeAt(i) } for(let i = 0; i < keyId.length; i++) { conentArray[i + 62] = keyId.charCodeAt(i) } for(let i = 0; i < uid.length; i++) { conentArray[i + 102] = uid.charCodeAt(i) } conentArray[122] = openMode conentArray[123] = keyType conentArray.set(this.timestampToArray(startDate), 124) conentArray.set(this.timestampToArray(expireDate), 128) conentArray[132] = useCountLimit / 256 conentArray[133] = useCountLimit % 256 conentArray[134] = isRound conentArray[135] = weekRound conentArray[136] = startHour conentArray[137] = startMin conentArray[138] = endHour conentArray[139] = endMin conentArray[140] = role for(let i = 0; i < password.length; i++) { conentArray[i + 141] = password.charCodeAt(i) } conentArray.set(this.currentLockInfo.token || this.timestampToArray(startDate), 161) conentArray[165] = 16 const md5Array = this.md5Encrypte(authUid + keyId, this.currentLockInfo.token || this.timestampToArray(startDate), this.currentLockInfo.publicKey) conentArray.set(md5Array, 166) const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray, false) return this.getWriteResult(this.addLockUser, data) }, // 获取写入结果 getWriteResult(request, params) { return new Promise(resolve => { const getWriteResultTimer = setTimeout(() => { resolve({ code: -1 }) }, 20000) characteristicValueCallback = async (data) => { // code 6 token过期,重新获取 if(data.code === 6) { console.log('token过期', request.toString()) console.log('token过期', params) resolve(await request(params)) } else { clearTimeout(getWriteResultTimer) resolve(data) } } }) }, // 开门 async openDoor(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } // 检查是否已添加为用户 const checkResult = await this.checkLockUser() if (!checkResult) { return { code: -1 } } const { name, uid, openMode, openTime, onlineToken } = data const length = 2 + 40 + 20 + 1 + 4 + 4 + 1 + 16 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.openDoor / 256 conentArray[1] = cmdIds.openDoor % 256 for(let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } for (let i = 0; i < uid.length; i++) { conentArray[i + 42] = uid.charCodeAt(i) } conentArray[62] = openMode conentArray.set(this.timestampToArray(openTime), 63) console.log('开门时token', this.currentLockInfo.token) conentArray.set(this.currentLockInfo.token || this.timestampToArray(openTime), 67) conentArray[71] = 16 const md5Array = this.md5Encrypte(name + uid, this.currentLockInfo.token || this.timestampToArray(openTime), this.currentLockInfo.signKey) conentArray.set(md5Array, 72) for (let i = 0; i < onlineToken.length; i++) { conentArray[i + 88] = onlineToken.charCodeAt(i) } const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) this.writeBLECharacteristicValue(packageArray) return this.getWriteResult(this.openDoor, data) }, // 清理用户 async cleanLockUser(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } const { name, authUid, keyId, uid, userNoList } = data const length = 2 + 40 + 20 + 40 + 20 + 2 + userNoList.length + 4 + 1 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.cleanUser / 256 conentArray[1] = cmdIds.cleanUser % 256 for (let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } for (let i = 0; i < authUid.length; i++) { conentArray[i + 42] = authUid.charCodeAt(i) } for (let i = 0; i < keyId.length; i++) { conentArray[i + 62] = keyId.charCodeAt(i) } for (let i = 0; i < uid.length; i++) { conentArray[i + 102] = uid.charCodeAt(i) } conentArray[122] = userNoList.length / 256 conentArray[123] = userNoList.length % 256 for (let i = 0; i < userNoList.length; i++) { conentArray[i + 124] = userNoList[i] } conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 124 + userNoList.length) conentArray[128 + userNoList.length] = 16 const md5Array = this.md5Encrypte(authUid + keyId, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.publicKey) conentArray.set(md5Array, 129 + userNoList.length) const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult(this.cleanLockUser, data) }, // 恢复出厂设置 async resetDevice(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } // 检查是否已添加为用户 const checkResult = await this.checkLockUser() if (!checkResult) { return { code: -1 } } const { name, authUid, keyId } = data const length = 2 + 40 + 20 + 4 + 1 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.resetDevice / 256 conentArray[1] = cmdIds.resetDevice % 256 for(let i = 0; i < name.length; i++) { conentArray[i + 2] = name.charCodeAt(i) } for(let i = 0; i < authUid.length; i++) { conentArray[i + 42] = authUid.charCodeAt(i) } conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 62) conentArray[66] = 16 const md5Array = this.md5Encrypte(name, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.publicKey) conentArray.set(md5Array, 67) const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult(this.resetDevice, data) }, // 重置开锁密码 async resetLockPassword(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } // 检查是否已添加为用户 const checkResult = await this.checkLockUser() if (!checkResult) { return { code: -1 } } const { keyId, uid } = data const length = 2 + 1 + 1 + 40 + 20 + 4 + 1 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.expandCmd / 256 conentArray[1] = cmdIds.expandCmd % 256 // 子命令 conentArray[2] = subCmdIds.resetLockPassword conentArray[3] = length - 4 for (let i = 0; i < keyId.length; i++) { conentArray[i + 4] = keyId.charCodeAt(i) } for (let i = 0; i < uid.length; i++) { conentArray[i + 44] = uid.charCodeAt(i) } conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 64) conentArray[68] = 16 const md5Array = this.md5Encrypte(keyId + uid, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.signKey) conentArray.set(md5Array, 69) const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult(this.resetLockPassword, data) }, // 设置密码 async setLockPassword(data) { // 确认蓝牙状态正常 if(this.bluetoothStatus !== 0) { console.log('写入未执行', this.bluetoothStatus) this.getBluetoothStatus() return { code: -1 } } // 确认设备连接正常 if(!this.currentLockInfo.connected) { const srerchResult = await this.searchAndConnectDevice() this.updateCurrentLockInfo({ ...this.currentLockInfo, deviceId: srerchResult }) console.log('设备ID:', this.currentLockInfo.deviceId) if(!srerchResult) { return { code: -1 } } const result = await this.connectBluetoothDevice() console.log('连接结果', result) if(!result) { return { code: -1 } } } // 检查是否已添加为用户 const checkResult = await this.checkLockUser() if (!checkResult) { return { code: -1 } } const { keyId, uid, pwdNo, operate, isAdmin, pwd, userCountLimit, startTime, endTime} = data const length = 2 + 1 + 1 + 40 + 20 + 2 + 1 + 1 + 20 + 2 + 4 + 4 + 4 + 1 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) conentArray[0] = cmdIds.expandCmd / 256 conentArray[1] = cmdIds.expandCmd % 256 // 子命令 conentArray[2] = subCmdIds.setLockPassword conentArray[3] = length - 3 for (let i = 0; i < keyId.length; i++) { conentArray[i + 4] = keyId.charCodeAt(i) } for (let i = 0; i < uid.length; i++) { conentArray[i + 44] = uid.charCodeAt(i) } conentArray[64] = pwdNo / 256 conentArray[65] = pwdNo % 256 conentArray[66] = operate conentArray[67] = isAdmin for (let i = 0; i < pwd.length; i++) { conentArray[i + 68] = pwd.charCodeAt(i) } conentArray[88] = userCountLimit / 256 conentArray[89] = userCountLimit % 256 conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 90) conentArray.set(this.timestampToArray(startTime), 94) conentArray.set(this.timestampToArray(endTime), 98) conentArray[102] = 16 const md5Array = this.md5Encrypte(keyId + uid, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.signKey) conentArray.set(md5Array, 103) const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) return this.getWriteResult(this.setLockPassword, data) } } })