NodeRed + HA + MQTT 一条链路看温度:从传感器到电子钟显示

2026-08-26

引子:电子钟上显示的温度,从哪来的?

客厅放了一个墨水屏电子钟,滚动显示日期、温度、湿度。每次路过瞄一眼,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 流

NodeRed 流:HA 事件 → function → MQTT 输出

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 物理显示 墨水屏刷新

如果想要扩展——比如再加一个甲醛传感器,或者把温度数据推送到时序数据库做趋势分析——只需要在链路中插入一个节点,其他环节完全不用动。这就是事件驱动架构的威力:每个环节解耦,数据在管道中流动,新需求只是加一个处理器,而不是重写整条链。