wx-starlock/stores/bluetooth.js
范鹏 13f45e1c26 1. 完成普通用户的展示适配
2. 解决苹果安卓获取的设备id不同的问题
2024-08-29 11:30:00 +08:00

1237 lines
41 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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'
// 定时器
let timer
// 特性值回调
let characteristicValueCallback = null
// 命令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() {
// 初始化蓝牙
const initResult = await this.initBluetooth()
if (initResult) {
// 更新蓝牙初始化状态
this.updateInitBluetooth(true)
// 监听蓝牙开关状态
this.onBluetoothState()
// 监听蓝牙连接状态
this.onBluetoothConnectStatus()
// 监听设备特征值变化
this.onBluetoothCharacteristicValueChange()
return true
} else {
return false
}
},
// 更新是否初始化蓝牙字段
updateInitBluetooth(value) {
this.isInitBluetooth = value
},
// 初始化蓝牙模块
initBluetooth() {
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
}
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]
})
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
default:
that.updateCurrentLockInfo({
...that.currentLockInfo,
token: decrypted.slice(2,6)
})
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
}
uni.startBluetoothDevicesDiscovery({
success: function (res) {
setTimeout(() => {
that.searchBluetoothDevices()
}, 300)
},
fail: async function (res) {
console.log('开始搜索失败', res)
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) {
const deviceList = res.devices
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() {
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) {
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(() => {
setTimeout(() => {
uni.stopBluetoothDevicesDiscovery()
clearInterval(timer)
}, 7000)
timer = 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') {
clearInterval(timer1)
clearInterval(timer2)
uni.stopBluetoothDevicesDiscovery()
resolve(deviceList[i].deviceId)
break
}
}
}
},
fail: async function (res) {
console.log('获取设备列表失败', res)
if(res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
}
}
})
}, 1000)
}, 300)
},
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 === 0)
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
})
return true
} else if(addUserCode === 0x0c) {
const { code: requestCode, data: requestData } = await getUserNoListRequest({
lockId: this.currentLockInfo.lockId
})
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
})
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, isCheckUser = true) {
const that = this
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return
}
// 确认设备连接正常
if(!that.currentLockInfo.connected) {
const srerchResult = await that.searchAndConnectDevice()
that.updateCurrentLockInfo({
...that.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', that.currentLockInfo.deviceId)
if(!srerchResult) return
const result = await that.connectBluetoothDevice()
if(!result) return
}
// 检查是否已添加为用户
if(isCheckUser) {
const checkResult = await that.checkLockUser()
if (!checkResult) return
}
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明文1AES1282SM4事先约定密钥3SM4设备指定密钥
* 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) {
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) {
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) {
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) {
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 timer = setTimeout(() => {
resolve({ code: -1 })
}, 10000)
characteristicValueCallback = async (data) => {
// code 6 token过期,重新获取
if(data.code === 6) {
resolve(await request(params))
} else {
clearTimeout(timer)
resolve(data)
}
}
})
},
// 开门
async openDoor(data) {
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)
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)
await this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.openDoor, data)
},
// 清理用户
async cleanLockUser(data) {
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, false)
return this.getWriteResult(this.cleanLockUser, data)
},
// 恢复出厂设置
async resetDevice(data) {
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) {
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 cleanUser(data) {
const { name, authUid, keyId, uid, userNoLength, userNoList } = data
// const length = 2 + 40 + 20 + 40 + 20 + 2 + userNoLength * 4 + 1 + 16
},
// 设置密码
async setLockPassword(data) {
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)
}
}
})