第 14 章

通知、門窗、氣候與異常處理實戰

通知與氣候控制會把感測資料轉成對人或設備的外部副作用。安全流程必須先驗證門窗與溫度,再做去重、速率限制、人工覆寫與 unavailable 分流。本章只產生「候選意圖」;通知與氣候 Action 全部停用,沒有真實通知 target、裝置 ID 或實體 ID。

為何需要本章

一個門窗感測器變成開啟,可以代表通風、忘記關門、感測器抖動或連線恢復後的舊狀態。溫度超過門檻也可能是字串解析錯誤、單位不同、陳舊資料或感測器 unavailable。若每次事件都直接送通知,使用者很快會忽略警報;若直接控制氣候裝置,錯誤資料可能造成耗能、設備頻繁啟停或與人工設定互相爭奪。

本章用三個非安全關鍵情境:

  • 門窗持續開啟後產生一次候選提醒,關閉時清除事件;不控制鎖具或警報。
  • 室內數值超過經核准門檻時產生氣候建議,最後仍需檢查門窗、模式與人工覆寫;不直接設定真實設備。
  • 資料 unknown、unavailable、非數值、過期或通知過密時走異常分支;不把異常當成可執行條件。
本章不發送任何通知,也不控制任何氣候裝置。所有 Home Assistant 監聽、查詢、entity 與 Action 節點保持停用。佔位符 PLACEHOLDER_NOTIFY_ACTION、PLACEHOLDER_CLIMATE_ENTITY 與 PLACEHOLDER_OPENING_ENTITY 不得替換成真值後直接部署。
palette 套件不等於 HA custom integration。Events: calendar、Tag、Zone 與 Action 等一般節點使用 Home Assistant Server 的 WebSocket/API 連線;Sentence,以及 Select、Binary Sensor、Button、Number、Sensor、Switch、Text 等 HA entity 節點,另要求 Home Assistant 安裝並載入 hass-node-red custom integration。本章不假設其版本;缺少它時必須停用相關分支。連線與節點分類先回看第 8 章。

事件、異常、提醒與控制是四條支線

分支輸入條件可產生結果禁止捷徑
狀態事件門窗或氣候實體的已知新狀態建立/更新候選事件事件一到就呼叫 Action
資料異常unknown、unavailable、null、非數值、過期遮罩診斷與健康計數把異常轉成 0、false 或 closed
通知事件持續、去重通過、速率額度存在候選通知意圖用動態上游 target 或無限重試
氣候控制數值、單位、門窗、模式、覆寫都有效候選控制意圖只看單一溫度就操作設備

通知是 Action 的一種使用情境,但 action 名稱、可用 target、data schema 與實際回應由 Home Assistant 中已安裝的通知整合決定。不能宣稱每個通知 action 都接受相同欄位,也不能把「節點沒有報錯」當作收件端已收到。氣候 action 同樣沒有跨整合的通用成功回應;驗證要依核准整合的文件與後續狀態,且限制重試。

使用者可以從 Home Assistant、設備面板、遙控器或另一條自動化改變氣候設定。manual override 必須是流程中的第一級狀態,不是錯誤。覆寫有效期間,流程可以觀察與提出診斷,但不送反向控制意圖;到期時重新評估,不自動恢復舊設定。

八步完成安全規格

  1. 建立資料契約。

    列出每個來源的 entity 類型、原始 state、attributes、單位、更新時間與允許值。使用整值佔位符,不在文件、Flow、Debug 放入真實家庭 ID 或通知目標。

  2. 把異常值先分流。

    unknown、unavailable、null、空字串、NaN、Infinity、單位不符與超出物理合理範圍先進 INVALID;只有明確通過的值才進門窗或氣候邏輯。

  3. 加入持續時間與恢復事件。

    門窗開啟需持續一段核准時間才建立提醒;關閉時取消尚未送出的候選並結束事件。氣候門檻使用回差,避免在邊界頻繁切換。

  4. 設計去重鍵與速率限制。

    去重鍵以匿名區域、事件類型與事件 generation 組成;為每事件冷卻、每區域上限與全域上限設定有限窗。超額只記計數,不排隊補發大量舊通知。

  5. 檢查相依狀態。

    氣候候選到期前,用 Current State 重查室內數值、門窗、設備模式、資料新鮮度與 manual override。任一查詢失敗都不控制。

  6. 只建立停用 Action。

    依07-action.json審查 action、target、data、queue 與 Block Input Overrides;保留 d: true,target 只用佔位符,Action 不接上游。

  7. 用假資料做矩陣測試。

    涵蓋開啟抖動、恢復、重複、通知爆量、非數值、單位錯誤、unavailable、手動覆寫、重啟與 Action 假失敗。測試只有手動 Inject 與 Debug,沒有外部連線。

  8. 分開核准觀察與動作。

    若需測試環境,先只核准唯讀監聽;通知測試 target 與氣候測試設備各自另行審查。正式家庭或生產 target 不在本章測試範圍。

通知 Action:data、去重與速率

候選通知資料

先在純訊息層建立以下抽象物件,不直接傳給 Action:

{
  "kind": "OPENING_STILL_OPEN",
  "anonymous_area": "PLACEHOLDER_AREA_ALIAS",
  "event_generation": "PLACEHOLDER_EVENT_GENERATION",
  "severity": "informational",
  "observed_at": "PLACEHOLDER_ISO_TIMESTAMP"
}

轉換層再依已核准通知 action 的官方 schema建立 data。一般情境可能需要文字內容,但欄位名稱與進階 data 不可跨整合假設;範例不填收件人、裝置、群組、URL、影像或可執行按鈕。訊息內容避免透露「家中無人」、精確地址、門窗 ID 或人員姓名。

Action 節點保險

  • action 固定為 PLACEHOLDER_NOTIFY_ACTION,不允許從 msg 動態覆寫;Block Input Overrides 開啟。
  • target 固定為 PLACEHOLDER_NOTIFY_TARGET,不使用真實 mobile app device ID、person、area、label 或群組。
  • data 由允許清單(allowlist)建構,不合併整個 msg.payload、context 或原始事件。
  • Queue 策略不是速率限制。斷線排隊可能在恢復後集中送出;通知流程通常應丟棄過期意圖,而不是 queue all。
  • Action 輸出只表示節點呼叫流程的結果,不能宣稱所有通知服務都回傳相同內容或終端已顯示。
規則例子超出時
事件去重同一 opening generation 只提醒一次捨棄重複,累加匿名計數
事件冷卻持續開啟期間不反覆提醒等明確恢復後才能建立新 generation
區域速率單一匿名區域每時窗有限次不送,標記 rate_limited
全域速率多感測器故障時保護收件端熔斷通知 Action,保留摘要
過期窗重連後舊事件超過容忍時間不補發,只記恢復摘要

通知失敗不能無限重試。設定最多次數、退避與總時限,且重試仍受同一去重鍵與全域上限控制。敏感警報需求應使用專門且受監控的通道;本章的一般通知不是生命安全保證。

門窗:持續開啟、恢復與隱私

門窗通常由 binary sensor 實體呈現,但實際 state 與 device class 必須在你的整合上查證。不要根據圖示或名稱猜 on 必定代表 open。建立映射前,先在隔離環境觀察已知開與關各一次;本章仍以 PLACEHOLDER_OPENING_ENTITY 取代真實 ID。

已知 CLOSED → 已知 OPEN
  → debounce → 建立 generation → 等待持續時間
  → 重查仍 OPEN → 通知去重/速率 → 候選通知 → 停用 Action

OPEN → CLOSED
  → 取消未到期候選 → 標記 resolved
  → 只有先前確實送過提醒時,才可能建立候選恢復通知

任一 → unknown / unavailable / 缺資料
  → 取消控制意圖 → anomaly 分支 → 不宣稱門已關閉

如果門在持續時間內關閉,不送「仍開啟」提醒。若提醒已產生後才關閉,是否需要恢復通知由產品規則決定;恢復通知與原事件共用 correlation key,避免被當成新的警報。Node-RED 或 Home Assistant 重啟後,先查目前狀態與 last_changed 新鮮度,不根據 context 中舊 generation 補送。

門窗事件能推測出入與作息。Debug 只顯示匿名區域、OPEN/CLOSED/INVALID、持續級距與 decision;不要記錄門名、使用者 ID、精確時間序列或原始 attributes。若要把 Zone、Tag 或 Sentence 加入「有人靠近」條件,風險更高:Zone 涉及位置、Tag 可能含 tag/device/user ID、Sentence 可能含原句。它們只能在另行核准後輸出匿名布林條件,節點保持停用。

Calendar、Zone、Tag、Sentence 的實用但保守用途

  • Calendar 可產生「核准時段」條件,但約 15 分鐘輪詢與臨時變更不適合作唯一保全依據。
  • Zone 可產生匿名 enter/leave 條件,但定位漂移或舊座標不能直接解除門窗提醒。
  • Tag 可作人工確認入口,但 tag ID 不是身分驗證,也不能直接觸發解鎖或氣候控制。
  • Sentence 可收「稍後提醒」意圖;response 模式會對外送回回應,故保持停用,不能假設語音辨識或回應一定成功。

氣候:數值驗證、回差與人工覆寫

數值進入比較前的六道閘門

  1. 值存在,且不是 unknown、unavailable、null 或空字串。
  2. 明確轉成有限數值;拒絕 NaN、Infinity 與混雜單位字串。
  3. 單位與流程設定一致;不能在未轉換時比較攝氏與華氏。
  4. 數值在該感測用途的合理範圍;異常高低不直接觸發控制。
  5. last_updated 或接收時間未超過新鮮度門檻;重連載入的舊值不當新事件。
  6. 來源在核准 exact 清單,未被 substring/regex 意外納入。

不要用單一門檻來回切換。例:高門檻產生「需要降溫」候選,只有降到較低恢復門檻並持續一段時間才清除;兩門檻之間保持現有決策。具體數值取決於空間、設備、健康與能源政策,本章不提供可直接套用的溫度。

候選控制前的相依檢查

  • 相關門窗全部是已知 CLOSED;任何 OPEN/INVALID 都阻擋氣候控制。
  • climate 實體存在且不是 unavailable;目前模式與可用屬性符合該整合文件。
  • manual override 不在有效期;若使用者剛調整設定,流程不反向覆蓋。
  • 最近同類意圖不在 cooldown,設備沒有短週期啟停風險。
  • Action 與 data 已依該實體支援能力核對,不猜測通用 service response。
最後狀態驗證不能省略。門窗事件與溫度事件可能不同步。候選訊息到達 Action 前再做 Current State 查詢;只要其中一項 unavailable、過期或查詢失敗,就輸出 NO_ACTION_INVALID_DEPENDENCY。

人工覆寫與恢復

人工覆寫資料至少包含匿名控制範圍、方向、建立時間、到期時間與來源類別,且有最大期限。覆寫期間仍可記錄「若無覆寫會產生的候選」供調整規則,但不得送 Action。覆寫到期時重新查詢,不重播期間累積的舊候選。若 Node-RED 重啟後無法確認覆寫狀態,預設不控制。

真正 Action 保持停用,使用 PLACEHOLDER_CLIMATE_ACTION 與 PLACEHOLDER_CLIMATE_ENTITY。data 不放實際 setpoint;只以 PLACEHOLDER_APPROVED_VALUE 標示待核准值。對設備的任何操作都要在非正式環境、有限 target、可人工中止的條件下另行驗證。

異常分支與 HA entity 節點清單

0.80.3 除了以一般 WebSocket 節點讀取既有 Home Assistant 實體,也能透過 Entity config 與另裝的 hass-node-red custom integration 建立或互動於 Node-RED 暴露的 entity 節點。只安裝 palette 套件不會滿足這個前提。這些節點不是「更安全的變數」;它們可能接受 Home Assistant 端外部操作或更新狀態,測試時全部停用。下表整理本情境可能用到的節點:

節點本章可用角色安全邊界
Events: calendar核准時段的候選條件輪詢延遲與行程隱私;不得作唯一保全條件
Sentence人工延後提醒的候選輸入原句、device/response ID 敏感;response 有外部副作用
Tag人工確認候選輸入tag/device/user ID 敏感;掃描不等於強身分驗證
Zone匿名位置條件座標敏感且可能漂移;不直接控制門鎖或氣候
Select entity暴露有限選項的覆寫介面listen/get/set 模式依設定;只接受已列選項且節點停用
Binary Sensor entity暴露匿名異常旗標輸入會更新整合實體;unknown 不可硬轉 false
Button entity人工確認候選按鈕按下會觸發流程,不等同授權;不直連 Action
Number entity暴露經限制的門檻候選值必須驗證數值、上下限與單位;set 有副作用
Sensor entity發布匿名健康摘要state/attributes 可能由訊息覆寫;不發布敏感原始資料
Switch entity有限期限的自動化啟用意圖無 persisted value 時 state storage 預設 enabled;須另設 fail-closed startup gate
Text entity受限的非敏感註記listen/get/set 需驗證長度與內容;不可承載秘密或任意 action

統一異常信封

{
  "category": "INVALID_STATE_OR_VALUE",
  "source_alias": "PLACEHOLDER_SOURCE_ALIAS",
  "observed_at": "PLACEHOLDER_ISO_TIMESTAMP",
  "decision": "NO_ACTION",
  "detail_code": "PLACEHOLDER_NON_SENSITIVE_CODE"
}

異常信封不含原始 state、attributes、位置、行程、語句、通知 target 或 device ID。Catch/Status 可收節點錯誤與狀態,但不得形成「錯誤就重送 Action」迴圈。計數達門檻時熔斷該分支,人工審查後再恢復。

故障時的預設

  • 通知整合 unavailable:不無限排隊,不切換到未核准 target。
  • 氣候裝置 unavailable:不控制,也不假裝已達 setpoint。
  • 門窗感測器 unavailable:不視為 closed,阻擋氣候候選。
  • Entity config/integration 未載入:暴露 entity 分支停用,不影響核心不動作策略。
  • 重連或重啟:清除過期候選,重新查詢;不補送舊通知、不重播舊控制。

故障排除

  • 同一門窗事件反覆通知:停用通知 Action,檢查 generation 是否在 CLOSED 前保持不變、去重鍵是否含不穩定時間戳、持續事件是否誤建新鍵。修正後以假事件驗證一次 OPEN 最多一個候選。
  • 重連後大量舊通知:Queue 或重試可能保存過期意圖。加入最大事件年齡、全域速率與恢復熔斷;超時事件只做匿名摘要,不補發。
  • 溫度比較結果顛倒:檢查字串到數值轉換、攝氏/華氏、NaN、門檻與回差方向。保留原始值只在短期受控診斷中查看,確認後立即清除。
  • 門窗 open 卻觸發氣候候選:確認整合的 on/off 語意、device class 映射、Current State 查詢輸出與 Switch 規則。OPEN/INVALID 都應阻擋,不能只排除字串 on。
  • manual override 被忽略:檢查覆寫 scope、到期時間、時區與重啟後儲存。覆寫判斷必須在候選生成與 Action 前各檢查一次;無法讀取時採不控制。
  • Action 節點無錯誤但沒有收到通知:不要推論通用成功回應。保持節點停用,依特定通知整合文件核對 action、target、data、授權與終端狀態;不得用真實 target 反覆試送。
  • Number/Select/Text entity 接到意外值:停用節點,檢查 mode、Entity config 與訊息覆寫。建立選項、範圍、長度與型別允許清單;這些 entity 不是可信輸入邊界。

固定來源與相關節點

本章涵蓋 11 個相關節點:Events: calendar、Select entity、Sentence、Tag、Zone、Binary Sensor entity、Button entity、Number entity、Sensor entity、Switch entity、Text entity。上表整理各自角色與邊界;Sentence 與七種 entity 節點需要 hass-node-red custom integration,所有可寫、觸發或對外回應模式都必須保持停用。Action 是情境依賴,版本欄位參考安全範例 07,不宣稱各通知或氣候整合具有共同 data 或回應。

常見問題

門窗變成 unavailable,可以當成 closed 以免一直提醒嗎?
不可以。unavailable 是不知道,不是已關閉。它應取消控制意圖、阻擋氣候分支並進異常診斷;是否另送健康提醒仍受去重與速率限制。
通知 Action 的 data 可以直接合併 msg.payload 嗎?
不建議。上游可能帶 target、敏感內容或整合不接受的欄位。用允許清單依核准 action schema 重建 data,並開啟 Block Input Overrides。
Queue all 能避免斷線期間漏通知嗎?
它不是可靠通知保證,也不是速率限制。恢復後集中送出舊訊息可能造成通知風暴;一般狀態提醒應設定過期窗並丟棄過期意圖。
溫度高於門檻就能直接呼叫 climate action 嗎?
不能。先驗證數值、單位、合理範圍、新鮮度、回差、門窗、設備狀態、cooldown 與 manual override;任一不明確都不控制。
Switch entity 可以當作安全的自動化總開關嗎?
不可以。它可表達啟用意圖,但不是存取控制;而且 0.80.3 在沒有 persisted value 時把 state storage 初始化為 enabled,不能宣稱預設關閉。設計必須另加明確的 fail-closed startup gate,只有完成狀態載入、權限與相依檢查後才開放,並在 Action 前再次檢查。
收到 Action 輸出是否代表手機已顯示通知或設備已完成?
不能做通用宣稱。不同 action 與整合的回應不同,節點呼叫完成不一定等於終端完成。依特定整合文件與後續狀態驗證,且不要無限重試。