Skip to content

MQTT通信参考 ​

连接使用mqtts、可信CA及主机名验证;当前测试服务地址为mqtts://yunstack-s.duruofu.cn:8883。客户端身份为ClientId=physicalId、Username=productId、Password=DeviceSecret。密码不写在URL中,不把客户端鉴权材料放入日志或通用固件。

Topic与ACL ​

以下以P=sys/{productId}/{physicalId}为前缀,设备发布时替换为自己的产品和物理标识,不能使用HTTP逻辑设备UUID。下行由后端发布;上行由设备发布。

相对P的Topic方向内容
thing/event/property/post上行实际属性、新测量、连接元数据或保活
thing/service/property/set下行即时属性控制
thing/service/property/set_reply上行原msgId、明确code
thing/event/capability/post上行当前epoch/boot的设备能力
thing/config/set下行持久完整scope快照
thing/config/set_reply上行当前配置接受/逐项应用结果
thing/event/config_report/post上行配置报告
thing/service/{serviceId}/call下行五个标准服务或内部binding_sync
thing/service/{serviceId}/call_reply上行五个标准服务回复;归属使用binding事件
thing/event/binding/post上行claim、ready、active、resume、boot_confirm、aborted
thing/ota/upgrade、thing/ota/notify下行OTA指令
thing/ota/progress上行OTA阶段和结果

设备授权只在自己的精确前缀下允许已列下行订阅;禁止订阅#或其他设备。服务端订阅使用$share/group1/加mqtt.constants.ts白名单,包含property、set_reply、config回复、五个服务回复、OTA和事件。事件通配监听并不等于业务消费任意事件;新产品增加服务还须同步ACL、订阅与分发,不能仅修改物模型。

后端订阅与发布均QoS1,固件控制/配置使用非retain报文。QoS1可能重复,必须用msgId、版本和执行水位幂等;不得把PubAck当执行确认。固件拒绝retain控制/配置,后端对归属、能力、配置及服务回复拒绝retain,不使用保留消息恢复归属。后端单PM2实例,部分即时控制等待在内存,当前不宣称多实例完整支持。

示例约定 ​

下例产品使用p_relay_4ch,physicalId为6055F97DE54C;UUID和epoch仅用于说明。运行时必须从当前有效归属和启动取得真实值,expiresAtUtc必须由当前UTC时间计算,不能复制旧时间常量。只读服务与持久配置故意没有targetBootId/有效期,不能为整齐补入未知字段。

即时控制 ​

发布到sys/p_relay_4ch/6055F97DE54C/thing/service/property/set:

json
{"msgId":"11111111-1111-4111-8111-111111111111","bindingEpoch":"0123456789abcdef0123456789abcdef","targetBootId":"22222222-2222-4222-8222-222222222222","expiresAtUtc":1793203230,"params":{"relay_1":true}}

expiresAtUtc表示UTC秒,当前后端生成约30秒有效窗口;发送前用Math.floor(Date.now()/1000)+30替换。该示例时间只在对应有效窗口可用。设备回复同主题的set_reply,最小成功体:

json
{"msgId":"11111111-1111-4111-8111-111111111111","code":200}

Topic身份必须与等待请求一致。非200立即失败,缺code不成功;硬件写失败不改变内存/NVS为虚假成功,回复500并上报实际状态。

property/post实际状态示例:

json
{"msgId":"33333333-3333-4333-8333-333333333333","params":{"relay_1":true,"relay_2":false,"relay_3":false,"relay_4":false}}

该普通属性报告没有epoch字段;归属/配置报告使用各自严格结构,不把不同消息类型的字段混用。

持久定时配置与接受回复 ​

发布到同设备thing/config/set:

json
{"schemaVersion":1,"msgId":"44444444-4444-4444-8444-444444444444","bindingEpoch":"0123456789abcdef0123456789abcdef","scope":"schedules","revision":1,"params":{"timezoneMinutes":480,"rules":[{"id":1,"enabled":true,"trigger":{"kind":"weekly","weekdays":31,"minute":480},"action":{"type":"property.set","params":{"relay_1":true}}}]}}

这表示UTC+8、周一至周五08:00开启通道一。全量替换scope;执行含义变化要换新规则ID。对应set_reply:

json
{"schemaVersion":1,"msgId":"44444444-4444-4444-8444-444444444444","bindingEpoch":"0123456789abcdef0123456789abcdef","bootId":"22222222-2222-4222-8222-222222222222","scope":"schedules","revision":1,"code":200,"acceptedRevision":1,"appliedRevision":1,"params":{"timezoneMinutes":480,"rules":[{"id":1,"enabled":true,"trigger":{"kind":"weekly","weekdays":31,"minute":480},"action":{"type":"property.set","params":{"relay_1":true}}}]}}

settings使用同一封装,params必须是该产品完整可写设置快照。以四路继电器默认设置为例:

json
{"schemaVersion":1,"msgId":"55555555-5555-4555-8555-555555555555","bindingEpoch":"0123456789abcdef0123456789abcdef","scope":"settings","revision":1,"params":{"relay_1_boot_delay_s":0,"relay_2_boot_delay_s":0,"relay_3_boot_delay_s":0,"relay_4_boot_delay_s":0}}

settings成功报告必须带items;每项包含id、desiredValue、可选reportedValue、acceptedRevision、appliedRevision及state。state=applied要求reportedValue严格等于desiredValue,state=pending_restart不带error,failed可带有界错误标识。非200报告不得附成功字段:

json
{"schemaVersion":1,"msgId":"55555555-5555-4555-8555-555555555555","bindingEpoch":"0123456789abcdef0123456789abcdef","bootId":"22222222-2222-4222-8222-222222222222","scope":"settings","revision":1,"code":409}

完整settings应用成功回复示例(实际重启前若待生效,应使用pending_restart及旧reported,而不是套用本成功值):

json
{
  "schemaVersion": 1,
  "msgId": "55555555-5555-4555-8555-555555555555",
  "bindingEpoch": "0123456789abcdef0123456789abcdef",
  "bootId": "22222222-2222-4222-8222-222222222222",
  "scope": "settings",
  "revision": 1,
  "code": 200,
  "acceptedRevision": 1,
  "appliedRevision": 1,
  "params": {
    "relay_1_boot_delay_s": 0,
    "relay_2_boot_delay_s": 0,
    "relay_3_boot_delay_s": 0,
    "relay_4_boot_delay_s": 0
  },
  "items": [
    {
      "id": "relay_1_boot_delay_s",
      "desiredValue": 0,
      "reportedValue": 0,
      "acceptedRevision": 1,
      "appliedRevision": 1,
      "state": "applied"
    },
    {
      "id": "relay_2_boot_delay_s",
      "desiredValue": 0,
      "reportedValue": 0,
      "acceptedRevision": 1,
      "appliedRevision": 1,
      "state": "applied"
    },
    {
      "id": "relay_3_boot_delay_s",
      "desiredValue": 0,
      "reportedValue": 0,
      "acceptedRevision": 1,
      "appliedRevision": 1,
      "state": "applied"
    },
    {
      "id": "relay_4_boot_delay_s",
      "desiredValue": 0,
      "reportedValue": 0,
      "acceptedRevision": 1,
      "appliedRevision": 1,
      "state": "applied"
    }
  ]
}

能力报告发布在thing/event/capability/post,完整四路示例:

json
{
  "schemaVersion": 1,
  "modelRevision": "1.0",
  "bootId": "22222222-2222-4222-8222-222222222222",
  "bindingEpoch": "0123456789abcdef0123456789abcdef",
  "settings": [
    "relay_1_boot_delay_s",
    "relay_2_boot_delay_s",
    "relay_3_boot_delay_s",
    "relay_4_boot_delay_s"
  ],
  "services": [
    "config_get",
    "config_apply_retry",
    "schedule_get",
    "countdown_set",
    "countdown_cancel"
  ],
  "scheduling": {
    "propertyIds": [
      "relay_1",
      "relay_2",
      "relay_3",
      "relay_4"
    ],
    "propertyTypes": [
      "bool"
    ],
    "maxRules": 16,
    "maxCountdowns": 4,
    "maxCountdownSeconds": 86400,
    "timezoneMinMinutes": -720,
    "timezoneMaxMinutes": 840
  }
}

标准服务 ​

serviceIdparams权限/有效期
config_get{"scope":"settings"}或schedules有效分享者可读;无boot目标/有效期
schedule_get{}有效分享者可读;设备需支持定时
config_apply_retry拥有者;仅当前failed已接受版本
countdown_set{"action":{"type":"property.set","params":{"relay_1":false}},"seconds":60}拥有者;1至设备最大秒数
countdown_cancel拥有者

写服务封装例:

json
{"msgId":"66666666-6666-4666-8666-666666666666","bindingEpoch":"0123456789abcdef0123456789abcdef","targetBootId":"22222222-2222-4222-8222-222222222222","expiresAtUtc":1793203230,"params":{"action":{"type":"property.set","params":{"relay_1":false}},"seconds":60}}

同样必须重算expiresAtUtc。只读config_get请求的完整封装为:

json
{"msgId":"77777777-7777-4777-8777-777777777777","bindingEpoch":"0123456789abcdef0123456789abcdef","params":{"scope":"schedules"}}

返回到对应call_reply。以下查询空规则的成功完整回复适用于该scope已经配置为revision=1的设备:

json
{"schemaVersion":1,"msgId":"77777777-7777-4777-8777-777777777777","serviceId":"config_get","bindingEpoch":"0123456789abcdef0123456789abcdef","bootId":"22222222-2222-4222-8222-222222222222","code":200,"result":{"configuration":{"scope":"schedules","revision":1,"acceptedRevision":1,"appliedRevision":1,"params":{"timezoneMinutes":0,"rules":[]}}}}

未配置scope返回revision/accepted/applied均0、settings空params/items或schedules空rules和时区0,不能冒充已应用默认配置。非200服务回复不带result。字段、尺寸及取值校验由operation-contract.ts、configuration-report.ts、service-report.ts和固件wire测试共同约束。

故障与安全 ​

默认控制/服务等待约30秒;服务请求记录120秒,设备占用35秒,自动查询记录120秒。retain、旧epoch、旧boot、错误Topic、未知字段、容量超限或冲突均应拒绝。持久配置使用版本幂等;短期写操作使用当前启动和有效期,不能通过重发过期消息恢复任务。归属与OTA有独立报文契约,见配网认领、OTA。