From fa526edeb644a7b86fc25461315be05190fca606 Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 15 Apr 2025 15:21:17 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=9F=E8=83=BD=E9=A1=B9=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=EF=BC=88=E5=B8=A6=E5=8F=82=E6=95=B0=EF=BC=89=E3=80=81=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E6=94=AF=E6=8C=81=E5=8A=9F=E8=83=BD=E9=A1=B9=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E3=80=81=E8=BF=9C=E7=A8=8B=E5=BC=80=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common.js | 108 +++++++++--------------------------- constant.js | 12 +++- format.js | 72 ++++++++++++++++++++++++ star-cloud/generalExtend.js | 76 +++++++++++++++---------- star-cloud/lock.js | 54 +++++++++++++++++- star-cloud/password.js | 6 +- 6 files changed, 213 insertions(+), 115 deletions(-) diff --git a/common.js b/common.js index 39b8817..c5e22d1 100644 --- a/common.js +++ b/common.js @@ -1006,7 +1006,7 @@ export async function parsingCharacteristicValue(binaryData) { isCoerced: this.requestParams.isForce, cardRight: this.requestParams.isAdmin, lockId: this.lockInfo.lockId, - cardNumber: this.requestParams.cardNumber, + cardNumber: decrypted[6] * 256 + decrypted[7], cardUserNo: this.requestParams.cardUserNo }) if (addResult.code === Result.Success.code) { @@ -1084,7 +1084,7 @@ export async function parsingCharacteristicValue(binaryData) { isCoerced: this.requestParams.isForce, fingerRight: this.requestParams.isAdmin, lockId: this.lockInfo.lockId, - fingerprintNumber: this.requestParams.fingerprintNumber, + fingerprintNumber: decrypted[6] * 256 + decrypted[7], fingerprintUserNo: this.requestParams.fingerprintUserNo }) if (addResult.code === Result.Success.code) { @@ -1159,7 +1159,7 @@ export async function parsingCharacteristicValue(binaryData) { isCoerced: this.requestParams.isForce, fingerRight: this.requestParams.isAdmin, lockId: this.lockInfo.lockId, - faceNumber: this.requestParams.faceNumber, + faceNumber: decrypted[6] * 256 + decrypted[7], faceUserNo: this.requestParams.faceUserNo }) if (addResult.code === Result.Success.code) { @@ -1228,7 +1228,7 @@ export async function parsingCharacteristicValue(binaryData) { isCoerced: this.requestParams.isForce, palmVeinRight: this.requestParams.isAdmin, lockId: this.lockInfo.lockId, - palmVeinNumber: this.requestParams.palmVeinNumber, + palmVeinNumber: decrypted[6] * 256 + decrypted[7], palmVeinUserNo: this.requestParams.palmVeinUserNo }) if (addResult.code === Result.Success.code) { @@ -1297,7 +1297,7 @@ export async function parsingCharacteristicValue(binaryData) { isCoerced: this.requestParams.isForce, remoteRight: this.requestParams.isAdmin, lockId: this.lockInfo.lockId, - remoteNumber: this.requestParams.remoteNumber, + remoteNumber: decrypted[6] * 256 + decrypted[7], remoteUserNo: this.requestParams.remoteUserNo }) if (addResult.code === Result.Success.code) { @@ -1318,7 +1318,6 @@ export async function parsingCharacteristicValue(binaryData) { // 收到锁版回复判断操作类型进行对应api操作 if (decrypted[2] === Result.Success.code) { const {data, featureEnable} = this.requestParams - console.log('this.requestParams', this.requestParams) let updateResult = new Result(); switch (this.requestParams.featureBit) { case supportFunctionsFeatureBit.passageMode: @@ -1327,8 +1326,8 @@ export async function parsingCharacteristicValue(binaryData) { passageMode: data.passageMode, passageModeConfig: [ { - startTime: data.startTime, - endTime: data.endTime, + startDate: data.startDate, + endDate: data.endDate, isAllDay: data.isAllDay, weekDays: data.weekDay, autoUnlock: data.autoUnlock, @@ -1366,10 +1365,25 @@ export async function parsingCharacteristicValue(binaryData) { } break; case subCmdIds.supportFunctions: - this.updateLockInfo({ - token: decrypted.slice(5, 9) - }) - this.characteristicValueCallback(new Result(decrypted[2])) + // 收到锁版回复判断操作类型进行对应api操作 + if (decrypted[2] === Result.Success.code) { + const {data, featureEnable} = this.requestParams + let updateResult = new Result(); + switch (this.requestParams.featureBit) { + case supportFunctionsFeatureBit.antiPrySwitch: + updateResult = await updateLockSettingRequest({ + lockId: this.lockInfo.lockId, + antiPrySwitch: featureEnable + }) + break; + default: + this.characteristicValueCallback(new Result(decrypted[2])) + break; + } + this.characteristicValueCallback(new Result(updateResult.code)) + } else { + this.characteristicValueCallback(new Result(decrypted[2])) + } break; default: break @@ -1496,73 +1510,3 @@ export async function disconnectDevice() { } -/** - * 用于判断时间戳是否是秒级别的,如果是的话则补充到毫秒级别 - * @param timestamp 时间戳 - * @returns {number|*} - * @private - */ -export function _convertToMilliseconds(timestamp) { - if (!timestamp) return timestamp; - return timestamp.toString().length === 10 ? timestamp * 1000 : timestamp; -} - -/** - * 检查并返回具体哪个参数为空 - * @param {Object} params - 参数对象 - * @param {Array} requiredFields - 必填字段列表 - * @returns {string | null} - 如果有字段为空,则返回错误消息;否则返回null - */ -export function checkRequiredFields(params, requiredFields) { - for (let field of requiredFields) { - if (params[field] === undefined || params[field] === null) { - return `${field} 不能为空`; - } - } - return null; -} - -/** - * 辅助函数:将分钟时间转换为两个字节的数组 - * @param minutes - * @returns {(number|number)[]} - */ -export function convertTimeToBytes(minutes) { - return [ - Math.floor(minutes / 256), // 高字节 - minutes % 256 // 低字节 - ]; -} - -/** - * 二进制字符串转换为十进制数字数组 - * 例如 '0111110' 会被转换为 [1, 2, 3, 4, 5](表示周一到周五) - * @param weekdayBit - * @returns {*[]} - */ -export function convertWeekdayBitToArray(weekdayBit) { - const weekdays = []; - const bits = weekdayBit.split(''); - bits.forEach((bit, index) => { - if (bit === '1') { - weekdays.push(index); - } - }); - return weekdays; -} - -/** - * 十进制数字数组转为二进制字符串 - * 例如 将 [1,2,3,4,5] 转换回 '0111110' - * @param weekdays - * @returns {string} - */ -export function convertWeekdayArrayToBit(weekdays) { - const bits = new Array(7).fill('0'); - weekdays.forEach(day => { - if (day >= 0 && day < 7) { - bits[day] = '1'; - } - }); - return bits.join(''); -} \ No newline at end of file diff --git a/constant.js b/constant.js index 689448b..700e5f7 100644 --- a/constant.js +++ b/constant.js @@ -17,7 +17,15 @@ export const cmdIds = { // 清理用户 cleanUser: 0x300c, // 扩展命令 - expandCmd: 0x3030 + expandCmd: 0x3030, + // 获取Wi-Fi列表 + getWifiList: 0x30f6, + // Wi-Fi列表 + wifiList: 0x30f7, + // 配网 + distributionNetwork: 0x30f4, + // 配网结果 + distributionNetworkResult: 0x30f5 } // 子命令ID @@ -68,6 +76,8 @@ export const subCmdIds = { supportFunctionsWithParams: 72, // 设置支持功能 supportFunctions: 70, + // 获取wifi列表 + // getWifiList: 70, } // 支持项对于功能位 diff --git a/format.js b/format.js index 051e0b7..a6444b0 100644 --- a/format.js +++ b/format.js @@ -100,3 +100,75 @@ export function removeTrailingZeros(data) { } return data.slice(0, currentIndex) } + + +/** + * 将分钟时间转换为两个字节的数组 + * @param minutes + * @returns {(number|number)[]} + */ +export function convertTimeToBytes(minutes) { + return [ + Math.floor(minutes / 256), // 高字节 + minutes % 256 // 低字节 + ]; +} + +/** + * 二进制字符串转换为十进制数字数组 + * 例如 '0111110' 会被转换为 [1, 2, 3, 4, 5](表示周一到周五) + * @param weekdayBit + * @returns {*[]} + */ +export function convertWeekdayBitToArray(weekdayBit) { + const weekdays = []; + const bits = weekdayBit.split(''); + bits.forEach((bit, index) => { + if (bit === '1') { + weekdays.push(index); + } + }); + return weekdays; +} + +/** + * 十进制数字数组转为二进制字符串 + * 例如 将 [1,2,3,4,5] 转换回 '0111110' + * @param weekdays + * @returns {string} + */ +export function convertWeekdayArrayToBit(weekdays) { + const bits = new Array(7).fill('0'); + weekdays.forEach(day => { + if (day >= 0 && day < 7) { + bits[day] = '1'; + } + }); + return bits.join(''); +} + +/** + * 用于判断时间戳是否是秒级别的,如果是的话则补充到毫秒级别 + * @param timestamp 时间戳 + * @returns {number|*} + * @private + */ +export function _convertToMilliseconds(timestamp) { + if (!timestamp) return timestamp; + return timestamp.toString().length === 10 ? timestamp * 1000 : timestamp; +} + +/** + * 检查并返回具体哪个参数为空 + * @param {Object} params - 参数对象 + * @param {Array} requiredFields - 必填字段列表 + * @returns {string | null} - 如果有字段为空,则返回错误消息;否则返回null + */ +export function checkRequiredFields(params, requiredFields) { + for (let field of requiredFields) { + if (params[field] === undefined || params[field] === null) { + return `${field} 不能为空`; + } + } + return null; +} diff --git a/star-cloud/generalExtend.js b/star-cloud/generalExtend.js index c1de6ea..a7f79d9 100644 --- a/star-cloud/generalExtend.js +++ b/star-cloud/generalExtend.js @@ -1,6 +1,16 @@ import {searchAndConnectDevice, writeBLECharacteristicValue} from "../uni/basic.js"; import {cmdIds, Result, subCmdIds, supportFunctionsFeatureBit} from '../constant' -import {convertWeekdaysToNumber, createPackageEnd, md5Encrypt, parseTimeToList, timestampToArray} from "../format.js"; +import { + convertWeekdaysToNumber, + createPackageEnd, + md5Encrypt, + parseTimeToList, + timestampToArray, + convertTimeToBytes, + convertWeekdayArrayToBit, + _convertToMilliseconds, + checkRequiredFields, +} from "../format.js"; import {sm4} from "sm-crypto"; import { checkRepeatCardName, @@ -15,8 +25,6 @@ import { updateIcCardRequest } from "../api.js"; -import {_convertToMilliseconds, checkRequiredFields, convertTimeToBytes, convertWeekdayArrayToBit} from "../common.js"; - /** * 注册扩展产品(卡片、指纹、人脸、遥控、掌静脉等) @@ -65,10 +73,19 @@ import {_convertToMilliseconds, checkRequiredFields, convertTimeToBytes, convert */ export async function registerExtendedProducts(params) { - const cardRequiredFields = ['type', 'keyId', 'uid', 'userCountLimit', 'operate', 'isAdmin', 'isForce', 'isRound', 'startDate', 'endDate']; + const cardRequiredFields = ['type', 'userCountLimit', 'operate', 'isAdmin', 'isForce', 'isRound', 'startDate', 'endDate']; const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `参数信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `参数信息不完整: ${missingField}`); + } + + // 循环类型参数校验 + if (params.isRound && params.isRound === 1) { + const roundRequiredFields = ['startTime', 'endTime', 'weekDay']; + const missingField = checkRequiredFields(params, roundRequiredFields); + if (missingField) { + return new Result(Result.codes.NotMoreData, null, `参数信息不完整: ${missingField}`); + } } // 如果是卡片的话需要增加的参数 @@ -78,7 +95,7 @@ export async function registerExtendedProducts(params) { const cardRequiredFields = ['cardName', 'cardType', 'cardNumber']; const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `卡片信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `卡片信息不完整: ${missingField}`); } // 检查卡昵称是否重复 const checkRepeatCardNameResult = await checkRepeatCardName({ @@ -101,7 +118,7 @@ export async function registerExtendedProducts(params) { } const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `卡片信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `卡片信息不完整: ${missingField}`); } } } @@ -112,7 +129,7 @@ export async function registerExtendedProducts(params) { const cardRequiredFields = ['fingerprintName', 'fingerprintNumber', 'fingerprintType']; const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `指纹信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `指纹信息不完整: ${missingField}`); } // 检查卡昵称是否重复 const checkRepeatFingerprintNameResult = await checkRepeatFingerprintName({ @@ -136,7 +153,7 @@ export async function registerExtendedProducts(params) { } const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `指纹信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `指纹信息不完整: ${missingField}`); } } } @@ -147,7 +164,7 @@ export async function registerExtendedProducts(params) { const cardRequiredFields = ['faceName', 'faceNumber', 'faceType']; const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `人脸信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `人脸信息不完整: ${missingField}`); } // 检查卡昵称是否重复 const checkRepeatFaceNameResult = await checkRepeatFaceName({ @@ -170,7 +187,7 @@ export async function registerExtendedProducts(params) { } const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `人脸信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `人脸信息不完整: ${missingField}`); } } } @@ -181,7 +198,7 @@ export async function registerExtendedProducts(params) { const cardRequiredFields = ['palmVeinName', 'palmVeinNumber', 'palmVeinType']; const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `掌静脉信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `掌静脉信息不完整: ${missingField}`); } // 检查卡昵称是否重复 const checkRepeatFaceNameResult = await checkRepeatPalmVeinName({ @@ -204,7 +221,7 @@ export async function registerExtendedProducts(params) { } const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `掌静脉信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `掌静脉信息不完整: ${missingField}`); } } } @@ -215,7 +232,7 @@ export async function registerExtendedProducts(params) { const cardRequiredFields = ['remoteName', 'remoteNumber', 'remoteType']; const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `遥控信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `遥控信息不完整: ${missingField}`); } // 检查卡昵称是否重复 const checkRepeatFaceNameResult = await checkRepeatRemoteName({ @@ -238,7 +255,7 @@ export async function registerExtendedProducts(params) { } const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `遥控信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `遥控信息不完整: ${missingField}`); } } } @@ -250,8 +267,6 @@ export async function registerExtendedProducts(params) { let { type, - keyId, - uid, operate, isAdmin, userCountLimit, @@ -265,6 +280,10 @@ export async function registerExtendedProducts(params) { } = params + const uid = this.accountInfo.uid.toString() + const keyId = this.lockInfo.keyId.toString() + + // 设置执行账号 const result = await this.login(uid) if (result.code !== Result.Success.code) { @@ -387,6 +406,7 @@ export async function registerExtendedProducts(params) { contentArray.set(md5Array, 94) + console.log('加密前:', Array.from(contentArray)) const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, { mode: 'ecb', output: 'array' @@ -517,8 +537,8 @@ export async function updateSupportFunctionsWithParams(params) { const featureBitFields = { [supportFunctionsFeatureBit.passageMode]: [ 'passageMode', - 'startTime', - 'endTime', + 'startDate', + 'endDate', 'isAllDay', 'weekDay', 'autoUnlock' @@ -534,7 +554,7 @@ export async function updateSupportFunctionsWithParams(params) { const dataFields = featureBitFields[params.featureBit]; for (const field of dataFields) { if (params.data === undefined || params.data[field] === undefined) { - return new Result(Result.NotMoreData, null, `data参数信息不完整: ${field}`); + return new Result(Result.codes.NotMoreData, null, `data参数信息不完整: ${field}`); } } } @@ -544,7 +564,7 @@ export async function updateSupportFunctionsWithParams(params) { const missingField = checkRequiredFields(params, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `参数信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `参数信息不完整: ${missingField}`); } // 深拷贝一份参数赋值 @@ -555,14 +575,14 @@ export async function updateSupportFunctionsWithParams(params) { try { switch (params.featureBit) { case supportFunctionsFeatureBit.passageMode: - const {passageMode, startTime, endTime, isAllDay, weekDay, autoUnlock} = params.data - const startTimeBytes = convertTimeToBytes(startTime); - const endTimeBytes = convertTimeToBytes(endTime); + const {passageMode, startDate, endDate, isAllDay, weekDay, autoUnlock} = params.data + const startDateBytes = convertTimeToBytes(startDate); + const endDateBytes = convertTimeToBytes(endDate); const weekDayBit = parseInt(String(convertWeekdayArrayToBit(weekDay)), 2); params.data = [ passageMode, - ...startTimeBytes, - ...endTimeBytes, + ...startDateBytes, + ...endDateBytes, isAllDay, weekDayBit, autoUnlock @@ -574,7 +594,7 @@ export async function updateSupportFunctionsWithParams(params) { } console.log('转换后的参数:', params.data) } catch (e) { - return new Result(Result.NotMoreData, null, `参数转换时异常,请检查参数格式: ${e}`); + return new Result(Result.codes.NotMoreData, null, `参数转换时异常,请检查参数格式: ${e}`); } } @@ -679,7 +699,7 @@ export async function updateSupportFunctionsWithParams(params) { * @param {Number} params.lockId 锁id (必填) * @returns {Promise} */ -export async function readSupportFunctionsSetting(params){ +export async function readSupportFunctionsSetting(params) { return await getLockSettingRequest(params) } diff --git a/star-cloud/lock.js b/star-cloud/lock.js index 454e351..6c90371 100644 --- a/star-cloud/lock.js +++ b/star-cloud/lock.js @@ -1,11 +1,12 @@ import {sm4} from 'sm-crypto' import {cmdIds, Result} from '../constant' import {searchAndConnectDevice, writeBLECharacteristicValue} from '../uni/basic' -import {createPackageEnd, md5Encrypt, timestampToArray} from '../format' +import {createPackageEnd, md5Encrypt, timestampToArray,checkRequiredFields} from '../format' import {getLockDetailRequest, getLockSettingDataRequest, remoteUnLockRequest} from '../api' import {getStorage, setStorage} from '../export' import log from '../log' + /** * 选择锁 * @param params @@ -381,3 +382,54 @@ export async function remoteUnLock(params) { lockId: params.lockId, }) } + +/** + * 搜索wifi列表 + * @param params + * @returns {Promise} + */ +export async function searchWifiList(params) { + // 需要校验的参数 + const baseRequiredFields = [ + 'uid', + ]; + const missingField = checkRequiredFields(params, baseRequiredFields); + if (missingField) { + return new Result(Result.NotMoreData, null, `参数信息不完整: ${missingField}`); + } + + // 确认设备连接正常 + if (!params.connected) { + const searchResult = await searchAndConnectDevice(this.lockInfo.bluetooth.bluetoothDeviceName) + if (searchResult.code !== Result.Success.code) { + return searchResult + } + this.updateLockInfo(searchResult.data) + } + + const {uid} = params + const length = 2 + 20 + const headArray = this.createPackageHeader(3, length) + + const contentArray = new Uint8Array(length) + contentArray[0] = cmdIds.getWifiList / 256 + contentArray[1] = cmdIds.getWifiList % 256 + for (let i = 0; i < uid.length; i++) { + contentArray[i + 2] = uid.charCodeAt(i) + } + console.log('加密前:', Array.from(contentArray)) + + const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, { + mode: 'ecb', + output: 'array' + }) + + const packageArray = createPackageEnd(headArray, cebArray) + + await writeBLECharacteristicValue(this.lockInfo.deviceId, + this.lockInfo.serviceId, + this.lockInfo.writeCharacteristicId, + packageArray) + + return this.getWriteResult(this.searchWifiList, params) +} \ No newline at end of file diff --git a/star-cloud/password.js b/star-cloud/password.js index 1650c21..2246b6a 100644 --- a/star-cloud/password.js +++ b/star-cloud/password.js @@ -1,11 +1,11 @@ import {sm4} from 'sm-crypto' import {cmdIds, Result, subCmdIds} from '../constant' import {searchAndConnectDevice, writeBLECharacteristicValue} from '../uni/basic' -import {createPackageEnd, md5Encrypt, timestampToArray} from '../format' +import {createPackageEnd, md5Encrypt, timestampToArray,checkRequiredFields} from '../format' import {checkPasswordRequest, getOfflinePasswordRequest} from '../api' import {getConfig} from "../common.js"; import StarCloud from "../star-cloud.js"; -import {checkRequiredFields} from "../common.js"; + /** @@ -84,7 +84,7 @@ export async function customPassword(params) { const missingField = checkRequiredFields(params.password, cardRequiredFields); if (missingField) { - return new Result(Result.NotMoreData, null, `参数信息不完整: ${missingField}`); + return new Result(Result.codes.NotMoreData, null, `参数信息不完整: ${missingField}`); }