ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

ESPHome接入Home Assistant:原生API与MQTT选型与实操对比

ESPHome接入Home Assistant:原生API与MQTT选型与实操对比

ESPHome 接入 HA 这件事,玩智能家居的迟早都会撞上。真正动手做的时候,你会发现卡住你的往往不是硬件接线,而是“用原生 API 还是 MQTT”这个选择题。我在把几个温湿度传感器和门窗传感器接入 Home Assistant 的过程中,两种方式都完整跑通过,踩过不少坑,这里把选型逻辑和实操细节一次性讲清楚。

先说结论:这不是一个“谁替代谁”的问题,而是“你的场景更适合哪条路”的问题。原生 API 适合单机少量设备、追求低延迟和免维护的场景;MQTT 适合多设备、多系统协同、需要跨平台复用的场景。大多数人的第一个 DIY 项目,我建议直接从原生 API 入手,跑通之后再根据需求决定要不要加 MQTT。

这篇内容适合正准备做 ESPHome 接入、或者已经在 HA 里管理着几个 ESP 设备的玩家。我会从原理差异、配置步骤、调试技巧一直讲到常见故障排查,尽量让新手照着一路点下来就能通,也让已经在用的人能解决一些“说不清为什么”的怪问题。

1. 先搞清楚 ESPHome 在 HA 里的两种角色

1.1 原生 API 接入的本质:一朵云在你的局域网里

ESPHome 的原生 API,本质上是 ESP32/ESP8266 固件里内置了一个服务端。设备上电后会主动去连接 Home Assistant 的 ESPHome API 端口(默认 6053),连接建立之后,HA 就能实时收到设备状态。

这里有个关键点容易搞混:ESPHome 设备并不是通过 Home Assistant 的 IP 去“被连接”,而是设备自己作为客户端发起连接,然后 Home Assistant 作为服务端接收。这意味着只要设备在局域网里能访问到 HA 的 IP 和端口,就能自动注册、自动上报。很多人在配置时搞反了方向,以为是 HA 去连接设备,结果一直连不上。

原生 API 的数据传输走的是二进制协议,不是给人看的 JSON,也不是 MQTT 那种纯文本消息。它的设计目标就是“低开销、高频次、实时性高”。传感器数据默认按你配置的 update_interval 上报,状态变化时能瞬间推送到 HA,几乎没有感知延迟。也就是说,你按一下开关,HA 的仪表盘几乎同步变化,这是原生 API 最直接的优势。

另外,原生 API 还有一个隐形的便利:HA 能直接对设备下发配置和 OTA 更新(就是无线固件升级)。只要设备在线,你在 ESPHome 仪表盘里点一下升级,设备会自动接下载、烧录、重启,全程不用拔线插线。这部分体验远超 MQTT 方案。MQTT 方案下你要升级固件,基本得靠串口线,除非你自己写额外的 OTA 组件。

1.2 MQTT 方案的定位:一台消息中转总线

MQTT 本身不是一个设备协议,而是一个发布/订阅消息协议。它的核心是Broker(消息代理服务器),所有设备都连接这个 Broker,设备之间不直接互相通信,而是通过“话题(Topic)”来发布和订阅消息。

在 ESPHome 里启用 MQTT 后,设备本质上变成了一个 MQTT 客户端。它会向 Broker 发布传感器数据、状态主题,也通过订阅主题来接收指令。Home Assistant 那边也作为 MQTT 客户端连接到同一个 Broker,两边通过话题交换数据。

这样做的好处是显而易见的:设备与 HA 不再是强绑定。设备只要连得上 Broker,不管 HA 在不在线、HA 的主机 IP 有没有变,设备都照样运行和发布消息。哪怕你把 HA 换成另一套系统,甚至是自己写的一段 Python 脚本,只要它支持 MQTT,就能直接接上,全部逻辑都通过主题的发布和订阅来解耦。这种松耦合的结构,在多系统协同、设备需要被多个平台消费数据的场景下非常实用。

但代价也是直接换来的:实时性、配置复杂度、状态同步都会比原生 API 差半截。MQTT 消息是文本格式,解析和序列化有额外开销,设备状态变化到 HA 展示出来会有轻微的延迟,在高频传感器场景下会明显感觉到不够丝滑。另外,MQTT 需要你自己维护 Broker 服务,多了一个要保证运行稳定的组件,多了几个可能出问题的环节,这也是很多新手觉得 MQTT 难用的原因。

2. 原生 API 与 MQTT 的底层差异和真实体验

2.1 原生 API 的“在线状态”处理为什么这么顺滑

原生 API 模式下,HA 对设备“是否在线”的判断非常直接:TCP 连接活着就是在线,断开就是离线。这个逻辑天然可靠,极少误报。HA 的设备状态面板上,你能实时看到设备在线还是离线,不会出现设备明明在跑却显示离线的情况。

MQTT 模式下,在线状态通过Last Will and Testament(遗嘱消息)来实现。设备连接 Broker 时可以设置一条遗嘱主题,如果设备异常断开,Broker 会代发这条遗嘱消息;正常登出时会先发布一条清除消息。HA 端通过订阅这个主题来感知设备是否在线。听起来很完善,但实操中一旦网络不稳、设备掉电、Broker 配置有误,经常出现设备离线了但 HA 还显示在线,或者反过来,设备在线却显示离线。状态不同步的排查难度比原生 API 高不少,这个差异只有在真正开机用起来之后才会体会得到。

2.2 MQTT 的“保留消息”功能:一把双刃剑

MQTT 里有一个非常有用的特性叫Retained Message(保留消息)。发布消息时可以设置 retain 标志,Broker 会保存这条消息的最新值,当有新的订阅者上线时,会立刻收到这条保留消息。

这个特性在做状态同步时很方便:HA 重启后,设备已经上报的最后一条状态还能拿到,界面不至于白板一片。但副作用也很明显:一旦设置了保留消息,重启 HA 后可能拿到的是“陈旧的过期状态”,如果设备后来没有再上报过新数据,HA 端就一直显示停在旧值上。我之前就遇到过传感器已经断电一周,HA 界面还显示着正常温度的情况,排查半天才发现是保留消息的锅。

原生 API 没有这种问题,因为它的状态完全由当前连接和数据上报驱动,没有“历史缓存”的概念。这个差异在你做多设备管理和长期运行时特别明显,原生 API 的“无状态”模式反而更省心。

2.3 两种方式在一个表格里看个明白

对比项原生 APIMQTT
实时性高,状态变化秒级推送中等,受 Broker 转发和文本序列化影响
在线状态TCP 连接直判,可靠依赖遗嘱消息,偶尔会误判
配置复杂度低,刷完固件自动发现高,需要配置 Broker、话题、QoS、自动发现
扩展性仅面向 Home Assistant面向一切 MQTT 客户端,多系统互通
OTA 升级网页一键升级基本只能串口烧录
依赖组件无额外服务需要独立的 Broker 服务
适合场景单一 HA 体系内的 DIY 设备多系统复用、跨平台协作、需消息持久化
故障点少多一个 Broker 环节,排查链路更长

这个表基本能概括整体差异,但在真实项目中,感受最深的不是“实时性”那一两秒,而是“出问题时你需要检查多少环节”。原生 API 出问题,翻来覆去就是设备配置和网络;MQTT 一出问题,你得查设备端配置、Broker 日志、话题匹配、HA 自动发现配置,排查路径长得多。

3. 实操:原生 API 接入 HA 的完整流程

3.1 准备工作与固件烧录

要跑通原生 API,你需要准备:

  • 一块 ESP32 或 ESP8266 开发板(推荐 ESP32,资源更充裕)
  • 一个传感器模块,比如 DHT11/DHT22 温湿度传感器,或者简单的 GPIO 按键
  • 一个烧录工具,最常用的是浏览器插件方式,ESPHome 官方推荐 ESP Web Tools;老设备也可以用 esptool.py 命令行或者烧录工具打勾的方式
  • 已经部署好的 Home Assistant 实例,并安装了 ESPHome 插件或加载项

我以一块 ESP32 开发板加 DHT22 温湿度传感器为例,整个接入过程的重点在于固件配置文件的编写。

新建一个固件配置文件,名字叫dht_sensor.yaml,内容大致如下:

esp32: board: esp32dev framework: type: arduino sensor: - platform: dht pin: GPIO4 model: DHT22 temperature: name: "客厅温度" humidity: name: "客厅湿度" update_interval: 30s api: encryption: key: "YOUR_ENCRYPTION_KEY" wifi: ssid: "你的WIFI名" password: "你的WIFI密码"

这里有几个细节值得注意。api:区块默认开启,但建议加上encryption.key。这个 key 可以用命令行生成,也可以用在线生成器生成,它用于 API 通信的加密,防止局域网里的其他设备伪造数据。新版 ESPHome 如果没配 key 也能跑,但会有安全提示。如果配了 key,HA 端添加集成时需要填入同一个 key。

加密 key 的生成命令在各平台略有不同,我自己常用的是 Python 的一行命令:

python -c "import secrets; print(secrets.token_hex(32))"

烧录方式不多说了,浏览器 Web 工具一键烧录、命令行 esptool 都能用。烧录完成后,设备会自动连接 WiFi,并在 ESPHome 仪表盘里显示在线。

3.2 在 HA 中添加原生 API 设备

这步算是整个流程里最顺滑的环节。打开 HA 的“设置”-“设备与服务”,添加“ESPHome”集成,输入设备的 IP 地址或者 ESPHome 仪表盘里显示的 hostname 即可。如果配置了加密 key,这里要填一次,之后就不会再问了。

添加成功后,HA 会自动发现设备上的所有实体。我刚才那个配置里有两个传感器实体,HA 会直接生成“客厅温度”和“客厅湿度”两个实体卡片。你可以在“开发者工具-状态”里看到实体 ID,比如sensor.ke_ting_wen_du,也可以在仪表盘里直接添加卡片。

整个过程中的一个注意点:如果设备在 ESPHome 仪表盘里已显示在线,但在 HA 里添加时提示“连接失败”,大概率是 API 加密配置不一致或者防火墙拦截了 6053 端口。先检查这两处,不要急着重刷固件。

另外,设备接入 HA 后,ESPHome 会把所有实体自动创建好,包括设备诊断信息、重启按钮、WiFi 信号强度等,不需要你手动注册。这也是原生 API 比 MQTT 体验提升最明显的地方之一——MQTT 方案下你还要手动写 MQTT 发现配置,或者靠自动发现脚本生成实体,整体笨重不少。

3.3 原生 API 模式下做自动化控制的技巧

接入了原生 API 之后,你不仅可以看传感器数据,还可以做控制。ESPHome 里定义开关、灯、风扇等组件后,HA 会自动生成对应的控制实体。

比如配置一个 GPIO 开关:

switch: - platform: gpio pin: GPIO12 name: "书房台灯"

刷入固件后,HA 里直接会多出一个switch.study_desk_lamp实体,你可以用它做自动化:传感器检测到人在就开灯,人走 5 分钟后关灯。这种模式比 MQTT 方案少绕一个大弯,尤其在自动化条件联动的场景里,原生 API 的高实时性优势很突出。

我习惯的实践是:把设备本身的控制逻辑尽量在 ESPHome 固件里实现,HA 只做编排。比如传感器每隔 30 秒上报一次温度和湿度,但阀值判断在 HA 自动化里做,这样调整规则不需要重刷固件,可维护性更好。

4. 再说说 MQTT 桥接的完整实操方案

4.1 搭建 MQTT Broker 并让 ESPHome 接入

如果你决定上 MQTT,首先要有一个 Broker。HA 自带一个 MQTT 集成,但它不是一个独立的 Broker 进程,而是一个客户端。需要一个真正的 Broker,目前最常见的是 Mosquitto,可以单独装在服务器上,也可以作为 HA 的加载项安装。

安装完成之后,在 HA 里配置 MQTT 集成的连接信息,然后在 ESPHome 固件配置里增加mqtt:区块:

mqtt: broker: 192.168.1.100 port: 1883 username: your_username password: your_password topic_prefix: esphome/livingroom

topic_prefix是给当前设备所有话题做前缀隔离用的。在一个 Broker 上跑多个设备时,这个前缀能避免不同设备的话题互相冲突。建议每个设备一个独立前缀,比如esphome/livingroom、esphome/bedroom。

刷入固件后,你可以在 MQTT Explorer 之类的工具里看到设备发布的主题。DHT22 传感器会向esphome/livingroom/sensor/ke_ting_wen_du/state这样的主题发布温度值。这就是 ESPHome 的自动话题生成规则,层级分明,调试的时候看主题结构就能猜出数据含义。

4.2 让 HA 发现并管理 MQTT 设备

MQTT 和原生 API 最大的体验落差在设备发现上。原生 API 接入后 HA 自动认设备,MQTT 则需要依赖MQTT Discovery机制。

MQTT Discovery 的原理很简单:设备上电后向一组特定的主题发布“配置消息”,消息内容是一个 JSON,描述了实体类型、名称、状态主题、可用性主题等信息。HA 订阅这些主题后自动解析并创建设备实体。ESPHome 默认就会发布这些 Discovery 消息,所以只要 MQTT 配置正确,HA 依然能自动发现设备,只不过中间多了一次中转。

自动发现依赖 JSON 配置的正确性,一旦设备状态主题写错、可用性主题配置不当、或者 QoS 设置无法对齐,就会造成“设备灯亮着但 HA 找不到实体”的情况。最常见的彩蛋:HA 里添加了 MQTT 集成,但没有启用 MQTT Discovery 开关,导致设备消息全浮在 Broker 上,但 HA 毫无感知。这个开关不少人都踩过坑,因为 HA 的 MQTT 集成默认不自动开 Discovery,需要手动在集成选项里勾选。

4.3 让原生 API 和 MQTT 同时存在的混合玩法

很多人以为原生 API 和 MQTT 二选一,其实 ESPHome 支持两种方式同时启用。固件配置里同时写api:和mqtt:区块,两者互不冲突,设备会同时向 HA 的 API 连接和 Broker 发布消息。

我自己就是这么干的,原因是:传感器数据走原生 API 保证 HA 界面的实时性,同时在 MQTT 上保留一份发布备份,方便其他脚本或者将来接别的系统用。HA 自动化用原生 API 的瞬时状态做判断,而在做数据采集、入数据库或者接入其他统计系统时,用 MQTT 的持久消息更稳定。

不过要提醒一句,混合模式会占用双倍的消息通道,设备也会同时维护两条连接。对 ESP32 来说基本无压力,但 ESP8266 这种只有 2MB Flash、内存也小的芯片,跑双通道会有一定风险,偶尔会因为资源紧张导致 WiFi 不稳定。如果你用 8266 且内存紧张,优先只用一种方式。

5. 常见问题与排查技巧实录

5.1 设备反复掉线、HA 里状态一跳一跳的

这是 ESPHome 玩家最常遇到的“鬼问题”。如果用的是原生 API,设备在 ESPHome 仪表盘里在线,但 HA 里实体状态却频繁“不可用”,大多数情况不是配置问题,而是网络稳定性问题。ESP 设备的 WiFi 信号弱,或者路由器开了“5G 优先”导致设备频繁漫游,都会造成 TCP 长连接断断续续。

排查步骤我可以直接给一个顺序:

  1. 在 ESPHome 日志里看有没有Connection lost之类的心跳超时记录。
  2. 检查设备所在位置的 WiFi 信号强度,用wifi组件的信号实体查看。
  3. 如果信号弱,加一个 WiFi 中继或者换一块增益天线,不要盲目调固件参数。
  4. 在配置里加上api: reboot_timeout: 30s,让设备在连接断开过久后自动重启,这个参数能减少卡死概率。
  5. 如果还不行,检查路由器是否开了 AP 隔离,开了就把 ESP 设备加到同一个网段。

MQTT 环境下,掉线问题还要多排查一步:看设备是不是在特定时间被 Broker 踢掉了。Mosquitto 默认允许的连接数有限,如果连接数满了,设备连接会被拒绝,表现就是设备日志不断重连,HA 里设备一会出现一会消失。调大max_connections或者清理掉一些废弃客户端连接就能解决。

5.2 多设备场景下的资源占用:8266 真的够用吗

我之前在 5 个 ESP8266 设备上同时开启原生 API 和 MQTT,运行一周后发现,有两块设备的日志里频繁出现 WiFi 重连记录,而把 MQTT 关掉只保留 API 后,问题就消失了。

这说明 8266 的资源在这两种协议同时启用时确实吃紧。ESP8266 的 RAM 只有大约 80KB 可用空间,固件本身加上 TCP 栈、WiFi 协议栈,再跑双通道,确实容易到临界点。ESP32 就好很多,RAM 资源宽裕,双通道毫无压力。

建议是:如果全是 8266 且网络环境一般,老老实实只选一种方式。如果确实要双通道,建议固件优化一下:把sensor的update_interval调长到 60 秒以上,减少数据频率,降低 CPU 和射频负载。

5.3 几个容易踩的配置细节

加密 key 不一致:原生 API 配了 encryption key 后,HA 添加集成时如果 key 填错,会一直提示认证失败,这个提示非常误导,因为它显示的是“连接失败”而不是“key 错误”。解决方法是到 ESPHome 配置文件里重新生成 key,再重新添加集成。

MQTT 主题里的特殊字符:设备名称如果含有中文或者特殊字符,MQTT 主题依然能发布,但你在终端里用工具订阅时可能会遇到显示问题。我的习惯是:设备名称尽量用拼音或英文,中文名称放到 HA 的设备自定义名里。

重启后 IP 变了:ESP 设备上电后会 DHCP 获取地址,如果路由器分配的租约时间短,或者设备重启频繁,IP 可能变化。这对原生 API 没影响(HA 通过 hostname 找设备),但如果你自己写脚本连接 MQTT Broker 时用了 IP 过滤,就要注意动态 IP 的变化。

5.4 固件升级踩坑:MQTT 模式下的 OTA 不是天然可用的

前面提过,原生 API 模式下 OTA 升级非常流畅,但 MQTT 模式下我遇到过一次设备升级失败。原因是 MQTT 模式下 OTA 需要额外的配置,你可以通过ota:区块配合 MQTT 来实现远程升级,但前提是设备能订阅相应的主题。如果同时启用了原生 API,最简单的做法是:OTA 升级时只用 API 通道,升级完再切回 MQTT 或双通道。

有同学可能会问,为什么 MQTT 模式下 OTA 默认不行?因为 OTA 本质是 HTTP 协议的文件传输,ESPHome 的 MQTT 组件最初只负责消息发布和订阅,不天然支持文件传输。后来靠ota:配置和特定主题的配合才能实现。所以如果你主要用 MQTT,又不想每次拔线,最好在固件里显式加上 OTA 配置,否则就只能接受串口烧录的现实。

5.5 自动发现不生效:大概率是“DSN”或“标识符”重复

MQTT Discovery 机制里,每个设备都需要一个唯一的 identifier。ESPHome 默认自己生成标识符,一般不会重复。但如果你从别处复制配置,或者同一块开发板在多个项目里复用,就可能出现两个设备用了相同的标识符,导致 HA 里出现“设备冲突”,自动发现只能识别到其中一个。

我在一次迁移项目时中过招:旧设备删掉后没清干净 Broker 里的保留消息,新设备上线又用了一样的前缀,结果 HA 里一直显示旧实体的幽灵状态。清理办法:到 MQTT Explorer 里订阅homeassistant/#主题,把所有相关装置响应的保留消息清掉,再重启设备。这个操作能解决大多数自动发现不全的问题。

6. 个人经验总结:我的设备选择思路

跑通了两种方案之后,我现在对新增设备有了一套明确的选型逻辑。

如果这个设备只有 HA 一个消费端,且放在固定位置、对实时性有要求,我直接选原生 API,原因是配置量最小、出故障概率最低、日常维护零成本。

如果这个设备的数据要共享给多个系统,比如我自己的监控脚本、手机端的 MQTT Dashboard、还有其他自动化平台,我优先走 MQTT。

如果设备本身比较高级,比如需要外部系统(Node-RED、ESP32-CAM 等)协同处理,我更倾向用 MQTT 的松耦合结构,因为解耦之后,任何一端出问题都不至于牵连其他组件。

最后再分享一个我个人的搭配:重要传感器(门磁、人体传感器)用原生 API 接 HA,因为自动化联动必须实时、可靠;数据采集类传感器(温湿度、光照度)同时开 MQTT 和 API,一方面保证 HA 界面流畅,一方面方便做长期日志趋势分析。要是你手头正好在纠结这两种协议,不妨先按这个思路做起来,跑一段时间再根据实际体验调整,毕竟 DIY 的乐趣本来就是折腾出来的。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进