feat:增加更新支持功能项设置(带参数)、读取支持功能项设置、远程开锁

This commit is contained in:
liyi 2025-04-15 15:21:17 +08:00
parent 9bc62bc6fb
commit fa526edeb6
6 changed files with 213 additions and 115 deletions

108
common.js
View File

@ -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<string>} 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('');
}

View File

@ -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,
}
// 支持项对于功能位

View File

@ -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<string>} 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;
}

View File

@ -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<void>}
*/
export async function readSupportFunctionsSetting(params){
export async function readSupportFunctionsSetting(params) {
return await getLockSettingRequest(params)
}

View File

@ -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<void>}
*/
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)
}

View File

@ -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}`);
}