Change、Switch、Range、Join/Split 等流程邏輯
先用 Change 建立一致的訊息結構,再用 Switch 與 Range 做可解釋的分流;只有資料量有界且確實需要逐項處理時,才引入 Split、Join、Sort 與 Batch。這一章全程使用手動輸入與 Debug,不觸發真實家庭設備。
為何需要本章
一條可靠的家庭流程不只要處理「正常溫度 23」,還要明確處理缺值、字串、超界數字、未知 topic,以及序列少一片的狀況。把所有邏輯塞進單一 Function 雖然可能運作,卻不容易從畫布看出資料在哪裡被改寫、哪個條件導向哪個輸出。核心邏輯節點能把這些決策顯示在節點與接線上。
Node-RED 5.0.2 的核心 function 群包含 Switch、Change、Range,也包含 Template、Delay、Trigger、Exec、RBE 與 Function;本章只深入前三者,Delay/Trigger 在第 7 章,Template 與 Function 分別在第 15 章、第 16 章。需要處理陣列或批次時,再閱讀後半的 Split/Join、Sort、Batch。
核心觀念
- Change 是資料映射:可 set、change、delete、move msg/flow/global property,typed value 決定來源與型別。規則依畫面順序作用,所以後一條可讀到前一條的結果。
- Switch 是路由:讀取指定 property,依規則送到一或多個輸出;是否檢查全部符合規則是重要語意。一定要規劃 else 或明確的丟棄理由。
- Range 是數值轉換:把輸入範圍線性映射到輸出範圍,並可對範圍外值採 scale、clamp、wrap 或 drop。它不是資料驗證器,非數值輸入仍應先攔截。
- Sequence 是多則訊息的集合關係:Split 產生多則帶
msg.parts的訊息;Join、Sort、Batch 依這些 metadata 或手動條件保存訊息後再輸出。 - 等待代表資源:Join、Sort、Batch 等待中的每則訊息都占用記憶體。來源若可能無限或遺失結尾,必須限制大小、設定可終止條件並提供錯誤觀察。
邏輯設計需把「沒有輸出」分成可辨識原因:條件不符合、Range drop、序列尚未完整、錯誤或節點停用都可能看起來一樣。每個階段放一個暫時 Debug,並用節點名稱寫出動詞,例如「拒絕非數字」、「舒適範圍」、「等待整批」。
匯入並驗證安全分流範例
- 先閱讀 Flow JSON。
開啟 02-switch-routing.json,確認只有 tab、Inject、Switch、Debug,不含 credentials、HA server、網路 endpoint 或副作用節點。
- 匯入到新的測試 tab。
使用 editor 的 Import 功能貼入檔案內容。匯入後再次檢視五個節點;不要在不理解內容時直接部署第三方 JSON。
- 手動測試符合路徑。
範例 Inject 送出字串「開啟」,Switch 讀取
msg.payload,第一條規則以 string 比較,第二條為 else。觸發後應只有對應 Debug 顯示值。 - 建立反例。
把 Inject 複製一份,分別測試「關閉」、空字串與 number。確認它們進入其他條件,而不是把畫面文字相似誤認為型別相同。
- 再擴充 Change 正規化。
在 Switch 前加入 Change,把測試輸入移到
msg.command並設定固定msg.topic。逐一檢查每條規則的 typed value,部署後重跑所有案例。
完成後停用不需要的 Debug。範例只示範 routing;「開啟」是文字,不會呼叫 Home Assistant Action。要操作設備時,仍須在後續章節加入 allowlist、狀態前置條件與明確 target。
Change:建立可維護的資料邊界
Change 適合做欄位搬移、預設值與資料結構正規化。5.0.2 編輯器提供 set、change、delete、move 四類規則;set/change 的來源可使用 typed input,set 也能選 deep copy。以下是室內感測輸入的規劃,不要求你填真實 entity ID:
| 規則 | 來源 | 目的 |
|---|---|---|
| set | msg.payload.temperature | msg.measurement.value |
| set | string °C | msg.measurement.unit |
| set | msg.topic | msg.measurement.source |
| delete | msg.authorization | 在進入通用診斷分支前移除不應存在的敏感欄位 |
規則順序會改變結果:先 move payload 再讀 payload.temperature,後一條就找不到原路徑。編輯後用完整訊息 Debug 對照。若從 object 複製到另一欄且兩邊都會被修改,勾選 deep copy 並驗證巢狀欄位互不影響。
Change 也能寫 flow/global context,但「能寫」不代表該寫。訊息內的暫時衍生值優先留在 msg;跨訊息狀態才考慮 context,且要在第 7 章設計範圍、上限與重啟語意。不要把 credentials、完整事件或無界陣列放入 context。
用名稱描述輸入輸出比「change 1」有效。例如「映射測量欄位」讓 reviewer 一眼知道作用;節點 description 可記錄接受型別與缺值路徑。若規則變多到難以閱讀,拆成「驗證後映射」與「清理公開輸出」兩個節點。
Switch 與 Range:條件與數值政策
Switch 的 property 可以是 msg、flow、global 或 expression 等編輯器提供的型別。先用一條規則處理「不存在/型別不符」,再做業務區間,最後保留 else。對溫度測試值可分為:低於下限、在舒適範圍、高於上限、其他;各輸出接 Debug 驗證,而不是直接接暖氣或冷氣 Action。
「check all rules」與「stop after first match」會影響同一訊息是否走多個輸出。重疊條件例如「大於 20」與「大於 25」可能同時符合。若輸出最終會造成副作用,通常先設計互斥範圍,再用測試矩陣證明每個輸入只走預期路徑。不要靠輸出 wire 的視覺位置推論先後。
| 測試輸入 | 預期 | 要避免的誤判 |
|---|---|---|
| number 23 | 正常範圍 | 與 string "23" 混淆 |
| 缺少欄位 | 缺值分支 | 落入 else 後被當成正常 |
null | 無值分支 | 當成 0 |
| 極端 number | 超界分支 | 未驗證就交給 Range |
Range 5.0.2 的 action 選項在原始碼中為 scale、clamp、roll、drop。假設把亮度百分比 0–100 映射為內部 0–1:scale 會依線性比例轉換;clamp 把超界結果限制在輸出端點;roll 將超界值繞回;drop 不輸出超界訊息。四種都是產品政策,沒有普遍正確答案。家庭控制通常應在 Range 前拒絕異常來源,而非以 clamp 掩蓋感測錯誤。
進階選讀:Split 與 Join
若你目前只需要條件分流,可先跳到故障排除;需要處理陣列或批次時再回來。
展開 Split/Join 的序列細節
Split 可拆分 string、array、object 或 Buffer;它建立 msg.parts 描述 sequence。對 array,原始碼會設定 sequence id、index、count、len;對 object 另有 key;對 string/Buffer 會帶 type 與分隔相關資訊。若輸入原本已有 parts,Split 會將舊 parts 放進巢狀 stack,讓 Join 有機會還原多層序列。
情境:Inject 一個最多五筆的虛構房間讀值陣列,Split 成單筆,Switch 移除無效值,Change 映射欄位,最後 Join 回 array。這裡有一個關鍵失敗:若 Switch 丟掉其中一片,原本自動 Join 可能仍依原 parts.count 等待不存在的訊息。核心 Switch 對 sequence 有專用規則與 pending group 行為,但你仍須設計「少片」政策,不能假設所有任意過濾都會自動修正 metadata。
Join 有自動、手動與 reduce 類型的組合能力;選項以 5.0.2 編輯器為準。自動模式依 Split 產生的 metadata 還原;手動模式要明確定義 count、timeout 或完成訊號。無結束標記的串流不適合無界等待。即使設定 timeout,也要決定部分資料是丟棄、標記不完整,或送往隔離分支。
nodeMaxMessageBufferLength 作最後防線,但流程本身仍要有界。測試至少包含:空陣列、單元素、正常多元素、途中丟一片、兩個 sequence 交錯、重複 index。Debug 觀察 msg.parts.id/index/count,對外分享前刪除任何環境資料。
進階選讀:Sort、Batch 與 msg.parts
展開排序、批次與 msg.parts 細節
Sort 可以排序單一訊息內的 array,也可依 msg.parts 收集 sequence 後排序。sequence 模式會檢查 parts 至少有 id/index,完成後重寫 index。排序 key 的型別與方向必須明確;把 number 當 string 排序會得到不同結果。對相同 key 是否需要穩定的次序,應以測試資料確認,而不是依賴偶然到達順序。
Batch 5.0.2 支援以數量重疊批次、時間間隔,以及按 topic 串接 sequence 等模式。它會建立或更新 msg.parts;某些模式要求 id/index/count 完整。重疊窗口會讓同一訊息出現在多批,這是設定語意,不是重複傳送 bug。時間批次可能在無資料時是否發空 sequence,取決於選項;不要憑名稱猜測,部署前用 Inject 實測。
msg.parts 欄位 | 角色 | 常見失敗 |
|---|---|---|
id | 區分並行 sequence | 自行覆寫造成兩批混合 |
index | 片段位置,通常從 0 起 | 過濾後留洞、重複或亂序 |
count | 已知總片數 | 聲稱的片數永遠到不齊 |
type/key/ch/len | 描述原容器與重組資訊 | Change 誤刪導致 Join 重組結構錯誤 |
parts | 巢狀 sequence stack | 多次 Split 後不理解層級 |
家庭情境可用 Batch 將短時間內的純測試通知摘要成小批,但不要用它延後安全警報,也不要讓窗口無界。排序與批次後都接 Debug 驗證 count、index 與 payload,再交給任何外寄通知節點。若流程重啟,記憶體中的待處理序列不應被假設一定保留;應允許來源重送或安全地放棄不完整批次。
故障排除
- Switch 同一訊息跑到兩個輸出:檢查規則是否重疊以及是否設定檢查所有規則。把條件改成互斥並重跑邊界值,不要靠 wire 順序消除副作用。
- Range 後完全沒有訊息:確認 property 是 number、路徑存在,並檢查 action 是否為 drop。用 Range 前後兩個 Debug 比對,避免直接改成 clamp 掩蓋來源錯誤。
- Join 永遠等待:觀察每片
parts.id/index/count,確認中途 Switch 或 Catch 是否丟片。設定有界 count/timeout 與不完整資料政策,必要時重建 metadata。 - 兩批資料混在一起:檢查是否手動覆寫
parts.id或 topic;以兩組交錯 Inject 重現,保持 sequence ID 由產生節點管理。 - Sort 看似數字順序錯誤:確認排序 key 是 number 而非 string,測試 2 與 10。若資料來自 parser,先在 Change 中驗證與轉換。
- 記憶體持續成長:檢查 Join/Sort/Batch pending sequence、Delay queue 與輸入速率。停止測試輸入,縮小 count/timeout/窗口並設定 runtime buffer 上限;不要只增加記憶體。
固定來源
- Node-RED 5.0.2 固定提交 61bd08d:
@node-red/nodes/core/function/10-switch.*、15-change.*、16-range.*與core/sequence。 - Node-RED 官方文件:Core nodes。
- Node-RED 官方 Cookbook:Route a message based on a property。
- 本站安全 Flow:02-switch-routing.json。
若其他文件的 UI 選項或預設不同,先核對 Node-RED 版本,再匯入本章安全範例,以手動輸入確認路由與序列行為。
常見問題
Change 和 Function 應選哪一個?
兩個來源同時送批次時,如何避免 Join 混在一起?
parts.id,不要用固定值覆寫;再以兩組交錯輸入驗證。若來源沒有可靠序列邊界,先建立有界且可終止的分組契約。Range 的 clamp 能代替輸入驗證嗎?
Split 後刪掉幾片,Join 會自動知道嗎?
parts.count 可能仍指向完整數量,導致等待。使用 sequence-aware 規則或重新設計 count/timeout,並用少片案例驗證。