跳转至

一次 MQTT 自动发现故障的完整复盘

这一篇复盘「Home Assistant 里的设备突然消失或重名,MQTT 自动发现的主题发生漂移」的典型故障。你的设备可能是别的协议、可能是别的 broker,但诊断思路通用:先抓主题看设备到底在不在发布,再对比自动发现 topic 与设备实际 topic,最后别被 retained 残留骗了。

故障概述

早上,HA 面板上一批 Zigbee 设备突然消失,另几个出现重名实体。设备本身通电正常、网关也在线,但实体要么不见、要么两个同名。用 mosquitto_sub -t '#' -v 一抓——设备其实还在发布,只是发布的 topic 前缀变了,自动发现订阅的旧主题收不到消息,于是 HA 认为设备离线。

影响

  • 消失的设备无法控制(开关、状态都不更新),自动化依赖这些实体的全部失效。
  • 重名实体导致 HA 自动生成的实体 ID 冲突,部分控制动作指向了错误的设备。
  • 持续约 1.5 小时,从发现到恢复发现订阅。
  • 因为设备「看起来还在」,网关状态灯正常,很容易误判成网关故障。
  • 连带损失:靠这些实体触发的场景(如离家自动关灯、温度联动)在这 1.5 小时里全部静默失效,用户感知是「自动化好像不灵了」。

时间线

时间 事件
T+0 用户反馈一批设备从 HA 面板消失
T+15 确认设备通电、网关在线,排除硬件掉线
T+30 mosquitto_sub -t '#' -v 抓主题,发现设备仍在发布
T+45 对比 discovery topic 与设备实际 topic,确认主题前缀漂移
T+60 定位到网关固件更新改了 base topic,开始适配
T+80 更新 HA 配置、清理残留 retained、重启发现订阅
T+90 设备实体恢复,重名消除,复盘收尾

根因

真正的触发点是网关固件更新改变了 MQTT base topic:更新后,设备的发布主题从 zigbee2mqtt/<device> 漂移成了 zigbee2mqtt/<device>/state 且 discovery 前缀也变了,HA 里订阅旧主题的自动发现就再也收不到 config/state 消息。叠加两个放大器:

  1. retained 残留:旧配置(homeassistant/.../config)的 retained 消息没清理,HA 重连后重放出旧实体,和新实体形成重名/幽灵。
  2. 发现订阅冲突:自动发现的 discovery topic 和手动配置的固定 topic 存在重叠,固件更新后两者对不齐,HA 部分实体吃到了错误 payload。

关键判据(也是复盘里的分水岭):mosquitto_sub -t '#' -v 显示设备在发布,而 HA 认不到——问题在发现/订阅链路,不在设备。

诊断里最容易踩的几个坑

这次拖慢恢复的不是「查不到」,而是误判成硬件或网关故障。几个真实教训:

  1. 先抓主题,再动网关。设备消失的第一反应容易是「网关坏了」。先用 mosquitto_sub -t '#' -v 抓 30 秒,确认设备还在发布——在发布就不是硬件问题。
  2. 别只看 HA 界面。HA 面板「实体消失」只说明 HA 收不到或认不到,不代表设备离线。要区分「设备不发布」和「HA 不认」,抓主题是唯一办法。
  3. retained 会重放旧配置。homeassistant/# 的 retained 消息在每次订阅者重连时重放,旧实体因此「复活」成幽灵。清残留 retained 要清干净,否则反复出现。
  4. 固件更新是常见诱因。设备/网关固件升级经常改 topic 结构或 discovery 格式。升级前先记录当前 topic,升级后对比,一眼看出漂移。
  5. 重名未必是 bug。同名实体也可能来自「手动配置 + 自动发现」都建了同一个设备,或 retained 与实时消息并存。查 payload 的来源,别急着删实体。

修复

一步步定位,从抓主题开始:

# 1) 抓全量主题,看设备还在不在发布(-v 显示 topic 与 payload)
mosquitto_sub -t '#' -v -C 30
# 设备 topic 有消息 → 在发布,不是硬件掉线

# 2) 抓自动发现 topic,对比 config/state
mosquitto_sub -t 'homeassistant/#' -v -C 30
# 看 discovery topic 与实际设备 topic 是否对得上

# 3) 看 retained 残留(订阅者重连时会被重放)
mosquitto_sub -t 'homeassistant/#' -v -C 30 --retained-only 2>/dev/null || \
mosquitto_sub -t 'homeassistant/#' -v -C 30   # 无 retained-only 选项时用这个看重放

临时止血:清理残留 retained + 重启发现订阅,让 HA 重新发现:

# 清掉旧 discovery 的 retained 残留(用空 payload 覆盖清除)
mosquitto_pub -t 'homeassistant/sensor/<old_device>/config' -n -r
# 确认清了
mosquitto_sub -t 'homeassistant/#' -v -C 10
# 触发重新发现:重启 HA 的 MQTT 集成或整个 HA

彻底解决:统一主题前缀 + 配置适配 + 版本化。查固件变更日志确认新的 base topic,更新 HA 配置适配新前缀,并建立主题命名规范:

# zigbee2mqtt 示例:明确统一的前缀与 discovery 配置
# 在 HA configuration.yaml 里显式写死 base topic,避免自动漂移
mqtt:
  discovery: true
  discovery_prefix: homeassistant

防再发

这次事故暴露的三件事,逐条上护栏:

  1. 主题命名规范:写死 base topic 与 discovery 前缀,固件升级前先记录当前主题,升级后对比;把「topic 变更」纳入升级检查清单。
  2. retained 清理策略:给 discovery/state 主题建立「过期清理」机制,定期清掉不再使用的 retained 消息,防幽灵实体堆积。
  3. MQTT broker 监控:监控订阅数($SYS/broker/clients/connected)与消息速率($SYS/broker/publish/messages/received),异常(掉线批量下降、重连风暴暴涨)提前告警。
  4. 配置版本化:把 HA 的 MQTT/zigbee2mqtt 配置纳入版本管理(git),升级前后可 diff,出问题秒回滚。
  5. 故障演练:定期模拟「topic 漂移」,验证抓主题定位、retained 清理、发现订阅重启的流程能在预期时间内完成。

复盘结论

设备消失先抓主题——mosquitto_sub -t '#' -v 确认设备还在不在发布,在发布就不是硬件问题。诊断顺序永远是:抓全量主题 → 对比 discovery topic → 查 retained 残留 → 适配新前缀。最容易被忽略的是 retained 残留:它会在重连时重放旧配置,制造重名和幽灵实体。

复盘到最后,最值钱的一句话是:监控的对象错了,反应就永远慢半拍。 我们以前「看 HA 面板」,它只在实体消失时才有信号;这次之后我们「监控 MQTT 订阅数与消息速率」,它能在 topic 漂移刚开始时就把问题暴露出来。把监控前移一层,往往比多写几段告警规则更能缩短 MTTR——这也是这次事故给我最大的收获:不是技术多难,而是把「该看的地方」看对。


关联阅读:[HA MQTT 自动发现../../homeassistant/home-assistant-mqtt-auto-discovery.md) · [MQTT 配置../../homeassistant/mqtt-setup.md) · [Zigbee2MQTT 网关配置../../homeassistant/zigbee2mqtt-gateway-config.md)