wx-starlock/stores/bluetooth.js
2025-02-17 15:41:41 +08:00

2618 lines
84 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'
import { updateElectricQuantityRequest } from '@/api/room'
import { reportOpenDoorRequest } from '@/api/lockRecords'
import { updateTimezoneOffsetRequest } from '@/api/user'
import log from '@/utils/log'
import { useUserStore } from '@/stores/user'
import { getLastRecordTimeRequest, uploadRecordRequest } from '@/api/record'
// 定时器
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,
// 注册卡片
registerCard: 24,
// 注册卡片确认
registerCardConfirm: 22,
// 注册卡片取消
registerCardCancel: 25,
// 注册指纹
registerFingerprint: 36,
// 注册指纹确认
registerFingerprintConfirm: 32,
// 注册指纹取消
registerFingerprintCancel: 37,
// 注册指纹过程
registerFingerprintProcess: 33,
// 注册人脸
registerFace: 81,
// 注册人脸确认
registerFaceConfirm: 82,
// 注册人脸取消
registerFaceCancel: 86,
// 注册人脸过程
registerFaceProcess: 83,
// 注册遥控
registerRemote: 26,
// 注册遥控确认
registerRemoteConfirm: 27,
// 注册遥控取消
registerRemoteCancel: 28,
// 注册掌纹
registerPalmVein: 42,
// 注册掌纹确认
registerPalmVeinConfirm: 43,
// 注册掌纹取消
registerPalmVeinCancel: 44,
// 同步操作记录
syncRecord: 41
}
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',
// 刚绑定的设备名称
bindedDeviceNameList: []
}
},
actions: {
// 保存刚绑定的设备名称
updateBindedDeviceName(name) {
if (!this.bindedDeviceNameList.includes(name)) {
this.bindedDeviceNameList.push(name)
}
},
// 更新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({})
let timestamp = new Date().getTime()
if (code === 0) {
this.serverTimestamp = parseInt(data.date / 1000, 10)
timestamp = data.date
}
return { code, data: { ...data, timestamp }, message }
},
// 关闭全部蓝牙监听并关闭蓝牙模拟
closeAllBluetooth() {
uni.offBluetoothAdapterStateChange()
uni.closeBluetoothAdapter({
success(res) {
console.log('关闭蓝牙模块', res)
}
})
},
// 初始化并监听
async initAndListenBluetooth(tipFlag) {
// 初始化蓝牙
const initResult = await this.initBluetooth(tipFlag)
if (initResult) {
// 更新蓝牙初始化状态
this.updateInitBluetooth(true)
// 监听蓝牙连接状态
this.onBluetoothConnectStatus()
// 监听设备特征值变化
this.onBluetoothCharacteristicValueChange()
return true
}
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: '确定',
success(res) {
if (res.confirm) {
uni.openAppAuthorizeSetting({
success(res) {
console.log(res)
}
})
}
}
})
}
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
})
}
})
},
// 解析特征值
async parsingCharacteristicValue(binaryData) {
const $user = useUserStore()
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)) * 1000,
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)
})
if (decrypted[46] === 0) {
that.updateCurrentLockInfo({
...that.currentLockInfo,
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: {
no: decrypted[9] * 256 + decrypted[10],
status: decrypted[11]
}
})
break
case subCmdIds.registerCard:
case subCmdIds.registerRemote:
case subCmdIds.registerPalmVein:
that.updateCurrentLockInfo({
...that.currentLockInfo,
token: decrypted.slice(5, 9)
})
characteristicValueCallback({
code: decrypted[2],
data: {
status: decrypted[9]
}
})
break
case subCmdIds.registerFingerprint:
case subCmdIds.registerFace:
that.updateCurrentLockInfo({
...that.currentLockInfo,
token: decrypted.slice(5, 9)
})
characteristicValueCallback({
code: decrypted[2],
data: {
maxProcess: decrypted[11]
}
})
break
case subCmdIds.registerCardConfirm:
uni.$emit('registerCardConfirm', {
status: decrypted[5],
cardNumber: decrypted[6] * 256 + decrypted[7]
})
break
case subCmdIds.registerFingerprintConfirm:
uni.$emit('registerFingerprintConfirm', {
status: decrypted[5],
fingerprintNumber: decrypted[6] * 256 + decrypted[7]
})
break
case subCmdIds.registerFaceConfirm:
uni.$emit('registerFaceConfirm', {
status: decrypted[5],
faceNumber: decrypted[6] * 256 + decrypted[7]
})
break
case subCmdIds.registerRemoteConfirm:
uni.$emit('registerRemoteConfirm', {
status: decrypted[5],
remoteNumber: decrypted[6] * 256 + decrypted[7]
})
break
case subCmdIds.registerPalmVeinConfirm:
uni.$emit('registerPalmVeinConfirm', {
status: decrypted[5],
palmVeinNumber: decrypted[6] * 256 + decrypted[7]
})
break
case subCmdIds.registerFingerprintProcess:
uni.$emit('registerFingerprintProcess', {
status: decrypted[5],
process: decrypted[6]
})
break
case subCmdIds.registerFaceProcess:
uni.$emit('registerFaceProcess', {
status: decrypted[5],
process: decrypted[6]
})
break
case subCmdIds.syncRecord:
if (decrypted[2] === 0 && decrypted[6] > 0) {
const records = []
const count = decrypted[6] || 0
for (let i = 0; i < count; i++) {
let password = decrypted.slice(14 + 17 * i, 14 + 17 * i + 10)
if (password.every(item => item === 0)) {
password = null
} else {
password = this.uint8ArrayToString(password)
}
const record = {
type: decrypted[7 + 17 * i],
user: decrypted[8 + 17 * i] * 256 + decrypted[9 + 17 * i],
date: this.arrayToTimestamp(decrypted.slice(10 + 17 * i, 14 + 17 * i)) * 1000,
success: 1,
password
}
records.push(record)
}
const { code, message } = await uploadRecordRequest({
records,
lockId: this.currentLockInfo.lockId
})
characteristicValueCallback({ code, data: { count }, message })
} else {
characteristicValueCallback({ code: decrypted[2] })
}
break
default:
break
}
break
case cmdIds.openDoor:
that.updateCurrentLockInfo({
...that.currentLockInfo,
token: decrypted.slice(2, 6)
})
console.log('开门', decrypted[6], that.currentLockInfo.token)
log.info({
code: decrypted[6],
message: `锁端返回状态码`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
characteristicValueCallback({
code: decrypted[6]
})
if (decrypted[6] === 0) {
log.info({
code: decrypted[6],
message: `开门成功`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
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)
})
}
break
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() {
setTimeout(() => {
that.searchBluetoothDevices()
}, 300)
},
async fail(res) {
console.log('开始搜索失败', res)
if (res.errno === 1509008) {
uni.showModal({
title: '提示',
content: '安卓手机蓝牙功能需要定位权限,请前往设置开启微信的定位权限后再试',
showCancel: false,
success(res) {
if (res.confirm) {
uni.openAppAuthorizeSetting({
success(res) {
console.log(res)
}
})
}
uni.navigateBack()
}
})
}
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.openAppAuthorizeSetting({
success(res) {
console.log(res)
}
})
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)
},
async fail(res) {
console.log('获取设备列表失败', res)
if (res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
}
}
})
}, 1000)
},
// 停止搜索蓝牙设备
stopGetBluetoothDevices() {
searchNumber = 10
searchTipFlag = true
clearInterval(timer)
uni.stopBluetoothDevicesDiscovery()
},
// 蓝牙状态提示
async getBluetoothStatus() {
if (this.bluetoothStatus === 1) {
const initResult = await this.initBluetooth()
if (!initResult) {
uni.showModal({
title: '提示',
content: '蓝牙尚未打开,请先打开蓝牙',
showCancel: false,
success(res) {
if (res.confirm) {
wx.openSystemBluetoothSetting({
success(res) {
console.log(res)
}
})
}
}
})
}
} 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
}
console.log('蓝牙权限', bluetooth, that.bluetoothStatus)
resolve(bluetooth)
},
fail() {
resolve(false)
}
})
})
},
// 连接蓝牙设备+获取设备服务+获取设备特征值
connectBluetoothDevice(number = 0) {
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,
timeout: 10000,
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,
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)
}
})
},
async fail(res) {
console.log('连接失败', res)
if (res.errno === 1509007) {
resolve(true)
return
}
if (res.errno === 1509001 && number < 1) {
// 超时直接返回
resolve(false)
return
}
if (number < 1) {
// 重新连接
resolve(await that.connectBluetoothDevice(number + 1))
}
}
})
})
},
// 更新当前锁信息
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)
}
})
},
// 周数组转换
convertWeekdaysToNumber(weekDay) {
let weekStr = '00000000'
// eslint-disable-next-line no-restricted-syntax
for (const day of weekDay) {
const index = day % 7 // 将周日的索引转换为0
weekStr = weekStr.substring(0, index) + '1' + weekStr.substring(index + 1)
}
// 倒序 weekStr
weekStr = weekStr.split('').reverse().join('')
return parseInt(weekStr, 2)
},
// 查找设备并连接
async searchAndConnectDevice() {
const that = this
let timer1
let timer2
return new Promise(resolve => {
uni.startBluetoothDevicesDiscovery({
success() {
let searchFlag = false
timer2 = setTimeout(async () => {
uni.stopBluetoothDevicesDiscovery()
clearInterval(timer1)
if (!searchFlag) {
uni.showModal({
title: '提示',
content:
'长时间未搜索到任何设备,请确认微信的附近设备权限开启后再试(鸿蒙系统在设置-隐私-定位服务中开启位置信息权限)',
showCancel: false,
success() {
uni.openAppAuthorizeSetting({
success(res) {
console.log(res)
}
})
}
})
resolve({ code: -22 })
} else {
resolve({ code: -4 })
}
}, 10500)
timer1 = setInterval(() => {
uni.getBluetoothDevices({
success(res) {
const deviceList = res.devices
if (searchFlag === false && res.devices.length > 0) {
searchFlag = true
}
for (let i = 0; i < deviceList.length; i++) {
if (deviceList[i]?.name === that.currentLockInfo.name) {
const uuid = deviceList[i]?.advertisServiceUUIDs[0]
console.log('设备UUID', uuid, uuid.slice(2, 8), uuid.slice(30, 32))
if (
uuid &&
uuid.slice(2, 8) === '758824' &&
(uuid.slice(30, 32) === '01' ||
that.bindedDeviceNameList.includes(that.currentLockInfo.name))
) {
uni.stopBluetoothDevicesDiscovery()
clearTimeout(timer2)
clearInterval(timer1)
resolve({
code: 0,
data: {
deviceId: deviceList[i].deviceId
}
})
break
} else if (
uuid &&
uuid.slice(2, 8) === '758824' &&
uuid.slice(30, 32) === '00'
) {
uni.stopBluetoothDevicesDiscovery()
clearTimeout(timer2)
clearInterval(timer1)
uni.hideLoading()
uni.showToast({
title: '锁已被重置,请重新绑定',
icon: 'none'
})
resolve({
code: -2
})
break
} else {
uni.stopBluetoothDevicesDiscovery()
clearTimeout(timer2)
clearInterval(timer1)
resolve({
code: -3
})
break
}
}
}
},
async fail(res) {
console.log('获取设备列表失败', res)
if (res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
}
}
})
}, 1000)
},
async fail(res) {
console.log('开始搜索失败', res)
if (res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
that.searchAndConnectDevice()
} else if (res.errno === 1509008) {
uni.showModal({
title: '提示',
content: '安卓手机蓝牙功能需要定位权限,请前往设置开启微信的定位权限后再试',
showCancel: false,
success(res) {
if (res.confirm) {
uni.openAppAuthorizeSetting({
success(res) {
console.log(res)
}
})
}
}
})
resolve({
code: -23
})
} else {
resolve({
code: -1
})
}
}
})
})
},
// 检查是否已添加为用户
async checkLockUser(flag = false) {
console.log('检查是否已添加为用户', this.currentLockInfo.lockUserNo)
if (this.currentLockInfo.lockUserNo === 0 || flag) {
const timestamp = parseInt(new Date().getTime() / 1000, 10)
const password = (Math.floor(Math.random() * 900000) + 100000).toString()
console.log('用户未添加,开始添加用户')
const addUserParams = {
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
: parseInt(this.currentLockInfo.startDate / 1000, 10),
expireDate:
this.currentLockInfo.endDate === 0
? 0xffffffff
: parseInt(this.currentLockInfo.endDate / 1000, 10),
useCountLimit: this.currentLockInfo.keyType === 3 ? 1 : 0xffff,
isRound: this.currentLockInfo.keyType === 4 ? 1 : 0,
weekRound:
this.currentLockInfo.keyType === 4
? this.convertWeekdaysToNumber(this.currentLockInfo.weekDays)
: 0,
startHour:
this.currentLockInfo.keyType === 4
? new Date(this.currentLockInfo.startDate).getHours()
: 0,
startMin:
this.currentLockInfo.keyType === 4
? new Date(this.currentLockInfo.startDate).getMinutes()
: 0,
endHour:
this.currentLockInfo.keyType === 4
? new Date(this.currentLockInfo.endDate).getHours()
: 0,
endMin:
this.currentLockInfo.keyType === 4
? new Date(this.currentLockInfo.endDate).getMinutes()
: 0,
role: 0,
password
}
const { code: addUserCode } = await this.addLockUser(addUserParams)
console.log('添加用户蓝牙结果', addUserCode, addUserParams)
if (addUserCode === 0) {
const { code } = await updateLockUserNoRequest({
keyId: this.keyId,
lockUserNo: this.currentLockInfo.lockUserNo
})
console.log('添加用户请求结果', code)
return true
}
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
})
console.log('清理用户蓝牙结果', cleanCode)
if (cleanCode === 0) {
return await this.checkLockUser()
}
return false
}
return addUserCode === 0x0f
}
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明文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, contentArray) {
// 拼接头部和内容
let mergerArray = new Uint8Array(headArray.length + contentArray.length)
mergerArray.set(headArray)
mergerArray.set(contentArray, 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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if (!result) {
return {
code: -1
}
}
}
const headArray = this.createPackageHeader(0, 42)
const contentArray = new Uint8Array(42)
contentArray[0] = cmdIds.getPublicKey / 256
contentArray[1] = cmdIds.getPublicKey % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
const packageArray = this.createPackageEnd(headArray, contentArray)
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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.getCommKey / 256
contentArray[1] = cmdIds.getCommKey % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 42] = keyId.charCodeAt(i)
}
for (let i = 0; i < authUid.length; i++) {
contentArray[i + 82] = authUid.charCodeAt(i)
}
contentArray.set(this.timestampToArray(nowTime), 102)
contentArray[106] = 16
const md5Array = this.md5Encrypte(
authUid + keyId,
contentArray.slice(102, 106),
this.currentLockInfo.publicKey
)
contentArray.set(md5Array, 107)
const cebArray = sm4.encrypt(contentArray, contentArray.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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.getLockStatus / 256
contentArray[1] = cmdIds.getLockStatus % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 42] = uid.charCodeAt(i)
}
contentArray.set(this.timestampToArray(nowTime), 62)
contentArray.set(this.timestampToArray(localTime), 66)
const cebArray = sm4.encrypt(contentArray, 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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.addUser / 256
contentArray[1] = cmdIds.addUser % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < authUid.length; i++) {
contentArray[i + 42] = authUid.charCodeAt(i)
}
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 62] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 102] = uid.charCodeAt(i)
}
contentArray[122] = openMode
contentArray[123] = keyType
contentArray.set(this.timestampToArray(startDate), 124)
contentArray.set(this.timestampToArray(expireDate), 128)
contentArray[132] = useCountLimit / 256
contentArray[133] = useCountLimit % 256
contentArray[134] = isRound
contentArray[135] = weekRound
contentArray[136] = startHour
contentArray[137] = startMin
contentArray[138] = endHour
contentArray[139] = endMin
contentArray[140] = role
for (let i = 0; i < password.length; i++) {
contentArray[i + 141] = password.charCodeAt(i)
}
contentArray.set(this.currentLockInfo.token || this.timestampToArray(startDate), 161)
contentArray[165] = 16
const md5Array = this.md5Encrypte(
authUid + keyId,
this.currentLockInfo.token || this.timestampToArray(startDate),
this.currentLockInfo.publicKey
)
contentArray.set(md5Array, 166)
const cebArray = sm4.encrypt(contentArray, 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) {
const $user = useUserStore()
return new Promise(resolve => {
const getWriteResultTimer = setTimeout(() => {
log.info({
code: -1,
message: `操作失败,蓝牙操作超时`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
resolve({ code: -1 })
}, 20000)
characteristicValueCallback = async data => {
// code 6 token过期,重新获取
if (data.code === 6) {
log.info({
code: 0,
message: `开门中更换过期token开始重试`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
clearTimeout(getWriteResultTimer)
resolve(await request(params))
} else {
clearTimeout(getWriteResultTimer)
resolve(data)
}
}
})
},
// 开门
async openDoor(data) {
const $user = useUserStore()
try {
log.info({
code: 0,
message: `开始开门`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
// 确认蓝牙状态正常
if (this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
if (this.bluetoothStatus === -1) {
log.info({
code: -25,
message: `开门失败,未知蓝牙状态, ${this.bluetoothStatus}`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (this.bluetoothStatus === 1) {
log.info({
code: -20,
message: `开门失败,蓝牙未开启, ${this.bluetoothStatus}`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (this.bluetoothStatus === 2) {
log.info({
code: -21,
message: `开门失败,小程序蓝牙权限被禁用, ${this.bluetoothStatus}`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (this.bluetoothStatus === 3) {
log.info({
code: -26,
message: `开门失败,蓝牙未开启且小程序蓝牙功能被禁用, ${this.bluetoothStatus}`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
}
return {
code: -1
}
}
log.info({
code: 0,
message: `开门中,蓝牙权限正常, ${this.bluetoothStatus}`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
// 确认设备连接正常
if (!this.currentLockInfo.connected) {
const searchResult = await this.searchAndConnectDevice()
if (searchResult.code === 0) {
log.info({
code: searchResult.code,
message: `开门中,已搜索到设备`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (searchResult.code === -1) {
log.info({
code: searchResult.code,
message: `开门失败,搜索失败`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (searchResult.code === -2) {
log.info({
code: searchResult.code,
message: `开门失败,锁已被重置`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (searchResult.code === -3) {
log.info({
code: searchResult.code,
message: `开门失败设备已找到但uuid异常`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (searchResult.code === -4) {
log.info({
code: searchResult.code,
message: `开门失败,未搜索到操作设备`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (searchResult.code === -22) {
log.info({
code: searchResult.code,
message: `开门失败,微信附近设备权限未开启`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else if (searchResult.code === -23) {
log.info({
code: searchResult.code,
message: `开门失败,微信定位权限未开启`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
}
if (searchResult.code !== 0) {
return { code: -1 }
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
const result = await this.connectBluetoothDevice()
if (result) {
log.info({
code: 0,
message: `开门中,设备连接成功`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
} else {
log.info({
code: -1,
message: `开门失败,设备连接失败`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
}
console.log('连接结果', result)
if (!result) {
return {
code: -1
}
}
}
log.info({
code: 0,
message: `开门中,设备已连接`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (!checkResult) {
log.info({
code: -1,
message: `开门失败,用户添加失败`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
return {
code: -1
}
}
log.info({
code: 0,
message: `开门中,用户已添加`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.openDoor / 256
contentArray[1] = cmdIds.openDoor % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 42] = uid.charCodeAt(i)
}
contentArray[62] = openMode
contentArray.set(this.timestampToArray(openTime), 63)
console.log('开门时token', this.currentLockInfo.token)
contentArray.set(this.currentLockInfo.token || this.timestampToArray(openTime), 67)
contentArray[71] = 16
const md5Array = this.md5Encrypte(
name + uid,
this.currentLockInfo.token || this.timestampToArray(openTime),
this.currentLockInfo.signKey
)
contentArray.set(md5Array, 72)
for (let i = 0; i < onlineToken.length; i++) {
contentArray[i + 88] = onlineToken.charCodeAt(i)
}
const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = this.createPackageEnd(headArray, cebArray)
log.info({
code: 0,
message: `开门中,开始写入`,
data: {
lockName: this.currentLockInfo.name,
lockId: this.currentLockInfo.lockId,
uid: $user.userInfo.uid,
nickname: $user.userInfo.nickname,
mobile: $user.userInfo.mobile,
email: $user.userInfo.email
}
})
this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.openDoor, data)
} catch (e) {
log.error({
code: -1,
error: e,
message: `开门失败,未知错误`
})
return {
code: -1
}
}
},
// 清理用户
async cleanLockUser(data) {
// 确认蓝牙状态正常
if (this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if (!this.currentLockInfo.connected) {
const searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.cleanUser / 256
contentArray[1] = cmdIds.cleanUser % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < authUid.length; i++) {
contentArray[i + 42] = authUid.charCodeAt(i)
}
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 62] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 102] = uid.charCodeAt(i)
}
contentArray[122] = userNoList.length / 256
contentArray[123] = userNoList.length % 256
for (let i = 0; i < userNoList.length; i++) {
contentArray[i + 124] = userNoList[i]
}
contentArray.set(
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
124 + userNoList.length
)
contentArray[128 + userNoList.length] = 16
const md5Array = this.md5Encrypte(
authUid + keyId,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.publicKey
)
contentArray.set(md5Array, 129 + userNoList.length)
const cebArray = sm4.encrypt(contentArray, 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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 } = data
const length = 2 + 40 + 20 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
const contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.resetDevice / 256
contentArray[1] = cmdIds.resetDevice % 256
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < authUid.length; i++) {
contentArray[i + 42] = authUid.charCodeAt(i)
}
contentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 62)
contentArray[66] = 16
const md5Array = this.md5Encrypte(
name,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.publicKey
)
contentArray.set(md5Array, 67)
const cebArray = sm4.encrypt(contentArray, 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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.expandCmd / 256
contentArray[1] = cmdIds.expandCmd % 256
// 子命令
contentArray[2] = subCmdIds.resetLockPassword
contentArray[3] = length - 4
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 4] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 44] = uid.charCodeAt(i)
}
contentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 64)
contentArray[68] = 16
const md5Array = this.md5Encrypte(
keyId + uid,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.signKey
)
contentArray.set(md5Array, 69)
const cebArray = sm4.encrypt(contentArray, 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 searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.expandCmd / 256
contentArray[1] = cmdIds.expandCmd % 256
// 子命令
contentArray[2] = subCmdIds.setLockPassword
contentArray[3] = length - 3
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 4] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 44] = uid.charCodeAt(i)
}
contentArray[64] = pwdNo / 256
contentArray[65] = pwdNo % 256
contentArray[66] = operate
contentArray[67] = isAdmin
for (let i = 0; i < pwd.length; i++) {
contentArray[i + 68] = pwd.charCodeAt(i)
}
contentArray[88] = userCountLimit / 256
contentArray[89] = userCountLimit % 256
contentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 90)
contentArray.set(this.timestampToArray(startTime), 94)
contentArray.set(this.timestampToArray(endTime), 98)
contentArray[102] = 16
const md5Array = this.md5Encrypte(
keyId + uid,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.signKey
)
contentArray.set(md5Array, 103)
const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.setLockPassword, data)
},
parseTimeToList(timeString) {
let timeList = [0, 0, 0, 0]
if (timeString.includes(':')) {
let timeParts = timeString.split(':')
timeList[2] = parseInt(timeParts[0], 10)
timeList[3] = parseInt(timeParts[1], 10)
}
return new Uint8Array(timeList)
},
// 注册身份认证
async registerAuthentication(data) {
// 确认蓝牙状态正常
if (this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if (!this.currentLockInfo.connected) {
const searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if (!result) {
return {
code: -1
}
}
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (!checkResult) {
return {
code: -1
}
}
let {
type,
keyId,
uid,
no,
operate,
isAdmin,
userCountLimit,
isForce,
isRound,
weekDays,
startDate,
endDate,
startTime,
endTime
} = data
startDate = Math.floor(startDate / 1000)
endDate = Math.floor(endDate / 1000)
const length = 2 + 1 + 1 + 40 + 20 + 2 + 2 + 1 + 1 + 1 + 4 + 1 + 1 + 4 + 4 + 4 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
const contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.expandCmd / 256
contentArray[1] = cmdIds.expandCmd % 256
// 子命令
if (type === 'card') {
contentArray[2] = subCmdIds.registerCard
} else if (type === 'fingerprint') {
contentArray[2] = subCmdIds.registerFingerprint
} else if (type === 'face') {
contentArray[2] = subCmdIds.registerFace
} else if (type === 'remote') {
contentArray[2] = subCmdIds.registerRemote
} else if (type === 'palmVein') {
contentArray[2] = subCmdIds.registerPalmVein
}
contentArray[3] = length - 3
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 4] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 44] = uid.charCodeAt(i)
}
contentArray[64] = (no || 0) / 256
contentArray[65] = (no || 0) % 256
contentArray[66] = (userCountLimit || 0xffff) / 256
contentArray[67] = (userCountLimit || 0xffff) % 256
contentArray[68] = operate
contentArray[69] = isAdmin || 0
contentArray[70] = isForce || 0
contentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 71)
contentArray[75] = isRound
contentArray[76] = this.convertWeekdaysToNumber(weekDays)
contentArray.set(this.timestampToArray(startDate), 77)
contentArray.set(this.timestampToArray(endDate), 81)
if (isRound) {
contentArray.set(this.parseTimeToList(startTime), 85)
contentArray.set(this.parseTimeToList(endTime), 89)
} else {
contentArray.set(new Uint8Array([0, 0, 0, 0]), 85)
contentArray.set(new Uint8Array([0, 0, 0, 0]), 89)
}
contentArray[93] = 16
const md5Array = this.md5Encrypte(
keyId + uid,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.signKey
)
contentArray.set(md5Array, 94)
const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.registerAuthentication, data)
},
// 注册身份认证取消
async registerAuthenticationCancel(data) {
const { type, keyId, uid } = data
const length = 2 + 1 + 1 + 40 + 20 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
const contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.expandCmd / 256
contentArray[1] = cmdIds.expandCmd % 256
// 子命令
if (type === 'card') {
contentArray[2] = subCmdIds.registerCardCancel
} else if (type === 'fingerprint') {
contentArray[2] = subCmdIds.registerFingerprintCancel
} else if (type === 'face') {
contentArray[2] = subCmdIds.registerFaceCancel
} else if (type === 'remote') {
contentArray[2] = subCmdIds.registerRemoteCancel
} else if (type === 'palmVein') {
contentArray[2] = subCmdIds.registerPalmVeinCancel
}
contentArray[3] = length - 3
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 4] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 44] = uid.charCodeAt(i)
}
contentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 64)
contentArray[68] = 16
const md5Array = this.md5Encrypte(
keyId + uid,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.signKey
)
contentArray.set(md5Array, 69)
const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.registerAuthenticationCancel, data)
},
// 获取操作记录
async syncRecord(params) {
const { uid, keyId } = params
const { code, data, message } = await this.syncSingleRecord({
uid,
keyId
})
if (code === 0) {
if (data?.count === 10) {
return await this.syncSingleRecord({
uid,
keyId
})
}
return { code, data, message }
}
return { code, data, message }
},
// 获取单次操作记录
async syncSingleRecord(data) {
// 确认蓝牙状态正常
if (this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if (!this.currentLockInfo.connected) {
const searchResult = await this.searchAndConnectDevice()
if (searchResult.code !== 0) {
return searchResult
}
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: searchResult.data.deviceId
})
console.log('设备ID', this.currentLockInfo.deviceId)
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 logsCount = 10
const timeResult = await getLastRecordTimeRequest({
lockId: this.currentLockInfo.lockId
})
if (timeResult.code !== 0) {
return timeResult
}
const operateDate = Math.ceil(timeResult.data.operateDate / 1000)
const currentDate = Math.ceil(timeResult.data.currentDate / 1000)
const length = 2 + 1 + 1 + 40 + 20 + 2 + 4 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
const contentArray = new Uint8Array(length)
contentArray[0] = cmdIds.expandCmd / 256
contentArray[1] = cmdIds.expandCmd % 256
// 子命令
contentArray[2] = subCmdIds.syncRecord
contentArray[3] = length - 3
for (let i = 0; i < keyId.length; i++) {
contentArray[i + 4] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
contentArray[i + 44] = uid.charCodeAt(i)
}
contentArray[64] = logsCount / 256
contentArray[65] = logsCount % 256
contentArray.set(this.timestampToArray(operateDate), 66)
contentArray.set(this.timestampToArray(currentDate), 70)
contentArray[74] = 16
const md5Array = this.md5Encrypte(
uid + keyId,
this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.currentLockInfo.bluetooth.publicKey
)
contentArray.set(md5Array, 75)
const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.syncSingleRecord, data)
}
}
})