Current State、Get Entities 與條件判斷
Current State v3 收到 msg 後從 HA WebSocket cache 讀單一 entity;Get Entities v1 則掃描 cache,依 state、device、area、floor、label 條件建立集合。兩者都不是即時 REST 查詢,也不是歷史資料。本章明確定義輸入覆寫、輸出 mapping、空結果與結果量上限。
為何需要本章
狀態查詢通常位於事件與副作用之間:上游說「有人移動」,Current State 再檢查模式是否允許;或 Get Entities 找出符合規則的候選集合。若把 cache 當成即時真相、把不存在當成 false,或允許不可信 msg.payload.* 改寫查詢,原本的安全條件就可能被繞過。
Current State 與 Get Entities 都有一個 input,但契約不同。Current State v3 可用 Block Input Overrides 鎖住設定的 entity;Get Entities v1 沒有相等的切換,固定會解析 msg.payload.rules 與多個 output 設定覆寫。這個差異決定了你應在哪裡清除輸入,以及能否把 HTTP、MQTT 或 webhook 訊息直接送入節點。
"d": true,Server/entity 是 PLACEHOLDER_*。只做文字檢視與匯入前審查,不連線、不部署、不接 Action。cache、歷史與等待不是同一種查詢
| 節點 | persisted type | 資料來源/生命週期 | 主要限制 |
|---|---|---|---|
| Current State v3 | api-current-state | 收到 msg 時讀單一 cached state | cache miss 會 error;可鎖 input override |
| Get Entities v1 | ha-get-entities | 收到 msg 時掃描 cached states,必要時查 registry | 無 Block Input Overrides;集合可能很大 |
| Get History | api-get-history | 對 HA history 發出查詢 | 沒有內建結果上限或請求 timeout;必須在架構外限制 |
| Wait Until | ha-wait-until | 保留一筆傳入 msg,等待條件或 timeout | 每節點只有一個 active slot;後來輸入會取代前一筆 |
本章同時比較 Current State、Get Entities、Get History 與 Wait Until。Current State/Get Entities 適合快速查看套件維護的 cache;Get History 適合由外部政策限制的過去區間;Wait Until 適合有明確 timeout 的單筆流程暫停。不要用 Get History 模擬高頻 current state,也不要把 Wait Until 當成多訊息等待佇列。
cache 由 WebSocket 連線與 state_changed 更新。它可能在啟動中尚未完整、斷線後陳舊、entity 移除後缺少,也可能保存 state 為 unknown/unavailable。節點輸出時間戳不等於你已證明資料新鮮;安全決策仍要把連線狀態與缺值納入 fail-closed 路徑。
安全檢視兩個停用範例
- 以文字工具閱讀檔案。
先看 05-current-state.json 與 06-get-entities.json。確認各只有手動 Inject、停用的 HA node、有限 payload Debug;這一步不執行任何 HA 操作。
- 核對版本與 placeholder。
Current State type/version 應為
api-current-state/3;Get Entities 應為ha-get-entities/1。兩者的 Server 及 entity 都只能是完整PLACEHOLDER_*。 - 檢查 Current State override 邊界。
範例的
blockInputOverrides是 true,因此設定的entity_id不接受msg.payload.entity_id或msg.payload.entityId取代。保留這個安全預設。 - 清除 Get Entities 覆寫欄位。
在紙面契約列出並拒絕外部輸入的
payload.rules、outputType、outputEmptyResults、outputLocationType、outputLocation、outputResultsCount。v1 沒有一個 checkbox 可以代替這份允許清單(allowlist)。 - 定義空結果與上限。
決定 0 筆是送空陣列、送數字 0、還是不送訊息;再為候選 entity 數、Debug 欄位與下游 fan-out 設定上限。不要把「測試環境只有幾個」當成永久限制。
- 先驗證失敗支線。
用紙面/單元測試涵蓋 cache miss、unknown、unavailable、0 筆、多筆超限、registry metadata 缺少。所有輸出只進隔離 Debug,且在現場審查前維持 HA 節點停用與副作用斷開。
這兩份範例都不是按一下即可執行的配方。Import 本身只把 JSON 放進 editor;但在未完成安全審查前仍不要進行 Deploy。真實 entity ID、device/area/floor/label ID 與完整 attributes 不應出現在截圖、issue 或公開 flow。
離線三列練習:Current State 與 Get Entities 節點保持停用且不連 HA,Action 全程斷開;只用 placeholder 紙面資料比對下列輸出契約。
| 假情境 | 紙面輸入 | 預期輸出/不動作 |
|---|---|---|
| Current State cache hit | PLACEHOLDER_ENTITY_ID 對應 PLACEHOLDER_STATE | 有限 Debug 收到 msg.payload="PLACEHOLDER_STATE";只供判讀,Action 仍斷開 |
| Current State cache miss | cache 中沒有 PLACEHOLDER_ENTITY_ID | 拋 InputError,由 Catch 進 NO_ACTION_CACHE_MISS;沒有正常輸出 |
| Get Entities 為 0 筆或超過核准上限 | 固定 rules 回傳 0,或 PLACEHOLDER_RESULT_COUNT > PLACEHOLDER_APPROVED_LIMIT | Count 可輸出 0;超限由 count gate 拒絕。兩者都不送 array/split、不接 Action |
Current State v3:單一 entity、比較與 mapping
v3 的 InputService 定義唯一可覆寫輸入 entityId:先看 msg.payload.entity_id,也接受 camelCase 的 msg.payload.entityId,否則使用設定的 entity_id。entity ID 還會經 Mustache render。當 Block Input Overrides 為 true 時,InputService 停用 message override,設定值勝出;若關閉,任何能寫入上述 payload 欄位的上游都可能改查另一個 entity。
controller 用 WebSocket getState(entityId) 讀 cache。找不到時拋出 InputError「not found」,不會送出一個 state=false 的正常訊息。找到後加入 timeSinceChangedMs;若選非字串 state type,保存 original_state 再轉換。這個物件來自 cache,設計下游時不要隨意改寫它的巢狀 attributes。
| 欄位 | v3 行為 | 安全建議 |
|---|---|---|
| Entity ID | 設定值可含 Mustache;未 block 時可被兩種 payload key 覆寫 | 使用完整 placeholder 文件化;Block Input Overrides 保持 true |
| If State | 比較目前 entity.state;設定後 true/false 分別走兩輸出 | 字串明確比較,unknown/unavailable 另分流 |
| For | 比較 timeSinceChangedMs 是否大於指定 duration | 是 cache timestamp 計算,不是等待 timer,也不是歷史證明 |
| State Type | 預設 string;非字串保留 original_state | 避免一般 boolean 的非空字串陷阱 |
| Output Properties | 預設 payload=state string、data=完整 entity | 改成最小允許清單,不固定假設 payload |
If State 未使用時,通常只有正常輸出。使用 If State 或 HA boolean 類型後,成立送第一輸出,不成立送第二輸出;第二輸出不是 error。For 只在條件先成立,且 comparator 為 is、is not、includes、does not include 時生效;duration 可來自 number、JSONata、msg、flow 或 global,負數會 error。
時間判斷採 entity.timeSinceChangedMs > forDurationMs,是嚴格大於而非大於等於。它依 last_changed 計算,不會等待到時間到,也不會查 recorder。若你要「等待直到」,應評估 Wait Until 並設定 timeout;若要證明過去區間,使用受限 Get History。
Get Entities v1:規則、覆寫與 result modes
v1 每次收到 msg 都取得 cached states,逐一檢查規則。state condition 可讀 state object 的 property;device、area、floor、label condition 需要 entity registry 及相關 registry metadata。area 可來自 entity 自身或 device;floor 由 area 取得;label 會檢查 entity、device、area 上的 labels。metadata 缺少時,該條件不會憑空匹配。
多條 rules 是逐條檢查,只要一條失敗就排除,等同 AND。runtime 會先排序 label、state、device、area、floor 來執行,但你不應依排序製造副作用;rules 只應是純比較。state property 不存在時,除 JSONata 特例外會判定不符合。
| outputType | 輸出形狀 | 0 筆行為 | 限制重點 |
|---|---|---|---|
array | HassEntity 陣列寫到指定 msg/flow/global location | outputEmptyResults=true 才送空陣列;否則不送 | 沒有內建最大筆數;下游前必須自行限制 |
count | 數字寫到指定 location | 送出 0 | 不包含 entity 明細,適合先做門檻 |
random | 上限 1 時為單一 entity;大於 1 時為陣列 | 不送 | outputResultsCount 只控制 random;訊息覆寫須先驗證為整數且 ≥1 |
split | 每 entity 一個 msg,payload 為該 entity | 不送 | 會 fan-out;必須先估算數量與 downstream 容量 |
Array、Count、Random 可把結果寫到 msg、flow 或 global 的指定 location;Split 不顯示 output location,固定逐筆放進 msg.payload。使用 flow/global 會讓資料跨訊息保存,可能留存完整 attributes;除非有明確 retention 與讀者,否則使用 msg 並立即縮減欄位。
Random 是抽樣,不是安全分配、公平輪替或密碼學隨機。Split 會 fan-out;選用前先限制候選量,下游若 Join 也要限制等待序列並設定 timeout。
_msgid,建立 msg.parts(含新的 sequence id、count 與逐筆 index),再 clone 每則訊息;下游 Join 應依 msg.parts 辨認序列。Get History 與 Wait Until 的精確資源邊界
Get History 0.80.3 會讀取六個 msg.payload 覆寫:startDate、endDate、entityId、entityIdType、relativeTime、flatten。它沒有 Block Input Overrides,因此不可信入口前必須刪除這六欄,或依允許清單與日期 schema 重建乾淨 payload。這個節點也沒有內建結果筆數上限;底層 Axios client 未設定 request timeout。短時間窗、單一 entity、回應大小預算、代理/呼叫端 timeout 與中止政策都是你必須在節點外建立的控制,不能描述成節點保證。
Wait Until controller 每個節點只有一個 active slot,保存一組 message 與 config。後來的輸入會先取消現有 timer,再取代已保存的訊息;它不是 pending queue。timeout=0 表示完全不建立 timer,可能一直等到條件成立、收到 reset、被後來輸入取代或節點停止。安全設定使用有限的正 timeout,將成功與逾時接到不同輸出;逾時只代表期限內未觀察到條件,不得直接觸發副作用。
輸入 override、輸出 mapping 與最小資料
Current State 的 Block Input Overrides 是明確安全界線:true 時 message 不能改 entityId。Get Entities 沒有同等 toggle;其 InputService 固定把下列 message properties 當候選設定。只要上游 payload 存在合規型別,便可取代 editor 設定。
| message property | 可改變什麼 | 不可信輸入處理 |
|---|---|---|
msg.payload.rules | 整組篩選規則 | 刪除;只由受信任 Change node 建立固定允許清單 |
msg.payload.outputType | array/count/random/split | 刪除,避免改成 split 放大訊息 |
msg.payload.outputEmptyResults | 空陣列是否仍輸出 | 刪除,避免改變控制流 |
msg.payload.outputLocationType | msg/flow/global | 刪除,防止跨 scope 寫入 |
msg.payload.outputLocation | 實際 property/path | 刪除,防止覆寫其他訊息或 context 欄位 |
msg.payload.outputResultsCount | random 回傳上限;runtime schema 只檢查 number | 刪除;若確實允許,先驗證為整數且 ≥1 |
「先驗證後轉送原 payload」仍不夠,因為危險 override key 還在。安全做法是建立全新的 msg 或先逐欄刪除,再由受信任節點寫入固定 rules。對 HTTP、MQTT、webhook 等來源,來源驗證與 schema 驗證都要做;即使 schema 合法,也不能允許任意 property path 或 global location。
輸出 mapping 要有資料契約。Current State 範例將 state 字串寫 payload、entity object 寫 data;Get Entities 範例將 array 寫 payload。若下游只需要 state 與 entity_id,就不要傳完整 attributes。Debug 設為指定 property,完成測試後停用;完整 entity 可能含位置、人物、裝置、媒體或診斷資訊。
一個安全的唯讀門檻可先用 Get Entities 的 count 模式確認候選數在預期區間,再在另一個受限查詢取得明細;但兩次 cache 掃描間仍可能變動,不能宣稱交易一致。需要原子業務語意時應在更合適的 HA action/automation 設計處理,而不是依 Node-RED 兩次讀取猜測。
missing entity、unknown/unavailable 與結果上限
Current State 的 entity 不在 cache 時會走 error,應由 Catch 捕捉並用 Status 觀察,只記錄清理後的 entity placeholder、節點名稱與錯誤類別,再終止控制流;不要把 error 自動改成 false 後接 Action。unknown 與 unavailable 則是找到 entity 後的正常字串值,應在 If State 或 Switch 分開路由到「不採取副作用」支線。
Get Entities 可能因 rules 太嚴、registry 尚未載入、metadata 缺少或 cache 空而得到 0 筆。Array 的 outputEmptyResults 會改變「有一則空訊息」與「完全不送」;Count 一定可以送 0;Random/Split 在 0 筆時不送。下游若有 timeout 或 Complete,必須知道是哪種契約,不能把沉默當成成功。
v1 的 array 與 split 沒有通用最大結果數。outputResultsCount 只用於 random,而且來自 message 時只通過 number 檢查;上游必須進一步拒絕小數、0 與負數,確保是整數且 ≥1。若 selector 可能涵蓋大量 entity,優先收窄 rules;再於下游用 count gate 拒絕超限,不要先 split 後才限流。對 flow/global output 另設保存期間與大小預算。
Get History 的 entity 允許清單、短時間窗、可接受筆數與請求 timeout 都是外部政策,不是 0.80.3 內建限制,本範例庫也沒有專用 Get History Flow。只有 Wait Until 有安全欄位範例 08-wait-until.json(節點停用、placeholder、10 秒 timeout、兩個輸出);它展示單一 active slot 的有限等待,不要拿來替代本章兩個 cache 範例。
故障排除
- Current State 回報 not found:不要建立假 false 訊息。檢查 placeholder 尚未被誤當正式 ID、cache 是否 ready、entity 是否已移除/改名、Block Input Overrides 是否阻擋預期輸入;保持副作用支線停用。
- 查到的不是設定 entity:檢查 Block Input Overrides 是否被關閉,以及上游是否帶
payload.entity_id或payload.entityId。重新開啟 block,清除 message key,再做紙面契約測試。 - Get Entities 規則在 editor 正確,runtime 卻不同:查看上游 payload 是否帶六類 override。v1 沒有 block toggle;用 Change 建立新訊息而非沿用外部 payload。
- 0 筆時下游沒有收到訊息:核對 outputType。Array 需要 outputEmptyResults=true 才送空陣列;Count 會送 0;Random/Split 都不送。不要把沉默誤判成節點故障。
- Split 造成大量訊息:立即中斷下游副作用,先改用 Count 估算,收窄 rules 並設定你的硬上限。
outputResultsCount對 Split 無效。 - For 邊界差一點:Current State 使用 timeSinceChangedMs 嚴格大於 duration,且依 last_changed。確認時鐘、state 是否真的 changed、duration 型別與單位,不以 recorder history 混解。
- device/area/floor/label 條件漏掉 entity:registry entry 或關聯 metadata 可能不存在。依 state、entity registry、device、area、floor、label 順序查缺值;不要放寬成全域 state 規則繞過。
固定來源
- HA WebSocket 0.80.3 固定提交 2cbbb69:核對
src/nodes/current-state、get-entities、get-history、wait-until與 shared InputService/SendSplit 行為。 - Current State 官方節點文件。
- Get Entities 官方節點文件。
- Home Assistant 官方 state object 文件:state、attributes、last_changed/last_updated 背景。
rolling 官方頁若與 0.80.3 固定提交不同,以固定提交為本章精確語意。安全下載:05-current-state.json、06-get-entities.json;兩者均使用 placeholder 且 HA 節點停用。
常見問題
Current State 會即時向 HA API 查一次嗎?
Block Input Overrides 會保護 Get Entities 嗎?
msg.payload.* override。outputResultsCount 是所有模式的結果上限嗎?
Current State 找不到 entity 時會從第二輸出送 false 嗎?
Split 模式如何識別同一組訊息?
msg.parts.id、count、index 並逐筆 clone,payload 是單一 entity。下游仍需限制序列數、Join 等待與 timeout。