Mosquitto MQTT + ESP32-P4 面板接入 Home Assistant 实录

本文基于 2026-10-18 的一次真实接入整理,Broker、HA 集成与端到端控制均已在实机跑通。
环境:NanoPi R5C / Armbian 13 / Docker 29.9 / Home Assistant 2026.10.2 (Container),局域网 192.168.*.30
成果:Mosquitto 2.x 常驻运行,匿名访问关闭,ha / p4 双账号最小权限,78 条状态主题 retained,P4 发一条 toggle 到实体动作全程约 4 秒
说明:文中密码、访问令牌、设备 UID 均已替换为占位符,内网 IP 以 192.168.*.* 掩码表示,照抄前请换成自己的值


目录


1. 目标:让面板和 HA 说上话

手上有一块 JC-ESP32P4-M3 触摸屏开发板,想把它当作家里的中控面板:按钮点一下能控制 HA 里的设备,屏幕上还能显示设备的当前状态。

方案没有走 HA 的 REST API,也没有在 P4 上跑复杂的 HTTP 客户端,而是用最省事的 MQTT:局域网里跑一个 Mosquitto,HA 和 P4 各自用独立账号连上去,靠主题约定分工。

需求 实现方式
HA 与 P4 通讯 局域网 Mosquitto Broker(1883),双方各一个账号
P4 控制 HA 设备 P4 发布 p4/cmd/<domain>/<object>/<service>,HA 自动化转发成服务调用
P4 显示设备状态 HA 的 mqtt_statestream 把状态镜像到 ha/<domain>/<object>/state(retain)
不让 P4 越权 ACL:p4 只能读 ha/#、读写 p4/#

完成状态

项目 状态
Mosquitto 2.x 容器 ✅ 已运行,随 Docker 自启
匿名访问 ✅ 已禁用(实测被拒)
账号 ha / p4 与 ACL ✅ 已生效(实测越权被断连)
局域网 1883 可达 ✅ Windows 侧直连成功
HA MQTT 集成 ✅ 已添加,状态 loaded
mqtt_statestream 状态镜像 ✅ 78 条 retained(3 climate / 2 light / 73 switch)
P4 → HA 控制桥 ✅ 实测:MQTT 发 toggle,实体 on→off→on
P4 ESPHome 包与示例 📄 已备好,待烧录

凭据文件(密码明文)放在仓库之外,例如 D:\HA\mqtt-credentials.txt 之类的本地文件,不要提交到 Git。本文示例里一律写成 <...> 占位符。


2. 拓扑与端口规划

1
2
3
4
5
6
7
8
                 ┌──────────────────────────────┐
│ NanoPi R5C 192.168.*.30 │
│ │
ESP32-P4 ───────┤ mosquitto :1883 (host 网络) │
(ESPHome) │ ▲ │
192.168.*.x │ │ 127.0.0.1 │
│ homeassistant :8123 │
└──────────────────────────────┘
项目 值
Broker 地址(P4 用) 192.168.*.30:1883
Broker 地址(HA 用) 127.0.0.1:1883(HA 是 host 网络)
WebSocket 未开启(P4 不需要)
TLS 未启用(仅内网,见第 10 节)
配置文件 /opt/mosquitto/config/
数据 / 日志 /opt/mosquitto/data/、/opt/mosquitto/log/

一个常被问到的问题:为什么 HA 用 127.0.0.1 而不是局域网 IP? 因为容器用了 network_mode: host,HA 容器里的 127.0.0.1 就是宿主机的 127.0.0.1,流量根本不经过网卡,省掉一层无谓的回环开销,也避免网卡切换时连接抖动。


3. 部署 Mosquitto

3.1 拉镜像:先绕开 Docker Hub

这台机器上 docker.io 直连超时,和之前装 HA 时一模一样。惯用办法是走国内镜像站拉取,再改回官方标签:

1
2
3
docker pull docker.m.daocloud.io/library/eclipse-mosquitto:2
docker tag docker.m.daocloud.io/library/eclipse-mosquitto:2 eclipse-mosquitto:2
docker rmi docker.m.daocloud.io/library/eclipse-mosquitto:2

实测 docker.m.daocloud.io、docker.1ms.run、dockerproxy.net、hub.rat.dev 四个站都能用,镜像 ID 38c0da4f2ef8,43.3 MB,体积比 HA 小得多。

改标签这步不能省。 compose 里写的是 eclipse-mosquitto:2,不改标签的话,下次 docker compose up 还是会去找官方源。

3.2 目录:和 HA 分开

1
mkdir -p /opt/mosquitto/{config,data,log}

用绝对路径挂载,与 /opt/homeassistant 完全分开。好处很实际:备份 HA 的 config 时不会误带上 broker 的数据,两边升级、迁移也互不影响。

3.3 mosquitto.conf

/opt/mosquitto/config/mosquitto.conf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
listener 1883 0.0.0.0
protocol mqtt

allow_anonymous false
password_file /mosquitto/config/passwd
acl_file /mosquitto/config/aclfile

persistence true
persistence_location /mosquitto/data/
autosave_interval 300

log_dest stdout
log_type error
log_type warning
log_type notice
connection_messages true
log_timestamp true
log_timestamp_format %Y-%m-%d %H:%M:%S

max_keepalive 120
max_connections -1

几个配置项的取舍:

  • allow_anonymous false 必须配合 password_file,只写前者容器会直接起不来。
  • log_dest stdout 让日志走 docker logs,交给 /etc/docker/daemon.json 里已有的 10 MB × 3 轮转,不写 eMMC——这点对长期跑在 SD 卡 / eMMC 上的小主机很重要。
  • persistence true 把 retained 消息落盘。P4 依赖的“当前状态”本质就是 retained 消息,broker 重启后 P4 一订阅就能拿到值,不用等实体下次变化。
  • autosave_interval 300 比默认的 1800 秒短,掉电最多丢 5 分钟的状态快照,对 eMMC 的写入压力也可接受。
  • max_keepalive 120 限制客户端心跳上限,防止某个客户端协商出超长 keepalive 导致 broker 长时间挂着死连接。

3.4 ACL:让 P4 只能看,不能改

/opt/mosquitto/config/aclfile:

1
2
3
4
5
6
user ha
topic readwrite #

user p4
topic read ha/#
topic readwrite p4/#
  • ha:MQTT 集成、设备发现、mqtt_statestream、控制桥都要用,索性给全量。
  • p4:只读 HA 的状态镜像;只能写自己的 p4/ 命名空间(命令、遥测、ESPHome 的 birth/debug 主题)。

这条规则的价值在“固件被改坏”的假设下才显现:即使 P4 上的程序出 bug、或者有人拿到了 P4 的账号,它也无法伪造 ha/... 状态去欺骗面板或 HA 的其它订阅者。权限边界画在 broker 上,比画在固件里可靠。

3.5 生成账号

用镜像自带的 mosquitto_passwd 生成哈希,避免明文写进配置文件:

1
2
3
4
5
6
7
8
9
10
docker run --rm --entrypoint mosquitto_passwd \
-v /opt/mosquitto/config:/mosquitto/config eclipse-mosquitto:2 \
-b -c /mosquitto/config/passwd ha '<HA_PASSWORD>'

docker run --rm --entrypoint mosquitto_passwd \
-v /opt/mosquitto/config:/mosquitto/config eclipse-mosquitto:2 \
-b /mosquitto/config/passwd p4 '<P4_PASSWORD>'

chown 1883:1883 /opt/mosquitto/config/passwd /opt/mosquitto/data /opt/mosquitto/log
chmod 640 /opt/mosquitto/config/passwd

踩过的坑:mosquitto_passwd -c 生成的文件属主是 root:root、权限 600,而镜像以 uid 1883 运行,容器会报 Unable to open pwfile 并退出。必须 chown 1883:1883 且给 640。第二次用 -b(不带 -c)追加账号,否则会把已有账号覆盖掉。

3.6 加入 compose

在 /opt/homeassistant/compose.yaml 末尾追加下面这段,原有的 homeassistant 段不动:

1
2
3
4
5
6
7
8
9
10
11
mosquitto:
container_name: mosquitto
image: eclipse-mosquitto:2
restart: unless-stopped
network_mode: host
volumes:
- /opt/mosquitto/config:/mosquitto/config
- /opt/mosquitto/data:/mosquitto/data
- /opt/mosquitto/log:/mosquitto/log
environment:
TZ: Asia/Shanghai

为什么用 network_mode: host:HA 已经是 host 网络,broker 也走 host,HA 里填 127.0.0.1:1883 即可;若改成端口映射,容器和宿主机之间多一层 NAT,排错时反而要多想一步“这个包到底从哪进来的”。内网单机服务,没必要引入这层复杂度。

3.7 启动

1
2
3
4
cd /opt/homeassistant
docker compose config --quiet # 先校验 YAML 和挂载路径
docker compose up -d mosquitto
docker compose ps

config --quiet 这步别省,它能在真正启动前抓出缩进错误和路径笔误。


4. 权限设计:ACL 才是重点

回头单独说一下这块,因为它是整套方案里最容易被忽略、又最值钱的部分。

很多教程给 P4 直接开 topic readwrite #,图省事,能跑通。但那样意味着:面板固件可以往 homeassistant/... 发发现消息,凭空注册一个假设备;也可以往 ha/light/xxx/state 写任意值,让面板显示“灯开着”而实际关着。

本文的划分把主题空间切成三层:

1
2
3
ha/...            只由 HA 写,P4 只读      ← 状态镜像(retain)
p4/cmd/... 只由 P4 写,HA 只读 ← 控制命令
p4/panel/... P4 自己的 birth/debug ← ESPHome 内部用

对应到 ACL 就是两行。如果面板最终只控制固定几台设备,还可以进一步收窄到具体主题(见第 10 节),把“读整个 ha/#”也砍掉。


5. 四项安全验证

配置写完不算数,得证明它真的拦得住。四个用例覆盖了认证和授权两面:

# 用例 命令要点 实测结果
1 匿名连接 不带账号 mosquitto_pub Connection Refused: not authorised ✅
2 ha 收发 订阅 p4/test/# 后发布 {"ok":1} 收到 {"ok":1} ✅
3 p4 读状态 ha 发 ha/light/demo/state=ON,p4 订阅 ha/# 收到 ON ✅
4 p4 越权写 p4 发布到 ha/light/demo/state 被断连(sending DISCONNECT)✅

第 4 条尤其值得看日志:Mosquitto 对 ACL 拒绝的发布不是静默丢弃,而是直接断开连接,日志里能看到 not authorised 和 sending DISCONNECT。这个行为对调试友好——P4 一直重连,基本就是账号密码错或 ACL 拒绝。

监听与启动日志:

1
2
LISTEN 0 100 0.0.0.0:1883 users:(("mosquitto",pid=74075,fd=5))
2026-10-18 15:23:22: Info: running mosquitto as user: mosquitto.

Windows 侧直连也确认了防火墙和路由没问题:

1
2
$c = New-Object System.Net.Sockets.TcpClient
$c.Connect('192.168.*.30',1883); "OK"

常用排错命令

1
2
3
docker logs mosquitto --tail=50
docker exec mosquitto mosquitto_sub -h 127.0.0.1 -u ha -P '<HA_PASSWORD>' -t '#' -v -W 3
ss -tlnp | grep 1883

第二条是“上帝视角”:用 ha 账号订阅全量主题,能一眼看出谁在发什么。-W 3 表示 3 秒后自动退出,不会挂住终端。


6. Home Assistant 侧接入

6.1 添加 MQTT 集成

界面路径是:设置 → 设备与服务 → 添加集成 → MQTT,填 127.0.0.1:1883、用户名 ha、密码。

本次为了可复现,用长期访问令牌直接调配置流 API 完成(等价于界面操作):

  1. POST /api/config/config_entries/flow,body {"handler":"mqtt"} → 返回 flow_id

  2. POST /api/config/config_entries/flow/<flow_id>,body:

    1
    2
    3
    {"broker":"127.0.0.1","port":1883,"protocol":"5","username":"ha",
    "password":"<HA_PASSWORD>",
    "other_settings":{"set_client_cert":false,"set_ca_cert":"off","transport":"tcp"}}
  3. 返回 type=create_entry,集成立即 loaded。

令牌只用于这一次调用,没有落盘。用完要撤销的话:HA 左下角头像 → 安全 → 长期访问令牌 → 删除。长期访问令牌等同于密码,尤其不要贴进博客、Issue 或聊天记录。

6.2 状态镜像(mqtt_statestream)

把下面这段加到 /opt/homeassistant/config/configuration.yaml 顶层,然后重启 HA:

1
2
3
4
5
6
7
8
9
10
11
mqtt_statestream:
base_topic: ha
publish_attributes: false
publish_timestamps: false
include:
domains:
- light
- switch
- fan
- cover
- climate

发布规则:

1
2
ha/<domain>/<object_id>/state        retain=true, qos=1
例:ha/light/demo_light/state → on

设计取舍:只镜像 light/switch/fan/cover/climate,不含 sensor。传感器高频刷新(温湿度、功率曲线)会把带宽和 broker 的 retained 存储迅速撑大,而面板真正需要的只是开关状态。要显示温度,把 sensor 加进 domains 即可,代价自己权衡。

publish_attributes: false 也很关键:设备属性(亮度、色温、HVAC 模式)动辄几十个字段,全量镜像后主题数量翻倍,而 P4 端通常只用得上 state 本身。

6.3 控制桥自动化

把下面两条追加进 /opt/homeassistant/config/automations.yaml,然后 开发者工具 → 操作 → automation.reload:

  1. P4 面板:MQTT 控制桥 —— 订阅 p4/cmd/+/+/+,从主题里拆出 domain / object_id / service,调对应服务。
  2. P4 面板:状态镜像初始化 —— HA 启动 15 秒后,把所有常用实体状态重新 retain 一遍。

第二条是为了补 mqtt_statestream 的一个缺口:它只在状态变化时发布,不主动发初始值。broker 重启、retained 数据清空、或者 HA 刚起来时,P4 订阅了也拿不到东西,屏幕只能显示 --。用一条启动后补发的自动化把这个洞堵上。

第一条的模板部分大致是:

1
2
3
4
5
6
7
8
9
10
11
12
trigger:
- platform: mqtt
topic: p4/cmd/+/+/+
action:
- variables:
domain: "{{ trigger.topic.split('/')[2] }}"
object_id: "{{ trigger.topic.split('/')[3] }}"
service: "{{ trigger.topic.split('/')[4] }}"
- service: "{{ domain }}.{{ service }}"
target:
entity_id: "{{ domain }}.{{ object_id }}"
data: "{{ trigger.payload | default({}, true) | from_json }}"

这样 service 直接就是 HA 的服务名,toggle / turn_on / turn_off / set_hvac_mode / open_cover 全部通用,新增设备不需要改任何后端代码。

6.4 没有 P4 也能验收

没有硬件不代表不能验证。用 broker 自带的客户端冒充面板就够了(本次就是这么验收的):

1
2
3
4
5
6
7
8
# 用 p4 账号发一条 toggle(实体换成你自己的)
docker run --rm --network host --entrypoint mosquitto_pub eclipse-mosquitto:2 \
-h 127.0.0.1 -u p4 -P '<P4_PASSWORD>' \
-t p4/cmd/light/demo_light/toggle -m '{}'

# 用 p4 账号看状态镜像(retain,订阅即得当前值)
docker run --rm --network host --entrypoint mosquitto_sub eclipse-mosquitto:2 \
-h 127.0.0.1 -u p4 -P '<P4_PASSWORD>' -t 'ha/+/+/state' -v -W 4

用 p4 账号而不是 ha 来验收还有额外好处:顺带证明了 ACL 里“读 ha/#”这条规则真的放行了,写 p4/cmd/... 也真的允许了。

6.5 实测记录

1
2
3
4
BEFORE : on
PUBLISHED <- p4/cmd/light/demo_light/toggle {}
AFTER : off <- 4 秒后 HA 实体已变
RESTORED: on <- 再发一次 toggle 还原
  • 控制桥自动化:last_triggered = 15:34:06
  • 状态镜像初始化自动化:last_triggered = 15:33:21(启动补发)
  • retained 主题共 78 条:ha/climate/* 3 条、ha/light/* 2 条、ha/switch/* 73 条

4 秒的延迟主要来自“MQTT → 自动化触发 → 服务调用 → 设备响应 → 状态回流”这条链路,其中设备本身的响应占大头。局域网内这个量级对面板交互完全够用。

6.6 关于实体构成的一点观察

这台 HA 里 light/switch/fan/cover/climate 的实际构成挺有意思:

类型 数量 说明
climate.* 3 壁挂式空调(Midea)
light.* 8 绝大多数是设备指示灯 / 氛围灯,其中 5 个 unknown/unavailable
switch.* 70+ 小米设备的子开关(摄像机 AI 检测、扫地机功能项等),不是物理开关

也就是说:列表里没有常规照明实体。如果你的目标是控制真正的灯或插座,先确认它们是否已经接入 HA,再谈面板按钮。P4 上的按钮直接填对应 entity_id 即可,桥不关心设备品牌——但前提是那个实体真实存在且可用。


7. 主题协议:一套不用改代码的桥

7.1 命令(P4 → HA)

1
p4/cmd/<domain>/<object_id>/<service>     负载:JSON 对象
场景 主题 负载
切换灯 p4/cmd/light/keting/toggle {}
开灯并调亮度 p4/cmd/light/keting/turn_on {"brightness":180}
关空调 p4/cmd/climate/keting_ac/turn_off {}
开窗帘 p4/cmd/cover/keting_curtain/open_cover {}
空调切制冷 p4/cmd/climate/keting_ac/set_hvac_mode {"hvac_mode":"cool"}

注意 object_id 是 entity_id 去掉 domain 前缀的部分:light.keting 对应 object_id=keting。用小写英文而不是中文,避免 URL / 主题编码上的麻烦。

7.2 状态(HA → P4)

1
ha/<domain>/<object_id>/state              retain=true(订阅即得当前值)

P4 只需订阅 ha/+/+/state 一个通配符主题,按 entity_id 分发到各个控件。

7.3 命名空间

1
p4/panel/...     ESPHome 自身的 birth/debug 主题(topic_prefix: p4/panel)

把 ESPHome 的内部主题和业务命令分到不同子树,p4/# 授权时不会互相干扰,抓包时也一眼能分清是面板自检还是用户操作。


8. ESP32-P4(ESPHome)侧

8.1 前提条件

  • ESPHome 里 P4 的写法是 variant: ESP32P4 + framework: esp-idf,且需要较新的版本(示例工程用 min_version: 2026.5.2)。版本太旧会直接报 esp_ldo / esp32_hosted 未知。
  • P4 没有内置 WiFi:靠封装里的 ESP32-C6 通过 esp32_hosted(SDIO)提供网络。
  • MIPI 屏需要 esp_ldo 给 DSI PHY 供电(常见 channel: 3、2.5V),再配 display: platform: mipi_dsi。
  • 这些板级配置沿用你现有工程即可,示例里只给占位注释。

8.2 引入桥包

把 p4-ha-bridge.yaml 放进 ESPHome 配置目录,secrets.yaml 加一行:

1
mqtt_p4_password: "<P4_PASSWORD>"

面板 yaml 里:

1
2
packages:
p4_ha_bridge: !include p4-ha-bridge.yaml

包内已经包含:

  • mqtt: 客户端(topic_prefix: p4/panel,discovery: false)
  • script.p4_send(domain, object_id, service, data) —— 发任意命令
  • script.p4_toggle(domain, object_id) —— 只发 toggle

discovery: false 是刻意的:ESPHome 的 MQTT discovery 会把面板自己注册成 HA 设备,而这里面板是控制端,不需要被控制,关掉能少一堆实体。

8.3 UI 绑定

按钮(每加一个设备复制一份,改 4 个值):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- button:
x: 24
y: 80
width: 220
height: 120
widgets:
- label: { text: "客厅灯", align: CENTER }
on_click:
- script.execute:
id: p4_send
domain: light
object_id: keting
service: toggle
data: "{}"

状态标签 + 订阅。订阅只需一次,放在 on_boot 里:

1
2
3
4
5
6
7
8
9
10
11
12
13
esphome:
on_boot:
priority: 600
then:
- lambda: |-
id(mqtt_bridge).subscribe("ha/+/+/state", [](const std::string &topic, const std::string &payload) {
// ha/light/keting/state -> light/keting
std::string eid = topic.substr(3, topic.size() - 9);
for (auto &c : eid) { if (c == '/') c = '.'; }
if (eid == "light.keting") {
lv_label_set_text(id(lbl_keting), payload.c_str());
}
}, 1);

那段 substr(3, size - 9) 是把 ha/ 前缀和 /state 后缀切掉,得到 light/keting,再把斜杠换成点,拼成 light.keting 这样的 entity_id。偏移量写死看着不优雅,但对固定格式的主题是最省 CPU 的写法。

8.4 两个实现细节

回调线程:ESP32 上 MQTT 回调运行在 esp-mqtt 的任务里,不是主循环。直接 lv_label_set_text 在实测中可用(ESPHome 自带组件也这么干),但如果面板复杂、出现偶发花屏或重启,改成“回调只写全局变量,interval 里刷新控件”即可。这是典型的“能用”和“稳”之间的取舍。

订阅只做一次:MQTTClientComponent::subscribe() 不去重,每次调用都会新增一条订阅,订阅表只增不减。所以:

  • 不要放在 on_connect 里——每次重连都会多加一条,跑一天能堆出几十条重复订阅,回调被重复执行。
  • 放在 on_boot,或只在首次连接时执行一次。
  • 重连后的重订阅由 ESPHome 组件自己完成,不用你操心。

9. 排错速查

现象 原因 处理
容器起不来,日志 Unable to open pwfile passwd 属主是 root chown 1883:1883 + chmod 640
docker pull eclipse-mosquitto:2 超时 Docker Hub 不可达 用镜像站拉 + docker tag
HA 报 Connection refused broker 没起 / 端口不对 ss -tlnp | grep 1883
P4 一直重连 账号密码错 / ACL 拒绝发布 docker logs mosquitto 看 not authorised
P4 订阅 ha/# 收不到 mqtt_statestream 没配,或 domain 不在 include 检查 configuration.yaml,重启 HA
P4 上电后状态是 -- statestream 不发初始值 等实体变化,或靠启动补发自动化
控制没反应 实体名 / 服务名写错,或被 condition 挡掉 开发者工具 → 模板里跑 states('light.keting')
ESPHome 编译报 esp_ldo / esp32_hosted 未知 ESPHome 版本太旧 升级到 2026.5+
P4 能连 broker 但 HA 收不到命令 HA 里没加 MQTT 集成 见 6.1
面板回调重复执行 订阅加在了 on_connect 改到 on_boot,只订阅一次

排错的顺序建议是:先 broker,再 HA,最后 P4。broker 用 mosquitto_sub 能收到就说明网络和认证没问题;HA 侧用开发者工具能手动调服务就说明集成和实体没问题;剩下的才轮到固件。


10. 安全与维护

边界

  • 1883 不要映射到公网。 外网访问走 Tailscale / WireGuard 之类的隧道,或者至少上 TLS(listener 8883 + cafile/certfile/keyfile)。

  • p4 账号权限已最小化。如果面板只控制固定几台设备,可以把 ACL 从“读整个 ha/#”收窄到具体主题:

    1
    2
    3
    4
    user p4
    topic read ha/light/keting/state
    topic read ha/switch/chafang/state
    topic write p4/cmd/light/keting/#

    这样即使 P4 完全失陷,攻击面也只有这两三个灯。

日常

  • 换密码:mosquitto_passwd 改完 docker restart mosquitto,再同步改 HA 集成和 ESPHome 的 secrets.yaml。三处必须一起改,漏一处就是“P4 一直重连”。

  • 备份:/opt/mosquitto 不在原有 HA 备份脚本范围内。retained 状态丢了会自动重建(靠启动补发),但配置建议单独备份:

    1
    tar czf /var/backups/mosquitto-$(date +%F).tar.gz /opt/mosquitto/config
  • 升级:docker compose pull mosquitto && docker compose up -d mosquitto。2.x 内部小版本可以直接升,跨大版本前先看 release notes 里 ACL 和配置项的变化。

  • 别把密码写进 compose 或 Git:凭据放本地文件或密码管理器,secrets.yaml 加进 .gitignore。


11. 写在最后

整套东西配置下来不到一小时,真正花时间的是把权限边界想清楚,以及验证它确实拦得住。四条测试用例里,前三条是“能用”,第四条才是“安全”——很多人只测前三条。

另一个感受是 mqtt_statestream + 通配符订阅这个组合的性价比很高:HA 侧几乎零代码(一段 YAML + 两条自动化),P4 侧也不用为每台设备写协议适配,toggle / turn_on / turn_off 这些服务名天然就是通用的。新增设备基本只是复制一个按钮、改四个值。

接下来准备把 P4 真正烧进去,接上实体跑一轮完整交互。ESPHome 那段 lv_label_set_text 在回调里是否稳定,还得在真实面板上多看几天。

唯一要提醒的是:文章里所有密码、ID、令牌都换成了占位符,你照抄的时候记得改。 尤其是长期访问令牌,它和密码等价,别顺手贴进任何一个公开的地方。


变更记录:

时间 操作
15:20 确认 1883 空闲、HA 无 MQTT 集成
15:22 拉取镜像(Hub 超时,改 daocloud 镜像站)
15:23 写入 mosquitto.conf / aclfile,生成 ha、p4 账号
15:24 compose 增加 mosquitto 服务并启动
15:25 四项验证通过(匿名拒绝、收发、读状态、越权拒绝)
15:30 备好 HA 自动化 / statestream / ESPHome 包
15:31 用长期访问令牌添加 MQTT 集成(entry loaded)
15:32 停 HA,追加 mqtt_statestream 与 2 条自动化,重启
15:34 端到端验证:p4 发 toggle → 实体 on→off→on;78 条 retained 状态主题