引子:电子钟上显示的温度,从哪来的?
客厅放了一个墨水屏电子钟,滚动显示日期、温度、湿度。每次路过瞄一眼,28.3°C、65.5%——数据很平常。但这条数据链路并不简单:
温度传感器 → Zigbee → Xiaomi 网关 → 小米云 → Home Assistant → NodeRed → MQTT Broker → ESP-D1 → 墨水屏
中间跨了 4 台设备、3 个协议,全程无人工干预,每 5 分钟刷新一次。这篇文章把这条链路拆开,每个环节怎么配、数据长什么样,全都写清楚。
整体架构
┌─────────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐
│ 温度传感器 │ ──→ │ Xiaomi 网关 │ ──→ │ HA 状态 │ ──→ │ NodeRed │ ──→ │ MQTT Broker │
│ (Zigbee) │ │ (网关 IP) │ │ REST API │ │ (处理流) │ │ (1883) │
└─────────────┘ └──────────────┘ └──────────┘ └──────────┘ └──────┬───────┘
│
▼
┌──────────────┐
│ ESP-D1 电子钟 │
│ (墨水屏) │
└──────────────┘

Step 1:传感器接入 Home Assistant
设备清单
| 传感器 | 位置 | 当前值 | 接入方式 |
|---|---|---|---|
| 米家蓝牙温湿度计 3 | 客厅 | 28.3°C / 65.5%RH | Xiaomi Miot Auto |
| 米家蓝牙温湿度计 3 | 主卧 | 25.0°C / 69.4%RH | Xiaomi Miot Auto |
两个传感器都是通过 Xiaomi Miot Auto 集成接入 Home Assistant 的。HA 运行在 Docker 中,容器名为 Home-Assistant-Core。
验证传感器数据
接入后,HA 会自动创建多个 sensor 实体,温度传感器的 entity_id 格式是 sensor.<设备名>_temperature:
# 通过 HA REST API 查看客厅温度
curl -s -H "Authorization: Bearer $HA_TOKEN" \
"http://your-ha-server:8123/api/states/sensor.miaomiaoc_cn_blt_3_19p32ttas5g00_t2_temperature_p_2_1"
# 返回示例
{
"entity_id": "sensor.miaomiaoc_cn_blt_3_19p32ttas5g00_t2_temperature_p_2_1",
"state": "28.3",
"attributes": {
"unit_of_measurement": "°C",
"device_class": "temperature",
"friendly_name": "客厅温湿度计 温度湿度传感器 温度"
},
"last_updated": "2026-08-25T15:54:13.093593+00:00"
}
注意:小米温湿度计通过蓝牙连接 Xiaomi 网关,再经小米云同步到 HA。如果米家 App 里能看到数据,HA 中就能读到。数据更新频率约 5 分钟一次。
Step 2:NodeRed 接收 HA 事件
NodeRed 运行在 Docker 中(容器名 NodeRed),负责接收 HA 的状态变更事件,处理数据后推送到 MQTT。
安装 NodeRed
# Docker 方式部署
docker run -d \
--name NodeRed \
--restart unless-stopped \
-p 1880:1880 \
-v /path/to/nodered/data:/data \
nodered/node-red
# 安装 HA 节点包
# 进入容器
docker exec -it NodeRed /bin/bash
# 在容器内安装
cd /data
npm install node-red-contrib-home-assistant-websocket
节点配置
关键流程只有 4 个节点:
[server-state-changed] → [function] → [mqtt out] → [ESP-D1]
① 事件触发节点:server-state-changed
监听 HA 中温度传感器的状态变更事件。配置:
- Server:HA 连接(配好 Token 和 URL)
- Output as:single entity
- Entity ID:留空(监听所有温度传感器)
② 数据处理节点:function
NodeRed 收到 HA 事件后,payload 是一个 JSON 对象,包含 new_state、old_state、entity_id。需要提取温度值,按 ESP-D1 的格式打包。
NodeRed 收到的原始数据示例:
{
"entity_id": "sensor.miaomiaoc_cn_blt_3_19p32ttas5g00_t2_temperature_p_2_1",
"new_state": {
"state": "28.3",
"attributes": {
"unit_of_measurement": "°C",
"friendly_name": "客厅温湿度计 温度"
}
},
"event": "state_changed"
}
function 节点代码:
// 识别是哪个传感器
var entity_id = msg.payload.entity_id;
var temp = parseFloat(msg.payload.new_state.state);
// 跳过非温度事件
if (isNaN(temp)) return null;
// 根据 entity_id 判断设备位置
var location = "未知";
if (entity_id.includes("19p32ttas5g00")) location = "客厅";
else if (entity_id.includes("1fra6pbmgkc00")) location = "主卧";
// 构造 ESP-D1 需要的显示格式
msg.payload = {
time_logo_id: 6,
time_logo_show: true,
display_text: location + " " + temp.toFixed(1) + "°C"
};
return msg;
③ MQTT 输出节点:mqtt out
配置:
- Server:MQTT Broker(your-mqtt-server:1883)
- Topic:esp-d1/display_json
- QoS:1
完整的 NodeRed 流

Step 3:MQTT Broker 中转
MQTT Broker 是 Mosquitto,同样运行在 Docker 中。ESP-D1 电子钟订阅了 esp-d1/display_json 主题,NodeRed 发布消息后,Mosquitto 负责转发。
验证消息
# 订阅主题,检查消息是否到达
mosquitto_sub -h your-mqtt-server -p 1883 -t "esp-d1/display_json"
# 收到消息示例
{"time_logo_id": 6, "time_logo_show": true, "display_text": "客厅 28.3°C"}
配置说明
| 参数 | 值 |
|---|---|
| Broker 地址 | your-mqtt-server:1883 |
| 协议 | MQTT v3.1.1 |
| QoS | 1(至少一次投递) |
| 认证 | 无(内网环境) |
⚠️ 安全注意:内网 MQTT 不要暴露到公网。如果必须远程访问,用 TLS + 强密码,或通过 VPN 隧道转发。
Step 4:ESP-D1 电子钟显示
硬件
ESP-D1(ESP8266 开发板) + 墨水屏(E-Paper)模块。
固件逻辑
ESP-D1 订阅 esp-d1/display_json 主题,收到 MQTT 消息后解析 JSON,更新墨水屏显示。
// Arduino 伪代码
void callback(char* topic, byte* payload, unsigned int length) {
String json = String((char*)payload).substring(0, length);
// 解析 JSON
int logoId = getJsonValue(json, "time_logo_id");
bool showLogo = getJsonValue(json, "time_logo_show");
String displayText = getJsonValue(json, "display_text");
// 更新墨水屏
epaper.clear();
if (showLogo) drawLogo(logoId);
drawText(displayText, 20, 60);
epaper.display();
}
数据格式
| 字段 | 类型 | 说明 |
|---|---|---|
time_logo_id |
int | 图标 ID(6=点阵数字) |
time_logo_show |
bool | 是否显示图标 |
display_text |
string | 要显示的文本 |
显示效果
电子钟平时显示日期时间。当温度传感器上报新数据时,NodeRed 触发流,通过 MQTT 推送温度信息到 ESP-D1,墨水屏上滚动显示 客厅 28.3°C 或 主卧 25.0°C。
全链路验证
用一条命令从传感器跟到屏幕:
# Step 1: 查 HA 状态
curl -s -H "Authorization: Bearer $HA_TOKEN" \
"http://your-ha-server:8123/api/states/sensor.miaomiaoc_cn_blt_3_19p32ttas5g00_t2_temperature_p_2_1" \
| jq '.state'
# Step 2: 查 NodeRed 流(通过 HA 事件触发,自动处理)
# 查看 NodeRed 日志
docker logs NodeRed --tail 20
# Step 3: 查 MQTT 消息
mosquitto_sub -h your-mqtt-server -p 1883 -t "esp-d1/display_json" -C 1
# Step 4: 看屏幕 —— 物理观察
踩坑记录
1. ESP-D1 的 Logo ID 映射
时间 logo 的 ID 映射不是从文档里抄来的,是一步步试出来的。发错 ID 设备不会响应,但 MQTT publish 返回成功(QoS=1),所以不能靠 publish 返回值判断。
正确做法:先看 smart-home 技能的参考文档,确认 topic 和 payload 格式再发。
2. NodeRed 的 HA 节点版本
NodeRed 的 HA 节点 node-red-contrib-home-assistant-websocket 需要 WebSocket 连接。如果 HA 用了反向代理或 TLS,需要确保 WebSocket 路径正确。
3. 温度刷新频率
小米温湿度计默认 5 分钟上报一次,这不是 NodeRed 或 HA 的问题,是传感器自身的省电策略。需要实时数据的场景(如温室监控),建议用有线传感器。
4. 传感器 entity_id 包含设备型号
miaomiaoc_cn_blt_3_19p32ttas5g00_t2_temperature_p_2_1 这个长字符串里有设备型号和 MAC 特征。function 节点中通过 includes() 匹配特征字符串来识别位置,而不是硬编码完整 ID,这样更换传感器时只需改匹配规则。
总结
这条链路的关键在于 每一步都只做一件事,数据格式清晰可溯源:
| 环节 | 职责 | 数据格式 |
|---|---|---|
| 传感器 + Xiaomi 网关 | 采集物理数据 | Zigbee → 蓝牙 |
| Home Assistant | 聚合、存储、API | REST JSON |
| NodeRed | 数据处理、格式转换 | JavaScript 对象 |
| MQTT Broker | 消息路由 | JSON 字符串 |
| ESP-D1 | 物理显示 | 墨水屏刷新 |
如果想要扩展——比如再加一个甲醛传感器,或者把温度数据推送到时序数据库做趋势分析——只需要在链路中插入一个节点,其他环节完全不用动。这就是事件驱动架构的威力:每个环节解耦,数据在管道中流动,新需求只是加一个处理器,而不是重写整条链。