mqtt(Zigbee2MQTT / Tasmota / ESPHome)
其它 adapter 一个只覆盖一个厂商,这个不一样:只要设备的数据能进 MQTT,rha 就能读它、 控它。Zigbee2MQTT(几百款 Zigbee 设备)、Tasmota、ESPHome、以及任何自己往 broker 发 数据的东西,走的都是这一条。
代价是它不认识你的设备——协议本身只是「主题 + 字节」,哪条主题是温度、payload 怎么解,
必须你在 devices.toml 里说清楚。
[[device]]name = "thermo_desk"label = "书桌温湿度计"adapter = "mqtt"broker = "192.168.52.10:1883"password = "${MQTT_PASSWORD}"
state_topic = "zigbee2mqtt/thermo_desk"availability_topic = "zigbee2mqtt/thermo_desk/availability"availability_json_field = "state"
manufacturer = "Aqara"model = "WSDCGQ11LM"
[[device.entity]]name = "temperature"json_field = "temperature"type = "float"unit = "°C"class = "temperature"
[[device.entity]]name = "humidity"json_field = "humidity"type = "float"unit = "%"class = "humidity"
[[device.entity]]name = "battery"json_field = "battery"type = "int"unit = "%"class = "battery"这一台产出三个实体:thermo_desk.temperature、thermo_desk.humidity、
thermo_desk.battery。
一条主题一个 JSON,拆成几个实体
Section titled “一条主题一个 JSON,拆成几个实体”Zigbee2MQTT 的典型形态是一台设备一条主题,payload 是一个 JSON,温度湿度电量全在 里面:
zigbee2mqtt/thermo_desk {"temperature":21.5,"humidity":48.0,"battery":100,"linkquality":72}所以上面那份配置里三个实体共用同一个 state_topic(写在设备级),各自用 json_field
从里面挑一个字段。用不上的字段(linkquality)不声明就是了,不会报错。
不写 json_field 表示整条 payload 就是值——Tasmota 那种 stat/plug/POWER 直接发
ON 的形态就是这样。两种可以在同一台设备上混用,逐实体决定。
没有自动发现,这是有意的
Section titled “没有自动发现,这是有意的”rha 不会连上 broker 听 homeassistant/+/+/config 自己长出实体来。
原因是 rha check 的契约:它必须能在完全不碰网络的情况下校验整份配置,包括「规则
引用的实体存不存在」。实体清单如果是运行期从 broker 长出来的,rha check 就只能说
「不知道」——而那正是这个项目最不想要的那种答案。
不等于要你手抄。rha spec-gen 已经是同款先例(联网从米家云拉规格、生成
spec.d/ 让你 review 后入库),MQTT 的发现器按同一条路走,配置
结构就是照「能被自动生成」设计的。发现器本身还没做,现在得手写。
控制:settable 加命令主题
Section titled “控制:settable 加命令主题”只读是默认。要能写,实体上标 settable = true,并且有命令主题可用(设备级
command_topic,或实体自己覆盖):
[[device]]name = "plug_desk"adapter = "mqtt"broker = "192.168.52.10:1883"state_topic = "zigbee2mqtt/plug_desk"command_topic = "zigbee2mqtt/plug_desk/set"
[[device.entity]]name = "state"json_field = "state"type = "bool"settable = truerha set plug_desk.state on 会往 zigbee2mqtt/plug_desk/set 发 {"state":"ON"}——
带 json_field 就包成 JSON,不带就发裸的 ON。
ON / OFF 这两个串可以用实体上的 on / off 改(有些固件用 1 / 0、
true / false)。
可用性:设备自己说的,不是猜的
Section titled “可用性:设备自己说的,不是猜的”availability_topic 是设备(或网关)汇报在线状态的主题。收到 payload_not_available
(默认 offline)时,这台设备名下的实体全部标不可用——同一个 broker 上的其它设备
不受影响。
新版 Zigbee2MQTT 发的是 {"state":"online"},所以要配 availability_json_field = "state";
旧版发裸的 online,去掉那行即可。
不配 availability_topic 也能跑,只是失去了「设备离线」这个信号——broker 断了仍然会让
实体不可用,那一层是连接状态给的。
一个 broker 一条连接
Section titled “一个 broker 一条连接”broker 写在每台设备上,但相同的 broker + 账号 + client id 只会开一条连接。三十台
Zigbee 设备共用一个网关,broker 上看到的是一个 client,不是三十个。
broker 要写 host 或 host:port,不要带 mqtt:// 前缀(当前只支持明文 TCP,
带 scheme 会在 rha check 直接报错)。主题里不能有通配符——rha 只按精确主题收发,
配了 + 或 # 会当场报错而不是留一个永远收不到消息的实体。
推送型,不参与轮询分层
Section titled “推送型,不参与轮询分层”MQTT 里没有「问一遍」这个动作,值是设备自己发布上来的。所以它名下的实体
poll_tier 恒为空,rha status 不会把它们标成「每 300 秒轮询
一次」——那对一台从不被轮询的设备是错的说法。
代价是冷启动时可能一段时间没值(要等设备下一次发布)。缓解手段是让设备/网关把状态发成
retained,那样 rha 一订阅就立刻拿到当前值。Zigbee2MQTT 的 retain: true 就是干这个的。
设备级:
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
availability_json_field | string | 可省略 | 可用性 payload 是 JSON 时取哪个字段。 Zigbee2MQTT 1.30 起默认发 |
availability_topic | string | 可省略 | 设备在线状态的主题(Zigbee2MQTT 是 不配也能跑: 那样实体的可用性只跟着 broker 连接走 —— broker 断了标不可用, broker 在就一直可用, 哪怕那台 Zigbee 设备已经没电了。 |
broker* | string | — | broker 地址, 不要带 同一个 |
client_id | string | 可省略 | MQTT client id。不写就用 rha 启动时生成的 只有想让某几台设备走一条独立连接时才需要写(写了就是另一条连接)。 注意两个 client 用同一个 id 连同一个 broker 会互相踢下线, 所以别在两台 机器上写同一个固定值。 |
command_topic | string | 可省略 | 这台设备的默认命令主题, 各实体可以自己覆盖。只有 |
entity | array | 可省略 | 这台设备暴露哪些实体。至少要有一个。 |
keep_alive_secs | integer | 可省略 | 心跳间隔秒数, 默认 60。 |
manufacturer | string | 可省略 | 厂商, 只用于北向协议的「配件信息」显示。MQTT 协议本身不带这个信息,
不写就留空(HomeKit 那边会显示成 |
model | string | 可省略 | 型号, 用途同 |
password | string | 可省略 | broker 密码。建议写成 敏感值。建议写成 ${ENV} 由环境变量插值, 不要把明文提交进配置文件。该值绝不进日志与 trace。 |
payload_available | string | 可省略 | 可用性主题里表示"在线"的值, 默认 |
payload_not_available | string | 可省略 | 可用性主题里表示"离线"的值, 默认 |
state_topic | string | 可省略 | 这台设备的默认状态主题, 各实体可以自己覆盖。 Zigbee2MQTT 的形态是一条主题一个 JSON( 不能含 |
username | string | 可省略 | broker 账号。broker 开了匿名就不用写。 |
每个 [[device.entity]]:
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
class | string | 可省略 | 语义类, 决定这个实体能不能导出到 HomeKit 这类北向协议。 adapter 给的是默认值, 一个实体承载的语义。 刻意窄 —— 封闭枚举加一项就要动 core 和所有 bridge, 宁可少。刻意不照抄 HomeKit
的 Service 列表: |
command_topic | string | 可省略 | 覆盖设备级的 |
json_field | string | 可省略 | 从 payload 的 JSON 里取哪个字段; 不写表示整条 payload 就是值。 支持 字段不在这条消息里不算错(Zigbee2MQTT 的增量上报只带变了的字段); 但
payload 压根不是 JSON 对象会报错 —— 那是 |
max | integer | 可省略 | 数值实体的取值上界, 见 |
min | integer | 可省略 | 数值实体的取值下界, 与 北向 bridge 靠它把设备刻度换算成协议刻度 —— 一根 HomeKit 转速滑条固定是
0-100 的百分比, 不知道设备侧是 1-4 档还是 1-100 无级就没法换算。
想把某个实体标成 |
name* | string | — | 能力名, 也就是实体 id 点号后面那半( |
off | string | 可省略 |
|
on | string | 可省略 |
精确匹配, 不折叠大小写。门磁这类设备发的是 |
settable | boolean | false | 这个实体能不能写( 默认 false。设成 true 就必须能定出命令主题(实体级或设备级的 |
state_topic | string | 可省略 | 覆盖设备级的 |
type* | string | — | 这个值读成什么: payload 里那个值该被读成什么。 |
unit | string | 可省略 | 单位, 只是给人看的标签(北向 bridge 看的是 |
type 的取值:
| 取值 | 含义 |
|---|---|
bool | 布尔。payload 里几乎不会是 JSON 的 |
int | 整数。 |
float | 小数。整数形态的 payload( 与 |
string | 配置里写 |