2053 lines
65 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.

import { defineStore } from 'pinia'
import { sm4 } from 'sm-crypto'
import { buildNumber, configs, version } from '@/starCloud/env'
import {
addCustomPasswordRequest,
bindLockRequest,
changeAdminKeyboardPwdRequest,
deleteLockRequest,
deletePasswordRequest,
getLastRecordTimeRequest,
getLockDetailRequest,
getLockNetTokenRequest,
getOfflinePasswordRequest,
getServerDatetimeRequest,
getStarCloudToken,
getUserNoListRequest,
removeBadLockRequest,
starCloudCreateUser,
updateElectricQuantityRequest,
updateLockUserNoRequest,
updatePasswordRequest,
uploadRecordRequest
} from '@/starCloud/api'
import { getStorage, removeStorage, setStorage } from '@/starCloud/storage'
import {
closeBLEConnection,
closeBluetoothAdapter,
createBLEConnection,
getBluetoothDevices,
offBluetoothAdapterStateChange,
onBLECharacteristicValueChange,
Result,
searchAndConnectDevice,
startBluetoothDevicesDiscovery,
stopBluetoothDevicesDiscovery,
writeBLECharacteristicValue
} from '@/starCloud/basic'
import {
arrayToTimestamp,
convertWeekdaysToNumber,
createPackageEnd,
md5Encrypt,
timestampToArray,
uint8ArrayToString
} from '@/starCloud/format'
import log from '@/starCloud/log'
/**
* 离线密码
* 该功能无需蓝牙交互直接请求服务端,详细参数说明请看星云接口文档
* @typedef {Object} OfflinePassword
* @property {String} keyboardPwdName - 密码名称
* @property {Number} keyboardPwdType - 密码类型
* @property {Number} isCoerced - 胁迫 1:胁迫 2:非胁迫
* @property {Number} startDate - 开始时间
* @property {Number} endDate - 结束时间
* @property {Number} hoursStart - 开始小时
* @property {Number} hoursEnd - 结束小时
*/
/**
* 自定义密码
* @typedef {Object} CustomPassword
* @property {String} keyboardPwdName - 密码名称
* @property {Number} keyboardPwdType - 密码类型
* @property {Number} isCoerced - 胁迫 1:胁迫 2:非胁迫
* @property {Number} startDate - 开始时间
* @property {Number} endDate - 结束时间
* @property {Number} keyboardPwd - 密码
* @property {Number} addType - 添加方式当前仅支持1 1:蓝牙 2:网关
* @property {Number} operate - 操作类型0:注册 1修改
* @property {Number} pwdRight - 是否是管理员密码0:否 1:是
*/
/**
* 账户信息信息
* @typedef {Object} AccountInfo
* @property {Number} uid 用户ID
* @property {String} username 用户名
* @property {String} password 密码
* @property {String} token token 非必填
*/
/**
* 锁信息
* @typedef {Object} lockInfo
* @property {Number} keyId - 钥匙ID
* @property {Number} lockId - 锁ID
* @property {String} lockName - 锁名称
* @property {String} lockAlias - 锁别名
* @property {Number} electricQuantity - 电量
* @property {Number} electricQuantityStandby - 备用电量
* @property {Number} electricQuantityDate - 电量更新时间
* @property {String} fwVersion - 固件版本
* @property {String} hwVersion - 硬件版本
* @property {Number} keyType - 钥匙类型
* @property {Number} passageMode - 常开模式
* @property {Number} userType - 用户类型
* @property {Number} startDate - 有效期开始时间
* @property {Number} endDate - 有效期结束时间
* @property {Object} weekDays - 循环周期
* @property {Number} remoteEnable - 是否支持远程开锁0-未知1-是2-否
* @property {Number} faceAuthentication - 是否实名认证0-未知1-是2-否
* @property {Number} lastFaceValidateTime - 最后一次人脸验证时间
* @property {Number} nextFaceValidateTime - 下次人脸验证时间
* @property {Number} keyRight - 是否授权管理员钥匙: 0-未知1-是2-否
* @property {Number} keyStatus - 钥匙状态
* @property {Object} bluetooth - 蓝牙
* @property {Number} sendDate - 发送时间
* @property {Number} isLockOwner - 是否是锁主
* @property {Object} lockFeature - 锁特征
* @property {Object} lockSetting - 锁设置
* @property {Number} lockUserNo - 用户编号
* @property {Number} senderUserId - 发送者用户ID
* @property {Number} isOnlyManageSelf - 如果是授权管理员此字段区分是否仅管理自己发的钥匙
* @property {Number} restoreCount - 重置次数
* @property {String} model - 模式
*/
/**
* 锁蓝牙信息
* @typedef {Object} bluetooth
* @property {String} bluetoothDeviceId - 设备ID
* @property {String} bluetoothDeviceName - 设备名称
* @property {String} publicKey - 公钥
* @property {String} privateKey - 私钥
* @property {String} signKey - 签名密钥
* @property {String} passwordKey - 密码密钥
*/
/**
* 锁设置信息
* @typedef {Object} lockSetting
* @property {Number} appUnlockOnline - 开门是否需要联网
*/
/**
* 请求返回数据
* @typedef {Object} requestData
* @property {String} userNos - 设备ID
*/
// 命令ID
const cmdIds = {
// 获取公钥
getPublicKey: 0x3090,
// 获取私钥
getCommKey: 0x3091,
// 获取锁状态
getLockStatus: 0x3040,
// 新增用户
addUser: 0x3001,
// 开门
openDoor: 0x3005,
// 重置设备
resetDevice: 0x3004,
// 清理用户
cleanUser: 0x300c,
// 扩展命令
expandCmd: 0x3030
}
// 子命令ID
const subCmdIds = {
// 修改管理员密码
updateAdminPassword: 2,
// 设置开锁密码
setLockPassword: 3,
// 重置开锁密码
resetLockPassword: 19,
// 同步开门记录
syncOpenRecord: 41
}
// 特性值回调
let characteristicValueCallback = null
// 完整数据
let completeArray
// 完整内容数据长度
let length
// 请求参数
let requestParams = null
// 计时器
let timer
export const useStarCloudStore = defineStore('starCloud', {
state() {
return {
// 环境
env: null,
// 客户端Id
clientId: null,
// 客户端密码
clientSecret: null,
// 小程序环境
envVersion: '',
// 锁信息
lockInfo: null,
// 服务器时间
serverTimestamp: 0,
// 时间差
timeDifference: 0,
// 搜索设备列表
searchDeviceList: [],
// 星云用户信息
userInfo: null,
// 账户信息
accountInfo: null,
// 消息序号
messageCount: 1,
// 是否上报日志
isReportLog: false
}
},
actions: {
/**
* 初始化星云
* @param params
* @param {String} params.clientId 客户端Id
* @param {String} params.clientSecret 客户端密码
* @param {String} params.env 环境
* @param {Boolean} params.isReportLog 是否上报日志
*/
initStarCloud(params) {
const { clientId, clientSecret, env } = params
const appInfo = uni.getAccountInfoSync()
this.envVersion = appInfo.miniProgram.envVersion
this.isReportLog = params.isReportLog
this.env = env || 'XHJ'
this.clientId = clientId
this.clientSecret = clientSecret
// 监听特性值变化
onBLECharacteristicValueChange(this.listenCharacteristicValue)
},
/**
* 注册星云
* @returns {Promise<Result>}
*/
async register() {
const { code, data, message } = await starCloudCreateUser({
clientId: this.clientId,
clientSecret: this.clientSecret
})
return new Result(code, data, message)
},
/**
* 退出登录
* @param params
* @param {Number} params.uid 用户ID
*/
logout(params) {
const { uid } = params
if (this.accountInfo?.uid === uid) {
this.userInfo = null
this.accountInfo = null
this.lockInfo = null
removeStorage('starCloudToken')
}
},
/**
* 选择锁
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {Number} params.lockId 锁ID
* @returns {Promise<Result>}
*/
async selectLock(params) {
const { accountInfo, lockId } = params
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
const { code, data, message } = await getLockDetailRequest({
lockId
})
if (code === Result.Success.code) {
this.lockInfo = data
let lockList = getStorage('starLockList')
if (!lockList) {
lockList = {}
}
if (lockList[accountInfo.uid]) {
const index = lockList[accountInfo.uid].findIndex(item => item.lockId === lockId)
if (index === -1) {
lockList[accountInfo.uid].push(this.lockInfo)
} else {
this.lockInfo.token = lockList[accountInfo.uid][index].token
lockList[accountInfo.uid][index] = this.lockInfo
}
setStorage('starLockList', lockList)
} else {
lockList[accountInfo.uid] = [this.lockInfo]
setStorage('starLockList', lockList)
}
} else {
const lockList = getStorage('starLockList')
if (lockList[accountInfo.uid]) {
const index = lockList[accountInfo.uid].findIndex(item => item.lockId === lockId)
if (index !== -1) {
this.lockInfo = lockList[accountInfo.uid][index]
return new Result(Result.Success.code, this.lockInfo)
}
}
}
return new Result(code, data, message)
},
/**
* 开门
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {String} params.type 开门方式 close: 关门 open: 开门
* @param {Boolean} params.disconnect 操作后是否断开连接
* @returns {Promise<Result>}
*/
async openDoor(params) {
const { accountInfo, type } = params
log.info(
new Result(
Result.Success.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`开始开门`
)
)
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
log.info(
new Result(
result.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`登录星云账号: ${result.message}`
)
)
// 确认设备连接正常
if (!params.connected) {
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName
)
log.info(
new Result(
searchResult.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`连接设备: ${searchResult.message}`
)
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.data)
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (checkResult.code !== Result.Success.code) {
return checkResult
}
log.info(
new Result(
checkResult.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`确认是否为锁用户: ${checkResult.message}`
)
)
// 是否需要联网
let onlineToken = ''
if (this.lockInfo.lockSetting.appUnlockOnline) {
const result = await this.getNetToken()
if (result.code === Result.Success.code) {
onlineToken = result.data.token
} else {
return result
}
}
log.info(
new Result(
checkResult.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`判断是否需要联网token: ${this.lockInfo.lockSetting.appUnlockOnline}`
)
)
// 开门方式
let openMode
if (type === 'close') {
openMode = this.lockInfo.lockSetting.appUnlockOnline ? 33 : 32
} else {
openMode = this.lockInfo.lockSetting.appUnlockOnline ? 1 : 0
}
const name = this.lockInfo.bluetooth.bluetoothDeviceName
const uid = this.accountInfo.uid.toString()
const openTime = Math.ceil(new Date().getTime() / 1000) + this.timeDifference
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(timestampToArray(openTime), 63)
console.log('开门时token', this.lockInfo.token)
contentArray.set(this.lockInfo.token || timestampToArray(openTime), 67)
contentArray[71] = 16
const md5Array = md5Encrypt(
name + uid,
this.lockInfo.token || timestampToArray(openTime),
this.lockInfo.bluetooth.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.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
log.info(
new Result(
Result.Success.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`开始写入`
)
)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
log.info(
new Result(
writeResult.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
uid: accountInfo.uid,
time: new Date().getTime()
},
`写入完成:${writeResult.message}`
)
)
return this.getWriteResult(this.openDoor, params)
},
/**
* 获取离线密码
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {OfflinePassword} params.password 密码信息
* @returns {Promise<Result>}
*/
async getOfflinePassword(params) {
const { accountInfo, password } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
return await getOfflinePasswordRequest({
...password,
lockId: this.lockInfo.lockId
})
},
/**
* 自定义密码
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {CustomPassword} params.password 密码信息
* @param {Boolean} params.disconnect 操作后是否断开连接
* @returns {Promise<Result>}
*/
async customPassword(params) {
const { accountInfo, password } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
// 确认设备连接正常
if (!params.connected) {
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.data)
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (checkResult.code !== Result.Success.code) {
return checkResult
}
requestParams = password
let { pwdNo, operate, keyboardPwd, startDate, endDate, pwdRight } = password
const uid = this.accountInfo.uid.toString()
const keyId = this.lockInfo.keyId.toString()
const isAdmin = pwdRight
const userCountLimit = 0xffff
startDate = Math.floor(startDate / 1000)
endDate = Math.floor(endDate / 1000)
keyboardPwd = keyboardPwd.toString()
if (!pwdNo) {
pwdNo = 0
}
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 < keyboardPwd.length; i++) {
contentArray[i + 68] = keyboardPwd.charCodeAt(i)
}
contentArray[88] = userCountLimit / 256
contentArray[89] = userCountLimit % 256
contentArray.set(this.lockInfo.token || new Uint8Array([0, 0, 0, 0]), 90)
contentArray.set(timestampToArray(startDate), 94)
contentArray.set(timestampToArray(endDate), 98)
contentArray[102] = 16
const md5Array = md5Encrypt(
keyId + uid,
this.lockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.lockInfo.bluetooth.signKey
)
contentArray.set(md5Array, 103)
const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.customPassword, params)
},
// 搜索设备
async searchDevice(callback) {
const result = await startBluetoothDevicesDiscovery()
if (result.code === Result.Success.code) {
const timestamp = new Date().getTime()
let queryFlag = false
timer = setInterval(async () => {
const queryResult = await getBluetoothDevices()
if (queryResult.code === Result.Success.code) {
const deviceList = queryResult.data
if (queryFlag === false && deviceList.length > 0) {
queryFlag = true
}
if (new Date().getTime() - timestamp > 10000 && !queryFlag) {
if (timer) {
clearInterval(timer)
}
callback(Result.NotAvailableWeChatNearbyDevicesEmpty)
}
const list = []
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') {
list.push(deviceList[i])
}
}
}
this.searchDeviceList = list
callback(
new Result(Result.Success.code, {
list
})
)
} else {
callback(queryResult)
}
}, 1000)
} else {
callback(result)
}
},
// 停止搜索
async stopSearchDevice() {
console.log('停止搜索')
if (timer) {
clearInterval(timer)
}
return await stopBluetoothDevicesDiscovery()
},
/**
* 绑定设备
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {String} params.name 设备名称
* @returns {Promise<Result>}
*/
async bindDevice(params) {
const { accountInfo, name } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
const device = this.searchDeviceList.find(item => item.name === name)
const connectResult = await createBLEConnection(device.deviceId)
if (connectResult.code === Result.Success.code) {
this.updateLockInfo({
...connectResult.data,
bluetooth: {
bluetoothDeviceId: device.deviceId,
bluetoothDeviceName: device.name
}
})
const publicKeyResult = await this.getPublicKey()
if (publicKeyResult.code !== Result.Success.code) {
return publicKeyResult
}
const commKeyResult = await this.getCommKey()
if (commKeyResult.code !== Result.Success.code) {
return commKeyResult
}
const lockStatusResult = await this.getLockStatus()
if (lockStatusResult.code !== Result.Success.code) {
return lockStatusResult
}
const timestamp = Math.ceil(new Date().getTime() / 1000)
const password = (Math.floor(Math.random() * 900000) + 100000).toString()
const addUserResult = await this.addLockUser({
params: {
name: this.lockInfo.bluetooth.bluetoothDeviceName,
keyId: '0',
authUid: this.accountInfo.uid.toString(),
uid: this.accountInfo.uid.toString(),
openMode: 1,
keyType: 0,
startDate: timestamp,
expireDate: 0xffffffff,
useCountLimit: 0xffff,
isRound: 0,
weekRound: 0,
startHour: 0,
startMin: 0,
endHour: 0,
endMin: 0,
role: 0xff,
password
},
disconnect: true
})
if (addUserResult.code !== Result.Success.code) {
return addUserResult
}
offBluetoothAdapterStateChange()
closeBluetoothAdapter()
const params = {
lockAlias: this.lockInfo.bluetooth.bluetoothDeviceName,
lockInfo: {
...this.lockInfo.lockConfig,
adminPwd: password
},
bluetooth: this.lockInfo.bluetooth,
lockUserNo: this.lockInfo.lockUserNo,
pwdTimestamp: this.lockInfo.pwdTimestamp,
featureValue: this.lockInfo.featureValue,
featureSettingValue: this.lockInfo.featureSettingValue,
featureSettingParams: this.lockInfo.featureSettingParams
}
const bindLockResult = await bindLockRequest(params)
if (bindLockResult.code === Result.Success.code) {
this.updateLockInfo({
lockId: bindLockResult.data.lockId,
keyId: bindLockResult.data.keyId,
adminPwd: password
})
}
return new Result(
bindLockResult.code,
{
lock: this.lockInfo
},
bindLockResult.message
)
}
return connectResult
},
/**
* 移除坏锁
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {List[int]} params.lockIds 锁Id列表
* @returns {Promise<Result>}
*/
async removeBadLock(params) {
const { accountInfo, lockIds } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
const { code, message } = await removeBadLockRequest({
lockIds: params.lockIds
})
if (code === Result.Success.code) {
const lockList = getStorage('starLockList')
if (lockList[accountInfo.uid]) {
lockIds.forEach(lockId => {
const index = lockList[accountInfo.uid].findIndex(item => item.lockId === lockId)
if (index !== -1) {
lockList[accountInfo.uid].splice(index, 1)
}
})
setStorage('starLockList', lockList)
}
}
return new Result(code, {}, message)
},
/**
* 删除锁
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
*/
async deleteLock(params) {
const { accountInfo } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
// 确认设备连接正常
if (!params.connected) {
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.data)
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (checkResult.code !== Result.Success.code) {
return checkResult
}
const {
token,
bluetooth: { publicKey, privateKey }
} = this.lockInfo
const authUid = this.lockInfo.uid.toString()
const name = this.lockInfo.bluetooth.bluetoothDeviceName
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(token || new Uint8Array([0, 0, 0, 0]), 62)
contentArray[66] = 16
const md5Array = md5Encrypt(name, token || new Uint8Array([0, 0, 0, 0]), publicKey)
contentArray.set(md5Array, 67)
const cebArray = sm4.encrypt(contentArray, privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.deleteLock, params)
},
/**
* 修改管理员密码
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {String} params.adminPwd 管理员密码
* @param {Boolean} params.disconnect 操作后是否断开连接
* @returns {Promise<Result>}
*/
async updateAdminPassword(params) {
const { adminPwd, accountInfo } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
// 确认设备连接正常
if (!params.connected) {
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.data)
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (checkResult.code !== Result.Success.code) {
return checkResult
}
requestParams = params
const uid = this.lockInfo.uid.toString()
const keyId = this.lockInfo.keyId.toString()
const pwdNo = 1
const userCountLimit = 0xff
const startDate = Math.floor(this.lockInfo.startDate / 1000)
const endDate = Math.floor(this.lockInfo.endDate / 1000)
const length = 2 + 1 + 1 + 40 + 20 + 2 + 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.updateAdminPassword
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
for (let i = 0; i < adminPwd.length; i++) {
contentArray[i + 66] = adminPwd.charCodeAt(i)
}
contentArray[86] = userCountLimit / 256
contentArray[87] = userCountLimit % 256
contentArray.set(this.lockInfo.token || new Uint8Array([0, 0, 0, 0]), 88)
contentArray.set(timestampToArray(startDate), 92)
contentArray.set(timestampToArray(endDate), 96)
contentArray[100] = 16
const md5Array = md5Encrypt(
keyId + uid,
this.lockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.lockInfo.bluetooth.signKey
)
contentArray.set(md5Array, 101)
const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.updateAdminPassword, params)
},
/**
* 同步全部开门记录
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {Boolean} params.disconnect 操作后是否断开连接
* @returns {Promise<Result>}
*/
async syncAllOpenRecord(params) {
const { accountInfo, disconnect } = params
const { code, data, message } = await this.syncOpenRecord({
accountInfo,
disconnect: false
})
if (code === Result.Success.code) {
if (data.count === 10) {
return await this.syncAllOpenRecord({
accountInfo,
disconnect
})
}
if (disconnect) {
await this.disconnectDevice()
}
return new Result(code, data, message)
}
return new Result(code, data, message)
},
/**
* 同步开门记录
* @param params
* @param {AccountInfo} params.accountInfo 账号信息
* @param {Boolean} params.disconnect 操作后是否断开连接
* @returns {Promise<Result>}
*/
async syncOpenRecord(params) {
const { accountInfo } = params
// 设置执行账号
const result = await this.login(accountInfo)
if (result.code !== Result.Success.code) {
return result
}
// 确认设备连接正常
if (!params.connected) {
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.data)
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (checkResult.code !== Result.Success.code) {
return checkResult
}
const uid = this.lockInfo.uid.toString()
const keyId = this.lockInfo.keyId.toString()
const logsCount = 10
const timeResult = await getLastRecordTimeRequest({
lockId: this.lockInfo.lockId
})
if (timeResult.code !== Result.Success.code) {
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.syncOpenRecord
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(timestampToArray(operateDate), 66)
contentArray.set(timestampToArray(currentDate), 70)
contentArray[74] = 16
const md5Array = md5Encrypt(
uid + keyId,
this.lockInfo.token || new Uint8Array([0, 0, 0, 0]),
this.lockInfo.bluetooth.publicKey
)
contentArray.set(md5Array, 75)
const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.syncOpenRecord, params)
},
// 获取服务器时间
async getServerTimestamp() {
const { code, data, message } = await getServerDatetimeRequest({})
if (code === Result.Success.code) {
this.serverTimestamp = Math.ceil(data.date / 1000)
this.timeDifference = Math.ceil((data.date - new Date().getTime()) / 1000)
}
return new Result(code, data, message)
},
/**
* 清理用户
* @returns {Promise<Result>}
*/
async cleanLockUser() {
// 确认设备连接正常
const searchResult = await searchAndConnectDevice(this.lockInfo.bluetooth.bluetoothDeviceName)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.data)
// 获取并处理锁信息
let { uid: authUid, keyId, token, bluetooth } = this.lockInfo
let { uid } = this.userInfo
authUid = authUid.toString()
uid = uid.toString()
keyId = keyId.toString()
const name = bluetooth.bluetoothDeviceName
// 获取用户列表
const { code: requestCode, data: requestData } = await getUserNoListRequest({
lockId: this.lockInfo.lockId
})
console.log('获取用户列表请求结果', requestCode, requestData)
if (requestCode !== 0) return Result.Fail
const userNoList = requestData.userNos
// 组装发送数据
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(token || new Uint8Array([0, 0, 0, 0]), 124 + userNoList.length)
contentArray[128 + userNoList.length] = 16
const md5Array = md5Encrypt(
authUid + keyId,
token || new Uint8Array([0, 0, 0, 0]),
bluetooth.publicKey
)
contentArray.set(md5Array, 129 + userNoList.length)
const cebArray = sm4.encrypt(contentArray, bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.cleanLockUser, { disconnect: false })
},
/**
* 登录星云
* @param {Object} accountInfo
* @returns {Promise<Result>}
*/
async login(accountInfo) {
let accounts = getStorage('starCloudAccount')
let userInfos = getStorage('starCloudUser')
if (!accounts) {
accounts = {}
}
if (!userInfos) {
userInfos = {}
}
this.accountInfo = accounts[accountInfo.uid]
if (this.accountInfo && this.accountInfo.token) {
this.userInfo = userInfos[accountInfo.uid]
setStorage('starCloudToken', this.accountInfo.token)
this.getServerTimestamp().then(() => {})
return Result.Success
}
const {
code,
data: userInfo,
message
} = await getStarCloudToken({
username: accountInfo.username,
password: accountInfo.password,
clientId: this.clientId,
clientSecret: this.clientSecret
})
if (code === Result.Success.code) {
this.userInfo = userInfo
this.accountInfo = {
username: accountInfo.username,
password: accountInfo.password,
token: userInfo.access_token,
uid: userInfo.uid
}
setStorage('starCloudToken', userInfo.access_token)
accounts[userInfo.uid] = {
uid: userInfo.uid,
username: accountInfo.username,
password: accountInfo.password,
token: userInfo.access_token
}
setStorage('starCloudAccount', accounts)
userInfos[userInfo.uid] = userInfo
setStorage('starCloudUser', userInfo)
// 获取服务器时间
this.getServerTimestamp().then(() => {})
}
return new Result(code, {}, message)
},
// 获取公钥
async getPublicKey() {
const headArray = this.createPackageHeader(0, 42)
const contentArray = new Uint8Array(42)
contentArray[0] = cmdIds.getPublicKey / 256
contentArray[1] = cmdIds.getPublicKey % 256
const name = this.lockInfo.bluetooth.bluetoothDeviceName
for (let i = 0; i < name.length; i++) {
contentArray[i + 2] = name.charCodeAt(i)
}
const packageArray = createPackageEnd(headArray, contentArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.getPublicKey, { disconnect: false })
},
// 获取私钥
async getCommKey() {
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
const name = this.lockInfo.bluetooth.bluetoothDeviceName
const keyId = '0'
const authUid = this.accountInfo.uid.toString()
await this.getServerTimestamp()
const nowTime = this.serverTimestamp
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(timestampToArray(nowTime), 102)
contentArray[106] = 16
const md5Array = md5Encrypt(
authUid + keyId,
contentArray.slice(102, 106),
this.lockInfo.bluetooth.publicKey
)
contentArray.set(md5Array, 107)
const cebArray = sm4.encrypt(contentArray, contentArray.slice(2, 18), {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.getCommKey, { disconnect: false })
},
// 获取锁状态
async getLockStatus() {
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
const name = this.lockInfo.bluetooth.bluetoothDeviceName
const uid = this.accountInfo.uid.toString()
await this.getServerTimestamp()
const nowTime = this.serverTimestamp
const date = new Date()
const localTime = this.serverTimestamp - date.getTimezoneOffset() * 60
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(timestampToArray(nowTime), 62)
contentArray.set(timestampToArray(localTime), 66)
const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.getLockStatus, { disconnect: false })
},
// 获取联网token
async getNetToken() {
const { code, data, message } = await getLockNetTokenRequest({
lockId: this.lockInfo.lockId
})
return new Result(code, data, message)
},
// 添加用户
async addLockUser(params) {
const { params: data } = params
// 确认设备连接正常
if (!params.connected) {
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName,
data.role !== 0xff
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
this.updateLockInfo(searchResult.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 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(timestampToArray(startDate), 124)
contentArray.set(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.lockInfo.token || timestampToArray(startDate), 161)
contentArray[165] = 16
const md5Array = md5Encrypt(
authUid + keyId,
this.lockInfo.token || timestampToArray(startDate),
this.lockInfo.bluetooth.publicKey
)
contentArray.set(md5Array, 166)
const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
const packageArray = createPackageEnd(headArray, cebArray)
const writeResult = await writeBLECharacteristicValue(
this.lockInfo.deviceId,
this.lockInfo.serviceId,
this.lockInfo.writeCharacteristicId,
packageArray
)
if (writeResult.code !== Result.Success.code) {
return writeResult
}
return this.getWriteResult(this.addLockUser, params)
},
// 获取写入结果
getWriteResult(request, params) {
return new Promise(resolve => {
const getWriteResultTimer = setTimeout(() => {
resolve(Result.Fail)
}, 20000)
characteristicValueCallback = async data => {
// code 6 token过期,重新获取
if (data.code === Result.NotTokenLock.code) {
log.info(
new Result(
data.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
time: new Date().getTime()
},
`token过期${data.message}`
)
)
resolve(await request({ ...params, connected: true }))
} else if (data.code === Result.NotRegisteredLock.code) {
const checkResult = await this.checkLockUser(true)
if (checkResult.code === Result.Success.code) {
resolve(await request({ ...params, connected: true }))
} else {
clearTimeout(getWriteResultTimer)
resolve(checkResult)
}
} else {
clearTimeout(getWriteResultTimer)
if (params.disconnect) {
await this.disconnectDevice()
}
console.log('写入结果', data, request, params)
log.info(
new Result(
data.code,
{
lockName: this.lockInfo.bluetooth.bluetoothDeviceName,
lockId: this.lockInfo.lockId,
time: new Date().getTime()
},
`开门结果:${data.message}`
)
)
resolve(data)
}
}
})
},
// 检查是否已添加为用户
async checkLockUser(forceAdd = false) {
if (this.lockInfo.lockUserNo === 0 || forceAdd) {
const timestamp = Math.floor(new Date().getTime() / 1000)
const password = (Math.floor(Math.random() * 900000) + 100000).toString()
console.log('用户未添加,开始添加用户')
const addUserParams = {
name: this.lockInfo.bluetooth.bluetoothDeviceName,
keyId: this.lockInfo.keyId.toString(),
authUid: this.lockInfo.uid.toString(),
uid: this.accountInfo.uid.toString(),
openMode: 1,
keyType: 0,
startDate:
this.lockInfo.startDate === 0 ? timestamp : Math.floor(this.lockInfo.startDate / 1000),
expireDate:
this.lockInfo.endDate === 0 ? 0xffffffff : Math.floor(this.lockInfo.endDate / 1000),
useCountLimit: this.lockInfo.keyType === 3 ? 1 : 0xffff,
isRound: this.lockInfo.keyType === 4 ? 1 : 0,
weekRound:
this.lockInfo.keyType === 4 ? convertWeekdaysToNumber(this.lockInfo.weekDays) : 0,
startHour: this.lockInfo.keyType === 4 ? new Date(this.lockInfo.startDate).getHours() : 0,
startMin:
this.lockInfo.keyType === 4 ? new Date(this.lockInfo.startDate).getMinutes() : 0,
endHour: this.lockInfo.keyType === 4 ? new Date(this.lockInfo.endDate).getHours() : 0,
endMin: this.lockInfo.keyType === 4 ? new Date(this.lockInfo.endDate).getMinutes() : 0,
role: 0,
password
}
const addUserResult = await this.addLockUser({
params: addUserParams,
disconnect: false
})
console.log('添加用户蓝牙结果', addUserResult)
if (addUserResult.code === Result.Success.code) {
const { code } = await updateLockUserNoRequest({
keyId: this.lockInfo.keyId,
lockUserNo: this.lockInfo.lockUserNo
})
console.log('添加用户请求结果', code)
return Result.Success
}
if (addUserResult.code === Result.NotMoreKeyLock.code) {
console.log('用户达上限,开始清理用户')
const { code: cleanCode } = await this.cleanLockUser()
console.log('清理用户蓝牙结果', cleanCode)
if (cleanCode === Result.Success.code) {
return await this.checkLockUser()
}
return Result.Fail
}
if (addUserResult.code === Result.ReadyHasKeyLock.code) {
return Result.Success
}
return Result.Fail
}
return Result.Success
},
// 更新锁信息
updateLockInfo(lockInfo) {
this.lockInfo = {
...this.lockInfo,
...lockInfo
}
const lockList = getStorage('starLockList')
if (lockList[this.accountInfo.uid]) {
const index = lockList[this.accountInfo.uid].findIndex(
item => item.lockId === this.lockInfo.lockId
)
if (index !== -1) {
lockList[this.accountInfo.uid][index] = this.lockInfo
}
setStorage('starLockList', lockList)
}
},
// 特征值变化回调
listenCharacteristicValue(res) {
if (res.deviceId === this.lockInfo.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 {
this.parsingCharacteristicValue(binaryData).then(() => {})
}
} 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) {
this.parsingCharacteristicValue(completeArray).then(() => {})
completeArray = null
}
}
}
},
// 解析特征值
async parsingCharacteristicValue(binaryData) {
// 0x20 明文 0x22 SM4事先约定密钥 0x23 SM4设备指定密钥
if (binaryData[7] === 0x20) {
if (binaryData[12] * 256 + binaryData[13] === cmdIds.getPublicKey) {
if (binaryData[14] === Result.Success.code) {
this.updateLockInfo({
bluetooth: {
...this.lockInfo.bluetooth,
publicKey: [...binaryData.slice(15, 31)]
}
})
}
characteristicValueCallback(new Result(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 < this.lockInfo.bluetooth.bluetoothDeviceName.length; i++) {
key[i] = this.lockInfo.bluetooth.bluetoothDeviceName.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] === Result.Success.code) {
this.updateLockInfo({
bluetooth: {
...this.lockInfo.bluetooth,
privateKey: decrypted.slice(3, 19),
signKey: decrypted.slice(19, 35)
},
pwdTimestamp: arrayToTimestamp(decrypted.slice(35, 39)) * 1000
})
console.log('privateKey', Array.from(this.lockInfo.bluetooth.privateKey))
console.log('signKey', Array.from(this.lockInfo.bluetooth.signKey))
}
characteristicValueCallback(new Result(decrypted[2]))
}
} else {
const cebBinaryData = binaryData.slice(12, binaryData.length - 2)
const decrypted = sm4.decrypt(cebBinaryData, this.lockInfo.bluetooth.privateKey, {
mode: 'ecb',
output: 'array'
})
console.log('ecb解密后的数据', decrypted)
const cmdId = decrypted[0] * 256 + decrypted[1]
switch (cmdId) {
case cmdIds.getLockStatus:
if (decrypted[2] === Result.Success.code) {
const lockConfig = {
vendor: uint8ArrayToString(decrypted.slice(3, 23)),
product: decrypted[23],
model: uint8ArrayToString(decrypted.slice(24, 44)),
fwVersion: uint8ArrayToString(decrypted.slice(44, 64)),
hwVersion: uint8ArrayToString(decrypted.slice(64, 84)),
serialNum0: uint8ArrayToString(decrypted.slice(84, 100)),
serialNum1: uint8ArrayToString(decrypted.slice(100, 116)),
btDeviceName: uint8ArrayToString(decrypted.slice(116, 132)),
electricQuantity: decrypted[132],
electricQuantityStandby: decrypted[133],
restoreCount: decrypted[134] * 256 + decrypted[135],
restoreDate: arrayToTimestamp(decrypted.slice(136, 140)),
icPartNo: uint8ArrayToString(decrypted.slice(140, 150)),
indate: arrayToTimestamp(decrypted.slice(150, 154)),
mac: uint8ArrayToString(decrypted.slice(154, 174)),
timezoneOffset: new Date().getTimezoneOffset() * 60
}
this.updateLockInfo({
featureValue: uint8ArrayToString(decrypted.slice(175, 175 + decrypted[174])),
featureSettingValue: 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('获取锁状态成功', this.lockInfo.lockConfig)
}
characteristicValueCallback(new Result(decrypted[2]))
break
case cmdIds.addUser:
this.updateLockInfo({
token: decrypted.slice(42, 46)
})
if (decrypted[46] === Result.Success.code) {
this.updateLockInfo({
lockUserNo: decrypted[47] * 256 + decrypted[48]
})
}
console.log('添加用户结果', decrypted[46], this.lockInfo.lockUserNo)
characteristicValueCallback(new Result(decrypted[46]))
break
case cmdIds.expandCmd:
const subCmdId = decrypted[3]
switch (subCmdId) {
case subCmdIds.updateAdminPassword:
this.updateLockInfo({
token: decrypted.slice(5, 9)
})
if (decrypted[2] === Result.Success.code) {
const result = await changeAdminKeyboardPwdRequest({
password: requestParams.adminPwd,
lockId: this.lockInfo.lockId
})
return characteristicValueCallback(new Result(result.code))
}
characteristicValueCallback(new Result(decrypted[2]))
break
case subCmdIds.resetLockPassword:
this.updateLockInfo({
token: decrypted.slice(5, 9)
})
characteristicValueCallback(new Result(decrypted[4]))
break
case subCmdIds.setLockPassword:
this.updateLockInfo({
token: decrypted.slice(5, 9)
})
if (decrypted[2] === Result.Success.code) {
if (decrypted[11] === Result.Success.code) {
const pwdNo = decrypted[9] * 256 + decrypted[10]
if (requestParams.operate === 0) {
const addResult = await addCustomPasswordRequest({
...requestParams,
pwdUserNo: pwdNo,
lockId: this.lockInfo.lockId
})
if (addResult.code === Result.Success.code) {
characteristicValueCallback(
new Result(addResult.code, {
pwdNo,
keyboardPwdId: addResult.data.keyboardPwdId,
keyboardPwd: addResult.data.keyboardPwd,
keyboardPwdStatus: addResult.data.keyboardPwdStatus,
pwdUserNo: pwdNo
})
)
} else {
characteristicValueCallback(
new Result(addResult.code, addResult.data, addResult.message)
)
}
} else if (requestParams.operate === 1) {
const updateResult = await updatePasswordRequest(requestParams)
if (updateResult.code === Result.Success.code) {
characteristicValueCallback(new Result(updateResult.code))
} else {
characteristicValueCallback(
new Result(updateResult.code, updateResult.data, updateResult.message)
)
}
} else if (requestParams.operate === 3) {
const deleteResult = await deletePasswordRequest(requestParams)
if (deleteResult.code === Result.Success.code) {
characteristicValueCallback(new Result(deleteResult.code))
} else {
characteristicValueCallback(
new Result(deleteResult.code, deleteResult.data, deleteResult.message)
)
}
}
} else {
characteristicValueCallback(new Result(decrypted[11]))
}
} else {
characteristicValueCallback(new Result(decrypted[2]))
}
break
case subCmdIds.syncOpenRecord:
if (decrypted[2] === Result.Success.code && 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 = uint8ArrayToString(password)
}
const record = {
type: decrypted[7 + 17 * i],
user: decrypted[8 + 17 * i] * 256 + decrypted[9 + 17 * i],
date: arrayToTimestamp(decrypted.slice(10 + 17 * i, 14 + 17 * i)) * 1000,
success: 1,
password
}
records.push(record)
}
const { code, message } = await uploadRecordRequest({
records,
lockId: this.lockInfo.lockId
})
characteristicValueCallback(
new Result(
code,
{
count
},
message
)
)
} else {
characteristicValueCallback(new Result(decrypted[2]))
}
break
default:
break
}
break
case cmdIds.openDoor:
this.updateLockInfo({
token: decrypted.slice(2, 6),
electricQuantity: decrypted[7],
electricQuantityStandby: decrypted[9]
})
if (decrypted[6] === Result.Success.code) {
updateElectricQuantityRequest({
lockId: this.lockInfo.lockId,
electricQuantity: decrypted[7],
electricQuantityStandby: decrypted[9]
})
}
characteristicValueCallback(new Result(decrypted[6], { lock: this.lockInfo }))
break
case cmdIds.resetDevice:
this.updateLockInfo({
token: decrypted.slice(2, 6)
})
if (decrypted[6] === Result.Success.code) {
const { code, message } = await deleteLockRequest({
lockId: this.lockInfo.lockId
})
if (code === Result.Success.code) {
const lockList = getStorage('starLockList')
if (lockList[this.accountInfo.uid]) {
const index = lockList[this.accountInfo.uid].findIndex(
item => item.lockId === this.lockInfo.lockId
)
if (index !== -1) {
lockList[this.accountInfo.uid].splice(index, 1)
}
setStorage('starLockList', lockList)
}
}
characteristicValueCallback(new Result(code, {}, message))
} else {
characteristicValueCallback(new Result(decrypted[6]))
}
break
default:
this.updateLockInfo({
token: decrypted.slice(2, 6)
})
console.log('默认结果', decrypted[6], this.lockInfo.token)
characteristicValueCallback(new Result(decrypted[6]))
break
}
}
},
// 获取配置
getConfig() {
let config = configs[this.env]
return {
...config,
version,
buildNumber
}
},
/*
* 生成包头
* 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
},
// 断开与设备的连接
async disconnectDevice() {
return await closeBLEConnection(this.lockInfo.deviceId)
}
}
})