跳转至

15 设备影子与命令控制

难度:中级|前置:MQTT、在线状态、状态机|目标:处理离线设备、状态同步和命令回执

为什么不能只“发一条命令”

平台下发:

把温度阈值设为 30。

可能发生:

  • 设备离线;
  • 消息到达但执行失败;
  • 设备执行成功但回执丢失;
  • 旧命令晚于新命令到达;
  • 用户重复点击;
  • 设备重启后恢复旧配置。

因此需要区分“希望设备是什么状态”和“设备报告自己是什么状态”。

设备影子

{
  "desired": {"threshold": 30},
  "reported": {"threshold": 28},
  "version": 12
}
  • desired:平台期望;
  • reported:设备实际报告;
  • delta:二者差异;
  • version:防止旧更新覆盖新状态。

影子适合最终状态同步,不适合所有瞬时动作。

状态设置与动作命令

状态设置

set threshold = 30

重复执行结果相同,更容易幂等。

动作命令

reboot
take_photo
dispense_once

可能产生一次性副作用,需要 command_id、回执、超时和幂等。

命令状态

created
→ dispatched
→ received
→ executing
→ succeeded / failed / timeout

Broker 接收成功不等于设备执行成功。回执应关联稳定的 command_id

版本和顺序

设备收到版本 12 后又收到版本 11,应拒绝旧版本。服务器也不能用旧 reported 覆盖新状态。

如果业务只需要同一设备内的顺序,可以按 device_id 分区,不必追求所有设备的全局顺序。

离线命令

必须明确策略:

  • 离线时立即失败;
  • 保存到设备上线后执行;
  • 只保留最后一个期望状态;
  • 设置命令过期时间;
  • 高风险命令禁止离线排队。

自测

  1. desired 和 reported 分别是谁的事实?
  2. 为什么 reboot 不适合仅用设备影子表达?
  3. Broker Ack 为什么不能代表设备执行成功?
  4. 旧版本消息如何避免覆盖新状态?

完成标准

  • 能解释设备影子的四个核心字段
  • 能区分状态设置和动作命令
  • 能设计命令状态与回执
  • 能定义离线命令策略

下一单元:时序数据与聚合