Skip to content

技术实现

小程序 IoT 功能的关键技术实现方案,涵盖 WebSocket 客户端、BLE 配网、Pinia 状态管理和物模型组件映射。作为 02.界面与交互设计 的技术落地文档。


1. WebSocket 客户端

1.1 连接管理

  • 使用 UniApp uni.connectSocket API
  • 连接时机: App onLaunch 或首页 onShow(Token 已存在时自动连接)
  • 连接参数: wss://host/ws?token=xxx
  • 断线重连:onSocketClose 回调中实现指数退避重连(1s → 2s → 4s → 最大 30s)
  • 前后台切换: App 切后台时断开连接节省资源,切前台时自动重连

1.2 消息处理

onSocketMessage 收到推送后解析 type 字段:

type处理
device.status更新 Pinia deviceStore 中对应设备的 onlineMap
device.shadow更新 Pinia deviceStore 中对应设备的 shadowMap

错误处理:收到格式不符的消息时记录日志并忽略,不做 Toast 提示(避免骚扰用户)。

1.3 订阅管理

时机操作
设备详情页 onShow发送 {"type":"subscribe","deviceIds":["d1..."]}
设备详情页 onHide发送 {"type":"unsubscribe","deviceIds":["d1..."]}
首页只订阅设备在线状态(列表不需实时属性值,点击进入详情页才订阅影子)

2. BLE 配网

2.1 蓝牙 API 调用链

wx.openBluetoothAdapter 初始化
  → wx.startBluetoothDevicesDiscovery 扫描
    → 过滤广播名称(YunStack- 前缀)
    → 用户点击目标设备
      → wx.createBLEConnection 建立连接
        → wx.getBLEDeviceServices 获取服务
          → wx.getBLEDeviceCharacteristics 获取特征值

2.2 GATT 服务定义

参数
Service UUID0xFF00(YunStack 配网服务)
Characteristic UUID0xFF01(WiFi 凭据写入,Write + Notify)
数据格式JSON 字符串 {"ssid":"xxx","password":"xxx"}
写入方式wx.writeBLECharacteristicValue,分片发送(MTU 限制,每包最大 20 字节)

2.3 状态反馈

设备通过 Notify 回复配网结果:

json
{"status":"connecting"}   // 正在连接 WiFi
{"status":"connected"}    // 连接成功
{"status":"failed","reason":"wrong_password"}  // 连接失败

小程序通过 onBLECharacteristicValueChange 接收通知。连接成功后设备关闭 BLE 广播,小程序断开 BLE 连接,等待设备通过 MQTT 上线(WebSocket 通知)。

2.4 错误处理

错误场景处理
蓝牙未开启引导用户打开手机蓝牙
未扫描到设备提示"未发现设备,请确认设备已通电并靠近手机"
连接超时(10 秒)提示重试
WiFi 密码错误提示重新输入

3. Pinia 状态管理

3.1 Store 设计

新增两个 IoT 相关 store:

deviceStore

state类型说明
devicesDevice[]设备列表(首页)
currentDeviceDevice当前查看的设备详情
loadingboolean列表加载状态
onlineMapMap<string, boolean>deviceId → 在线状态(WebSocket 更新)
shadowMapMap<string, object>deviceId → 最新属性值(WebSocket 更新)

provisionStore

state类型说明
stepnumber当前配网步骤(1-5)
bleDevicesBLEDevice[]扫描到的 BLE 设备列表
connectedDeviceBLEDevice已连接的设备
wifiSSIDstring用户输入的 WiFi SSID
errorstring配网错误信息

3.2 数据流

首页 onShow → deviceStore.fetchDevices() → GET /api/v1/devices → 填充 devices[]

WebSocket 推送 → deviceStore.updateOnline(deviceId, status) → 更新 onlineMap

设备详情 → deviceStore.fetchDeviceDetail(id) → GET /api/v1/devices/:id → 填充 currentDevice

控制操作 → deviceStore.controlDevice(id, params) → POST /api/v1/devices/:id/control → 等待 set_reply → 乐观更新 shadowMap

4. 物模型 → UI 组件映射

4.1 组件注册表

根据物模型中属性的 ui.component 字段渲染对应组件:

ui.component渲染组件说明
switch<switch-card>大圆形开关按钮,点击切换
multi-switch<switch-list>多路开关列表
slider<slider-card>滑块控件,显示当前值 + 单位
display<display-card>只读数显,大字体数值 + 单位 + 名称
未指定<display-card>默认渲染为只读显示

4.2 组件数据绑定

属性字段绑定
属性 id组件标题
属性 type值格式(bool 渲染开关,float/int 渲染数值)
属性 access: "r"组件不可操作(只读)
属性 unit组件单位显示

5. API 客户端

5.1 请求封装

  • 使用 UniApp uni.request 封装,统一 baseURL 和请求头(Authorization: Bearer <token>
  • 响应拦截:code !== "00000" 时统一 Toast 错误信息
  • Token 过期(code=A0004)→ 自动调用 refresh-token → 重试原请求 → 刷新失败则跳转登录页

5.2 请求重试

  • 网络超时(errMsg: "request:fail timeout")→ Toast 提示"网络异常,请检查网络" + 手动重试按钮
  • 不自动重试(避免重复提交控制指令)