From bcb7a9cfa182bcc3ec309119268dd2d4eeca927f Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 11 Jun 2025 17:29:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0=E9=85=8D=E7=BD=91?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api.js | 2 +- common.js | 39 ++- constant.js | 10 +- format.js | 216 ++++++++----- obfuscate-uni.js | 6 +- package-lock.json | 4 +- star-cloud.js | 3 +- star-cloud/configureNetwork.js | 227 ++++++++------ uni.md | 62 ++-- uni/basic.js | 556 +++++++++++++++++---------------- uni/index.js | 22 +- 11 files changed, 638 insertions(+), 509 deletions(-) diff --git a/api.js b/api.js index eb704a2..10598f4 100644 --- a/api.js +++ b/api.js @@ -126,7 +126,7 @@ export function deleteLockRequest(data) { }) } -// 删除锁 +// 更新锁电量 export function updateElectricQuantityRequest(data) { return request({ url: '/v1/lock/updateElectricQuantity', diff --git a/common.js b/common.js index c5e22d1..cb70656 100644 --- a/common.js +++ b/common.js @@ -3,7 +3,7 @@ import { arrayToTimestamp, convertWeekdaysToNumber, createPackageEnd, - md5Encrypt, + md5Encrypt, parseWifiList, removeTrailingZeros, timestampToArray, uint8ArrayToString @@ -53,6 +53,7 @@ import { version } from './export' import log from './log' +import {emitSearchWiFiResultEventUni} from "./uni/storage.js"; /** * 同步开门记录 @@ -573,6 +574,7 @@ export function getWriteResult(request, params) { return new Promise(resolve => { const getWriteResultTimer = setTimeout(() => { resolve(Result.Fail) + console.log("超时失败") }, 20000) this.characteristicValueCallback = async data => { // code 6 token过期,重新获取 @@ -733,7 +735,6 @@ export function listenCharacteristicValue(res) { // 解析特征值 export async function parsingCharacteristicValue(binaryData) { - // 0x20 明文 0x22 SM4(事先约定密钥) 0x23 SM4(设备指定密钥) if (binaryData[7] === 0x20) { if (binaryData[12] * 256 + binaryData[13] === cmdIds.getPublicKey) { @@ -746,7 +747,16 @@ export async function parsingCharacteristicValue(binaryData) { }) } this.characteristicValueCallback(new Result(binaryData[14])) + } else if (binaryData[12] * 256 + binaryData[13] === cmdIds.searchWiFi) { + this.characteristicValueCallback(new Result(binaryData[14])) } + // } else if (binaryData[12] * 256 + binaryData[13] === cmdIds.configureNetworkResult) { + // console.log("收到明文数据configureNetworkResult", Array.from(binaryData)) + // this.characteristicValueCallback(new Result(binaryData[14])) + // } else if (binaryData[12] * 256 + binaryData[13] === cmdIds.configureNetwork) { + // console.log("收到明文数据configureNetwork", Array.from(binaryData)) + // this.characteristicValueCallback(new Result(binaryData[14])) + // } } else if (binaryData[7] === 0x22) { // 截取入参 const cebBinaryData = binaryData.slice(12, binaryData.length - 2) @@ -759,7 +769,7 @@ export async function parsingCharacteristicValue(binaryData) { mode: 'ecb', output: 'array' }) - console.log('ecb解密后的数据', decrypted) + console.log('SM4(事先约定密钥) ecb解密后的数据', decrypted) if (decrypted[0] * 256 + decrypted[1] === cmdIds.getCommKey) { if (decrypted[2] === Result.Success.code) { @@ -775,6 +785,9 @@ export async function parsingCharacteristicValue(binaryData) { console.log('signKey', Array.from(this.lockInfo.bluetooth.signKey)) } this.characteristicValueCallback(new Result(decrypted[2])) + } else if (decrypted[0] * 256 + decrypted[1] === cmdIds.configureNetworkResult) { + console.log("SM4(事先约定密钥) ecb解密后的数据configureNetworkResult", Array.from(binaryData)) + this.characteristicValueCallback(new Result(binaryData[14])) } } else { const cebBinaryData = binaryData.slice(12, binaryData.length - 2) @@ -783,7 +796,7 @@ export async function parsingCharacteristicValue(binaryData) { output: 'array', padding: 'none' }) - console.log('ecb解密后的数据', decrypted) + console.log('SM4(设备指定密钥) ecb解密后的数据', decrypted) const cmdId = decrypted[0] * 256 + decrypted[1] @@ -1091,7 +1104,6 @@ export async function parsingCharacteristicValue(binaryData) { // 增加返回一个指纹序号 addResult.data.fingerprintNumber = decrypted[6] * 256 + decrypted[7]; } - console.log('registerFingerprintConfirm', decrypted) // 触发指纹确认事件 emitRegisterFingerprintConfirmEvent(addResult) // 断开蓝牙连接 @@ -1430,6 +1442,23 @@ export async function parsingCharacteristicValue(binaryData) { this.characteristicValueCallback(new Result(decrypted[6])) } break + case cmdIds.searchWiFiResult: + if (decrypted[2] === Result.Success.code) { + const wifiInfoList = parseWifiList(decrypted); + emitSearchWiFiResultEventUni({ + code: wifiInfoList.status, + numberOfSsid: wifiInfoList.numberOfSsid, + wifiList: wifiInfoList.wifiList, + }) + } + break; + case cmdIds.configureNetworkResult: + if (decrypted[2] === Result.Success.code) { + this.characteristicValueCallback(new Result(decrypted[2], null, "配网成功")) + } else { + this.characteristicValueCallback(new Result(decrypted[2], null, "配网失败")) + } + break; default: this.updateLockInfo({ token: decrypted.slice(2, 6) diff --git a/constant.js b/constant.js index 4aadaf7..33d8da1 100644 --- a/constant.js +++ b/constant.js @@ -18,14 +18,14 @@ export const cmdIds = { cleanUser: 0x300c, // 扩展命令 expandCmd: 0x3030, - // 获取Wi-Fi列表 - getWifiList: 0x30f6, + // 获取Wi-Fi + searchWiFi: 0x30F6, // Wi-Fi列表 - wifiList: 0x30f7, + searchWiFiResult: 0x30F7, // 配网 - distributionNetwork: 0x30f4, + configureNetwork: 0x30F4, // 配网结果 - distributionNetworkResult: 0x30f5 + configureNetworkResult: 0x30F5 } // 子命令ID diff --git a/format.js b/format.js index deb0fab..d364304 100644 --- a/format.js +++ b/format.js @@ -1,104 +1,105 @@ -import { md5 } from 'js-md5' +import {md5} from 'js-md5' import crc from 'crc' // 周数组转换 export function convertWeekdaysToNumber(weekDay) { - let weekStr = '00000000' + let weekStr = '00000000' - // eslint-disable-next-line no-restricted-syntax - for (const day of weekDay) { - const index = day % 7 - weekStr = weekStr.substring(0, index) + '1' + weekStr.substring(index + 1) - } + // eslint-disable-next-line no-restricted-syntax + for (const day of weekDay) { + const index = day % 7 + weekStr = weekStr.substring(0, index) + '1' + weekStr.substring(index + 1) + } - // 倒序 weekStr - weekStr = weekStr.split('').reverse().join('') + // 倒序 weekStr + weekStr = weekStr.split('').reverse().join('') - return parseInt(weekStr, 2) + return parseInt(weekStr, 2) } // 时间戳转二进制 export function timestampToArray(timestamp) { - const array = new Uint8Array(4) - array[0] = (timestamp & 0xff000000) >> 24 - array[1] = (timestamp & 0xff0000) >> 16 - array[2] = (timestamp & 0xff00) >> 8 - array[3] = timestamp & 0xff - return array + const array = new Uint8Array(4) + array[0] = (timestamp & 0xff000000) >> 24 + array[1] = (timestamp & 0xff0000) >> 16 + array[2] = (timestamp & 0xff00) >> 8 + array[3] = timestamp & 0xff + return array } + // 时分字符串转二进制 export function parseTimeToList(timeString) { - let timeList = [0, 0, 0, 0] + let timeList = [0, 0, 0, 0] - if (timeString.includes(':')) { - let timeParts = timeString.split(':') - timeList[2] = parseInt(timeParts[0], 10) - timeList[3] = parseInt(timeParts[1], 10) - } + if (timeString.includes(':')) { + let timeParts = timeString.split(':') + timeList[2] = parseInt(timeParts[0], 10) + timeList[3] = parseInt(timeParts[1], 10) + } - return new Uint8Array(timeList) + return new Uint8Array(timeList) } // md5加密 export function md5Encrypt(text, token, key) { - const length = text.length + 4 + 16 - const md5Array = new Uint8Array(length) + const length = text.length + 4 + 16 + const md5Array = new Uint8Array(length) - for (let i = 0; i < text.length; i++) { - md5Array[i] = text.charCodeAt(i) - } + for (let i = 0; i < text.length; i++) { + md5Array[i] = text.charCodeAt(i) + } - md5Array.set(token, text.length) - md5Array.set(key, text.length + 4) + md5Array.set(token, text.length) + md5Array.set(key, text.length + 4) - const md5Text = md5(md5Array) - return new Uint8Array(md5Text.match(/.{1,2}/g).map(byte => parseInt(byte, 16))) + const md5Text = md5(md5Array) + return new Uint8Array(md5Text.match(/.{1,2}/g).map(byte => parseInt(byte, 16))) } // 生成包尾 头部数据+内容数据 export function createPackageEnd(headArray, contentArray) { - // 拼接头部和内容 - let mergerArray = new Uint8Array(headArray.length + contentArray.length) - mergerArray.set(headArray) - mergerArray.set(contentArray, headArray.length) + // 拼接头部和内容 + let mergerArray = new Uint8Array(headArray.length + contentArray.length) + mergerArray.set(headArray) + mergerArray.set(contentArray, headArray.length) - // crc加密 - const crcResult = crc.crc16kermit(mergerArray) + // crc加密 + const crcResult = crc.crc16kermit(mergerArray) - // 拼接crc - let newArray = new Uint8Array(mergerArray.length + 2) - newArray.set(mergerArray) - newArray.set([crcResult / 256, crcResult % 256], mergerArray.length) + // 拼接crc + let newArray = new Uint8Array(mergerArray.length + 2) + newArray.set(mergerArray) + newArray.set([crcResult / 256, crcResult % 256], mergerArray.length) - return newArray + return newArray } // 二进制转时间戳 export function arrayToTimestamp(array) { - const timestamp = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3] - return timestamp >>> 0 + const timestamp = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3] + return timestamp >>> 0 } // 二进制转字符串 export function uint8ArrayToString(uint8Array) { - let str = '' - for (let i = 0; i < uint8Array.length; i++) { - if (uint8Array[i] !== 0) { - str += String.fromCharCode(uint8Array[i]) + let str = '' + for (let i = 0; i < uint8Array.length; i++) { + if (uint8Array[i] !== 0) { + str += String.fromCharCode(uint8Array[i]) + } } - } - return str + return str } // 去除特征值尾部0 export function removeTrailingZeros(data) { - const featureCount = data[0] - let currentIndex = 1 - for (let i = 0; i < featureCount; i++) { - const length = data[currentIndex + 1] - currentIndex += 2 + length - } - return data.slice(0, currentIndex) + const featureCount = data[0] + let currentIndex = 1 + for (let i = 0; i < featureCount; i++) { + const length = data[currentIndex + 1] + currentIndex += 2 + length + } + return data.slice(0, currentIndex) } @@ -108,10 +109,10 @@ export function removeTrailingZeros(data) { * @returns {(number|number)[]} */ export function convertTimeToBytes(minutes) { - return [ - Math.floor(minutes / 256), // 高字节 - minutes % 256 // 低字节 - ]; + return [ + Math.floor(minutes / 256), // 高字节 + minutes % 256 // 低字节 + ]; } /** @@ -121,14 +122,14 @@ export function convertTimeToBytes(minutes) { * @returns {*[]} */ export function convertWeekdayBitToArray(weekdayBit) { - const weekdays = []; - const bits = weekdayBit.split(''); - bits.forEach((bit, index) => { - if (bit === '1') { - weekdays.push(index); - } - }); - return weekdays; + const weekdays = []; + const bits = weekdayBit.split(''); + bits.forEach((bit, index) => { + if (bit === '1') { + weekdays.push(index); + } + }); + return weekdays; } /** @@ -138,15 +139,15 @@ export function convertWeekdayBitToArray(weekdayBit) { * @returns {string} - 7位二进制字符串,从右到左分别代表周一到周日 */ export function convertWeekdayArrayToBit(weekdays) { - const bits = new Array(7).fill('0'); - weekdays.forEach(day => { - if (day >= 1 && day <= 7) { - // 将1-7转换为0-6的数组索引 - const index = day === 7 ? 0 : day - 1; - bits[index] = '1'; - } - }); - return bits.join(''); + const bits = new Array(7).fill('0'); + weekdays.forEach(day => { + if (day >= 1 && day <= 7) { + // 将1-7转换为0-6的数组索引 + const index = day === 7 ? 0 : day - 1; + bits[index] = '1'; + } + }); + return bits.join(''); } /** @@ -156,8 +157,8 @@ export function convertWeekdayArrayToBit(weekdays) { * @private */ export function _convertToMilliseconds(timestamp) { - if (!timestamp) return timestamp; - return timestamp.toString().length === 10 ? timestamp * 1000 : timestamp; + if (!timestamp) return timestamp; + return timestamp.toString().length === 10 ? timestamp * 1000 : timestamp; } /** @@ -167,10 +168,53 @@ export function _convertToMilliseconds(timestamp) { * @returns {string | null} - 如果有字段为空,则返回错误消息;否则返回null */ export function checkRequiredFields(params, requiredFields) { - for (let field of requiredFields) { - if (params[field] === undefined || params[field] === null) { - return `${field} 不能为空`; + for (let field of requiredFields) { + if (params[field] === undefined || params[field] === null) { + return `${field} 不能为空`; + } } - } - return null; + return null; } + + +/** + * 转换解析wifi列表数据 + * @param data + * @returns {{status: *, numberOfSsid: *, wifiList: *[]}} + */ +export function parseWifiList(data) { + // data: Array or Uint8Array + // 跳过前两位命令码 + const status = data[2]; + const numberOfSsid = data[3]; + const wifiList = []; + let offset = 4; + for (let i = 0; i < numberOfSsid; i++) { + // 取32字节SSID + const ssidBytes = data.slice(offset, offset + 32); + // 用自定义函数转字符串并去除末尾0 + const ssid = _bytesToString(ssidBytes).trim(); + + offset += 32; + // 取rssi + let rssi = data[offset]; + // 通常rssi是无符号byte,需转为有符号 + if (rssi > 127) rssi = rssi - 256; + offset += 1; + // 过滤空名 + if (ssid) { + wifiList.push({ssid, rssi}); + } + } + return {status, numberOfSsid, wifiList}; +} + +function _bytesToString(bytes) { + // 只适用于ASCII和UTF-8无多字节字符的场景 + let str = ''; + for (let i = 0; i < bytes.length; i++) { + if (bytes[i] === 0) break; // 截断0 + str += String.fromCharCode(bytes[i]); + } + return str; +} \ No newline at end of file diff --git a/obfuscate-uni.js b/obfuscate-uni.js index c29de8e..7ef05ae 100644 --- a/obfuscate-uni.js +++ b/obfuscate-uni.js @@ -5,7 +5,11 @@ import ignore from 'ignore' import { uniVersion } from './env.js' // 获取当前目录和构建目标目录 -const sourceDir = path.dirname(new URL(import.meta.url).pathname) +const sourceDir = path.dirname( + process.platform === 'win32' + ? new URL(import.meta.url).pathname.slice(1) + : new URL(import.meta.url).pathname +) const distDir = path.join(sourceDir, 'dist') const gitignorePath = path.join(sourceDir, '.gitignore') diff --git a/package-lock.json b/package-lock.json index 53f8424..faf44fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "star-cloud-uni", - "version": "1.0.10", + "version": "1.0.14", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "star-cloud-uni", - "version": "1.0.10", + "version": "1.0.14", "dependencies": { "buffer": "^6.0.3", "crc": "^4.3.2", diff --git a/star-cloud.js b/star-cloud.js index 6beb3c1..61b2f73 100644 --- a/star-cloud.js +++ b/star-cloud.js @@ -10,6 +10,7 @@ import * as elec from './star-cloud/elec.js' import * as coldWater from './star-cloud/coldWater.js' import * as hotWater from './star-cloud/hotWater.js' import * as generalExtend from './star-cloud/generalExtend.js' +import * as configureNetwork from './star-cloud/configureNetwork.js' import { onBLECharacteristicValueChange } from './uni/basic' /** @@ -141,7 +142,7 @@ class StarCloud { * @param {Boolean} params.isReportLog 是否上报日志 */ init(params) { - Object.assign(StarCloud.prototype, device, lock, other, password, record, user, common, elec,coldWater, hotWater,generalExtend) + Object.assign(StarCloud.prototype, device, lock, other, password, record, user, common, elec,coldWater, hotWater,generalExtend,configureNetwork) const { clientId, clientSecret, env, platform, accounts,clientUrl } = params this.envVersion = 'release' diff --git a/star-cloud/configureNetwork.js b/star-cloud/configureNetwork.js index f645d3b..1ce1656 100644 --- a/star-cloud/configureNetwork.js +++ b/star-cloud/configureNetwork.js @@ -1,20 +1,9 @@ import { - _convertToMilliseconds, checkRequiredFields, - convertWeekdaysToNumber, createPackageEnd, md5Encrypt, - parseTimeToList, - timestampToArray + createPackageEnd, } from "../format.js"; -import {cmdIds, Result, subCmdIds} from "../constant.js"; -import { - checkRepeatCardName, - checkRepeatFaceName, - checkRepeatFingerprintName, - checkRepeatPalmVeinName, - checkRepeatRemoteName -} from "../api.js"; +import {cmdIds, Result} from "../constant.js"; import {searchAndConnectDevice, writeBLECharacteristicValue} from "../uni/basic.js"; -import {sm4} from "sm-crypto"; /** * 开始搜索wifi @@ -23,7 +12,7 @@ import {sm4} from "sm-crypto"; export async function startSearchWiFi(params) { const uid = this.accountInfo.uid.toString() // 设置执行账号 - const result = await this.login(uid) + const result = await this.login(params.uid) if (result.code !== Result.Success.code) { return result } @@ -38,97 +27,26 @@ export async function startSearchWiFi(params) { // 检查是否已添加为用户 const checkResult = await this.checkLockUser() - if (!checkResult) { - return { - code: -1 - } + if (checkResult.code !== Result.Success.code) { + return checkResult } - const length = 2 + 1 + 1 + 40 + 20 + 2 + 2 + 1 + 1 + 1 + 4 + 1 + 1 + 4 + 4 + 4 + 4 + 1 + 16 - const headArray = this.createPackageHeader(3, length) + // 判断锁是否支持wifi功能 + if (!this.lockInfo.lockFeature || this.lockInfo.lockFeature.wifi !== 1) { + return new Result(Result.codes.Fail, null, '该锁不支持wifi配置'); + } + + + const length = 2 + 20 + const headArray = this.createPackageHeader(0, length) const contentArray = new Uint8Array(length) - contentArray[0] = cmdIds.expandCmd / 256 - contentArray[1] = cmdIds.expandCmd % 256 - // 子命令 - if (type === 'card') { - contentArray[2] = subCmdIds.registerCard - } else if (type === 'fingerprint') { - contentArray[2] = subCmdIds.registerFingerprint - } else if (type === 'face') { - contentArray[2] = subCmdIds.registerFace - } else if (type === 'remote') { - contentArray[2] = subCmdIds.registerRemote - } else if (type === 'palmVein') { - contentArray[2] = subCmdIds.registerPalmVein - } - contentArray[3] = length - 3 - - for (let i = 0; i < keyId.length; i++) { - contentArray[i + 4] = keyId.charCodeAt(i) - } - + contentArray[0] = cmdIds.searchWiFi / 256 + contentArray[1] = cmdIds.searchWiFi % 256 for (let i = 0; i < uid.length; i++) { - contentArray[i + 44] = uid.charCodeAt(i) + contentArray[i + 2] = i < uid.length ? uid.charCodeAt(i) : 0; } - - // 子命令 - if (type === 'card') { - contentArray[64] = (params.cardNumber || 0) / 256 - contentArray[65] = (params.cardNumber || 0) % 256 - } else if (type === 'fingerprint') { - contentArray[64] = (params.fingerprintNumber || 0) / 256 - contentArray[65] = (params.fingerprintNumber || 0) % 256 - } else if (type === 'face') { - contentArray[64] = (params.faceNumber || 0) / 256 - contentArray[65] = (params.faceNumber || 0) % 256 - } else if (type === 'remote') { - contentArray[64] = (params.remoteNumber || 0) / 256 - contentArray[65] = (params.remoteNumber || 0) % 256 - } else if (type === 'palmVein') { - contentArray[64] = (params.palmVeinNumber || 0) / 256 - contentArray[65] = (params.palmVeinNumber || 0) % 256 - } - - contentArray[66] = (userCountLimit || 0xffff) / 256 - contentArray[67] = (userCountLimit || 0xffff) % 256 - contentArray[68] = operate - contentArray[69] = isAdmin || 0 - contentArray[70] = isForce || 0 - - contentArray.set(this.lockInfo.token || new Uint8Array([0, 0, 0, 0]), 71) - - contentArray[75] = isRound - contentArray[76] = convertWeekdaysToNumber(weekDay) - - contentArray.set(timestampToArray(startDate), 77) - contentArray.set(timestampToArray(endDate), 81) - - if (isRound) { - contentArray.set(parseTimeToList(startTime), 85) - contentArray.set(parseTimeToList(endTime), 89) - } else { - contentArray.set(new Uint8Array([0, 0, 0, 0]), 85) - contentArray.set(new Uint8Array([0, 0, 0, 0]), 89) - } - - contentArray[93] = 16 - - const md5Array = md5Encrypt( - keyId + uid, - this.lockInfo.token || new Uint8Array([0, 0, 0, 0]), - this.lockInfo.bluetooth.signKey - ) - - contentArray.set(md5Array, 94) - - console.log('加密前:', Array.from(contentArray)) - const cebArray = sm4.encrypt(contentArray, this.lockInfo.bluetooth.privateKey, { - mode: 'ecb', - output: 'array' - }) - - const packageArray = createPackageEnd(headArray, cebArray) + const packageArray = createPackageEnd(headArray, contentArray) const writeResult = await writeBLECharacteristicValue(this.lockInfo.deviceId, this.lockInfo.serviceId, @@ -137,6 +55,115 @@ export async function startSearchWiFi(params) { if (writeResult.code !== Result.Success.code) { return writeResult } - return this.getWriteResult(this.registerExtendedProducts, params) + return this.getWriteResult(this.startSearchWiFi, params) } +/** + * 连接wifi + * @param params + * @returns {Promise} + */ +export async function connectWiFi(params) { + const cardRequiredFields = ['ssid', 'password']; + const missingField = checkRequiredFields(params, cardRequiredFields); + if (missingField) { + return new Result(Result.codes.NotMoreData, null, `参数信息不完整: ${missingField}`); + } + + let { + ssid, + password, + configureJson = JSON.stringify({ + starcloudRpcPeerId: "6HnEcGnXMUcLQoE7rnC4aXMVJmojMnKAjqKHrt4TmN1U", + starcloudReportPeerId: "G3ehYz8djE35CTE2LWn5xe2nD51UpjC4hWd3vqVmXViE", + starcloudUrl: "http://cloud.skychip.top", + userPeerId: "C2HjHNy9LsjxW2QEmceiNDTN6XSXFDUZ3fYsnBigVQXA", + scdUrl: "http://scd.skychip.top:8710", + starlockPeerId: "0b3bd6327daafe2da24fdd0cae76c71477f32e3ef8ab", + clientId: "sBfWAwdMqVKIMBj4dPuRextHViC266aE", + secretKey: "zNn1AluC6sTVAtA4dX", + userPeerld: "zC2HjHNy9LsjxW2QEmceiNDTN6XSXFDUZ3fYsnBigVQX" + }) + } = params + + + const uid = this.accountInfo.uid.toString() + // 设置执行账号 + const result = await this.login(params.uid) + 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 + } + + // 判断锁是否支持wifi功能 + if (!this.lockInfo.lockFeature || this.lockInfo.lockFeature.wifi !== 1) { + return new Result(Result.codes.Fail, null, '该锁不支持wifi配置'); + } + + // 处理 configureJson + let configureJsonBytes = []; + if (configureJson) { + for (let i = 0; i < configureJson.length; i++) { + const code = configureJson.charCodeAt(i); + if (code < 0x80) { + configureJsonBytes.push(code); + } else if (code < 0x800) { + configureJsonBytes.push(0xc0 | (code >> 6)); + configureJsonBytes.push(0x80 | (code & 0x3f)); + } else { + configureJsonBytes.push(0xe0 | (code >> 12)); + configureJsonBytes.push(0x80 | ((code >> 6) & 0x3f)); + configureJsonBytes.push(0x80 | (code & 0x3f)); + } + } + } + const configureJsonLength = configureJsonBytes.length; + + // 计算总长度 + const length = 2 + 30 + 20 + 2 + configureJsonLength; + const headArray = this.createPackageHeader(0, length); + const contentArray = new Uint8Array(length); + + contentArray[0] = cmdIds.configureNetwork / 256; + contentArray[1] = cmdIds.configureNetwork % 256; + // SSID: 30字节,补0 + for (let i = 0; i < 30; i++) { + contentArray[i + 2] = i < ssid.length ? ssid.charCodeAt(i) : 0; + } + // Password: 20字节,补0 + for (let i = 0; i < 20; i++) { + contentArray[i + 32] = i < password.length ? password.charCodeAt(i) : 0; + } + // configureJsonLength: 2字节(大端序) + contentArray[52] = (configureJsonLength >> 8) & 0xff; + contentArray[53] = configureJsonLength & 0xff; + // configureJson内容 + for (let i = 0; i < configureJsonLength; i++) { + contentArray[54 + i] = configureJsonBytes[i]; + } + + console.log("配网命令:", Array.from(contentArray)) + const packageArray = createPackageEnd(headArray, contentArray) + console.log("蓝牙包:", Array.from(packageArray)) + 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.connectWiFi, params) +} \ No newline at end of file diff --git a/uni.md b/uni.md index fd89dc2..4b1c707 100644 --- a/uni.md +++ b/uni.md @@ -675,6 +675,38 @@ refreshHotWaterInfo(params) **返回** 无 +# 监听事件 + +**监听相关事件** + +| 事件名 | 描述 | 返回值示例 | +|----------------------------|----------|-------------------------------------------------------| +| registerCardConfirm | 卡片注册成功 | {cardNumber:1,cardId:1} | +| registerFingerprintProcess | 指纹注册进度 | {status: 0,process: 0};**process总步数在上方统一`Result`中返回** | +| registerFingerprintConfirm | 指纹注册成功 | {fingerprintNumber:1,fingerprintId:1} | +| registerFaceProcess | 人脸注册进度 | {faceNumber:1,faceId:1} | +| registerFaceConfirm | 人脸注册成功 | {faceNumber:1,faceId:1} | +| registerPalmVeinConfirm | 掌静脉注册成功 | {palmVeinNumber:1,palmVeinId:1} | +| registerRemoteConfirm | 遥控注册成功 | {remoteNumber:1,remoteId:1} | +| searchWiFiResult | wifi搜索结果 | {status:0, numberOfSsid:7, wifiList:[Object]} | + +**监听事件示例** + +```js +// uniapp 监听注册卡片完成事件 +uni.$on('registerCardConfirm', async data => { + // TODO +}) +// uniapp 监听注册指纹进度 +uni.$on('registerFingerprintProcess', async data => { + // TODO +}) +// uniapp 指纹注册成功 +uni.$on('registerFingerprintConfirm', async data => { + // TODO +}) +``` + # 扩展功能操作(ic卡、指纹、人脸、遥控、掌静脉) ## 注册、修改、删除、 删除全部 @@ -736,36 +768,6 @@ registerExtendedProducts(params) > > 收到锁版提示后,将卡片放置读卡器 - -**监听相关事件** - -| 事件名 | 描述 | 返回值示例 | -|----------------------------|---------|-------------------------------------------------------| -| registerCardConfirm | 卡片注册成功 | {cardNumber:1,cardId:1} | -| registerFingerprintProcess | 指纹注册进度 | {status: 0,process: 0};**process总步数在上方统一`Result`中返回** | -| registerFingerprintConfirm | 指纹注册成功 | {fingerprintNumber:1,fingerprintId:1} | -| registerFaceProcess | 人脸注册进度 | {faceNumber:1,faceId:1} | -| registerFaceConfirm | 人脸注册成功 | {faceNumber:1,faceId:1} | -| registerPalmVeinConfirm | 掌静脉注册成功 | {palmVeinNumber:1,palmVeinId:1} | -| registerRemoteConfirm | 遥控注册成功 | {remoteNumber:1,remoteId:1} | - -**监听事件示例** - -```js -// uniapp 监听注册卡片完成事件 -uni.$on('registerCardConfirm', async data => { - // TODO -}) -// uniapp 监听注册指纹进度 -uni.$on('registerFingerprintProcess', async data => { - // TODO -}) -// uniapp 指纹注册成功 -uni.$on('registerFingerprintConfirm', async data => { - // TODO -}) -``` - ## 取消操作 **调用方法** diff --git a/uni/basic.js b/uni/basic.js index 44f69d8..3aa23bc 100644 --- a/uni/basic.js +++ b/uni/basic.js @@ -1,4 +1,4 @@ -import { Result } from '../constant' +import {Result} from '../constant' /** * @typedef {Object} err @@ -9,365 +9,367 @@ import { Result } from '../constant' // 查找设备并连接 export function searchAndConnectDevice(name, reset = true) { - // 循环查找设备 - let timer - // 超时计时器 - let timeoutTimer + // 循环查找设备 + let timer + // 超时计时器 + let timeoutTimer - return new Promise(async resolve => { - const result = await startBluetoothDevicesDiscovery() - if (result.code === Result.Success.code) { - let searchFlag = false - timeoutTimer = setTimeout(async () => { - await stopBluetoothDevicesDiscovery() - clearInterval(timer) - if (!searchFlag) { - resolve(Result.NotAvailableWeChatNearbyDevicesEmpty) - } else { - resolve(Result.Fail) - } - }, 10500) - timer = setInterval(async () => { - const queryResult = await getBluetoothDevices() - if (queryResult.code === Result.Success.code) { - const deviceList = queryResult.data - if (searchFlag === false && deviceList.length > 0) { - searchFlag = true - } - for (let i = 0; i < deviceList.length; i++) { - if (deviceList[i]?.name === name) { - const uuid = deviceList[i]?.advertisServiceUUIDs[0] - if (uuid && uuid.slice(2, 8) === '758824') { + return new Promise(async resolve => { + const result = await startBluetoothDevicesDiscovery() + if (result.code === Result.Success.code) { + let searchFlag = false + timeoutTimer = setTimeout(async () => { await stopBluetoothDevicesDiscovery() - clearTimeout(timeoutTimer) clearInterval(timer) - if (uuid.slice(30, 32) === '00' && reset) { - resolve(Result.DeviceHasBeenReset) + if (!searchFlag) { + resolve(Result.NotAvailableWeChatNearbyDevicesEmpty) } else { - const connectResult = await createBLEConnection(deviceList[i].deviceId) - resolve(connectResult) + resolve(Result.Fail) } - break - } - if (uuid && uuid.slice(0, 2) === '75') { - await stopBluetoothDevicesDiscovery() - clearTimeout(timeoutTimer) - clearInterval(timer) - if (uuid.slice(4, 6) === '00' && reset) { - resolve(Result.DeviceHasBeenReset) + }, 10500) + timer = setInterval(async () => { + const queryResult = await getBluetoothDevices() + if (queryResult.code === Result.Success.code) { + const deviceList = queryResult.data + if (searchFlag === false && deviceList.length > 0) { + searchFlag = true + } + for (let i = 0; i < deviceList.length; i++) { + if (deviceList[i]?.name === name) { + const uuid = deviceList[i]?.advertisServiceUUIDs[0] + if (uuid && uuid.slice(2, 8) === '758824') { + await stopBluetoothDevicesDiscovery() + clearTimeout(timeoutTimer) + clearInterval(timer) + if (uuid.slice(30, 32) === '00' && reset) { + resolve(Result.DeviceHasBeenReset) + } else { + const connectResult = await createBLEConnection(deviceList[i].deviceId) + resolve(connectResult) + } + break + } + if (uuid && uuid.slice(0, 2) === '75') { + await stopBluetoothDevicesDiscovery() + clearTimeout(timeoutTimer) + clearInterval(timer) + if (uuid.slice(4, 6) === '00' && reset) { + resolve(Result.DeviceHasBeenReset) + } else { + const connectResult = await createBLEConnection(deviceList[i].deviceId) + resolve(connectResult) + } + break + } + } + } } else { - const connectResult = await createBLEConnection(deviceList[i].deviceId) - resolve(connectResult) + resolve(queryResult) } - break - } - } - } + }, 1000) } else { - resolve(queryResult) + resolve(result) } - }, 1000) - } else { - resolve(result) - } - }) + }) } // 蓝牙操作报错处理 async function handleError(err, event) { - if (err.errCode === 10000) { - const result = await openBluetoothAdapter() - if (result.code === Result.Success.code) { - return await event() + if (err.errCode === 10000) { + const result = await openBluetoothAdapter() + if (result.code === Result.Success.code) { + return await event() + } + return result } - return result - } - if (err.errCode === 10001) { - if (err.state === 3) { - return Result.NotAvailableWeChatBluetoothPermission + if (err.errCode === 10001) { + if (err.state === 3) { + return Result.NotAvailableWeChatBluetoothPermission + } + return Result.NotAvailableBluetooth } - return Result.NotAvailableBluetooth - } - if (err.errno === 3) { - return Result.NotAvailableWeChatNearbyDevicesPermission - } - if (err.errno === 103) { - return Result.NotAvailableBluetoothPermission - } - if (err.errno === 1509008) { - return Result.NotAvailableWeChatLocationPermission - } - if (err.errMsg === 'openBluetoothAdapter:fail already opened') { - return Result.Success - } - return Result.Fail + if (err.errno === 3) { + return Result.NotAvailableWeChatNearbyDevicesPermission + } + if (err.errno === 103) { + return Result.NotAvailableBluetoothPermission + } + if (err.errno === 1509008) { + return Result.NotAvailableWeChatLocationPermission + } + if (err.errMsg === 'openBluetoothAdapter:fail already opened') { + return Result.Success + } + return Result.Fail } // 初始化蓝牙模块 function openBluetoothAdapter() { - return new Promise(resolve => { - uni.openBluetoothAdapter({ - success() { - resolve(Result.Success) - }, - async fail(err) { - resolve(await handleError(err)) - } + return new Promise(resolve => { + uni.openBluetoothAdapter({ + success() { + resolve(Result.Success) + }, + async fail(err) { + resolve(await handleError(err)) + } + }) }) - }) } // 关闭蓝牙模块 export function closeBluetoothAdapter() { - uni.closeBluetoothAdapter() + uni.closeBluetoothAdapter() } // 移除蓝牙适配器的全部监听 export function offBluetoothAdapterStateChange() { - uni.offBluetoothAdapterStateChange() + uni.offBluetoothAdapterStateChange() } // 监听蓝牙特征值改变 export function onBLECharacteristicValueChange(callback) { - uni.onBLECharacteristicValueChange(res => { - callback(res) - }) + uni.onBLECharacteristicValueChange(res => { + callback(res) + }) } // 开始搜索附近的蓝牙设备 export function startBluetoothDevicesDiscovery() { - return new Promise(resolve => { - uni.startBluetoothDevicesDiscovery({ - success() { - resolve(Result.Success) - }, - async fail(err) { - resolve(await handleError(err, startBluetoothDevicesDiscovery)) - } + return new Promise(resolve => { + uni.startBluetoothDevicesDiscovery({ + success() { + resolve(Result.Success) + }, + async fail(err) { + resolve(await handleError(err, startBluetoothDevicesDiscovery)) + } + }) }) - }) } // 获取所有已发现的蓝牙设备 export function getBluetoothDevices() { - return new Promise(resolve => { - uni.getBluetoothDevices({ - success(res) { - resolve(new Result(Result.Success.code, res.devices)) - }, - async fail(err) { - resolve(await handleError(err, getBluetoothDevices)) - } + return new Promise(resolve => { + uni.getBluetoothDevices({ + success(res) { + resolve(new Result(Result.Success.code, res.devices)) + }, + async fail(err) { + resolve(await handleError(err, getBluetoothDevices)) + } + }) }) - }) } // 停止搜索附近的蓝牙设备 export function stopBluetoothDevicesDiscovery() { - return new Promise(resolve => { - uni.stopBluetoothDevicesDiscovery({ - success() { - resolve(Result.Success) - }, - async fail(err) { - resolve(await handleError(err)) - } + return new Promise(resolve => { + uni.stopBluetoothDevicesDiscovery({ + success() { + resolve(Result.Success) + }, + async fail(err) { + resolve(await handleError(err)) + } + }) }) - }) } // 连接低功耗蓝牙设备 export function createBLEConnection(deviceId, reconnectNumber = 0) { - return new Promise(resolve => { - uni.createBLEConnection({ - deviceId, - timeout: 10000, - async success() { - const res = await getBLEDeviceServicesAndCharacteristics(deviceId) - await notifyBLECharacteristicValueChange( - deviceId, - res.data.serviceId, - res.data.notifyCharacteristicId - ) - // 使用同步方法获取系统信息 - const systemInfo = uni.getSystemInfoSync(); - - if (systemInfo.platform === 'android') { - // 如果是Android机型,则设置BLE MTU - uni.setBLEMTU({ - deviceId: deviceId, - mtu: 512, - fail: res => { - // console.log('mtu fail', res); - }, - success: res => { - // console.log('mtu success', res); - } - }); - } else { - // 对于非Android设备,可以在这里添加其他逻辑或者直接忽略 - // console.log('当前设备不是Android,跳过设置BLE MTU'); - } - resolve(res) - }, - async fail(err) { - if (err.errno === 1509007) { - const res = await getBLEDeviceServicesAndCharacteristics(deviceId) - await notifyBLECharacteristicValueChange( + return new Promise(resolve => { + uni.createBLEConnection({ deviceId, - res.data.serviceId, - res.data.notifyCharacteristicId - ) - resolve(res) - } else if (err.errno === 1509001 && reconnectNumber < 1) { - resolve(Result.Fail) - } else if (reconnectNumber < 1) { - resolve(await createBLEConnection(deviceId, reconnectNumber + 1)) - } else { - resolve(Result.Fail) - } - } + timeout: 10000, + async success() { + const res = await getBLEDeviceServicesAndCharacteristics(deviceId) + await notifyBLECharacteristicValueChange( + deviceId, + res.data.serviceId, + res.data.notifyCharacteristicId + ) + // 使用同步方法获取系统信息 + const systemInfo = uni.getSystemInfoSync(); + + if (systemInfo.platform === 'android') { + // 如果是Android机型,则设置BLE MTU + uni.setBLEMTU({ + deviceId: deviceId, + mtu: 512, + fail: res => { + // console.log('mtu fail', res); + }, + success: res => { + // console.log('mtu success', res); + } + }); + } else { + // 对于非Android设备,可以在这里添加其他逻辑或者直接忽略 + // console.log('当前设备不是Android,跳过设置BLE MTU'); + } + resolve(res) + }, + async fail(err) { + if (err.errno === 1509007) { + const res = await getBLEDeviceServicesAndCharacteristics(deviceId) + await notifyBLECharacteristicValueChange( + deviceId, + res.data.serviceId, + res.data.notifyCharacteristicId + ) + resolve(res) + } else if (err.errno === 1509001 && reconnectNumber < 1) { + resolve(Result.Fail) + } else if (reconnectNumber < 1) { + resolve(await createBLEConnection(deviceId, reconnectNumber + 1)) + } else { + resolve(Result.Fail) + } + } + }) }) - }) } // 获取服务及对应特征值 async function getBLEDeviceServicesAndCharacteristics(deviceId) { - const { code, data } = await getBLEDeviceServices(deviceId) - if (code === Result.Success.code) { - const { serviceId } = data - const { - code, - data: { notifyCharacteristicId, writeCharacteristicId } - } = await getBLEDeviceCharacteristics(deviceId, serviceId) + const {code, data} = await getBLEDeviceServices(deviceId) if (code === Result.Success.code) { - return new Result(Result.Success.code, { - deviceId, - serviceId, - notifyCharacteristicId, - writeCharacteristicId - }) + const {serviceId} = data + const { + code, + data: {notifyCharacteristicId, writeCharacteristicId} + } = await getBLEDeviceCharacteristics(deviceId, serviceId) + if (code === Result.Success.code) { + return new Result(Result.Success.code, { + deviceId, + serviceId, + notifyCharacteristicId, + writeCharacteristicId + }) + } + return Result.Fail } return Result.Fail - } - return Result.Fail } // 获取设备的服务 function getBLEDeviceServices(deviceId) { - return new Promise(resolve => { - uni.getBLEDeviceServices({ - deviceId, - success(res) { - let serviceId - for (let i = 0; i < res.services.length; i++) { - if (res.services[i].uuid.indexOf('FFF0') !== -1) { - serviceId = res.services[i].uuid - } - } - if (!serviceId) { - resolve(Result.Fail) - return - } - resolve(new Result(Result.Success.code, { serviceId })) - }, - fail() { - resolve(Result.Fail) - } + return new Promise(resolve => { + uni.getBLEDeviceServices({ + deviceId, + success(res) { + let serviceId + for (let i = 0; i < res.services.length; i++) { + if (res.services[i].uuid.indexOf('FFF0') !== -1) { + serviceId = res.services[i].uuid + } + } + if (!serviceId) { + resolve(Result.Fail) + return + } + resolve(new Result(Result.Success.code, {serviceId})) + }, + fail() { + resolve(Result.Fail) + } + }) }) - }) } // 获取服务的特征值 function getBLEDeviceCharacteristics(deviceId, serviceId) { - return new Promise(resolve => { - uni.getBLEDeviceCharacteristics({ - deviceId, - serviceId, - success(res) { - let notifyCharacteristicId - let writeCharacteristicId - for (let i = 0; i < res.characteristics.length; i++) { - const characteristic = res.characteristics[i] - if (characteristic.properties.notify) { - notifyCharacteristicId = characteristic.uuid - } - if (characteristic.properties.write) { - writeCharacteristicId = characteristic.uuid - } - } - if (notifyCharacteristicId && writeCharacteristicId) { - resolve( - new Result(Result.Success.code, { notifyCharacteristicId, writeCharacteristicId }) - ) - } else { - resolve(Result.Fail) - } - }, - fail() { - resolve(Result.Fail) - } + return new Promise(resolve => { + uni.getBLEDeviceCharacteristics({ + deviceId, + serviceId, + success(res) { + let notifyCharacteristicId + let writeCharacteristicId + for (let i = 0; i < res.characteristics.length; i++) { + const characteristic = res.characteristics[i] + if (characteristic.properties.notify) { + notifyCharacteristicId = characteristic.uuid + } + if (characteristic.properties.write) { + writeCharacteristicId = characteristic.uuid + } + } + if (notifyCharacteristicId && writeCharacteristicId) { + resolve( + new Result(Result.Success.code, {notifyCharacteristicId, writeCharacteristicId}) + ) + } else { + resolve(Result.Fail) + } + }, + fail() { + resolve(Result.Fail) + } + }) }) - }) } // 订阅特征值 function notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) { - return new Promise(resolve => { - uni.notifyBLECharacteristicValueChange({ - deviceId, - serviceId, - characteristicId, - state: true, - success() { - resolve(Result.Success) - }, - fail() { - resolve(Result.Fail) - } + return new Promise(resolve => { + uni.notifyBLECharacteristicValueChange({ + deviceId, + serviceId, + characteristicId, + state: true, + success() { + resolve(Result.Success) + }, + fail() { + resolve(Result.Fail) + } + }) }) - }) } // 断开与低功耗蓝牙设备的连接 export function closeBLEConnection(deviceId) { - return new Promise(resolve => { - uni.closeBLEConnection({ - deviceId, - success() { - console.log('断开连接成功') - resolve(Result.Success) - }, - fail() { - console.log('断开连接失败') - resolve(Result.Fail) - } + return new Promise(resolve => { + uni.closeBLEConnection({ + deviceId, + success() { + console.log('断开连接成功') + resolve(Result.Success) + }, + fail() { + console.log('断开连接失败') + resolve(Result.Fail) + } + }) }) - }) } // 写入特征值 export function writeBLECharacteristicValue(deviceId, serviceId, characteristicId, binaryData) { - return new Promise(resolve => { - const count = Math.ceil(binaryData.length / 20) - let successCount = 0 - for (let i = 0; i < count; i++) { - const writeData = binaryData.slice(i * 20, i === count - 1 ? binaryData.length : (i + 1) * 20) - uni.writeBLECharacteristicValue({ - deviceId, - serviceId, - characteristicId, - value: writeData.buffer, - success() { - successCount++ - if (successCount === count) { - resolve(Result.Success) - } - }, - fail() { - resolve(Result.Fail) + return new Promise(resolve => { + const count = Math.ceil(binaryData.length / 20) + let successCount = 0 + for (let i = 0; i < count; i++) { + const writeData = binaryData.slice(i * 20, i === count - 1 ? binaryData.length : (i + 1) * 20) + uni.writeBLECharacteristicValue({ + deviceId, + serviceId, + characteristicId, + value: writeData.buffer, + success(res) { + successCount++ + if (successCount === count) { + resolve(Result.Success) + } + console.log("writeBLECharacteristicValue res", res) + }, + fail(err) { + console.error("writeBLECharacteristicValue err", err) + resolve(Result.Fail) + } + }) } - }) - } - }) + }) } diff --git a/uni/index.js b/uni/index.js index 170ce84..e995cdd 100644 --- a/uni/index.js +++ b/uni/index.js @@ -1,5 +1,6 @@ import starCloudInstance from '../star-cloud' import {Result, supportFunctionsFeatureBit} from '../constant' + export {Result} /** @@ -345,7 +346,6 @@ export const readSupportFunctionsSetting = async params => { } - /** * 获取Ic卡列表 * @param params.lockId 锁id (必填) @@ -413,4 +413,24 @@ export const remoteUnLock = async params => { */ export const getLockList = async params => { return await starCloudInstance.getLockList(params) +} + +/** + * 启动搜索wifi + * @param params + * @returns {Promise<*>} + */ +export const startSearchWiFi = async params => { + return await starCloudInstance.startSearchWiFi(params) +} + + +/** + * 连接wifi + * @param params.ssid wifi昵称 + * @param params.password wifi密码 + * @returns {Promise<*>} + */ +export const connectWiFi = async params => { + return await starCloudInstance.connectWiFi(params) } \ No newline at end of file