如果你有讓 AI agent 或排程每天自動幫你跑事情,它壞掉的那天,多半不會告訴你。要知道它還活著,得反過來做:讓它每次做完都來報到,超過時間沒報到就叫你。這個叫 heartbeat(心跳),也有人叫 dead-man switch。
我家的 Home Assistant 今年有五次「儀表板說正常,其實已經壞了」,最長的 285 天,沒有一次是系統告訴我的。這篇用這五次講清楚:為什麼「沒有錯誤訊息」不等於「沒事」,heartbeat 要怎麼裝。
為什麼壞了不會說
自動化、排程、AI agent,回報的都是「指令送出了」,不是「事情發生了」。
叫喇叭播一段廣播,喇叭回「收到」;叫腳本去續期憑證,腳本回「執行完畢」。中間每多一次交棒,回報跟現實就多脫鉤一層。而最難抓的那種壞法,是根本沒有被叫起來:觸發條件永遠不成立,就沒有執行、沒有錯誤、沒有紀錄,去翻 log 乾乾淨淨。
所以要多一條反方向的線:不是等它報錯,是等它報平安。下面五次,每一次都是這條線該抓、當時卻沒有的。

五次,都是人發現的
回到家,該亮的燈沒亮
今年 4 月那陣子,回家常常發現燈沒亮。4 月 11 日查下去,負責開燈的那條自動化是關著的。
我家的「回家」是一串自動化接力:起點那條先把開燈那條關掉,免得它提早跑,做一輪雜務,最後再把它開回來。那天下午,雜務裡叫除濕機切模式的那一步,雲端 API 回了錯誤。Home Assistant 的預設是動作序列裡任何一步出錯,整條就停在那裡,排在最後的「開回來」永遠沒跑到。
關掉很簡單,開回來排在最後面,而最後面是最容易沒跑到的位置。 修法是在會失敗的那一步加 continue_on_error: true。
回到家,燈又不對
8 月 13 日,夜燈又不亮了。把每台 Zigbee 裝置最後一次通訊的時間拉出來,才對出臥室那顆動作感應器 8 月 1 日就沒電了,玄關那顆 8 月 3 日,各自掉線 12 天和 10 天。
這些裝置透過 Zigbee2MQTT(簡稱 Z2M)進到系統,它的 availability 預設是關的。所以裝置失聯時,系統不會把它標成離線,而是原封不動保留最後一次收到的值。玄關那顆最後回報的是「有人」,於是在系統眼裡,我家玄關從那天起二十四小時都有人站著。自動化等的是「從沒人變成有人」那一下,這一下永遠不會再來。
同一批清點,浴室那顆水感測器已經沒電 285 天,儀表板上一直顯示正常。
這類失敗在自動化層查不到
症狀在自動化層(該跑的沒跑),根因在裝置層(電池沒電)。automation trace 裡乾乾淨淨,因為沒觸發就沒有 trace。
耳朵:廣播沒有聲音
三顆 HomePod 做全屋廣播,洗衣機洗好、有人按門鈴都會出聲。7 月 12 日換了播報音檔,之後全部無聲,18 天後才認真查。
系統這邊每一次都回報成功,log 一個錯誤都沒有。查出來兩個故障互相掩護:Home Assistant 只把音檔的網址交給喇叭,由喇叭自己回頭抓,而那個網址指到一個沒有服務在聽的地方;另外某次系統更新後,三顆喇叭的識別碼全變了,整合一路重試連不上。失敗發生在喇叭端,Home Assistant 交出網址就算完成了。
覺得最近太安靜
有一條自動化靠讀特定的通知信觸發。5 月覺得它太安靜,一翻,52 天沒觸發過。根因是我自己設的 Gmail 規則把那些信自動封存,系統只讀收件匣;HTML 信光是開頭的樣式宣告就塞滿讀信的長度上限,關鍵字永遠對不上。
它撐得了 52 天,是因為這條本來一個月才響幾次。「正常」長得跟「壞掉」一模一樣的東西,可以壞很久。
順手看到:系統其實每個月都有說
憑證自動續期每月 1 號跑,從 5 月起連續三次失敗,每次都有寄信。這封信我每個月都收到,每個月都沒細看。
為什麼沒細看:另一套腳本用 HTTP 回應碼判斷重啟成功沒有,但那個服務重啟時常態回 504,所以每一次成功的重啟都會寄出一封失敗信。收了幾個月「其實沒事」的失敗信,那個信箱的每一封都變成雜訊,包括真的那三封。
8 月底的續集:手動續期,部署程式回報成功,線上憑證卻沒換。最後是放在家以外的一台監視器量到「還剩 20 天到期」才叫的。這是五次裡唯一一次,是系統自己發現的,因為它是唯一一條「等報平安」的線。
AI agent 也一樣
同樣的事,在智慧家庭以外也天天發生。我現在有幾件事是讓 AI 寫好腳本、每天自己跑的,其中一支負責「部落格上線之後,去 FB 貼文底下留連結」。它判斷部落格上線了沒的方法,是去抓網址看有沒有回 200;但網站的防護把它擋成 403,它就一直判定「還沒上線」,等時間到了放棄,只寫一行 log,沒有通知任何人。那則留言漏掉了,是我自己看到才補上的。
形狀跟上面五次一模一樣:沒有報錯,不代表有做到。現在這類排程的做法是,做完要報到,失敗要推播到手機,不是只寫 log。
2026 年,三層監控怎麼分工
現在有現成的工具,但它們管的範圍不一樣,得三層都裝。

第三層先講:heartbeat,該發生的有沒有發生
這層管「太安靜」和「順手看到」那兩次,也是 AI agent 最需要的那一層:任務做完要來報到,超過時限沒報到就叫。
自架的 Uptime Kuma 有 push 型監視器,Healthchecks.io 是專門做這件事的服務:任務真的完成之後打一個網址說「我活著」,時限內沒收到就發警報。
兩個重點。第一,報到的依據要是「任務真的完成了」,不是「腳本執行了」:續期腳本要驗的是憑證到期日往後移了,重啟腳本要驗的是服務活著回來了。第二,收心跳的那台不能是被監控的那台,Home Assistant 停電的時候,它連「我停電了」都發不出去。我家現在在家以外放了一台只負責收心跳的監視器,家裡的系統每分鐘報到一次。
第一層:裝置有沒有活著(Zigbee2MQTT availability)
Z2M 內建,預設關閉。 開了之後失聯的裝置會真的變成 unavailable,而不是留著過期的值假裝還活著。兩個參數:active 給插電、會主動回報的裝置(預設 10 分鐘),passive 給電池型裝置(預設 25 小時)。全域值偏鬆,之後再逐台收緊。
第二層:設定有沒有腐化(Watchman)
HACS(Home Assistant Community Store)上的套件。 它掃設定檔裡引用到的每一個 entity 和服務,檢查現在還存不存在。喇叭重新配對後名字變了,27 處自動化瞬間指向不存在的 entity,就是這一層該抓的。它判斷「可用」是看 Home Assistant 的狀態,所以值凍結的那種失聯它一樣看不見,不能取代第一層。
可以直接抄的部分
離線監控 checklist
- 至少一個心跳接收器放在家以外的地方,排程任務做完要報到
- 檢查所有會寄警報的腳本:它的成功判準是「結果」還是「回應碼」
- 失敗要推播到你會看的地方,不是只寫 log
- Z2M 的
availability打開,active與passive兩個時限分開設 - 加一條每 30 分鐘掃 unavailable 的自動化(YAML 在下面)
- 通知只在清單有變化時才發,不然很快就會被你靜音
- 電池型裝置額外做一張電量清單,低於門檻先提醒
- 所有 device trigger 改成 state trigger 並加上
from: - 每一個
automation.turn_off找出對應的turn_on,而且不能排在會失敗的步驟後面 - 裝 Watchman,改名、換裝置、刪整合之後跑一次報告
- 每一條「很少觸發」的自動化,在儀表板上放「距離上次觸發幾天」
離線通知自動化
這條是我實際在跑的版本,helper 名稱換成佔位名。每 30 分鐘掃一次所有 MQTT 來源的 unavailable entity,收斂到裝置層級去重,只有清單跟上次不一樣才發通知。
# 先建一個 input_text helper 存上一次的離線清單,名字自己取,下面用 offline_device_list 當佔位
alias: Zigbee 裝置離線通知
mode: single
triggers:
- trigger: time_pattern
minutes: "/30"
actions:
- variables:
offline_list: >-
{{ integration_entities('mqtt')
| select('is_state', 'unavailable')
| map('device_attr', 'name_by_user')
| reject('none') | unique | sort | join(', ') }}
previous: "{{ states('input_text.offline_device_list') }}"
- condition: template
value_template: "{{ offline_list != previous }}"
- action: input_text.set_value
target:
entity_id: input_text.offline_device_list
data:
value: "{{ offline_list[:255] }}"
- action: notify.notify # 換成你自己的通知服務
data:
title: "Zigbee 裝置離線狀態變更"
message: >-
{% if offline_list %}目前離線:{{ offline_list }}
{% else %}全部裝置已恢復連線{% endif %}
[:255] 是因為 input_text 有長度上限。name_by_user 是你在介面上給裝置取的名字,沒取名的會被 reject('none') 濾掉,所以要先把裝置都取好名。
最後
系列最後一篇怎麼開始會把監控排在第四站,說它是唯一一個晚裝代價會累積的;這篇就是那個代價。checklist 跑一遍,你會知道家裡有幾台裝置正在假裝活著,也會知道你的排程和 AI agent 裡,哪幾支壞了其實沒人會發現。
回家流程那串自動化裡做了哪些事、家裡怎麼判斷有人正在回家,這裡不寫。
下一篇從資安講起:家裡一台閘道器,正連著一台在中國的伺服器~
