From 4d3319a7832a253c41f4ccb288ad3cf906f3ce2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8C=83=E9=B9=8F?= Date: Fri, 6 Dec 2024 09:32:28 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E8=AF=B4=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- miniprogram/doc/README.md | 518 ++++++++++++++++++++++++++++++----- miniprogram/doc/example.jpeg | Bin 7233 -> 0 bytes 2 files changed, 447 insertions(+), 71 deletions(-) delete mode 100644 miniprogram/doc/example.jpeg diff --git a/miniprogram/doc/README.md b/miniprogram/doc/README.md index d56548f..f5b8369 100644 --- a/miniprogram/doc/README.md +++ b/miniprogram/doc/README.md @@ -1,5 +1,8 @@ ## 星云SDK +APPID: **wxf2c491f734bbf82f** +1. 登录小程序后台-选择设置->第三方设置->插件管理->点击“添加插件”->搜索对应APPID->添加插件->等待审核通过 +2. 项目中引用需在app.json文件中添加 ```json { "plugins": { @@ -10,112 +13,412 @@ } } ``` - +3. 页面中引用 ```javascript +// 引用插件 const plugin = requirePlugin('starCloud') +``` -/** - * 初始化星云 - * @param params - * @param {String} params.clientId 客户端Id - * @param {String} params.clientSecret 客户端密码 - * @param {String} params.env 环境 - * @param {Boolean} params.isReportLog 是否上报日志 - */ -plugin.init(params) +## 公共参数 -// 注册,后续所有方法调用返回值均为code, data, message +> 返回结果 +{.is-success} +```javascript +// Result类 +// 除初始化星云外,所有方法的统一返回结构 +const { Result } = requirePlugin('starCloud') // code对应报错码有三部分组合构成,锁端报错码+星云服务端报错码+自定义报错码 -// Result类定义了所有自定义报错码,具体报错码请查看Result类 -const { code, data, message } = await plugin.register() +const { code, data, message } = Result.Success +// 常用判断方式 if (code === Result.Success.code) { // 逻辑代码 } else { // 错误处理 } +``` +|code|变量名|描述| +| -- | -- | -- | +|0|Success|成功| +|-1|Fail|失败| +|-10|NotMoreData|没有更多数据| +|-20|NotAvailableBluetooth|蓝牙尚未打开,请先打开蓝牙| +|-21|NotAvailableBluetoothPermission|小程序蓝牙功能被禁用,请打开小程序蓝牙权限| +|-22|NotAvailableWeChatNearbyDevicesPermission|蓝牙功能需要附近设备权限,请前往设置开启微信的附近设备权限后再试| +|-23|NotAvailableWeChatLocationPermission|蓝牙功能需要定位权限,请前往设置开启微信的定位权限后再试| +|-24|NotAvailableWeChatNearbyDevicesEmpty|蓝牙功能需要定位服务,请前往设置开启定位服务后再试| +|-25|NotAvailableWeChatBluetoothPermission|微信的蓝牙权限被禁用,请前往设置开启微信的蓝牙权限后再试| +|-30|DeviceHasBeenReset|设备已被重置| +|251|ReadyHasPassword|该密码已存在| + +> 账号信息 AccountInfo +{.is-success} +```javascript +AccountInfo accountInfo +``` + +|名称|类型|描述| +| -- | -- | -- | +|uid|Number|用户uid| +|username|String|用户名| +|password|String|密码| + +> 锁信息 LockInfo +{.is-success} +```javascript +LockInfo lock +``` + +| 名称 | 类型 | 描述 | +|---------------------------|--------|----------------------------| +| deviceId | String | 设备 ID | +| serviceId | String | 服务 ID | +| notifyCharacteristicId | String | 通知特征值 ID | +| writeCharacteristicId | String | 写入特征值 ID | +| pwdTimestamp | Number | 密码时间戳(毫秒) | +| featureValue | String | 功能值 | +| featureSettingValue | String | 功能设置值 | +| featureSettingParams | Array.\ | 功能设置参数 | +| lockUserNo | Number | 锁用户编号 | +| lockId | Number | 锁 ID | +| keyId | Number | 钥匙 ID | +| adminPwd | String | 管理密码 | +| bluetooth | Bluetooth | 蓝牙信息 | +| lockConfig | LockConfig | 锁配置信息 | + +> 蓝牙信息 Bluetooth +{.is-success} +```javascript +Bluetooth bluetooth +``` + +| 名称 | 类型 | 描述 | +|------------------------|--------|----------------| +| bluetoothDeviceId | String | 蓝牙设备 ID | +| bluetoothDeviceName | String | 蓝牙设备名称 | +| publicKey | Array.\ | 公钥 | +| privateKey | Array.\ | 私钥 | +| signKey | Array.\ | 签名密钥 | + +> 锁配置信息 LockConfig +{.is-success} +```javascript +LockConfig lockConfig +``` + +| 名称 | 类型 | 描述 | +|-----------------------|--------|----------------------------| +| vendor | String | 厂商 | +| product | Number | 产品号 | +| model | String | 型号 | +| fwVersion | String | 固件版本 | +| hwVersion | String | 硬件版本 | +| serialNum0 | String | 序列号 0 | +| serialNum1 | String | 序列号 1 | +| btDeviceName | String | 蓝牙设备名称 | +| electricQuantity | Number | 电量 | +| electricQuantityStandby | Number | 备用电量 | +| restoreCount | Number | 重置次数 | +| restoreDate | Number | 重置日期(时间戳秒) | +| icPartNo | String | 芯片型号 | +| indate | Number | 入网日期(时间戳秒) | +| mac | String | MAC 地址 | +| timezoneOffset | Number | 时区偏移(秒) | + +## 初始化 + +> 初始化星云 +{.is-success} +1. 调用方法 +```javascript +/** + * 初始化星云 + */ +plugin.initStarCloud(params) +``` + +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|clientId|String|客户端Id| +|clientSecret|String|客户端密码| +|env|String|环境(SKY,XHJ)| +|isReportLog|Boolean|是否上报日志| + +3. 返回 + 无 + +## 账户相关 + +> 注册 +{.is-success} + +1. 调用方法 +```javascript +/** + * 注册 + * @returns Result + */ +const result = await plugin.register() +``` + +2. 入参 + 无 + +3. 返回 + +|名称|类型|描述| +| -- | -- | -- | +|username|String|用户名| +|password|String|密码| +|uid|Number|用户uid| + + +> 退出登录 +{.is-success} +1. 调用方法 +```javascript /** * 退出登录 * @param params - * @param {Number} params.uid 用户ID + * @param {int} params.uid 用户uid + * @returns Result */ -const { code, data, message } = await plugin.logout(params) +const result = await plugin.logout(params) +``` +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|uid|Number|用户uid| + +3. 返回 + 无 + +## 设备相关 + +> 搜索设备 +{.is-success} + +1. 调用方法 +```javascript +/** + * 搜索蓝牙设备 + * @returns Result + */ +plugin.searchDevice(callback) +const callback = async result => { + if(result.code === Result.Success.code) { + this.setData({ + list: result.data.list + }) + } +} +``` + +2. 入参 + callback 回调方法 + +3. 返回 + +|名称|类型|描述| +| -- | -- | -- | +|deviceId|String|蓝牙设备 id| +|RSSI|Number|当前蓝牙设备的信号强度,单位 dBm| +|connectable|Boolean|当前蓝牙设备是否可连接( Android 8.0 以下不支持返回该值 )| +|advertisData|ArrayBuffer|当前蓝牙设备的广播数据段中的 ManufacturerData 数据段| +|advertisServiceUUIDs|Array.\|当前蓝牙设备的广播数据段中的 ServiceUUIDs 数据段| +|localName|String|当前蓝牙设备的广播数据段中的 LocalName 数据段| +|name|String|蓝牙设备名称,某些设备可能没有| + +> 停止搜索设备 +{.is-success} + +1. 调用方法 +```javascript +/** + * 停止搜索 + * @returns Result + */ +const result = await stopSearchDevice() +``` + +2. 入参 + 无 + +3. 返回 + 无 + +> 绑定设备 +{.is-success} + +1. 调用方法 +```javascript +/** + * 绑定设备 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {String} params.name 设备名称 + * @returns Result + */ +const result = await plugin.bindDevice(params) +if(result.code === Result.Success.code) { + this.setData({ + lock: result.data.lock + }) +} +``` + +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|accountInfo|AccountInfo|账号信息| +|name|String|设备名称| + +3. 返回 + +|名称|类型|描述| +| -- | -- | -- | +|lock|LockInfo|锁信息| + +## 锁基础功能 + +> 选择锁(对锁进行操作前调用) +{.is-success} +1. 调用方法 +```javascript /** * 选择锁 * @param params * @param {AccountInfo} params.accountInfo 账号信息 * @param {Number} params.lockId 锁ID - * @returns {Promise} + * @returns Result */ -const { code, data, message } = await plugin.selectLock(params) +const result = await plugin.selectLock(params) +``` +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|accountInfo|AccountInfo|账号信息| +|lockId|Number|锁ID| + +3. 返回 + 无 + +> 开关门 +{.is-success} +1. 调用方法 +```javascript /** * 开门 * @param params * @param {AccountInfo} params.accountInfo 账号信息 * @param {String} params.type 开门方式 close: 关门 open: 开门 * @param {Boolean} params.disconnect 操作后是否断开连接 - * @returns {Promise} + * @returns Result */ -const { code, data, message } = await plugin.openDoor(params) +const result = await plugin.openDoor(params) +``` -/** - * 获取离线密码 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {OfflinePassword} params.password 密码信息 - * @returns {Promise} - */ -const data = await plugin.getOfflinePassword(params) +2. 入参 -/** - * 自定义密码 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {CustomPassword} params.password 密码信息 - * @param {Boolean} params.disconnect 操作后是否断开连接 - * @returns {Promise} - */ -const data = await plugin.customPassword(params) +|名称|类型|描述| +| -- | -- | -- | +|accountInfo|AccountInfo|账号信息| +|type|String|开门方式| +|disconnect|Boolean|操作后是否断开连接| -/** - * 搜索蓝牙设备 - */ -await plugin.searchDevice(searchDevice) -const searchDevice = async result => { -} - -/** - * 停止搜索 - */ -await plugin.stopSearchDevice() - -/** - * 绑定设备 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {String} params.name 设备名称 - * @returns {Promise} - */ -const data = await plugin.bindDevice(params) - -/** - * 移除坏锁 - * @param params - * @param {AccountInfo} params.accountInfo 账号信息 - * @param {List[int]} params.lockIds 锁Id列表 - * @returns {Promise} - */ -const data = await plugin.removeBadLock(params) +3. 返回 + 无 +> 删除锁 +{.is-success} +1. 调用方法 +```javascript /** * 删除锁 * @param params * @param {AccountInfo} params.accountInfo 账号信息 + * @returns Result */ -const data = await plugin.deleteLock(params) +const result = await plugin.deleteLock(params) +``` +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|accountInfo|AccountInfo|账号信息| + +3. 返回 + 无 + +## 锁密码相关 + +> 获取离线密码 +{.is-success} + +1. 调用方法 +```javascript +/** + * 获取离线密码 + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {OfflinePassword} params.password 密码信息 + * @returns Result + */ +const result = await plugin.getOfflinePassword(params) +``` + +2. 入参 + +OfflinePassword 密码信息 + +| 属性 | 类型 | 描述 | +|-------------------|----------|-------------------| +| keyboardPwdName | String | 密码名称 | +| keyboardPwdType | Number | 密码类型 | +| isCoerced | Number | 是否胁迫模式 1:胁迫 2:非胁迫 | +| startDate | Number | 开始日期时间戳(毫秒,永久默认为0) | +| endDate | Number | 结束日期时间戳(毫秒,永久默认为0) | +| hoursStart | Number | 开始时间(小时, 不需要时传0) | +| hoursEnd | Number | 结束时间(小时, 不需要时传0) | + +keyboardPwdType 密码类型 +| 状态 | 状态说明 | +|------|---------------| +| 1 | 单次 | +| 2 | 永久 | +| 3 | 限时 | +| 4 | 删除 | +| 5 | 周末循环 | +| 6 | 每日循环 | +| 7 | 工作日循环 | +| 8 | 周一循环 | +| 9 | 周二循环 | +| 10 | 周三循环 | +| 11 | 周四循环 | +| 12 | 周五循环 | +| 13 | 周六循环 | +| 14 | 周天循环 | + +3. 返回 + +| 属性 | 类型 | 描述 | +|-------------------|----------|-------------------| +| keyboardPwd | Number | 密码 | +| keyboardPwdId | Number | 密码 | + + +> 修改超级管理员密码 +{.is-success} + +1. 调用方法 +```javascript /** * 修改管理员密码 * @param params @@ -124,20 +427,93 @@ const data = await plugin.deleteLock(params) * @param {Boolean} params.disconnect 操作后是否断开连接 * @returns {Promise} */ -const data = await plugin.updateAdminPassword(params) +const result = await plugin.updateAdminPassword(params) +``` +2. 入参 + +| 属性 | 类型 | 描述 | +|-------------------|----------|-------------------| +| accountInfo | AccountInfo | 账号信息 | +| adminPwd | String | 管理员密码 | +| disconnect | Boolean | 操作后是否断开连接 | + +3. 返回 + 无 + +## 锁记录相关 + +> 同步开门记录 +{.is-success} + +1. 调用方法 +```javascript /** * 同步全部开门记录 * @param params * @param {AccountInfo} params.accountInfo 账号信息 * @param {Boolean} params.disconnect 操作后是否断开连接 - * @returns {Promise} + * @returns Result */ -const data = await plugin.syncAllOpenRecord(params) +const result = await plugin.syncOpenDoorRecord() +``` +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|accountInfo|AccountInfo|账号信息| +|disconnect|Boolean|操作后是否断开连接| + +3. 返回 + 无 + + +## 其他 + +> 获取服务器时间 +{.is-success} + +1. 调用方法 +```javascript /** * 获取服务器时间 - * @returns {Promise} + * @returns Result */ -const data = await plugin.getServerTimestamp() +const result = await plugin.getServerTime() ``` + +2. 入参 + 无 + +3. 返回 + +|名称|类型|描述| +| -- | -- | -- | +|date|Number|时间戳(毫秒)| + +> 移除坏锁 +{.is-success} + +1. 调用方法 +```javascript +/** + * 移除坏锁 + * @param params + * @param {AccountInfo} params.accountInfo 账号信息 + * @param {List[int]} params.lockIds 锁Id列表 + * @returns Result + */ +const result = await plugin.removeBadLock(params) +``` + +2. 入参 + +|名称|类型|描述| +| -- | -- | -- | +|accountInfo|AccountInfo|账号信息| +|lockIds|Array.\|锁Id列表| + +3. 返回 + 无 + diff --git a/miniprogram/doc/example.jpeg b/miniprogram/doc/example.jpeg deleted file mode 100644 index 20d0735706a8c5cd4f895c0ba2ff894a816ab601..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7233 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