MQTT 适配器
#
MQTT 介绍MQTT是一种机器对机器(M2M)/"物联网 "连接协议。它被设计为一种极其轻量级的发布/订阅消息传输。它对于需要少量代码占用和/或网络带宽很高的远程地点的连接非常有用。
MQTT 适配器在paho.mqtt.golang上实现,有助于与 MQTT 经纪商进行通信,以与链接设备进行交互。
#
MQTT 杂谈#
数据结构我们知道,MQTT 是没有固定的结构,所以没有标准的主题命名模式和payload格式。发布者组织数据结构的方式将直接影响到订阅者的使用情况。在社区中,我们总结了两种常见的模式。下面我们来看看。
第一种模式可以命名为属性主题:发布者将属性扁平化为主题,然后将属性的有效载荷发送到对应的主题。它在 Github 上有一个代表:HomieMQTT 公约。
Homie 很有意思,它最大的特点就是自发现,也就是订阅者不需要知道数据结构,只需要订阅根主题,然后公约实现客户端就会反映出所有属性,包括名称、描述、值、类型等。但是,属性主题模式会创建很多主题,所以需要一个类似 Homie 的公约来保证标准化和可扩展性。
另一种直接将属性压缩成一个有效载荷的模式可以命名为属性消息。发布者将属性序列化为一种目标格式,如 XML、JSON 或自定义表单,然后将整个序列化结果发送给一个主题。
Attributed Message模式减少了 topic 的使用频率,但订阅者需要知道如何在每个主题中反序列化有效载荷,并了解数据的组织结构。更好的方法是在所有主题中使用相同的序列化格式,并引入数据结构的层次描述。例如,如果发布者选择 JSON 作为序列化格式,发布者可以在另一个主题中附加数据结构的JSONschema。
#
操作在 MQTT 中,对于数据的pub/sub(pub:发布,sub:订阅)只有两种方式:一是在同一个主题上执行pub/sub,二是将pub/sub分为两个主题。
第一种方式不受欢迎,可能需要在有效载荷中加入操作命令。
虽然使用声明式管理的系统(如Kubernetes)可以避免上述的命令式操作,但当发布者做了pub时,必须引入一个sub,这在功耗极低的环境下是不可接受的。
因此,第二种方式会更容易被接受。由于属性已经被扁平化,在属性主题模式下,发布者可以将数据发送到与属性对应的特殊后缀的主题。例如,Homie 更喜欢使用以set
结尾的 topic 来接收值的变化。
对于属性消息模式也是如此,期望发布者需要选择只发送修改的属性还是所有属性。
#
公约MQTTDevice 集成了MQTT 插件的配置。
#
AttributedTopic 模式指定pattern: AttributedTopic
来与多主题中扁平化属性的设备进行交互。
指定templated topic,用:path
关键字来渲染对应属性名的目标主题。
或用path
字段说明:path
。
将读写属性指定为readOnly: false
改变可写属性,发布到另一个主题。
注意,:operator
可以被覆盖。
#
AttributedMessage指定pattern: AttributedMessage
与设备交互,将其属性压缩在一个主题中。
note
AttributedMessage
模式目前只支持 JSON 格式的有效载荷内容。
If the JSON of payload content looks as below:
通过属性name
提取内容。
或说明重新启动 path
参数的JSONPath。
指定一个readOnly: false'
的可写属性。
更改发布主题:
#
JSONPathnote
JSONPath 只在AttributedMessage
模式下可用。
MQTT 适配器集成了tidwall/gjson和tidwall/sjson。
对于Read Only属性,path
字段可以接受GJSON Path Syntax,这是一种神奇而丰富的路径检索机制。
但是,针对Writable属性,path
字段只能接受restrictedSJSON Path Syntax。
为了保证一个属性的读写路径一致,MQTT 适配器在Writable属性上阻止了以下路径:
children.-1
children|@reverse
child*.2
c?ildren.0
friends.#.first
#
用户案例试想一下,我们的家用电器是非常智能的,可以主动向 MQTT agent 报告自己的状态信息,然后我们就会用MQTTDevice
来连接和获取这些信息。例如,我们的厨房门可以告诉我们它的生产信息,它的关闭状态等等。
我们可以用 "AttributedTopic "模式定义一个 "MQTTDevice "设备连接来监视我们的厨房门。
在 "AttributedTopic "模式中,每个 "property "都是一个 topic,默认情况下,property 的 "name "可以作为":path "关键字来呈现 topic,最后得到对应的 topic 来订阅。默认情况下,属性的 "name "可以作为":path "关键字来呈现该主题,并最终获得相应的主题来订阅。
厨房灯也会将其属性报告给 MQTT agent,让我们可以远程控制厨房灯。
我们可以利用MQTTDevice
设备链接的可写属性来控制厨房灯。
使用 "readOnly: false "来确定一个可写属性。此外,属性级别 "operator "可以覆盖 "AttributedTopic "模式中协议级别的定义。
例如,我们可以打开厨房的灯,将其调整到中等亮度。
此外,我们可以在同一个MQTTDevice
设备链接中监控门和灯的状态。
最近新买了一盏智能卧室灯,但是发现传输的数据格式和之前的不一样。
我们可以定义一个 "MQTTDevice "设备链接与 "AttributedMessage "模式来监控新的卧室灯。
在
AttributedMessage
模式中,整个链接是一个主题。默认情况下,属性的 "name "可以作为 payload 内容的检索路径。
如果需要的话,我们可以修改上面的MQTTDevice
设备链接,关闭新卧室的灯。
#
注册信息版本 | 注册名称 | 端点 Socket | 是否可用 |
---|---|---|---|
v1alpha1 | adaptors.edge.cattle.io/mqtt | mqtt.sock | 是 |
#
支持模板类型 | 设备组 | 版本 | 是否可用 |
---|---|---|---|
MQTTDevice | devices.edge.cattle.io | v1alpha1 | 是 |
#
支持的平台操作系统 | 架构 |
---|---|
linux | amd64 |
linux | arm |
linux | arm64 |
#
使用方式#
权限对 Octopus 授予权限,如下所示:
#
YAML 示例指定 "MQTTDevice "设备链接,订阅厨房房间门的信息。
指定 "MQTTDevice "设备链接,订阅卧室灯的信息。
更多的 "MQTTDevice "设备链接示例,请参考deploy/e2e目录,并使用deploy/e2e/simulator.yaml进行快速体验。
#
MQTTDevice参数 | 描述 | 类型 | 是否必填 |
---|---|---|---|
metadata | 元数据 | metav1.ObjectMeta | false |
spec | 定义 "MQTTDevice"的预期状态 | MQTTDeviceSpec | 是 |
status | 定义 "MQTTDevice"的实际状态 | MQTTDeviceStatus | 否 |
#
MQTTDeviceSpec参数 | 描述 | 类型 | 是否必填 |
---|---|---|---|
protocol | 指定访问设备时使用的协议 | MQTTDeviceProtocol | 是 |
properties | 指定设备的属性 | []MQTTDeviceProperty | 否 |
#
MQTTDeviceStatus参数 | 描述 | 类型 | 是否必填 |
---|---|---|---|
properties | 上报设备的属性 | []MQTTDeviceStatusProperty | 否 |
#
MQTTDeviceProtocol参数 | 描述 | 类型 | 是否必填 |
---|---|---|---|
pattern | 指定 MQTTDevice 协议的模式 | MQTTDevicePattern | 是 |
client | 指定客户端的设置 | MQTTClientOptions | 是 |
message | 指定消息的设置 | MQTTMessageOptions | 是 |
#
MQTTDevicePattern参数 | 描述 | 类型 |
---|---|---|
AttributedMessage | 将属性压缩成一条消息,一个主题有其所有的属性值 | string |
AttributedTopic | 扁平化属性到主题,每个主题都有自己的属性值 | string |
#
MQTTDeviceProperty参数 | 描述 | 类型 | 是否必填 |
---|---|---|---|
annotations | 指定属性的注释 | map[string]string | 否 |
name | 指定属性的名称 | string | 是 |
description | 指定属性的描述 | string | 否 |
readOnly | 指定该属性是否为只读,默认为 "true" | *bool | 否 |
type | 指定属性的类型 | MQTTDevicePropertyType | 否 |
value | 指定属性的值,只在可写属性中可用 | MQTTDevicePropertyValue | 否 |
path | 指定 topic 的:path 关键字的渲染路径,默认与name 相同。在AttributedTopic 模式下,该路径将呈现在 topic 上;在AttributedMessage 模式下,该路径应该是一个JSONPath ,可以访问 payload 内容。 | string | 否 |
operator | 指定用于呈现主题的:operator 关键字的操作符。 | MQTTMessageTopicOperator](/docs/octopus/adaptors/mqtt-extension/index#mqttmessagetopicoperator)。 | 否 |
qos | 指定消息的 QoS,只有在AttributedTopic 模式下才有。默认值是1 。 | MQTTMessageQoSLevel | 否 |
retained | 指定是否保留最后发布的消息,只有在AttributedTopic 模式下才有。默认为 "true"。 | *bool | 否 |
MQTT 适配器会返回 MQTT broker 接收到的原始数据,因此,"type "的意义并不是告诉 MQTT 适配器如何处理有效载荷,而是让用户描述期望值。因此,"type "的含义不是告诉 MQTT 适配器如何处理有效载荷,而是让用户描述期望的内容。
#
MQTTDeviceStatusProperty参数 | 描述 | 类型 | 是否必填 |
---|---|---|---|
annotations | 属性的注释 | map[string]string | 否 |
name | 属性的名称 | string | 是 |
description | 属性的描述 | string | 否 |
readOnly | 该属性是否为只读,默认为 "true" | *bool | 否 |
type | 属性的类型 | MQTTDevicePropertyType | 否 |
value | 属性的值,只在可写属性中可用 | MQTTDevicePropertyValue | 否 |
path | topic 的:path 关键字的渲染路径,默认与name 相同。 在AttributedTopic 模式下,这个路径将在 topic 上呈现;在AttributedMessage 模式下,这个路径应该是一个JSONPath ,可以访问 payload 内容 | string | 否 |
operator | 用于呈现主题的:operator 关键字的操作符。 | MQTTMessageTopicOperator。 | 否 |
qos | 消息的 QoS,只有在AttributedTopic 模式下才有。默认值是1 。 | MQTTMessageQoSLevel | 否 |
retained | 是否保留最后发布的消息,只有在AttributedTopic 模式下才有。默认为 "true"。 | *bool | 否 |
#
MQTTDevicePropertyType参数 | 描述 | 类型 |
---|---|---|
string | 属性数据类型为 string | string |
int | 属性数据类型为 int | string |
float | 属性数据类型为 float | string |
boolean | 属性数据类型为 boolean | string |
array | 属性数据类型为 array | string |
object | 属性数据类型为 object | string |
#
MQTTDevicePropertyValueMQTTDevicePropertyValue 需要根据type
输入相应的内容。例如: