diff --git a/api.js b/api.js index 58a9882..06ca312 100644 --- a/api.js +++ b/api.js @@ -1,4 +1,4 @@ -import request from '@/starCloud/request' +import { request } from './export' // 创建账号 export function starCloudCreateUser(data) { diff --git a/common.js b/common.js new file mode 100644 index 0000000..50c7eb5 --- /dev/null +++ b/common.js @@ -0,0 +1,1040 @@ +import { sm4 } from 'sm-crypto' +import { + arrayToTimestamp, + convertWeekdaysToNumber, + createPackageEnd, + md5Encrypt, + timestampToArray, + uint8ArrayToString +} from './format' +import { buildNumber, configs, version } from './env' +import { cmdIds, Result, subCmdIds } from './constant' +import { + closeBLEConnection, + searchAndConnectDevice, + writeBLECharacteristicValue +} from '@/starCloud/uni/basic' +import { + addCustomPasswordRequest, + changeAdminKeyboardPwdRequest, + deleteLockRequest, + deletePasswordRequest, + getLastRecordTimeRequest, + getLockNetTokenRequest, + getStarCloudToken, + getUserNoListRequest, + updateElectricQuantityRequest, + updateLockUserNoRequest, + updatePasswordRequest, + uploadRecordRequest +} from './api' +import { getStorage, setStorage } from './export' +import log from '@/starCloud/uni/log' + +/** + * 同步开门记录 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns {Promise} + */ +export async function 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) +} + +/** + * 清理用户 + * @returns {Promise} + */ +export async function 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} + */ +export async function 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) + + 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) +} + +// 获取公钥 +export async function 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 }) +} + +// 获取私钥 +export async function 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 }) +} + +// 获取锁状态 +export async function 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 +export async function getNetToken() { + const { code, data, message } = await getLockNetTokenRequest({ + lockId: this.lockInfo.lockId + }) + return new Result(code, data, message) +} + +// 添加用户 +export async function 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) +} + +// 获取写入结果 +export function getWriteResult(request, params) { + return new Promise(resolve => { + const getWriteResultTimer = setTimeout(() => { + resolve(Result.Fail) + }, 20000) + this.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}` + ), + name: 'openDoor' + }) + resolve(await request({ ...params, connected: true }).bind(this)) + } 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 }).bind(this)) + } 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}` + ), + name: 'openDoor' + }) + resolve(data) + } + } + }) +} + +// 检查是否已添加为用户 +export async function 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 +} + +// 更新锁信息 +export function 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) + } +} + +// 特征值变化回调 +export function 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 + ) { + this.length = binaryData[8] * 256 + binaryData[9] + if (this.length + 14 > binaryData.length) { + this.completeArray = binaryData + } else { + this.parsingCharacteristicValue(binaryData).then(() => {}) + } + } else if (this.completeArray) { + const combinedArray = new Uint8Array(this.completeArray.length + binaryData.length) + combinedArray.set(this.completeArray, 0) + combinedArray.set(binaryData, this.completeArray.length) + this.completeArray = combinedArray + if (this.length + 14 === this.completeArray.length) { + this.parsingCharacteristicValue(this.completeArray).then(() => {}) + this.completeArray = null + } + } + } +} + +// 解析特征值 +export async function 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)] + } + }) + } + this.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)) + } + this.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) + } + this.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) + this.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: this.requestParams.adminPwd, + lockId: this.lockInfo.lockId + }) + return this.characteristicValueCallback(new Result(result.code)) + } + this.characteristicValueCallback(new Result(decrypted[2])) + + break + case subCmdIds.resetLockPassword: + this.updateLockInfo({ + token: decrypted.slice(5, 9) + }) + this.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 (this.requestParams.operate === 0) { + const addResult = await addCustomPasswordRequest({ + ...this.requestParams, + pwdUserNo: pwdNo, + lockId: this.lockInfo.lockId + }) + if (addResult.code === Result.Success.code) { + this.characteristicValueCallback( + new Result(addResult.code, { + pwdNo, + keyboardPwdId: addResult.data.keyboardPwdId, + keyboardPwd: addResult.data.keyboardPwd, + keyboardPwdStatus: addResult.data.keyboardPwdStatus, + pwdUserNo: pwdNo + }) + ) + } else { + this.characteristicValueCallback( + new Result(addResult.code, addResult.data, addResult.message) + ) + } + } else if (this.requestParams.operate === 1) { + const updateResult = await updatePasswordRequest(this.requestParams) + if (updateResult.code === Result.Success.code) { + this.characteristicValueCallback(new Result(updateResult.code)) + } else { + this.characteristicValueCallback( + new Result(updateResult.code, updateResult.data, updateResult.message) + ) + } + } else if (this.requestParams.operate === 3) { + const deleteResult = await deletePasswordRequest(this.requestParams) + if (deleteResult.code === Result.Success.code) { + this.characteristicValueCallback(new Result(deleteResult.code)) + } else { + this.characteristicValueCallback( + new Result(deleteResult.code, deleteResult.data, deleteResult.message) + ) + } + } + } else { + this.characteristicValueCallback(new Result(decrypted[11])) + } + } else { + this.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 + }) + this.characteristicValueCallback( + new Result( + code, + { + count + }, + message + ) + ) + } else { + this.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] + }).then(() => {}) + } + this.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) + } + } + this.characteristicValueCallback(new Result(code, {}, message)) + } else { + this.characteristicValueCallback(new Result(decrypted[6])) + } + break + default: + this.updateLockInfo({ + token: decrypted.slice(2, 6) + }) + console.log('默认结果', decrypted[6], this.lockInfo.token) + this.characteristicValueCallback(new Result(decrypted[6])) + break + } + } +} + +// 获取配置 +export function getConfig() { + let config = configs[this.env] + return { + ...config, + version, + buildNumber + } +} + +/* + * 生成包头 + * encryptionType 加密类型 0:明文,1:AES128,2:SM4(事先约定密钥),3:SM4(设备指定密钥) + * originalLength 原始数据长度 + * */ +export function 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 +} + +// 断开与设备的连接 +export async function disconnectDevice() { + return await closeBLEConnection(this.lockInfo.deviceId) +} diff --git a/constant.js b/constant.js new file mode 100644 index 0000000..02480b4 --- /dev/null +++ b/constant.js @@ -0,0 +1,184 @@ +// 命令ID +export const cmdIds = { + // 获取公钥 + getPublicKey: 0x3090, + // 获取私钥 + getCommKey: 0x3091, + // 获取锁状态 + getLockStatus: 0x3040, + // 新增用户 + addUser: 0x3001, + // 开门 + openDoor: 0x3005, + // 重置设备 + resetDevice: 0x3004, + // 清理用户 + cleanUser: 0x300c, + // 扩展命令 + expandCmd: 0x3030 +} + +// 子命令ID +export const subCmdIds = { + // 修改管理员密码 + updateAdminPassword: 2, + // 设置开锁密码 + setLockPassword: 3, + // 重置开锁密码 + resetLockPassword: 19, + // 同步开门记录 + syncOpenRecord: 41 +} + +export class Result { + static codes = { + Success: 0, + Fail: -1, + + NotMoreData: -10, + + NotAvailableBluetooth: -20, + NotAvailableBluetoothPermission: -21, + NotAvailableWeChatNearbyDevicesPermission: -22, + NotAvailableWeChatLocationPermission: -23, + NotAvailableWeChatNearbyDevicesEmpty: -24, + NotAvailableWeChatBluetoothPermission: -25, + DeviceHasBeenReset: -30, + + NotRegisteredLock: 4, + NotTokenLock: 6, + NotMoreKeyLock: 12, + ReadyHasKeyLock: 15, + ReadyHasPassword: 251 + } + + static resultsMap = new Map([ + [Result.codes.Success, { message: '成功', data: {} }], + [Result.codes.Fail, { message: '失败', data: {} }], + + [Result.codes.NotMoreData, { 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.NotAvailableWeChatBluetoothPermission, + { + 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: {} }], + [Result.codes.ReadyHasPassword, { 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 NotMoreData() { + return new Result(Result.codes.NotMoreData) + } + + // 蓝牙未开启 + 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 NotAvailableWeChatBluetoothPermission() { + return new Result(Result.codes.NotAvailableWeChatBluetoothPermission) + } + + // 设备已被重置 + 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) + } + + // 密码已存在 + static get ReadyHasPassword() { + return new Result(Result.codes.ReadyHasPassword) + } +} diff --git a/export.js b/export.js new file mode 100644 index 0000000..3279379 --- /dev/null +++ b/export.js @@ -0,0 +1,35 @@ +import { getStorageUni, removeStorageUni, setStorageUni } from './uni/storage' +import { getStorageWeb, removeStorageWeb, setStorageWeb } from './web/storage' +import requestUni from '@/starCloud/uni/request' +import requestWeb from '@/starCloud/web/request' +import starCloudInstance from './star-cloud' + +export const setStorage = (key, value) => { + if (starCloudInstance.platform === 2) { + setStorageWeb(key, value) + } else { + setStorageUni(key, value) + } +} + +export const getStorage = key => { + if (starCloudInstance.platform === 2) { + return getStorageWeb(key) + } + return getStorageUni(key) +} + +export const removeStorage = key => { + if (starCloudInstance.platform === 2) { + removeStorageWeb(key) + } else { + removeStorageUni(key) + } +} + +export const request = async params => { + if (starCloudInstance.platform === 2) { + return await requestWeb(params) + } + return await requestUni(params) +} diff --git a/star-cloud.js b/star-cloud.js new file mode 100644 index 0000000..12038a9 --- /dev/null +++ b/star-cloud.js @@ -0,0 +1,187 @@ +import { getStorage, setStorage } from './export' +import * as device from './star-cloud/device.js' +import * as lock from './star-cloud/lock.js' +import * as other from './star-cloud/other.js' +import * as password from './star-cloud/password.js' +import * as record from './star-cloud/record.js' +import * as user from './star-cloud/user.js' +import * as common from './common.js' +import { onBLECharacteristicValueChange } from './uni/basic' + +/** + * 账户信息信息 + * @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 + */ + +class StarCloud { + constructor(methodModules) { + // 环境 + this.env = null + // 客户端Id + this.clientId = null + // 客户端密码 + this.clientSecret = null + // 小程序环境 + this.envVersion = '' + // 锁信息 + this.lockInfo = null + // 服务器时间 + this.serverTimestamp = 0 + // 时间差 + this.timeDifference = 0 + // 搜索设备列表 + this.searchDeviceList = [] + // 星云用户信息 + this.userInfo = null + // 账户信息 + this.accountInfo = null + // 消息序号 + this.messageCount = 1 + // 是否上报日志 + this.isReportLog = false + // 请求参数 + this.requestParams = null + // 特性值回调 + this.characteristicValueCallback = null + // 完整数据 + this.completeArray = [] + // 完整内容数据长度 + this.length = 0 + // 计时器 + this.timer = null + // 平台 + this.platform = 1 + + this.loadMethods(methodModules) + this.loadFromStorage() + } + + static saveFieldToStorage(key, value) { + setStorage(`StarCloud_${key}`, value) + } + + /** + * 初始化星云 + * @param params + * @param {String} params.clientId 客户端Id + * @param {String} params.clientSecret 客户端密码 + * @param {String} params.env 环境 + * @param {Number} params.platform 平台 1:uni 2:web 默认为1 + * @param {Boolean} params.isReportLog 是否上报日志 + */ + init(params) { + Object.assign(StarCloud.prototype, device, lock, other, password, record, user, common) + + const { clientId, clientSecret, env, platform } = params + this.envVersion = 'release' + if (platform !== 2) { + const appInfo = uni.getAccountInfoSync() + this.envVersion = appInfo.miniProgram.envVersion + + // 监听特性值变化 + onBLECharacteristicValueChange(this.listenCharacteristicValue.bind(this)) + } + + this.isReportLog = params.isReportLog + + this.env = env || 'XHJ' + this.clientId = clientId + this.clientSecret = clientSecret + this.platform = platform || 1 + } + + loadMethods(methodModules) { + methodModules.forEach(methods => { + Object.entries(methods).forEach(([key, method]) => { + if (typeof method === 'function') { + this[key] = method.bind(this) + } + }) + }) + } + + loadFromStorage() { + Object.keys(this).forEach(key => { + const value = getStorage(`StarCloud_${key}`) + if (value !== undefined) { + this[key] = value + } + }) + } +} + +const starCloudInstance = new Proxy(StarCloud.prototype, { + get(target, key) { + return Reflect.get(target, key) + }, + set(target, key, value) { + StarCloud.saveFieldToStorage(key, value) + return Reflect.set(target, key, value) + } +}) +Object.setPrototypeOf(StarCloud, starCloudInstance) + +export default starCloudInstance diff --git a/star-cloud/device.js b/star-cloud/device.js new file mode 100644 index 0000000..ea0e439 --- /dev/null +++ b/star-cloud/device.js @@ -0,0 +1,164 @@ +import { + closeBluetoothAdapter, + createBLEConnection, + getBluetoothDevices, + offBluetoothAdapterStateChange, + startBluetoothDevicesDiscovery, + stopBluetoothDevicesDiscovery +} from '../uni/basic' +import { Result } from '../constant' +import { bindLockRequest } from '../api' + +// 搜索设备 +export async function searchDevice(callback) { + const result = await startBluetoothDevicesDiscovery() + if (result.code === Result.Success.code) { + const timestamp = new Date().getTime() + let queryFlag = false + this.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 (this.timer) { + clearInterval(this.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) + } +} + +// 停止搜索 +export async function stopSearchDevice() { + console.log('停止搜索') + if (this.timer) { + clearInterval(this.timer) + } + return await stopBluetoothDevicesDiscovery() +} + +/** + * 绑定设备 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {String} params.name 设备名称 + * @returns {Promise} + */ +export async function 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 +} diff --git a/star-cloud/lock.js b/star-cloud/lock.js new file mode 100644 index 0000000..ab4b229 --- /dev/null +++ b/star-cloud/lock.js @@ -0,0 +1,367 @@ +import { sm4 } from 'sm-crypto' +import { cmdIds, Result } from '../constant' +import { searchAndConnectDevice, writeBLECharacteristicValue } from '../uni/basic' +import { createPackageEnd, md5Encrypt, timestampToArray } from '../format' +import { getLockDetailRequest, getLockSettingDataRequest } from '../api' +import { getStorage, setStorage } from '../export' +import log from '../uni/log' + +/** + * 选择锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Number} params.lockId 锁ID + * @returns {Promise} + */ +export async function 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 if (code === Result.Fail.code) { + 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} + */ +export async function 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() + }, + `开始开门` + ), + name: 'openDoor' + }) + + // 设置执行账号 + 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}` + ), + name: 'openDoor' + }) + + // 确认设备连接正常 + 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}` + ), + name: 'openDoor' + }) + 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}` + ), + name: 'openDoor' + }) + + // 是否需要联网 + 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}` + ), + name: 'openDoor' + }) + + // 开门方式 + 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() + }, + `开始写入` + ), + name: 'openDoor' + }) + + 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}` + ), + name: 'openDoor' + }) + + return this.getWriteResult(this.openDoor, params) +} + +/** + * 删除锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + */ +export async function 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 {Number} params.lockId 锁 Id + * @returns {Promise} + */ +export async function getLockSupportFeatures(params) { + const { accountInfo, lockId } = params + + // 设置执行账号 + const result = await this.login(accountInfo) + if (result.code !== Result.Success.code) { + return result + } + + const { code, data, message } = await getLockSettingDataRequest({ + lockId + }) + if (code === Result.Success.code) { + return new Result(code, { ...data.lockFeature }, message) + } + return new Result(code, data, message) +} diff --git a/star-cloud/other.js b/star-cloud/other.js new file mode 100644 index 0000000..35804fa --- /dev/null +++ b/star-cloud/other.js @@ -0,0 +1,46 @@ +import { getServerDatetimeRequest, removeBadLockRequest } from '../api' +import { Result } from '../constant' +import { getStorage, setStorage } from '../export' + +/** + * 移除坏锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {List[int]} params.lockIds 锁Id列表 + * @returns {Promise} + */ +export async function 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) +} + +// 获取服务器时间 +export async function 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) +} diff --git a/star-cloud/password.js b/star-cloud/password.js new file mode 100644 index 0000000..beb1a2b --- /dev/null +++ b/star-cloud/password.js @@ -0,0 +1,295 @@ +import { sm4 } from 'sm-crypto' +import { cmdIds, Result, subCmdIds } from '../constant' +import { searchAndConnectDevice, writeBLECharacteristicValue } from '../uni/basic' +import { createPackageEnd, md5Encrypt, timestampToArray } from '../format' +import { checkPasswordRequest, getOfflinePasswordRequest } from '../api' + +/** + * 离线密码 + * 该功能无需蓝牙交互直接请求服务端,详细参数说明请看星云接口文档 + * @typedef {Object} OfflinePassword + * @property {String} keyboardPwdName - 密码名称 + * @property {Number} keyboardPwdType - 密码类型 + * @property {Number} lockId - 锁 Id + * @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:是 + * @property {Number} keyboardPwdId - 密码ID + */ + +/** + * 获取离线密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {OfflinePassword} params.password 密码信息 + * @returns {Promise} + */ +export async function getOfflinePassword(params) { + const { accountInfo, password } = params + // 设置执行账号 + const result = await this.login(accountInfo) + if (result.code !== Result.Success.code) { + return result + } + return await getOfflinePasswordRequest(password) +} + +/** + * 自定义密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {CustomPassword} params.password 密码信息 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns {Promise} + */ +export async function customPassword(params) { + const { accountInfo, password } = params + // 设置执行账号 + const result = await this.login(accountInfo) + if (result.code !== Result.Success.code) { + return result + } + + this.requestParams = password + let { pwdNo, operate, keyboardPwd, startDate, endDate, pwdRight } = password + + if (operate === 0 || operate === 1) { + const checkPasswordParams = { + lockId: this.lockInfo.lockId, + keyboardPwd + } + if (password.keyboardPwdId) { + checkPasswordParams.keyboardPwdId = password.keyboardPwdId + } + const checkPasswordResult = await this.checkPassword(checkPasswordParams) + if (checkPasswordResult.code === -3) { + return Result.ReadyHasPassword + } + if (checkPasswordResult.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.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) +} + +/** + * 修改管理员密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {String} params.adminPwd 管理员密码 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns {Promise} + */ +export async function 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 + } + + this.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) +} + +// 检查密码名称与密码是否已存在 +export async function checkPassword(params) { + return await checkPasswordRequest(params) +} diff --git a/star-cloud/record.js b/star-cloud/record.js new file mode 100644 index 0000000..2c19c49 --- /dev/null +++ b/star-cloud/record.js @@ -0,0 +1,31 @@ +import { Result } from '../constant' + +/** + * 同步全部开门记录 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns {Promise} + */ +export async function 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) +} diff --git a/star-cloud/user.js b/star-cloud/user.js new file mode 100644 index 0000000..34ddc70 --- /dev/null +++ b/star-cloud/user.js @@ -0,0 +1,30 @@ +import { removeStorage } from '../export' +import { Result } from '../constant' +import { starCloudCreateUser } from '../api' + +/** + * 注册星云 + * @returns {Promise} + */ +export async function 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 + */ +export function logout(params) { + const { uid } = params + if (this.accountInfo?.uid === uid) { + this.userInfo = null + this.accountInfo = null + this.lockInfo = null + removeStorage('starCloudToken') + } +} diff --git a/starCloud.js b/starCloud.js deleted file mode 100644 index e949fd1..0000000 --- a/starCloud.js +++ /dev/null @@ -1,2107 +0,0 @@ -import { defineStore } from 'pinia' -import { sm4 } from 'sm-crypto' -import { buildNumber, configs, version } from '@/starCloud/env' -import { - addCustomPasswordRequest, - bindLockRequest, - changeAdminKeyboardPwdRequest, - checkPasswordRequest, - deleteLockRequest, - deletePasswordRequest, - getLastRecordTimeRequest, - getLockDetailRequest, - getLockNetTokenRequest, - getLockSettingDataRequest, - 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} lockId - 锁 Id - * @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} - */ - 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} - */ - 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 if (code === Result.Fail.code) { - 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} - */ - 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() - }, - `开始开门` - ), - name: 'openDoor' - }) - - // 设置执行账号 - 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}` - ), - name: 'openDoor' - }) - - // 确认设备连接正常 - 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}` - ), - name: 'openDoor' - }) - 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}` - ), - name: 'openDoor' - }) - - // 是否需要联网 - 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}` - ), - name: 'openDoor' - }) - - // 开门方式 - 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() - }, - `开始写入` - ), - name: 'openDoor' - }) - - 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}` - ), - name: 'openDoor' - }) - - return this.getWriteResult(this.openDoor, params) - }, - /** - * 获取离线密码 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {OfflinePassword} params.password 密码信息 - * @returns {Promise} - */ - 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) - }, - /** - * 自定义密码 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {CustomPassword} params.password 密码信息 - * @param {Boolean} params.disconnect 操作后是否断开连接 - * @returns {Promise} - */ - async customPassword(params) { - const { accountInfo, password } = params - // 设置执行账号 - const result = await this.login(accountInfo) - if (result.code !== Result.Success.code) { - return result - } - - requestParams = password - let { pwdNo, operate, keyboardPwd, startDate, endDate, pwdRight } = password - - if (operate === 0 || operate === 1) { - const checkPasswordParams = { - lockId: this.lockInfo.lockId, - keyboardPwd - } - if (password.keyboardPwdId) { - checkPasswordParams.keyboardPwdId = password.keyboardPwdId - } - const checkPasswordResult = await this.checkPasswordRequest(checkPasswordParams) - if (checkPasswordResult.code === -3) { - return Result.ReadyHasPassword - } - if (checkPasswordResult.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.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} - */ - 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} - */ - 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} - */ - 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} - */ - 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} - */ - 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) - }, - - /** - * 获取锁支持项 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {Number} params.lockId 锁 Id - * @returns {Promise} - */ - async getLockSupportFeatures(params) { - const { accountInfo, lockId } = params - - // 设置执行账号 - const result = await this.login(accountInfo) - if (result.code !== Result.Success.code) { - return result - } - - const { code, data, message } = await getLockSettingDataRequest({ - lockId - }) - if (code === Result.Success.code) { - return new Result(code, { ...data.lockFeature }, message) - } - return new Result(code, data, message) - }, - - /** - * 清理用户 - * @returns {Promise} - */ - 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} - */ - 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) - - 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}` - ), - name: 'openDoor' - }) - 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}` - ), - name: 'openDoor' - }) - 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:明文,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) - }, - // 检查密码名称与密码是否已存在 - async checkPasswordRequest(params) { - return await checkPasswordRequest(params) - } - } -}) diff --git a/storage.js b/storage.js deleted file mode 100644 index febe265..0000000 --- a/storage.js +++ /dev/null @@ -1,18 +0,0 @@ -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}:` -} diff --git a/basic.js b/uni/basic.js similarity index 66% rename from basic.js rename to uni/basic.js index 120726a..cae8c1e 100644 --- a/basic.js +++ b/uni/basic.js @@ -1,155 +1,4 @@ -export class Result { - static codes = { - Success: 0, - Fail: -1, - - NotMoreData: -10, - - NotAvailableBluetooth: -20, - NotAvailableBluetoothPermission: -21, - NotAvailableWeChatNearbyDevicesPermission: -22, - NotAvailableWeChatLocationPermission: -23, - NotAvailableWeChatNearbyDevicesEmpty: -24, - NotAvailableWeChatBluetoothPermission: -25, - DeviceHasBeenReset: -30, - - NotRegisteredLock: 4, - NotTokenLock: 6, - NotMoreKeyLock: 12, - ReadyHasKeyLock: 15, - ReadyHasPassword: 251 - } - - static resultsMap = new Map([ - [Result.codes.Success, { message: '成功', data: {} }], - [Result.codes.Fail, { message: '失败', data: {} }], - - [Result.codes.NotMoreData, { 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.NotAvailableWeChatBluetoothPermission, - { - 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: {} }], - [Result.codes.ReadyHasPassword, { 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 NotMoreData() { - return new Result(Result.codes.NotMoreData) - } - - // 蓝牙未开启 - 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 NotAvailableWeChatBluetoothPermission() { - return new Result(Result.codes.NotAvailableWeChatBluetoothPermission) - } - - // 设备已被重置 - 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) - } - - // 密码已存在 - static get ReadyHasPassword() { - return new Result(Result.codes.ReadyHasPassword) - } -} +import { Result } from '../constant' /** * @typedef {Object} err diff --git a/uni/index.js b/uni/index.js new file mode 100644 index 0000000..f6018ce --- /dev/null +++ b/uni/index.js @@ -0,0 +1,169 @@ +import starCloudInstance from '../star-cloud' + +/** + * 初始化星云 + * @param params + * @param {String} params.clientId 客户端Id + * @param {String} params.clientSecret 客户端密码 + * @param {String} params.env 环境 + * @param {Boolean} params.isReportLog 是否上报日志 + */ +export const init = params => { + starCloudInstance.init({ + ...params, + platform: 1 + }) +} + +/** + * 注册星云 + * @returns Result + */ +export const register = async () => { + return await starCloudInstance.register() +} + +/** + * 退出登录 + * @param params + * @param {Number} params.uid 用户ID + */ +export const logout = params => { + starCloudInstance.logout(params) +} + +/** + * 选择锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Number} params.lockId 锁ID + * @returns Result + */ +export const selectLock = async params => { + return await starCloudInstance.selectLock(params) +} + +/** + * 开门 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {String} params.type 开门方式 close: 关门 open: 开门 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns Result + */ +export const openDoor = async params => { + return await starCloudInstance.openDoor(params) +} + +/** + * 获取离线密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {OfflinePassword} params.password 密码信息 + * @returns Result + */ +export const getOfflinePassword = async params => { + return await starCloudInstance.getOfflinePassword(params) +} + +/** + * 自定义密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {CustomPassword} params.password 密码信息 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns Result + */ +export const customPassword = async params => { + return await starCloudInstance.customPassword(params) +} + +/** + * 搜索蓝牙设备 + * @callback {Function} callback 回调方法 + * @returns Result + */ +export const searchDevice = async callback => { + return await starCloudInstance.searchDevice(callback) +} + +/** + * 停止搜索 + * @returns Result + */ +export const stopSearchDevice = async () => { + return await starCloudInstance.stopSearchDevice() +} + +/** + * 绑定设备 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {String} params.name 设备名称 + * @returns Result + */ +export const bindDevice = async params => { + return await starCloudInstance.bindDevice(params) +} + +/** + * 移除坏锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {List[int]} params.lockIds 锁Id列表 + * @returns Result + */ +export const removeBadLock = async params => { + return await starCloudInstance.removeBadLock(params) +} + +/** + * 删除锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + */ +export const deleteLock = async params => { + return await starCloudInstance.deleteLock(params) +} + +/** + * 修改管理员密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {String} params.adminPwd 管理员密码 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns Result + */ +export const updateAdminPassword = async params => { + return await starCloudInstance.updateAdminPassword(params) +} + +/** + * 同步开门记录 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Boolean} params.disconnect 操作后是否断开连接 + * @returns Result + */ +export const syncOpenDoorRecord = async params => { + return await starCloudInstance.syncAllOpenRecord(params) +} + +/** + * 获取服务器时间 + * @returns Result + */ +export const getServerTime = async () => { + return await starCloudInstance.getServerTimestamp() +} + +/** + * 获取锁支持项 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Number} params.lockId 锁 Id + * @returns Result + */ +export const getLockSupportFeatures = async params => { + return await starCloudInstance.getLockSupportFeatures(params) +} diff --git a/log.js b/uni/log.js similarity index 65% rename from log.js rename to uni/log.js index 04f22ea..eb51c38 100644 --- a/log.js +++ b/uni/log.js @@ -1,49 +1,43 @@ -import { useStarCloudStore } from '@/starCloud/starCloud' +import starCloudInstance from '../star-cloud' const log = wx.getRealtimeLogManager ? wx.getRealtimeLogManager() : null export default { debug() { if (!log) return - const $starCloud = useStarCloudStore() - if (!$starCloud.isReportLog) return + if (!starCloudInstance.isReportLog) return const logger = log.tag(arguments[0].name) logger.info(arguments[0].message, arguments[0]) }, info() { if (!log) return - const $starCloud = useStarCloudStore() - if (!$starCloud.isReportLog) return + if (!starCloudInstance.isReportLog) return const logger = log.tag(arguments[0].name) logger.info(arguments[0].message, arguments[0]) }, warn() { if (!log) return - const $starCloud = useStarCloudStore() - if (!$starCloud.isReportLog) return + if (!starCloudInstance.isReportLog) return const logger = log.tag(arguments[0].name) logger.warn(arguments[0].message, arguments[0]) }, error() { if (!log) return - const $starCloud = useStarCloudStore() - if (!$starCloud.isReportLog) return + if (!starCloudInstance.isReportLog) return const logger = log.tag(arguments[0].name) logger.error(arguments[0].message, arguments[0]) }, setFilterMsg(msg) { if (!log || !log.setFilterMsg) return if (typeof msg !== 'string') return - const $starCloud = useStarCloudStore() - if (!$starCloud.isReportLog) return + if (!starCloudInstance.isReportLog) return const logger = log.tag(arguments[0].name) logger.setFilterMsg(JSON.stringify(arguments[0])) }, addFilterMsg(msg) { if (!log || !log.addFilterMsg) return if (typeof msg !== 'string') return - const $starCloud = useStarCloudStore() - if (!$starCloud.isReportLog) return + if (!starCloudInstance.isReportLog) return const logger = log.tag(arguments[0].name) logger.addFilterMsg(JSON.stringify(arguments[0])) } diff --git a/request.js b/uni/request.js similarity index 93% rename from request.js rename to uni/request.js index 58b2073..f90c83b 100644 --- a/request.js +++ b/uni/request.js @@ -1,6 +1,6 @@ -import { getStorage, removeStorage } from '@/starCloud/storage' -import { useStarCloudStore } from '@/starCloud/starCloud' -import { Result } from '@/starCloud/basic' +import { getStorage, removeStorage } from '../export' +import starCloudInstance from '../star-cloud' +import { Result } from '../constant' /* * config @@ -13,7 +13,7 @@ import { Result } from '@/starCloud/basic' * */ const request = config => { - const starCloud = useStarCloudStore() + const starCloud = starCloudInstance let timer return new Promise(async resolve => { const baseConfig = starCloud.getConfig() diff --git a/uni/storage.js b/uni/storage.js new file mode 100644 index 0000000..8e55c81 --- /dev/null +++ b/uni/storage.js @@ -0,0 +1,18 @@ +import starCloudInstance from '../star-cloud' + +export function setStorageUni(key, value) { + return uni.setStorageSync(getPrefix() + key, value) +} + +export function getStorageUni(key) { + return uni.getStorageSync(getPrefix() + key) +} + +export function removeStorageUni(key) { + return uni.removeStorageSync(getPrefix() + key) +} + +function getPrefix() { + const starCloud = starCloudInstance + return starCloud?.envVersion ? `${starCloud?.envVersion}:` : '' +} diff --git a/web/index.js b/web/index.js new file mode 100644 index 0000000..2c7c696 --- /dev/null +++ b/web/index.js @@ -0,0 +1,74 @@ +import starCloudInstance from '../star-cloud' + +/** + * 初始化星云 + * @param params + * @param {String} params.clientId 客户端Id + * @param {String} params.clientSecret 客户端密码 + * @param {String} params.env 环境 + */ +export const init = params => { + starCloudInstance.init({ + ...params, + isReportLog: false, + platform: 2 + }) +} + +/** + * 注册星云 + * @returns Result + */ +export const register = async () => { + return await starCloudInstance.register() +} + +/** + * 退出登录 + * @param params + * @param {Number} params.uid 用户ID + */ +export const logout = params => { + starCloudInstance.logout(params) +} + +/** + * 获取离线密码 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {OfflinePassword} params.password 密码信息 + * @returns Result + */ +export const getOfflinePassword = async params => { + return await starCloudInstance.getOfflinePassword(params) +} + +/** + * 移除坏锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {List[int]} params.lockIds 锁Id列表 + * @returns Result + */ +export const removeBadLock = async params => { + return await starCloudInstance.removeBadLock(params) +} + +/** + * 获取服务器时间 + * @returns Result + */ +export const getServerTime = async () => { + return await starCloudInstance.getServerTimestamp() +} + +/** + * 获取锁支持项 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {Number} params.lockId 锁 Id + * @returns Result + */ +export const getLockSupportFeatures = async params => { + return await starCloudInstance.getLockSupportFeatures(params) +} diff --git a/web/request.js b/web/request.js new file mode 100644 index 0000000..f5c6aab --- /dev/null +++ b/web/request.js @@ -0,0 +1,107 @@ +import { getStorage, removeStorage } from '@/starCloud/uniapp/storage' +import starCloudInstance from '@/starCloud/star-cloud' +import { Result } from '@/constants/result' + +/* + * config + * baseUrl: 请求域名 + * url: 请求路径 + * method: 请求方法 + * header: 请求头 + * token: 请求token + * data: 请求参数 + * */ + +const request = config => { + const starCloud = starCloudInstance + let timer + let res = null // 在外部定义res,避免finally块报错 + 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) + + try { + const response = await fetch(URL, { + method, + headers: { + ...header, + 'Content-Type': 'application/json' + }, + body: JSON.stringify(data) + }) + + res = await response.json() // 在这里将res赋值 + if (timer) { + clearTimeout(timer) + } + + if (response.ok) { + const { errcode, errmsg, data } = res + if (errcode === 10003) { + removeStorage('starCloudToken') + removeStorage('starCloudUser') + const { code } = await starCloud.login({ + username: starCloud.starCloudAccountInfo.username, + password: starCloud.starCloudAccountInfo.password, + uid: starCloud.starCloudAccountInfo.uid + }) + if (code === Result.Success.code) { + resolve(await request(config)) + } + } else { + resolve({ + code: errcode, + data, + message: errmsg + }) + } + } else { + resolve(new Result(Result.Fail.code, {}, '网络访问失败,请检查网络是否正常')) + } + } catch (error) { + console.log('网络访问失败', error) + if (timer) { + clearTimeout(timer) + } + resolve(new Result(Result.Fail.code, {}, '网络访问失败,请检查网络是否正常')) + } finally { + // 访问res时确保它已定义 + console.log(URL.substring(baseConfig.baseUrl.length + 1), { + env: baseConfig.name, + url: URL.substring(baseConfig.baseUrl.length + 1), + req: config?.data || {}, + code: res?.errcode || null, // 如果res未定义,fallback为null + res: res?.data || null, // 如果res未定义,fallback为null + token: header?.authorization || '', + message: res?.errmsg || '', // 如果res未定义,fallback为空字符串 + duration: new Date().getTime() - timestamp + }) + } + }) +} + +export default request diff --git a/web/storage.js b/web/storage.js new file mode 100644 index 0000000..f9f23be --- /dev/null +++ b/web/storage.js @@ -0,0 +1,19 @@ +import starCloudInstance from '../star-cloud' + +export function setStorageWeb(key, value) { + return localStorage.setItem(getPrefix() + key, JSON.stringify(value)) +} + +export function getStorageWeb(key) { + const value = localStorage.getItem(getPrefix() + key) + return value ? JSON.parse(value) : null +} + +export function removeStorageWeb(key) { + return localStorage.removeItem(getPrefix() + key) +} + +function getPrefix() { + const starCloud = starCloudInstance + return starCloud?.envVersion ? `${starCloud?.envVersion}:` : '' +}