Home Assistant 裡總有那麼幾件事,內建整合做不到,但在終端機打一行指令就解決了。把那一行接進 Home Assistant 有兩個內建工具:shell_command 和 command_line。
這兩個是 Home Assistant 裡最不好除錯的功能。失敗的時候通常什麼訊息都沒有,只有一個 unknown 的實體或一個什麼都沒發生的按鈕。這篇會一路做到「看得見錯誤訊息」為止。
這篇會做完的事
先確認你要做的事是不是根本不需要這兩個(很多人在這裡就可以收工了)
搞懂一件會改變一切的事:指令裡有沒有範本,決定它用哪一種方式執行
做出三個 command_line 感測器、一個開關,每一個都有驗證步驟
學會兩種除錯方法,其中一種可以直接看到指令的 stdout、stderr 和離開碼
處理 60 秒硬性逾時 —— 這是最多人卡住卻沒人告訴他的地方
用 ssh 從 Home Assistant 控制另一台機器,含金鑰怎麼產生、known_hosts 怎麼建
全部做完約兩小時。需要一台 Home Assistant(這篇以 HAOS 為主)和一台想被控制的機器(NAS、路由器、電腦都行,沒有也可以跳過 ssh 那幾步)。
第零步:先確認你不需要這兩個
shell_command 是最後手段,不是第一選擇。 它沒有錯誤處理、沒有重試、依賴容器裡剛好有那支程式,而且升級之後可能默默失效。
動手之前先對一次這張表:
你想做的事 先用這個(內建、不必寫指令) ----------------------------- ---------------------------------------- 喚醒一台電腦(Wake on LAN) 內建「Wake on LAN」整合, 動作叫 wake_on_lan.send_magic_packet 打一個 HTTP API 內建 rest_command: 讀一個 HTTP API 做成感測器 內建 rest: 平台 ping 看某台機器在不在 內建「Ping」整合(已改成介面設定,沒有 YAML) 看 Home Assistant 主機的 CPU、記憶體、磁碟 內建「System Monitor」整合 關機/重開 Home Assistant 內建 homeassistant.restart 等動作 定期把設定備份出去 內建備份系統(設定 → 系統 → 備份), 2025.1 整套重做過,支援 Google Drive/OneDrive 真的需要外部指令的 為什麼 ----------------------------- ---------------------------------------- 去另一台機器上跑東西 ssh 讀主機上某個特定檔案的內容 cat /sys/... 這種沒有整合包裝的東西 呼叫某個只有命令列介面的工具 例如某些廠商自己的 CLI 把一堆東西串成一行 雖然串接有限制,見第二步 複製代碼
舉一個最常見的例子 :舊教學一律教你用 shell_command: "wakeonlan AA:BB:CC D:EE:FF" 來喚醒電腦。今天的正確做法是內建整合:
設定 → 裝置與服務 → 新增整合 ,搜尋 Wake on LAN ,填入 MAC 位址。之後在自動化裡這樣用:
actions: - action: wake_on_lan.send_magic_packet data: mac: "AA:BB:CC:DD:EE:FF" 複製代碼
你會看到 :這個動作不需要容器裡有任何外部程式,也不會有 60 秒逾時的問題。
看完表還是需要外部指令的話,往下走。
第一步:搞懂兩者的差別
shell_command —— 執行一個動作 。它變成一個「動作(action)」,你在自動化或腳本裡呼叫它。它不會變成實體,儀表板上看不到它
command_line —— 執行指令並讀取輸出 ,把輸出變成一個實體 (感測器、二元感應器、開關、窗簾、通知)。它會定期自己跑
判斷方法只有一句:你在乎輸出嗎? 在乎 → command_line。不在乎、只是要它去做一件事 → shell_command。
第二步:那件會改變一切的事 —— 有沒有範本
這一步是整篇最重要的一段,而且幾乎所有舊教學都寫錯了。
shell_command 有兩種執行模式,它自己決定要用哪一種:
指令裡沒有 {{ }} → 交給 shell 執行 管線 |、重導向 > >>、~ 家目錄展開 全部可用 指令裡有 {{ }} → 不經過 shell,直接執行那個程式 管線、重導向、~ 全部不能用,會被當成一般文字參數 複製代碼
官方文件的原話是:用了範本之後,shell_command「runs in a more secure environment that doesn't allow any shell helpers such as automatically expanding the home directory character (~), using pipe symbols (|) to run multiple commands, or operators redirecting output (such as > and >>)」。
還有一條附帶限制:只有第一個空格之後的內容可以用範本產生 ,指令名稱本身必須是寫死的字。所以 "{{ my_cmd }} --flag" 這種寫法無效。
這件事有三個直接後果,每一個都會咬人:
後果一:帶範本的指令其實是注入安全的。 舊教學說「使用者輸入塞進 shell 指令是命令注入的典型場景」,那對帶範本的 shell_command 不成立 —— 沒有 shell,分號和反引號就只是兩個普通字元。
後果二:你不能同時要「帶參數」和「用管線」。 這是真正的限制。要兩者兼得,把指令包成一個腳本檔:
#!/bin/bash # 放在 /config/shell/report.sh,記得 chmod +x # $1 是第一個參數 df -h "$1" | tail -1 | awk '{print $5}' | tr -d '%' 複製代碼
然後在 configuration.yaml 裡:
# /config/configuration.yaml 最外層 shell_command: disk_usage: "bash /config/shell/report.sh {{ path }}" 複製代碼
管線寫在腳本檔裡,shell_command 只負責把參數傳進去。 這樣兩邊都有了。