跳转到内容

MQTT(Home Assistant 自动发现)

把 rha 的实体发布出去。主要用途是 Home Assistant:桥会发一份符合 HA 自动发现约定的 配置,HA 那边零手工配置就能认出全屋设备。任何订阅得了 MQTT 的东西(Node-RED、Grafana、 自己写的脚本)同样能直接用。

[[bridge]]
name = "ha"
type = "mqtt"
broker = "192.168.52.10:1883"
password = "${MQTT_PASSWORD}"
export = ["plug_desk.*", "thermo_desk.*"]
exclude = ["*.indicator"]

name / export / exclude / enabled 是所有 bridge 共有的字段,说明在 bridges.toml;本页只讲 type = "mqtt" 的私有参数。

最小配置只要 broker 一行,其余全有缺省。

rha/<设备>/<能力>/state 实体当前值
rha/<设备>/<能力>/set 往这里发就是下命令(仅可写实体)
rha/<设备>/<能力>/availability 这个实体可不可用
rha/status 桥自己在不在

前缀 rha 由 base_topic 决定。

一个实体一条主题,不是一台设备一条 JSON。 三条理由,按重要性:

  1. rha 的状态单位就是实体。一台设备一条 JSON 意味着任何一个字段变化都要把整台设备的当前 值拼一遍发出去——而其中一部分可能是快照恢复的旧值、甚至根本 还没有值。发出去就等于替设备编了一份它没说过的完整状态。
  2. HA 的自动发现里每个实体各有自己的 state_topic。一条 JSON 就得给每条配置带 value_template,而模板写错是静默的(HA 那边只表现为值永远 unknown)。
  3. 一个实体的读 / 写 / 可用性共用同一个前缀,broker 侧做 ACL 时一台设备一条规则就够。

桥自身的可用性是两段(rha/status)、实体主题是四段,撞不上——哪怕真有一台设备叫 status,它的实体主题也是 rha/status/<能力>/state。这个形状照抄 HA 自己的 homeassistant/status。

discovery = true(缺省)时,桥会往 homeassistant/<组件>/<base_topic>/<设备>_<能力>/config 发一份 retained 的配置。组件类型 由实体的语义类推出来——温度是 sensor、插座是 switch、 档位是 number、门铃是 event。

device 块用的是 devices.toml 里的 label / manufacturer / model,所以在 HA 里 看到的是「书桌插座 / Xiaomi / cuco.plug.v3」而不是一串 id。

单位取的是语义类的规范单位而不是 adapter 的方言:miot 报的 celsius 到 HA 那边是 °C。

改过 HA 的发现前缀才需要动 discovery_prefix。不想让 HA 掺和(只想拿数据喂 Grafana) 就 discovery = false,状态照发。

留存与遗嘱:断电之后还说得清

Section titled “留存与遗嘱:断电之后还说得清”

发现配置和实体状态都是 retained(broker 替你记着最后一条)。不这样的话 HA 一重启就 把全屋 rha 实体丢了,要等每个实体各自下一次状态变化才逐个回来——温度计可能几分钟,门磁 可能几天。

瞬时事件(门铃按下这类)刻意不留存:它是「刚刚发生了一件事」,留存意味着任何新连上来 的客户端都会收到一次,等于门铃自己响了。

桥还挂了一条遗嘱(last will):连接非正常断开时由 broker 代发 rha/status = offline。 没有它,rha 被 kill -9 或者断网时 HA 那边会显示一切正常,实际早就没人在发数据了。优雅 关停时桥会自己先发一条 offline——正常断开会让 broker 丢掉遗嘱,那条必须自己补。

发现配置 retained 意味着 broker 会永远替你记着。把一个实体从 export 里去掉之后, 那条配置还在,HA 每次重启都会重建一个再也收不到值的幽灵实体。

所以桥在自己的状态目录(<配置目录>/bridges/<name>/)里记了一份「上次宣告过哪些主题」, 下次启动往消失的那些发一条空 payload 把配置撤回。删掉那个文件不会出错,只是那一批旧配置 从此没人撤得掉,得自己去 broker 上清。

都会在 rha check 阶段点名报出来,不会静默丢:

  • 字符串档位(空调伴侣的 "small_fan" / "large_fan" 这种)。HA 的 select 需要一份 选项列表,而 rha 的取值域目前只装得下整数。
  • 没有取值范围的数值档位。HA 的 number 必须带 min / max,硬给一个 0–100 是撒谎 ——一台 1–4 档的风扇会画出一根拖到 57 就失败的滑条。

用 exclude 排掉它们即可。

如果你同时用了 mqtt adapter 读设备,两边连的是同一个中枢。 不过桥有自己的 client id(缺省 <base_topic>-bridge),所以在 broker 上是一条独立连接 ——它必须独立,遗嘱是逐连接的属性。

字段类型默认说明
base_topicstring可省略

所有主题的前缀, 缺省 rha。发出去的主题形如 <base_topic>/<设备名>/<能力名>/state。

只能是一段, 且只能用 [A-Za-z0-9_-]: 它同时被当作 Home Assistant 的 node_id 与 unique_id 前缀用出去, 而 HA 对这两处的字符集就是这么规定的。 写了别的字符是加载期错误, 不会被悄悄替换掉 —— 悄悄替换的话主题和 HA 里的 实体 id 会对不上, 而两边都不报错。

broker*string—

broker 地址, host 或 host:port(端口默认 1883)。

不要带 mqtt:// / tcp:// 前缀 —— 带了是 rha check 期硬错误。当前只有 明文 TCP, 没有 TLS(理由见 rha-mqtt 的说明: TLS 会把 musl 全静态变体搞坏)。

client_idstring可省略

本桥的 MQTT client id, 缺省 <base_topic>-bridge。

桥必须有一个与南向设备不同的 client id: 遗嘱(last will)是连接级属性, 而 rha-mqtt 按 client id 等参数复用连接 —— 撞上一台南向 MQTT 设备的连接 就意味着谁先连上谁的遗嘱说了算。缺省值已经保证了这一点, 除非你手动把它改成 与某台设备相同。

同一个局域网里跑两套 rha 指向同一个 broker 时必须给它们不同的值(或者不同的 base_topic), 否则两个会话会在 broker 上互相踢下线。

discoveryboolean可省略

要不要发自动发现配置, 缺省开。

关掉之后状态照发, 只是 HA 不会自动认出实体(得自己写 YAML)。给的是 「只想喂 Node-RED / Grafana, 别往 HA 里塞东西」这种场景。

discovery_prefixstring可省略

Home Assistant 的自动发现前缀, 缺省 homeassistant。只有改过 HA 那边 discovery_prefix 设置的人需要动它。

keep_alive_secsinteger可省略

心跳间隔秒数, 默认 60。

passwordstring
Secret
可省略

broker 密码。建议写成 ${MQTT_PASSWORD} 由环境变量插值。

敏感值。建议写成 ${ENV} 由环境变量插值, 不要把明文提交进配置文件。该值绝不进日志与 trace。

usernamestring可省略

broker 账号。broker 开了匿名就不用写。