瑞瀛物联网数据传输协议(MQTT)
<p>[TOC]</p>
<h2>1、概述</h2>
<p>本协议时针对网关和云平台之间进行有效通信而设计的一种数据交换规范,数据格式为 JSON,为了方便描述其对象定义,描述格式参照 GraphQL 的标准。</p>
<ul>
<li>gwMAC网关的 COO MAC 地址,用来唯一标识网关</li>
<li>productKey子设备的品类编码</li>
<li>deviceName子设备的 MAC 地址,用来唯一标识子设备</li>
<li>event设备的事件名称,详见品类的物模型定义</li>
<li>service设备的服务名称,详见品类的物模型定义</li>
</ul>
<h2>2、设备接入</h2>
<h3>2.1、设备身份注册</h3>
<h4>2.1.1、网关动态注册</h4>
<p>网关向平台发起注册请求:</p>
<ul>
<li>数据上行</li>
<li>-请求 Topic:rex/auth/register/device</li>
<li>响应 Topic: rex/auth/register/${gwMAC}</li>
<li>请求数据格式:DeviceRegisterReq</li>
<li>响应数据格式:DeviceRegisterRes</li>
</ul>
<h4>2.1.2子设备动态注册</h4>
<p>子设备通过网关向平台发起注册请求:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/sub/register</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/sub/register_reply</li>
<li>请求数据格式:DeviceListReq</li>
<li>响应数据格式:SubDeviceRegisterRes</li>
</ul>
<h3>2.2、拓扑关系管理</h3>
<h4>2.2.1、子设备入网通知</h4>
<p>子设备加入 ZigBee 网络后,通常需要将该子设备与网关进行拓扑关系的绑定,以方便后期的运维操作:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/topo/join</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/topo/join_reply</li>
<li>请求数据格式:TopoAddReq</li>
<li>响应数据格式:DeviceList</li>
</ul>
<h4>2.2.2、子设备离网通知</h4>
<p>子设备离开 ZigBee 网络后,网关将通知平台解除绑定:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/topo/leave</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/topo/leave_reply</li>
<li>请求数据格式:DeviceListReq</li>
<li>响应数据格式:DeviceList</li>
</ul>
<h4>2.2.3、云端下发子设备列表</h4>
<p>在某些场景下,平台通知网关网络中可能存在的子设备,要求网关主动发起设备发现功能,此时扫描出来的子设备将无需上报至平台:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/topo/add</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/topo/add_reply</li>
<li>请求数据格式:DeviceListReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h4>2.2.4、云端主动删除子设备</h4>
<p>云端可以主动要求指定的子设备离网:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/topo/delete</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/topo/delete_reply</li>
<li>请求数据格式:DeviceListReq</li>
<li>响应数据格式:DeviceList</li>
</ul>
<h4>2.2.5、获取设备拓扑关系</h4>
<p>为了保证网关与平台间的拓扑关系保持一致,平台可以获取网关的设备列表,一旦发现双方设备列表不一致,平台应当主动发起 2.2.3 或 2.2.4 请求以保持后续的数据一致性:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/topo/get</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/topo/get_reply</li>
<li>请求数据格式:CommonReq</li>
<li>响应数据格式:DeviceList</li>
</ul>
<h3>2.3、设备状态管理</h3>
<h4>2.3.1、网关上线</h4>
<p>网关上线时通知平台:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/login</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/login_reply</li>
<li>请求数据格式:CommonReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h4>2.3.2、网关下线</h4>
<p>网关下线时通知平台:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/logout</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/logout_reply</li>
<li>请求数据格式:CommonReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h4>2.2.3、子设备上线</h4>
<p>子设备上线时通知平台:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/sub/login</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/sub/login_reply</li>
<li>请求数据格式:DeviceLoginReq</li>
<li>响应数据格式:DeviceList</li>
</ul>
<h4>2.3.4、子设备下线</h4>
<p>子设备离线时通知平台,ZigBee 设备离线时不会主动通知网关,目前是根据 2 倍心跳周期时长内未收到子设备任何报文来判断子设备离线:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/sub/logout</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/sub/logout_reply</li>
<li>请求数据格式:DeviceListReq</li>
<li>响应数据格式:DeviceList</li>
</ul>
<h2>3、消息通信</h2>
<h3>3.1、设备属性、事件、服务</h3>
<h4>3.1.1、属性上报</h4>
<p>设备上报属性,其中属性名称和值类型需要参考每一种品类的物模型定义:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/${productKey}/${deviceName}/thing/event/property/post</li>
<li>响应 Topic: rex/${productKey}/${deviceName}/thing/event/property/post_reply</li>
<li>请求数据格式:PropertyPostReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h4>3.1.2、属性设置</h4>
<p>设置设备属性,其中属性名称和值类型需要参考每一种品类的物模型定义:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/${productKey}/${deviceName}/thing/service/property/set</li>
<li>响应 Topic: rex/${productKey}/${deviceName}/thing/service/property/set_reply</li>
<li>请求数据格式:PropertySetReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h4>3.1.3、事件上报</h4>
<p>设备的事件上报,其中事件名称和相应的参数需要参考每一种品类的物模型定义:</p>
<ul>
<li>数据上行</li>
<li>请求 Topic: rex/${productKey}/${deviceName}/thing/event/${event}/post</li>
<li>响应 Topic: rex/${productKey}/${deviceName}/thing/event/${event}/post_reply</li>
<li>请求数据格式:EventPostReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h4>3.1.4、服务调用</h4>
<p>设备的服务调用,其中服务名称和相应的参数需要参考每一种品类的物模型定义,如未加特殊说明,服务调用均为异步调用,无法通过响应内容获知该服务调用是否成功:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/${productKey}/${deviceName}/thing/service/${service}</li>
<li>响应 Topic: rex/${productKey}/${deviceName}/thing/service/${service}_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h2>4、监控运维</h2>
<h3>4.1、OTA升级</h3>
<h4>4.1.1、版本信息上报</h4>
<p>网关上线后,会主动上报版本信息:</p>
<ul>
<li>数据上行</li>
<li>Topic: rex/R101Gateway/${gwMAC}/ota/device/inform</li>
<li>请求数据格式:OtaInform</li>
</ul>
<h4>4.1.2、平台推送 OTA 升级包信息</h4>
<p>平台在需要时,将主动推送升级包要求网关进行 OTA 升级</p>
<ul>
<li>数据下行</li>
<li>Topic: rex/R101Gateway/${gwMAC}/ota/device/upgrade</li>
<li>请求数据格式:OtaUpgrade</li>
</ul>
<h4>4.1.3、设备上报升级进度</h4>
<p>网关升级过程中会上报升级进度:</p>
<ul>
<li>数据上行</li>
<li>Topic: rex/R101Gateway/${gwMAC}/ota/device/progress</li>
<li>请求数据格式:OtaProgress</li>
</ul>
<h4>4.1.4、子设备 OTA 推送</h4>
<p>平台可以发起子设备的 OTA 升级:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/${productKey}/${deviceName}/ota/device/upgrade</li>
<li>请求数据格式:OtaUpgrade</li>
</ul>
<h4>4.1.5、子设备升级进度上报</h4>
<p>子设备升级过程中也同样会上报升级进度:</p>
<ul>
<li>数据上行</li>
<li>Topic: rex/${productKey}/${deviceName}/ota/device/progress</li>
<li>请求数据格式:OtaProgress</li>
</ul>
<h2>5、本地场景</h2>
<h3>5.1、设备别名</h3>
<p>设备的 MAC 地址具有全局唯一性,在实际的业务场景中如果场景的配置直接使用 MAC 作为设备标识的话,一旦出现设备更换则会导致所有涉及该设备的场景配置均需要重新配置;另一方面,不同的设备 MAC 地址也不利于快速地复制场景到其它的网络中。因此,需要设计一个设备别名机制,只需要保持设备别名相同,场景就能寻找到该设备。</p>
<h4>5.1.1、设置设备别名</h4>
<p>平台将子设备的 MAC 地址与设备别名进行绑定:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/${productKey}/${deviceName}/thing/service/alias/set</li>
<li>响应 Topic: rex/${productKey}/${deviceName}/thing/service/alias/set_reply</li>
<li>请求数据格式:AliasSetReq</li>
<li>响应数据格式:CommonRes</li>
</ul>
<h3>5.2、场景管理</h3>
<h4>5.2.1、创建或更新场景</h4>
<p>平台将场景的配置下发到网关:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_create_update</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_create_update_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h4>5.2.2、删除场景</h4>
<p>平台通知网关删除指定场景:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_delete</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_delete_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h4>5.2.3、触发场景</h4>
<p>触发执行场景:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_trigger</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_trigger_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h4>5.2.4、启用场景</h4>
<p>启用场景:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_enable</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_enable_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h4>5.2.5、禁用场景</h4>
<p>禁用场景,场景一旦被禁用后,将不会被自动触发,但仍然可以通过 5.2.3 节的指令触发该场景:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_disable</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/scene_disable_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h2>6、其它功能</h2>
<h3>6.1、ZigBee 相关指令</h3>
<h4>6.1.1、创建 ZigBee 网络</h4>
<p>创建 ZigBee 网络:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/create_zigbee_network</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/create_zigbee_network_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<p>其中,请求参数有:</p>
<ul>
<li>Trug表示是否使用随即参数建网,默认值为false</li>
<li>ZigbeeNetworkParam为 base64 编码后的网络参数</li>
</ul>
<p>如:</p>
<pre><code>{
&quot;id&quot;: 1671602294,
&quot;params&quot;: [{
&quot;key&quot;: &quot;Trug&quot;,
&quot;value&quot;: false
},{
&quot;key&quot;: &quot;ZigbeeNetworkParam&quot;,
&quot;value&quot;: &quot;22irgAXVs3DbaAoWAgEAANtoq4AF1bNwAgAAAI06rAlSBHzHoPwseYZgC8Szk9OZBgtANJGpe09jAkVwAIAbAACAAwA=&quot;
}]
}</code></pre>
<h4>6.1.2、离开 ZigBee 网络</h4>
<p>离开 ZigBee 网络:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/leave_zigbee_network</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/leave_zigbee_network_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<h4>6.1.3、开启扫网</h4>
<p>允许子设备入网:</p>
<ul>
<li>数据下行</li>
<li>请求 Topic: rex/R101Gateway/${gwMAC}/thing/service/start_permit_join</li>
<li>响应 Topic: rex/R101Gateway/${gwMAC}/thing/service/start_permit_join_reply</li>
<li>请求数据格式:ServiceCallReq</li>
<li>响应数据格式:ServiceCallRes</li>
</ul>
<p>其中,请求参数有:</p>
<ul>
<li>duration表示开启扫网的时长,默认为30秒,最小值10秒,最大值300秒</li>
<li>installCode不为空则表示支持 installCode 入网</li>
<li>mac为 MAC 地址的数组,不为空表示白名单入网,在白名单以外的设备拒绝入网请求</li>
</ul>
<p>示例:</p>
<pre><code>{
&quot;id&quot;: 1671602296,
&quot;params&quot;: [{
&quot;key&quot;: &quot;duration&quot;,
&quot;value&quot;: 30
},{
&quot;key&quot;: &quot;installCode&quot;,
&quot;value&quot;: &quot;0123456789ABCDEF&quot;
},{
&quot;key&quot;: &quot;mac&quot;,
&quot;value&quot;: [&quot;0123456789ABCDEF&quot;]
}]
}</code></pre>
<h2>7、数据结构定义</h2>
<pre><code>input DeviceRegisterReq {
deviceName: String!
productKey: String!
}
input CommonReq {
id: ID! # 消息 id 在当前设备中要求具有唯一性
}
input DeviceListReq {
id: ID!
params: [DeviceParam!]
}
input DeviceParam {
deviceName: String! # 设备名称
productKey: String! # 设备的产品 ProductKey
}
input TopoAddReq {
id: ID!
deviceName: String! # 子设备名称
productKey: String! # 子设备所属产品的 ProductKey
}
input DeviceLoginReq {
id: ID!
productKey: String!
deviceName: String!
}
input PropertyPostReq {
id: ID!
params: [DeviceProperty!]
}
input DeviceProperty {
name: String!
value: Value!
time: Timestamp!
}
input PropertySetReq {
id: ID!
params: [DevicePropertySet!]
}
input DevicePropertySet {
name: String!
value: Value!
}
input EventPostReq {
id: ID!
params: [KeyValuePair!]
time: Timestamp!
}
input KeyValuePair {
key: String!
value: Value!
}
input ServiceCallReq {
id: ID!
params: [KeyValuePair!]
}
input AliasSetReq {
id: ID!
alias: String!
}
input OtaInform {
id: ID!
version: String!
module: String! # 模块名称
}
input OtaUpgrade {
id: ID!
size: Int
version: String
module: String
url: String!
md5: String
params: [KeyValuePair!]
}
input OtaProgress {
id: ID!
step: Int!
module: String1
desc: String
}
union Value = Int | Float | String | Boolean
type CommonRes {
id: ID!
code: Int!
}
type DeviceRegisterRes {
code: Int!
device: RegisterData!
}
type SubDeviceRegisterRes {
id: ID!
code: Int!
data: [RegisterData!]
}
type RegisterData {
deviceName: String!
productKey: String!
deviceSecret: String!
}
type DeviceList {
id: ID!
code: Int!
data: [DeviceData!]
}
type DeviceData {
deviceName: String!
productKey: String!
}
type ServiceCallRes {
id: ID!
code: Int!
data: [KeyValueData!]
}
type KeyValueData {
key: String!
value: Value!
}</code></pre>
<h2>8、错误信息码</h2>