From cbf8505bae1e80cf2d9c92148f1ddbf8419f1197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E9=B9=8F?= Date: Sat, 12 Oct 2024 14:35:58 +0800 Subject: [PATCH] init --- .gitignore | 1 + README.md | 6 + api.js | 91 +++++ basic.js | 442 ++++++++++++++++++++++ env.js | 24 ++ format.js | 83 +++++ request.js | 104 ++++++ starCloud.js | 1013 ++++++++++++++++++++++++++++++++++++++++++++++++++ storage.js | 18 + 9 files changed, 1782 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 api.js create mode 100644 basic.js create mode 100644 env.js create mode 100644 format.js create mode 100644 request.js create mode 100644 starCloud.js create mode 100644 storage.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/README.md b/README.md new file mode 100644 index 0000000..e2ef19a --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +## 星云SDK + +### 1. 安装 + +### 2. 需安装的npm包 +`npm install buffer crc js-md5 pinia sm-crypto` diff --git a/api.js b/api.js new file mode 100644 index 0000000..d3a7b6b --- /dev/null +++ b/api.js @@ -0,0 +1,91 @@ +import request from '@/starCloud/request' + +// 创建账号 +export function starCloudCreateUser(data) { + return request({ + url: '/createUser', + method: 'POST', + data + }) +} + +// 获取访问令牌 +export function getStarCloudToken(data) { + return request({ + url: '/oauth2/token', + method: 'POST', + data + }) +} + +// 更新锁用户 +export function updateLockUserNoRequest(data) { + return request({ + url: '/v1/key/updateLockUserNo', + method: 'POST', + data + }) +} + +// 获取所有锁用户 +export function getUserNoListRequest(data) { + return request({ + url: '/v1/key/getUserNoList', + method: 'POST', + data + }) +} + +// 获取手机联网token +export function getLockNetTokenRequest(data) { + return request({ + url: '/v1/lock/getLockNetToken', + method: 'POST', + data + }) +} + +// 获取服务器时间 +export function getServerDatetime(data) { + return request({ + url: '/v1/lock/queryDate', + method: 'POST', + data + }) +} + +// 获取锁详情 +export function getLockDetail(data) { + return request({ + url: '/v1/lock/detail', + method: 'POST', + data + }) +} + +// 获取离线密码 +export function getOfflinePassword(data) { + return request({ + url: '/v1/keyboardPwd/get', + method: 'POST', + data + }) +} + +// 添加自定义密码 +export function addCustomPassword(data) { + return request({ + url: '/v1/keyboardPwd/add', + method: 'POST', + data + }) +} + +// 更新密码 +export function updatePassword(data) { + return request({ + url: '/v1/keyboardPwd/update', + method: 'POST', + data + }) +} diff --git a/basic.js b/basic.js new file mode 100644 index 0000000..6a0032f --- /dev/null +++ b/basic.js @@ -0,0 +1,442 @@ +export class Result { + static codes = { + Success: 0, + Fail: -1, + + NotAvailableBluetooth: -20, + NotAvailableBluetoothPermission: -21, + NotAvailableWeChatNearbyDevicesPermission: -22, + NotAvailableWeChatLocationPermission: -23, + NotAvailableWeChatNearbyDevicesEmpty: -24, + DeviceHasBeenReset: -30, + + NotRegisteredLock: 4, + NotTokenLock: 6, + NotMoreKeyLock: 12, + ReadyHasKeyLock: 15, + } + + static resultsMap = new Map([ + [Result.codes.Success, { message: '成功', data: {} }], + [Result.codes.Fail, { message: '失败', data: {} }], + + [Result.codes.NotAvailableBluetooth, { message: '蓝牙未开启', data: {} }], + [Result.codes.NotAvailableBluetoothPermission, { message: '小程序蓝牙权限被禁用', data: {} }], + [Result.codes.NotAvailableWeChatNearbyDevicesPermission, { message: '微信附近的设备权限被禁用', data: {} }], + [Result.codes.NotAvailableWeChatLocationPermission, { message: '微信定位权限被禁用', data: {} }], + [Result.codes.NotAvailableWeChatNearbyDevicesEmpty, { message: '微信附近的设备权限无法使用', data: {} }], + [Result.codes.DeviceHasBeenReset, { message: '设备已被重置', data: {} }], + + [Result.codes.NotRegisteredLock, { message: '用户在锁端未注册', data: {} }], + [Result.codes.NotTokenLock, { message: '用户在锁端token失效', data: {} }], + [Result.codes.NotMoreKeyLock, { message: '锁端钥匙数量已达上限', data: {} }], + [Result.codes.ReadyHasKeyLock, { message: '用户已是锁端用户', data: {} }], + ]) + + constructor(code, data, message) { + const result = Result.resultsMap.get(code) + if (result) { + this.code = code + this.message = message || result.message + this.data = data || result.data + } else { + this.code = code + this.message = message || '' + this.data = data || {} + } + } + + // 成功 + static get Success() { + return new Result(Result.codes.Success) + } + + // 失败(默认错误) + static get Fail() { + return new Result(Result.codes.Fail) + } + + // 蓝牙未开启 + static get NotAvailableBluetooth() { + return new Result(Result.codes.NotAvailableBluetooth) + } + + // 小程序蓝牙权限被禁用 + static get NotAvailableBluetoothPermission() { + return new Result(Result.codes.NotAvailableBluetoothPermission) + } + + // 微信附近的设备权限被禁用 + static get NotAvailableWeChatNearbyDevicesPermission() { + return new Result(Result.codes.NotAvailableWeChatNearbyDevicesPermission) + } + + // 微信定位权限被禁用 + static get NotAvailableWeChatLocationPermission() { + return new Result(Result.codes.NotAvailableWeChatLocationPermission) + } + + // 微信附近的设备权限无法使用(针对小米空白通行证,鸿蒙系统隐私中的定位服务) + static get NotAvailableWeChatNearbyDevicesEmpty() { + return new Result(Result.codes.NotAvailableWeChatNearbyDevicesEmpty) + } + + // 设备已被重置 + static get DeviceHasBeenReset() { + return new Result(Result.codes.DeviceHasBeenReset) + } + + // 用户在锁端未注册 + static get NotRegisteredLock() { + return new Result(Result.codes.NotRegisteredLock) + } + + // 用户在锁端token失效 + static get NotTokenLock() { + return new Result(Result.codes.NotTokenLock) + } + + // 锁端钥匙数量已达上限 + static get NotMoreKeyLock() { + return new Result(Result.codes.NotMoreKeyLock) + } + + // 锁端钥匙数量已达上限 + static get ReadyHasKeyLock() { + return new Result(Result.codes.ReadyHasKeyLock) + } +} + + +/** + * @typedef {Object} err + * @property {number} errno - 错误代码 + * @property {number} errCode - 错误代码 + * @property {String} errMsg - 错误信息 + */ + +// 查找设备并连接 +export function searchAndConnectDevice(name) { + // 循环查找设备 + let timer + // 超时计时器 + let timeoutTimer + + return new Promise(async (resolve) => { + const result = await startBluetoothDevicesDiscovery() + if(result.code === Result.Success.code) { + let searchFlag = false + timeoutTimer = setTimeout(async () => { + await stopBluetoothDevicesDiscovery() + clearInterval(timer) + if (!searchFlag) { + resolve(Result.NotAvailableWeChatNearbyDevicesEmpty) + } else { + resolve(Result.Fail) + } + }, 10500) + timer = setInterval(async () => { + const queryResult = await getBluetoothDevices() + if(queryResult.code === Result.Success.code) { + const deviceList = queryResult.data + if (searchFlag === false && deviceList.length > 0) { + searchFlag = true + } + for (let i = 0; i < deviceList.length; i++) { + if (deviceList[i]?.name === name) { + const uuid = deviceList[i]?.advertisServiceUUIDs[0] + if(uuid && uuid.slice(2, 8) === '758824') { + await stopBluetoothDevicesDiscovery() + clearTimeout(timeoutTimer) + clearInterval(timer) + if(uuid.slice(30, 32) === '00') { + resolve(Result.DeviceHasBeenReset) + } else if (uuid.slice(30, 32) === '01') { + const connectResult = await createBLEConnection(deviceList[i].deviceId) + resolve(connectResult) + } else { + resolve(Result.Fail) + } + break + } + } + } + } else { + resolve(queryResult) + } + }, 1000) + } else { + resolve(result) + } + }) +} + +// 蓝牙操作报错处理 +async function handleError (err, event) { + if (err.errCode === 10000) { + const result = await openBluetoothAdapter() + if (result.code === Result.Success.code) { + return await event() + } else { + return result + } + } else if (err.errCode === 10001) { + return Result.NotAvailableBluetooth + } else if (err.errno === 3) { + return Result.NotAvailableWeChatNearbyDevicesPermission + } else if (err.errno === 103) { + return Result.NotAvailableBluetoothPermission + } else if (err.errno === 1509008) { + return Result.NotAvailableWeChatLocationPermission + } else if (err.errMsg === 'openBluetoothAdapter:fail already opened') { + return Result.Success + } else { + return Result.Fail + } +} + +// 初始化蓝牙模块 +function openBluetoothAdapter() { + return new Promise((resolve) => { + uni.openBluetoothAdapter({ + success() { + resolve(Result.Success) + }, + async fail (err) { + resolve(await handleError(err)) + } + }) + }) +} + +// 关闭蓝牙模块 +function closeBluetoothAdapter() { + return new Promise((resolve) => { + uni.closeBluetoothAdapter({ + success() { + resolve(Result.Success) + }, + async fail (err) { + resolve(await handleError(err)) + } + }) + }) +} + +// 移除蓝牙适配器的全部监听 +function offBluetoothAdapterStateChange() { + uni.offBluetoothAdapterStateChange() +} + +// 监听蓝牙特征值改变 +export function onBLECharacteristicValueChange(callback) { + uni.onBLECharacteristicValueChange((res) => { + callback(res) + }) +} + +// 开始搜索附近的蓝牙设备 +function startBluetoothDevicesDiscovery() { + return new Promise((resolve) => { + uni.startBluetoothDevicesDiscovery({ + success() { + resolve(Result.Success) + }, + async fail (err) { + resolve(await handleError(err, startBluetoothDevicesDiscovery)) + } + }) + }) +} + +// 获取所有已发现的蓝牙设备 +function getBluetoothDevices() { + return new Promise((resolve) => { + uni.getBluetoothDevices({ + success(res) { + resolve(new Result(Result.Success.code, res.devices)) + }, + async fail () { + resolve(await handleError(err, getBluetoothDevices)) + } + }) + }) +} + +// 停止搜索附近的蓝牙设备 +function stopBluetoothDevicesDiscovery() { + return new Promise((resolve) => { + uni.stopBluetoothDevicesDiscovery({ + success() { + resolve(Result.Success) + }, + async fail () { + resolve(await handleError(err)) + } + }) + }) +} + +// 连接低功耗蓝牙设备 +function createBLEConnection(deviceId, reconnectNumber = 0) { + return new Promise((resolve) => { + uni.createBLEConnection({ + deviceId, + timeout: 10000, + async success () { + const res = await getBLEDeviceServicesAndCharacteristics(deviceId) + await notifyBLECharacteristicValueChange(deviceId, res.data.serviceId, res.data.notifyCharacteristicId) + resolve(res) + }, + async fail (err) { + if (err.errno === 1509007) { + const res = await getBLEDeviceServicesAndCharacteristics(deviceId) + await notifyBLECharacteristicValueChange(deviceId, res.data.serviceId, res.data.notifyCharacteristicId) + resolve(res) + } else if (err.errno === 1509001 && reconnectNumber < 1) { + resolve(Result.Fail) + } else if (reconnectNumber < 1) { + resolve(await createBLEConnection(deviceId, reconnectNumber + 1)) + } else { + resolve(Result.Fail) + } + } + }) + }) +} + +// 获取服务及对应特征值 +async function getBLEDeviceServicesAndCharacteristics (deviceId) { + const { code, data } = await getBLEDeviceServices(deviceId) + if (code === Result.Success.code) { + const { serviceId } = data + const { + code, + data: { notifyCharacteristicId, writeCharacteristicId } + } = await getBLEDeviceCharacteristics(deviceId, serviceId) + if (code === Result.Success.code) { + return new Result(Result.Success.code, { deviceId, serviceId, notifyCharacteristicId, writeCharacteristicId }) + } else { + return Result.Fail + } + } else { + return Result.Fail + } +} + +// 获取设备的服务 +function getBLEDeviceServices(deviceId) { + return new Promise((resolve) => { + uni.getBLEDeviceServices({ + 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 + } + } + if(!serviceId) { + resolve(Result.Fail) + return + } + resolve(new Result(Result.Success.code, { serviceId })) + }, + fail() { + resolve(Result.Fail) + } + }) + }) +} + +// 获取服务的特征值 +function getBLEDeviceCharacteristics(deviceId, serviceId) { + return new Promise((resolve) => { + uni.getBLEDeviceCharacteristics({ + 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 + } + } + if(notifyCharacteristicId && writeCharacteristicId) { + resolve(new Result(Result.Success.code, { notifyCharacteristicId, writeCharacteristicId })) + } else { + resolve(Result.Fail) + } + }, + fail() { + resolve(Result.Fail) + } + }) + }) +} + +// 订阅特征值 +function notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) { + return new Promise((resolve) => { + uni.notifyBLECharacteristicValueChange({ + deviceId, + serviceId, + characteristicId, + state: true, + success() { + resolve(Result.Success) + }, + fail() { + resolve(Result.Fail) + } + }) + }) +} + +// 断开与低功耗蓝牙设备的连接 +export function closeBLEConnection(deviceId) { + return new Promise((resolve) => { + uni.closeBLEConnection({ + deviceId, + success() { + console.log('断开连接成功') + resolve(Result.Success) + }, + fail() { + console.log('断开连接失败') + resolve(Result.Fail) + } + }) + }) +} + +// 写入特征值 +export function writeBLECharacteristicValue(deviceId, serviceId, characteristicId, binaryData) { + return new Promise((resolve) => { + const count = Math.ceil(binaryData.length / 20) + let successCount = 0 + for(let i = 0; i < count; i++) { + const writeData = binaryData.slice(i * 20, i === (count - 1) ? binaryData.length : (i + 1) * 20) + uni.writeBLECharacteristicValue({ + deviceId, + serviceId, + characteristicId, + value: writeData.buffer, + success() { + successCount++ + if(successCount === count) { + resolve(Result.Success) + } + }, + fail() { + resolve(Result.Fail) + } + }) + } + }) +} diff --git a/env.js b/env.js new file mode 100644 index 0000000..47435d6 --- /dev/null +++ b/env.js @@ -0,0 +1,24 @@ +// 版本号 +export const version = '1.0.0' +// 构建号 +export const buildNumber = 1 + +// 环境配置 +export const configs = { + DEV: { + name: 'DEV', + baseUrl: 'https://dev.cloud.star-lock.cn/sdk' + }, + PRE: { + name: 'PRE', + baseUrl: 'https://pre.cloud.star-lock.cn' + }, + XHJ: { + name: 'XHJ', + baseUrl: 'https://cloud.star-lock.cn' + }, + SKY: { + name: 'SKY', + baseUrl: 'https://cloud.star-lock.cn' + } +} diff --git a/format.js b/format.js new file mode 100644 index 0000000..3bd8313 --- /dev/null +++ b/format.js @@ -0,0 +1,83 @@ +import { + md5 +} from 'js-md5' +import crc from 'crc' + +// 周数组转换 +export function convertWeekdaysToNumber(weekDay) { + let weekStr = '00000000' + + 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) +} + +// 时间戳转二进制 +export function 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 +} + +// md5加密 +export function md5Encrypt(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))) +} + +// 生成包尾 头部数据+内容数据 +export function 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 +} + +// 二进制转时间戳 +export function arrayToTimestamp(array) { + const timestamp = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3] + return timestamp >>> 0 +} + +// 二进制转字符串 +export function uint8ArrayToString(uint8Array) { + let str = '' + for (let i = 0; i < uint8Array.length; i++) { + if (uint8Array[i] !== 0) { + str += String.fromCharCode(uint8Array[i]) + } + } + return str +} diff --git a/request.js b/request.js new file mode 100644 index 0000000..3f436de --- /dev/null +++ b/request.js @@ -0,0 +1,104 @@ +import { getStorage, removeStorage } from '@/starCloud/storage' +import { useStarCloudStore } from '@/starCloud/starCloud' +import { Result } from '@/starCloud/basic' + +/* +* config +* baseUrl: 请求域名 +* url: 请求路径 +* method: 请求方法 +* header: 请求头 +* token: 请求token +* data: 请求参数 +* */ + +const request = (config) => { + const starCloud = useStarCloudStore() + let timer + return new Promise(async (resolve) => { + const baseConfig = starCloud.getConfig() + + const token = config?.token ? config.token : getStorage('starCloudToken') + // 请求地址 + const URL = config.baseUrl ? config.baseUrl + config.url : baseConfig.baseUrl + config.url + + // 默认请求头 + const headerDefault = { + version: baseConfig.version + '+' + baseConfig.buildNumber + } + const header = { + ...headerDefault, + ...config.header + } + const method = config.method || 'POST' + const data = { + ...config.data, + accessToken: token, + clientId: starCloud.clientId + } + const timestamp = new Date().getTime() + + // 超时处理 + timer = setTimeout(() => { + resolve(new Result(Result.Fail.code, {},'网络访问失败,请检查网络是否正常')) + }, 3200) + + uni.request({ + url: URL, + method, + header, + data, + timeout: 3000, + async success (res) { + const { statusCode, data } = res + if (timer) { + clearTimeout(timer) + } + if (statusCode === 200) { + const code = data.errcode + const message = data.errmsg + if (code === 10003) { + removeStorage('starCloudToken') + removeStorage('starCloudUser') + const { code } = await starCloud.login( + starCloud.accountInfo.username, + starCloud.accountInfo.password + ) + if(code === Result.Success.code) { + resolve(await request(config)) + } + } else { + resolve({ + code, + data: data.data, + message + }) + } + } else { + resolve(new Result(Result.Fail.code, {},'网络访问失败,请检查网络是否正常')) + } + }, + async fail (res) { + console.log('网络访问失败', res) + if (timer) { + clearTimeout(timer) + } + resolve(new Result(Result.Fail.code, {},'网络访问失败,请检查网络是否正常')) + }, + async complete (res) { + console.log(URL.substring(baseConfig.baseUrl.length + 1), { + env: baseConfig.name, + url: URL.substring(baseConfig.baseUrl.length + 1), + req: config?.data || {}, + code: res?.data?.errcode, + res: res?.data?.data, + token: header?.authorization || '', + message: res?.data?.errmsg, + duration: new Date().getTime() - timestamp + }) + } + }) + }) +} + +export default request diff --git a/starCloud.js b/starCloud.js new file mode 100644 index 0000000..2257827 --- /dev/null +++ b/starCloud.js @@ -0,0 +1,1013 @@ +import { defineStore } from 'pinia' +import { buildNumber, configs, version } from '@/starCloud/env' +import { + addCustomPassword, + getLockDetail, + getLockNetTokenRequest, + getOfflinePassword, + getServerDatetime, + getStarCloudToken, + getUserNoListRequest, + starCloudCreateUser, + updateLockUserNoRequest, updatePassword +} from '@/starCloud/api' +import { getStorage, setStorage } from '@/starCloud/storage' +import { + closeBLEConnection, + onBLECharacteristicValueChange, + Result, + searchAndConnectDevice, + writeBLECharacteristicValue +} from '@/starCloud/basic' +import { + arrayToTimestamp, + convertWeekdaysToNumber, + createPackageEnd, + md5Encrypt, + timestampToArray, + uint8ArrayToString +} from '@/starCloud/format' +import { sm4 } from 'sm-crypto' + +/** + * 锁信息 + * @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 = { + // 设置开锁密码 + setLockPassword: 3, + // 重置开锁密码 + resetLockPassword: 19 +} + +// 特性值回调 +let characteristicValueCallback = null +// 完整数据 +let completeArray +// 完整内容数据长度 +let length +// 请求参数 +let requestParams = null + +export const useStarCloudStore = defineStore('starCloud', { + state() { + return { + // 环境 + env: null, + // 客户端Id + clientId: null, + // 客户端密码 + clientSecret: null, + // 星云是否登录 + loginStarCloud: false, + // 星云用户信息 + userInfo: null, + // 账户信息 + accountInfo: null, + // 锁信息 + lockInfo: null, + // 消息序号 + messageCount: 1, + // 服务器时间 + serverTimestamp: 0, + // 时间差 + timeDifference: 0, + // 小程序环境 + envVersion: '' + } + }, + actions: { + // 初始化星云 + initStarCloud(clientId, clientSecret, env = 'XHJ') { + const appInfo = uni.getAccountInfoSync() + this.envVersion = appInfo.miniProgram.envVersion + + this.env = env + this.clientId = clientId + this.clientSecret = clientSecret + // 监听特性值变化 + onBLECharacteristicValueChange(this.listenCharacteristicValue) + }, + // 注册星云 + async register() { + const { + code, + data, + message + } = await starCloudCreateUser({ + clientId: this.clientId, + clientSecret: this.clientSecret + }) + return new Result(code, data, message) + }, + // 登录星云 + async login(username, password) { + this.accountInfo = { + username, + password + } + setStorage('starCloudUser', {"username":"vridg_Hgn3uuO1Sh36Hc2oI","access_token":"Bh7fBK8gIIsHPmZJG6Jpr4ka0tNHbAVJ3i43v69a","uid":650,"refresh_token":"TguBYKgPu9T0b5V96opXzk0gGaj3hMTpHANVsc1y","scope":"user,key,room","token_type":"Bearer","expires_in":7349225}) + setStorage('starCloudToken', 'Bh7fBK8gIIsHPmZJG6Jpr4ka0tNHbAVJ3i43v69a') + + const token = getStorage('starCloudToken') + if (token) { + this.userInfo = getStorage('starCloudUser') + this.loginStarCloud = true + // 获取服务器时间 + this.getServerTimestamp() + .then(() => {}) + return Result.Success + } + console.log('登录星云', username, password) + const { + code, + data: userInfo, + message + } = await getStarCloudToken({ + username: username, + password: password, + clientId: this.clientId, + clientSecret: this.clientSecret + }) + if (code === Result.Success.code) { + this.userInfo = userInfo + setStorage('starCloudToken', userInfo.access_token) + setStorage('starCloudUser', userInfo) + this.loginStarCloud = true + // 获取服务器时间 + this.getServerTimestamp() + .then(() => {}) + } + return new Result(code, {}, message) + }, + // 选择锁 + async selectLock(lockId) { + const { + code, + data, + message + } = await getLockDetail({ + lockId + }) + if (code === Result.Success.code) { + const lockList = getStorage('lockList') + if(lockList) { + const index = lockList.findIndex(item => item.lockId === lockId) + if (index === -1) { + lockList.push(data) + } else { + lockList[index] = data + } + setStorage('lockList', lockList) + } else { + setStorage('lockList', [data]) + } + this.lockInfo = data + } else { + const lockList = getStorage('lockList') + console.log('锁列表', lockList) + if(lockList) { + const index = lockList.findIndex(item => item.lockId === lockId) + if (index !== -1) { + this.lockInfo = lockList[index] + return new Result(Result.Success.code, this.lockInfo) + } + } + } + return new Result(code, data, message) + }, + // 清理用户 + async cleanLockUser(disconnect = false) { + // 确认设备连接正常 + 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) + }, + // 开门 + async openDoor(type = 'open', disconnect) { + // 确认设备连接正常 + 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 + } + + // 是否需要联网 + 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 + } + } + + // 开门方式 + 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.userInfo.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) + + 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.openDoor, disconnect) + }, + // 获取离线密码 + async getOfflinePassword(password) { + return await getOfflinePassword({ + ...password, + lockId: this.lockInfo.lockId + }) + }, + // 自定义密码 + async customPassword(password, disconnect) { + // 确认设备连接正常 + 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.userInfo.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, disconnect, password) + }, + + + + // 获取联网token + async getNetToken() { + const { + code, + data, + message + } = await getLockNetTokenRequest({ + lockId: this.lockInfo.lockId + }) + return new Result(code, data, message) + }, + // 获取服务器时间 + async getServerTimestamp() { + const { + code, + data, + message + } = await getServerDatetime({}) + 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) + }, + // 添加用户 + async addLockUser(data, disconnect = false) { + // 确认设备连接正常 + const searchResult = await searchAndConnectDevice(this.lockInfo.bluetooth.bluetoothDeviceName) + 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, disconnect, data) + }, + // 获取写入结果 + getWriteResult(request, disconnect = false, params) { + return new Promise(resolve => { + const getWriteResultTimer = setTimeout(() => { + resolve(Result.Fail) + }, 20000) + characteristicValueCallback = async (data) => { + // code 6 token过期,重新获取 + if (data.code === Result.NotTokenLock.code) { + resolve(await request(params)) + } else if (data.code === Result.NotRegisteredLock.code) { + const checkResult = await this.checkLockUser(true) + if (checkResult.code === Result.Success.code) { + resolve(await request(params)) + } else { + clearTimeout(getWriteResultTimer) + resolve(checkResult) + } + } else { + clearTimeout(getWriteResultTimer) + if(disconnect) { + await this.disconnectDevice() + } + console.log('写入结果', data, request, params) + 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.userInfo.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(addUserParams) + 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 + } else 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() + } else { + return Result.Fail + } + } else if (addUserResult.code === Result.ReadyHasKeyLock.code) { + return Result.Success + } else { + return Result.Fail + } + } else { + return Result.Success + } + }, + // 更新锁信息 + updateLockInfo(lockInfo) { + this.lockInfo = { + ...this.lockInfo, + ...lockInfo + } + }, + // 特征值变化回调 + 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.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.token) + characteristicValueCallback(new Result(decrypted[46])) + break + case cmdIds.expandCmd: + const subCmdId = decrypted[3] + switch (subCmdId) { + 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 addCustomPassword({ + ...requestParams, + pwdUserNo: pwdNo, + lockId: this.lockInfo.lockId + }) + if (addResult.code === Result.Success.code) { + characteristicValueCallback(new Result(addResult.code, { + pwdNo: pwdNo, + keyboardPwdId: addResult.data.keyboardPwdId, + keyboardPwd: addResult.data.keyboardPwd + })) + } else { + characteristicValueCallback(new Result(addResult.code, addResult.data, addResult.message)) + } + } else if (requestParams.operate === 1) { + const updateResult = await updatePassword({ + ...requestParams, + keyboardPwdId: this.lockInfo.keyboardPwdId + }) + if (updateResult.code === Result.Success.code) { + characteristicValueCallback(new Result(updateResult.code)) + } else { + characteristicValueCallback(new Result(updateResult.code, updateResult.data, updateResult.message)) + } + } + } else { + characteristicValueCallback(new Result(decrypted[11])) + } + } else { + characteristicValueCallback(new Result(decrypted[2])) + } + break + } + break + case cmdIds.openDoor: + this.updateLockInfo({ + token: decrypted.slice(2, 6) + }) + console.log('开门', decrypted[6], this.lockInfo.token) + 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:明文,1:AES128,2:SM4(事先约定密钥),3:SM4(设备指定密钥) + * originalLength 原始数据长度 + * */ + createPackageHeader(encryptionType, originalLength) { + // 头部数据 + let headArray = new Uint8Array(12) + + // 固定包头 + headArray[0] = 0xEF + headArray[1] = 0x01 + headArray[2] = 0xEE + headArray[3] = 0x02 + + // 包类型 发送 + headArray[4] = 0x01 + + // 包序号 + headArray[5] = this.messageCount / 256 + headArray[6] = this.messageCount % 256 + this.messageCount++ + + // 包标识 + if (encryptionType === 0) { + headArray[7] = 0x20 + } else if (encryptionType === 2) { + headArray[7] = 0x22 + } else { + headArray[7] = 0x23 + } + + // 数据长度 + if (encryptionType === 0) { + headArray[8] = originalLength / 256 + headArray[9] = originalLength % 256 + } else { + const length = Math.ceil(originalLength / 16) * 16 + headArray[8] = length / 256 + headArray[9] = length % 256 + } + headArray[10] = originalLength / 256 + headArray[11] = originalLength % 256 + + return headArray + }, + // 断开与设备的连接 + async disconnectDevice() { + return await closeBLEConnection(this.lockInfo.deviceId) + }, + } +}) diff --git a/storage.js b/storage.js new file mode 100644 index 0000000..b6a8fe9 --- /dev/null +++ b/storage.js @@ -0,0 +1,18 @@ +import { useStarCloudStore } from '@/starCloud/starCloud' + +export function setStorage(key, value) { + return uni.setStorageSync(getPrefix() + key, value) +} + +export function getStorage(key) { + return uni.getStorageSync(getPrefix() + key) +} + +export function removeStorage(key) { + return uni.removeStorageSync(getPrefix() + key) +} + +function getPrefix() { + const starCloud = useStarCloudStore() + return `${ starCloud.envVersion }:` +}