|
|
只會發一個唯讀的溫度數字,MQTT 的價值只用到一成。
這篇要做四件在那之後才有意思的事:做出 HA 能「控制」的實體、讓 HA 知道你的裝置掛了、一則 JSON 餵好幾個實體,以及把上面全部寫進兩種真的會跑的裝置端程式 —— 一支 Python 服務,和一台你自己接線的 ESP8266。
從零件清單、接線、電阻怎麼算,到完整的程式碼與燒錄後該看到的日誌,全部在這一篇裡。

做完這篇你會有什麼
- 一支跑在 NAS 或小主機上的 Python 服務,把系統狀態變成 HA 的實體,而且斷掉時 HA 會顯示「不可用」
- 一台 ESP8266,能回報溫度、能被 HA 遠端開關繼電器
- 用「裝置層探索」一則訊息宣告整台裝置的所有實體,而不是一個實體發一則
- 看得懂 availability、遺囑訊息、value_template 各自在解決什麼問題
需要準備的東西
- 一個能連的 MQTT broker。還沒有的話第一步會從頭建一個
- 已經能用的 Home Assistant
- 電腦上裝好 mosquitto 的命令列工具(sudo apt install -y mosquitto-clients,macOS 是 brew install mosquitto)
下面全部用 192.168.1.20 當 broker 的 IP,記得換成你自己的。
---
第一步:先確定有一個能連的 broker
已經有了的話,用這行確認它真的通,然後跳到第二步:
- mosquitto_sub -h 192.168.1.20 -p 1883 -u 你的帳號 -P 你的密碼 -t '#' -v
複製代碼
游標停住不動就是成功(現在還沒人發訊息)。跳回提示字元並印出錯誤的話,往下把 broker 建起來。
1-1. 建立目錄與設定檔
- sudo mkdir -p /opt/mosquitto/{config,data,log}
複製代碼
存成 /opt/mosquitto/config/mosquitto.conf(整份):
- # Mosquitto 2.0 起,沒有 listener 這一行的話它只綁 127.0.0.1,
- # 別台機器完全連不進來,而且啟動日誌看起來一切正常。
- listener 1883
- protocol mqtt
- # 2.0 起只要寫了 listener,這個預設就是 false。明寫出來比較不會忘。
- allow_anonymous false
- password_file /mosquitto/config/passwd
- # retain 訊息要能撐過重開機,不然 HA 裡的 MQTT 裝置會全部消失。
- persistence true
- persistence_location /mosquitto/data/
- autosave_interval 1800
- log_dest stdout
- log_type error
- log_type warning
- log_type notice
- log_type information
- connection_messages true
- log_timestamp true
複製代碼
1-2. docker-compose.yml(整份)
存成 /opt/mosquitto/docker-compose.yml:
- services:
- mosquitto:
- image: eclipse-mosquitto:2.0.22
- container_name: mosquitto
- restart: unless-stopped
- ports:
- - "1883:1883"
- volumes:
- - ./config:/mosquitto/config
- - ./data:/mosquitto/data
- - ./log:/mosquitto/log
- environment:
- - TZ=Asia/Taipei
複製代碼
1-3. 建帳號(這篇會用到三個)
- # 第一個用 -c(建立新檔)。⚠ 之後絕對不要再用 -c,它會把前面的帳號全部清掉。
- sudo docker run --rm -it -v /opt/mosquitto/config:/mosquitto/config eclipse-mosquitto:2.0.22 mosquitto_passwd -c /mosquitto/config/passwd homeassistant
- sudo docker run --rm -v /opt/mosquitto/config:/mosquitto/config eclipse-mosquitto:2.0.22 mosquitto_passwd -b /mosquitto/config/passwd pyagent 換成你的密碼
- sudo docker run --rm -v /opt/mosquitto/config:/mosquitto/config eclipse-mosquitto:2.0.22 mosquitto_passwd -b /mosquitto/config/passwd espdev 換成你的密碼
- sudo chmod 0700 /opt/mosquitto/config/passwd
複製代碼
1-4. 啟動
- cd /opt/mosquitto && docker compose up -d && docker compose logs -f
複製代碼
你會看到什麼(正常)
- 1790276210: mosquitto version 2.0.22 starting
- 1790276210: Config loaded from /mosquitto/config/mosquitto.conf.
- 1790276210: Opening ipv4 listen socket on port 1883.
- 1790276210: Opening ipv6 listen socket on port 1883.
- 1790276210: mosquitto version 2.0.22 running
複製代碼
1-5. 接上 HA
設定 → 裝置與服務 → 新增整合,搜尋 MQTT,填 192.168.1.20 / 1883 / homeassistant / 密碼。
你會看到什麼:整合清單多出 MQTT 卡片。同時開著訂閱視窗的話,會立刻跑出一行 homeassistant/status online。
接下來全程保持一個訂閱視窗開著,這是後面每一步的驗證管道:
- mosquitto_sub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t '#' -v
複製代碼
---

第二步:做出一個 HA 可以「控制」的開關
唯讀的感測器只有 state_topic(HA 讀)。可控的實體要多一個 command_topic(HA 寫)。
整個資料流是這樣:
- 你在 HA 按下開關 → HA 往 command_topic 發 ON
- 你的裝置訂閱了 command_topic,收到 ON → 真的去把電器打開
- 裝置做完之後,往 state_topic 回報 ON
- HA 收到 state_topic 的 ON,才把介面上的開關畫成「開」
⚠ 第四步不是多餘的。HA 不會因為「我發了指令」就假設成功。裝置沒回報,介面上的開關會彈回原來的位置 —— 這是正確的設計,代表你看到的狀態一定是裝置真的回報過的。
發一個 switch 出來(在另一個視窗執行):
- mosquitto_pub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t 'homeassistant/switch/bath_fan/config' -r -m '{
- "name": null,
- "unique_id": "bath_fan_001",
- "state_topic": "home/bath/fan/state",
- "command_topic": "home/bath/fan/set",
- "payload_on": "ON",
- "payload_off": "OFF",
- "state_on": "ON",
- "state_off": "OFF",
- "optimistic": false,
- "device": {
- "identifiers": ["bath_controller"],
- "name": "浴室抽風機",
- "manufacturer": "DIY",
- "model": "esp8266-relay-v1"
- },
- "origin": {"name": "manual-test", "sw": "1.0"}
- }'
複製代碼
三個新欄位
- name 寫 null —— 代表「這個實體直接沿用裝置的名字」。不寫 null 而寫「開關」的話,實體會變成「浴室抽風機 開關」,一台裝置只有一個主要實體時很囉嗦
- payload_on / payload_off —— HA 發出去的指令內容。state_on / state_off 則是 HA 讀狀態時要認什麼字。兩組預設都是 ON/OFF,裝置回報的是 1/0 或 true/false 的話要在這裡寫清楚
- optimistic: false —— 「不要樂觀」。設成 true 的話 HA 發完指令就自己把介面切過去,不等裝置回報。有 state_topic 的時候一律設 false,不然你會看到一個顯示「開」但其實沒開的開關
你會看到什麼
設定 → 裝置與服務 → MQTT 底下多一台「浴室抽風機」,裡面一個開關實體,狀態是「不可用」或空白 —— 因為還沒有人往 state_topic 回報過。
手動扮演裝置。開第三個視窗,訂閱指令主題:
- mosquitto_sub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t 'home/bath/fan/set' -v
複製代碼
現在到 HA 按那個開關。
你會看到什麼
而 HA 介面上的開關會彈回「關」。這是對的 —— 沒有人回報狀態。
手動回報一次:
- mosquitto_pub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t 'home/bath/fan/state' -m 'ON'
複製代碼
你會看到什麼:HA 的開關立刻變成「開」並停在那裡。
這個往返你親手跑過一次,後面寫裝置端程式時就不會搞不清楚誰該發什麼。
---
第三步:讓 HA 知道裝置掛了(availability + 遺囑)
這是最常被略過、但最值得做的一項。
沒有它的時候:裝置斷電了,HA 裡的溫度停在最後一個數字不動。你看到「26.4 度」,以為一切正常,其實那是三天前的數字。自動化照樣會拿它去做判斷。
3-1. 在設定裡加 availability
- mosquitto_pub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t 'homeassistant/switch/bath_fan/config' -r -m '{
- "name": null,
- "unique_id": "bath_fan_001",
- "state_topic": "home/bath/fan/state",
- "command_topic": "home/bath/fan/set",
- "payload_on": "ON",
- "payload_off": "OFF",
- "optimistic": false,
- "availability_topic": "home/bath/fan/status",
- "payload_available": "online",
- "payload_not_available": "offline",
- "device": {
- "identifiers": ["bath_controller"],
- "name": "浴室抽風機",
- "manufacturer": "DIY",
- "model": "esp8266-relay-v1"
- },
- "origin": {"name": "manual-test", "sw": "1.0"}
- }'
複製代碼
payload_available 和 payload_not_available 的預設值就是 online 和 offline,寫出來只是讓人看設定檔時不用去查。
3-2. 自己發一次 online
- mosquitto_pub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t 'home/bath/fan/status' -m 'online' -r
複製代碼
你會看到什麼:HA 裡那個開關從「不可用」恢復成可以操作。
3-3. 發一次 offline 看看
- mosquitto_pub -h 192.168.1.20 -p 1883 -u homeassistant -P 密碼 -t 'home/bath/fan/status' -m 'offline' -r
複製代碼
|
|