feat: 添加绑定锁相关功能

This commit is contained in:
范鹏 2024-10-30 14:09:02 +08:00
parent bd3de78775
commit 64beacd035
4 changed files with 347 additions and 32 deletions

View File

@ -1,22 +1,30 @@
## 星云SDK
### 1. 安装
在需要引用的项目中执行命令
`git subtree add --prefix=starCloud git@code.star-lock.cn:xhj/starcloud-sdk-uniapp.git master`
更新
`git subtree pull --prefix=starCloud git@code.star-lock.cn:xhj/starcloud-sdk-uniapp.git master`
### 2. 需安装的npm包
`npm install buffer crc js-md5 pinia sm-crypto`
pinia是全局状态管理工具需在main.js中引入
```javascript
import * as Pinia from 'pinia'
const store = Pinia.createPinia()
app.use(store)
```
### 3. 使用
```javascript
import { useStarCloudStore } from '@/starCloud/starCloud'
const $starCloud = useStarCloudStore()
/**
@ -31,7 +39,7 @@ $starCloud.initStarCloud(clientId, clientSecret, env)
// code对应报错码有三部分组合构成锁端报错码+星云服务端报错码+自定义报错码
// Result类定义了所有自定义报错码具体报错码请查看Result类
const { code, data, message } = await $starCloud.register()
if(code === Result.Success.code) {
if (code === Result.Success.code) {
// 逻辑代码
} else {
// 错误处理
@ -48,6 +56,7 @@ const { code, data, message } = await $starCloud.login(username, password)
* 选择锁
* @property {Number} lockId - 锁Id
*/
// 所需信息data中会返回如data中没有可通过$starCloud.lockInfo获取
const { code, data, message } = await $starCloud.selectLock(lockId)
/**
@ -109,4 +118,22 @@ const data = await $starCloud.customPassword({
operate: 0,
pwdRight: 0
}, true)
/**
* 搜索蓝牙设备
*/
await $starCloud.searchDevice(searchDevice)
const searchDevice = async result => {
}
/**
* 停止搜索
*/
await $starCloud.stopSearchDevice()
/**
* 连接蓝牙设备
* @property {String} name - 设备名称
*/
const data = await $starCloud.bindDevice(name)
```

19
api.js
View File

@ -46,7 +46,7 @@ export function getLockNetTokenRequest(data) {
}
// 获取服务器时间
export function getServerDatetime(data) {
export function getServerDatetimeRequest(data) {
return request({
url: '/v1/lock/queryDate',
method: 'POST',
@ -55,7 +55,7 @@ export function getServerDatetime(data) {
}
// 获取锁详情
export function getLockDetail(data) {
export function getLockDetailRequest(data) {
return request({
url: '/v1/lock/detail',
method: 'POST',
@ -64,7 +64,7 @@ export function getLockDetail(data) {
}
// 获取离线密码
export function getOfflinePassword(data) {
export function getOfflinePasswordRequest(data) {
return request({
url: '/v1/keyboardPwd/get',
method: 'POST',
@ -73,7 +73,7 @@ export function getOfflinePassword(data) {
}
// 添加自定义密码
export function addCustomPassword(data) {
export function addCustomPasswordRequest(data) {
return request({
url: '/v1/keyboardPwd/add',
method: 'POST',
@ -82,10 +82,19 @@ export function addCustomPassword(data) {
}
// 更新密码
export function updatePassword(data) {
export function updatePasswordRequest(data) {
return request({
url: '/v1/keyboardPwd/update',
method: 'POST',
data
})
}
// 绑定智能锁
export function bindLockRequest(data) {
return request({
url: '/v1/lock/initialize',
method: 'POST',
data
})
}

View File

@ -36,7 +36,10 @@ export class Result {
],
[
Result.codes.NotAvailableWeChatNearbyDevicesEmpty,
{ message: '微信附近的设备权限无法使用', data: {} }
{
message: '手机定位服务被关闭',
data: {}
}
],
[Result.codes.DeviceHasBeenReset, { message: '设备已被重置', data: {} }],
@ -133,7 +136,7 @@ export class Result {
*/
// 查找设备并连接
export function searchAndConnectDevice(name) {
export function searchAndConnectDevice(name, reset = true) {
// 循环查找设备
let timer
// 超时计时器
@ -166,13 +169,11 @@ export function searchAndConnectDevice(name) {
await stopBluetoothDevicesDiscovery()
clearTimeout(timeoutTimer)
clearInterval(timer)
if (uuid.slice(30, 32) === '00') {
if (uuid.slice(30, 32) === '00' && reset) {
resolve(Result.DeviceHasBeenReset)
} else if (uuid.slice(30, 32) === '01') {
} else {
const connectResult = await createBLEConnection(deviceList[i].deviceId)
resolve(connectResult)
} else {
resolve(Result.Fail)
}
break
}
@ -256,7 +257,7 @@ export function onBLECharacteristicValueChange(callback) {
}
// 开始搜索附近的蓝牙设备
function startBluetoothDevicesDiscovery() {
export function startBluetoothDevicesDiscovery() {
return new Promise(resolve => {
uni.startBluetoothDevicesDiscovery({
success() {
@ -270,7 +271,7 @@ function startBluetoothDevicesDiscovery() {
}
// 获取所有已发现的蓝牙设备
function getBluetoothDevices() {
export function getBluetoothDevices() {
return new Promise(resolve => {
uni.getBluetoothDevices({
success(res) {
@ -284,7 +285,7 @@ function getBluetoothDevices() {
}
// 停止搜索附近的蓝牙设备
function stopBluetoothDevicesDiscovery() {
export function stopBluetoothDevicesDiscovery() {
return new Promise(resolve => {
uni.stopBluetoothDevicesDiscovery({
success() {
@ -298,7 +299,7 @@ function stopBluetoothDevicesDiscovery() {
}
// 连接低功耗蓝牙设备
function createBLEConnection(deviceId, reconnectNumber = 0) {
export function createBLEConnection(deviceId, reconnectNumber = 0) {
return new Promise(resolve => {
uni.createBLEConnection({
deviceId,

View File

@ -2,23 +2,28 @@ import { defineStore } from 'pinia'
import { sm4 } from 'sm-crypto'
import { buildNumber, configs, version } from '@/starCloud/env'
import {
addCustomPassword,
getLockDetail,
addCustomPasswordRequest,
bindLockRequest,
getLockDetailRequest,
getLockNetTokenRequest,
getOfflinePassword,
getServerDatetime,
getOfflinePasswordRequest,
getServerDatetimeRequest,
getStarCloudToken,
getUserNoListRequest,
starCloudCreateUser,
updateLockUserNoRequest,
updatePassword
updatePasswordRequest
} from '@/starCloud/api'
import { getStorage, setStorage } from '@/starCloud/storage'
import {
closeBLEConnection,
createBLEConnection,
getBluetoothDevices,
onBLECharacteristicValueChange,
Result,
searchAndConnectDevice,
startBluetoothDevicesDiscovery,
stopBluetoothDevicesDiscovery,
writeBLECharacteristicValue
} from '@/starCloud/basic'
import {
@ -125,6 +130,8 @@ let completeArray
let length
// 请求参数
let requestParams = null
// 计时器
let timer
export const useStarCloudStore = defineStore('starCloud', {
state() {
@ -150,7 +157,9 @@ export const useStarCloudStore = defineStore('starCloud', {
// 时间差
timeDifference: 0,
// 小程序环境
envVersion: ''
envVersion: '',
// 搜索设备列表
searchDeviceList: []
}
},
actions: {
@ -211,7 +220,7 @@ export const useStarCloudStore = defineStore('starCloud', {
},
// 选择锁
async selectLock(lockId) {
const { code, data, message } = await getLockDetail({
const { code, data, message } = await getLockDetailRequest({
lockId
})
if (code === Result.Success.code) {
@ -427,7 +436,7 @@ export const useStarCloudStore = defineStore('starCloud', {
},
// 获取离线密码
async getOfflinePassword(password) {
return await getOfflinePassword({
return await getOfflinePasswordRequest({
...password,
lockId: this.lockInfo.lockId
})
@ -531,6 +540,272 @@ export const useStarCloudStore = defineStore('starCloud', {
return this.getWriteResult(this.customPassword, disconnect, password)
},
// 搜索设备
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, {
list
})
)
} else {
callback(queryResult)
}
}, 1000)
} else {
callback(result)
}
},
// 停止搜索
async stopSearchDevice() {
console.log('停止搜索')
if (timer) {
clearInterval(timer)
}
return await stopBluetoothDevicesDiscovery()
},
// 绑定设备
async bindDevice(name) {
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(
{
name: this.lockInfo.bluetooth.bluetoothDeviceName,
keyId: '0',
authUid: this.userInfo.uid.toString(),
uid: this.userInfo.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
},
true
)
if (addUserResult.code !== Result.Success.code) {
return addUserResult
}
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
})
}
return bindLockResult
}
return connectResult
},
// 获取公钥
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, 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.userInfo.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, 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.userInfo.uid.toString()
await this.getServerTimestamp()
const nowTime = this.serverTimestamp
const date = new Date()
const localTime = Math.floor(date.getTime() / 1000) - 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, false)
},
// 获取联网token
async getNetToken() {
const { code, data, message } = await getLockNetTokenRequest({
@ -540,7 +815,7 @@ export const useStarCloudStore = defineStore('starCloud', {
},
// 获取服务器时间
async getServerTimestamp() {
const { code, data, message } = await getServerDatetime({})
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)
@ -550,7 +825,10 @@ export const useStarCloudStore = defineStore('starCloud', {
// 添加用户
async addLockUser(data, disconnect = false) {
// 确认设备连接正常
const searchResult = await searchAndConnectDevice(this.lockInfo.bluetooth.bluetoothDeviceName)
const searchResult = await searchAndConnectDevice(
this.lockInfo.bluetooth.bluetoothDeviceName,
data.role !== 0xff
)
if (searchResult.code !== Result.Success.code) {
return searchResult
}
@ -806,12 +1084,12 @@ export const useStarCloudStore = defineStore('starCloud', {
bluetooth: {
...this.lockInfo.bluetooth,
privateKey: decrypted.slice(3, 19),
signKey: decrypted.slice(19, 35),
pwdTimestamp: arrayToTimestamp(decrypted.slice(35, 39)) * 1000
}
signKey: decrypted.slice(19, 35)
},
pwdTimestamp: arrayToTimestamp(decrypted.slice(35, 39)) * 1000
})
console.log('privateKey', Array.from(this.lockInfo.bluetooth.privateKey))
console.log('signKey', Array.from(this.lockInfo.signKey))
console.log('signKey', Array.from(this.lockInfo.bluetooth.signKey))
}
characteristicValueCallback(new Result(decrypted[2]))
}
@ -893,7 +1171,7 @@ export const useStarCloudStore = defineStore('starCloud', {
if (decrypted[11] === Result.Success.code) {
const pwdNo = decrypted[9] * 256 + decrypted[10]
if (requestParams.operate === 0) {
const addResult = await addCustomPassword({
const addResult = await addCustomPasswordRequest({
...requestParams,
pwdUserNo: pwdNo,
lockId: this.lockInfo.lockId
@ -912,7 +1190,7 @@ export const useStarCloudStore = defineStore('starCloud', {
)
}
} else if (requestParams.operate === 1) {
const updateResult = await updatePassword({
const updateResult = await updatePasswordRequest({
...requestParams,
keyboardPwdId: this.lockInfo.keyboardPwdId
})