大部分自動化的形狀是「觸發 → 做事 → 結束」,三秒鐘跑完。但生活裡很多流程不是這樣:洗衣機開始運轉,然後你要等它洗完;你出門了,然後要等五分鐘再確認門鎖;系統問你一個問題,然後要等你按下按鈕。
這種「做一件事 → 等某件事發生 → 再做下一件」的流程,靠的是 wait 。
這篇會做完的事
搞懂兩種 wait 的差別,以及為什麼選錯會讓自動化永遠卡住
做一個用功率判斷的洗衣機完成通知(不需要洗衣機本身是智慧家電)
做一個「離家後確認門鎖」,含可以按的通知按鈕
學會 wait.completed / wait.remaining / wait.trigger 三個變數
處理「同一條自動化跑兩次,按鈕會互相搶」這個官方明文的問題
加上兩層保險:卡住的偵測,以及重新啟動後的補救
學會在不等兩小時的情況下,把整條鏈路測完
全部做完約兩小時。需要一個智慧插座(有功率量測的那種)、一台裝了 Home Assistant 手機 App 的手機。沒有智慧插座的話,第三步會給一個用其他感測器替代的方法。
第零步:確認你的起點
0-1 找出你的通知服務名稱
到 開發者工具 → 動作 ,在搜尋框打 notify 。
你會看到 一串以 notify. 開頭的動作,其中會有 notify.mobile_app_xxx ,xxx 是你手機在 Home Assistant 裡的名稱。
一個 mobile_app 都沒有 → 手機還沒裝 App 或還沒登入。到 App Store 或 Google Play 裝 Home Assistant ,登入之後回來重新整理這一頁。
把那個完整名稱抄下來 ,這篇所有 notify.mobile_app_charles 都要換成你自己的。
0-2 如果有智慧插座,找出功率感測器
開發者工具 → 狀態 ,篩選欄打 power 或你的插座名稱。
你會看到 類似:
sensor.xi_yi_ji_gong_lu 3.2 unit_of_measurement: W device_class: power 複製代碼
0-3 量出洗衣機的功率門檻(這一步不能猜)
不要抄任何教學給的數字。 每一台洗衣機的待機功率和運轉功率都不一樣。
方法:把洗衣機接上智慧插座,跑一次完整的洗衣程序,然後到那個感測器的歷史 頁看曲線。
你會看到 三段很不一樣的數值:
待機(沒在洗) 0 到 5 W ← 記下上限,叫它「待機上限」 運轉(洗、脫水) 150 到 800 W ← 記下下限,叫它「運轉下限」 中間換程序的短暫低谷 0 到 20 W ← 記下它持續多久 複製代碼
那三個數字後面都會用到。中間那個低谷特別重要 —— 很多洗衣機在進水、排水、換程序的時候功率會短暫掉到接近零,持續幾十秒到兩分鐘。沒有量過就寫判斷式的話,洗到一半就會通知你洗好了。
沒有智慧插座怎麼辦 :改用其他訊號。震動感測器(貼在機身上)、門磁(貼在洗衣機門上)、或者洗衣機本身如果有 Wi-Fi 就直接用它的實體。判斷邏輯完全一樣,只是把「功率低於 5 瓦」換成「震動感測器是 off」。
第一步:兩種 wait 的差別(選錯會永遠卡住)
wait_for_trigger 等一個「事件發生」—— 狀態從 A 變成 B 的那一瞬間 已經是 B 了也不算,它會繼續等下一次變化 wait_template 等一個「條件成立」—— 現在就成立的話立刻通過 複製代碼
用一個具體的例子看差別:
# A:等門「變成」開 - wait_for_trigger: - trigger: state entity_id: binary_sensor.men to: "on" # 執行到這一行時門已經是開的 → 它會繼續等, # 直到門關起來再打開一次。門如果一直開著,它永遠不會通過 # B:等門「是」開 - wait_template: "{{ is_state('binary_sensor.men', 'on') }}" # 執行到這一行時門已經是開的 → 立刻通過 複製代碼
九成的情況你要的是 wait_template ,因為你在意的是狀態,不是變化那一瞬間。
什麼時候才該用 wait_for_trigger :
等一個事件 (按下通知按鈕、收到 webhook、遙控器被按)—— 事件本來就沒有「狀態」可言
等好幾種情況其中之一 ,而且要知道是哪一種發生的
需要用到 for: (持續多久)這種只有觸發器才有的修飾
1-1 wait_template 有一個官方明文的限制,一定要知道
官方原話:「The template is re-evaluated whenever an entity ID that it references changes state. If you use non-deterministic functions like now() in the template it will not be continuously re-evaluated, but only when an entity ID that is referenced is changed.」
翻成白話:wait_template 只在「它引用到的實體」變化時才重算。用 now() 寫時間條件,它不會自己重算。
所以這樣寫會永遠卡住:
# 錯:這個 wait 可能永遠不會通過 - wait_template: "{{ now() > today_at('07:00') }}" 複製代碼
兩個正解 :
# 正解一:改用 wait_for_trigger 等時間 - wait_for_trigger: - trigger: time at: "07:00:00" timeout: "12:00:00" # 正解二:在範本裡引用一個會定期變化的實體 # 先到 設定 → 裝置與服務 → 新增整合 → 搜尋 Time & Date,勾選「時間」 - wait_template: > {{ states('sensor.time') is not none and now() > today_at('07:00') }} 複製代碼
第二步:一定要加 timeout(先做最小版本)
沒有 timeout 的 wait 會無限期卡在那裡。 它會一直佔著一個執行位置,在「執行中的自動化」裡看得到,但你不會注意到 —— 直到某天發現這條自動化再也沒動過。