第 4 章

第一條無副作用 Flow:Inject、Change、Debug

用手動 Inject 建立字串訊息,經 Change 改寫 msg.payload,最後只在 Debug sidebar 觀察。流程不含 Home Assistant Action、不寫檔、不發網路請求,也不設定啟動或週期自動 Inject。

為何需要本章

第一條 Flow 的目的不是控制燈,而是證明你理解 Node-RED 的最小執行模型:節點收到一個 message object、處理它,再沿 wire 傳給下一個節點。若一開始就接上真實照明或通知,即使結果「有動」,你也很難分辨是訊息、條件、Home Assistant 連線或 Action 設定哪一層正確。

本章以客廳環境監測的「資料標記」作為想像情境,但只處理範例文字。你按一次 Inject 按鈕,Change 把 msg.payload 從「原始訊息」改成「已完成安全轉換」,Debug 只顯示 payload。沒有真實 entity ID、位置、端點、憑證或私人內容。

成功定義:只有在你主動按下 Inject 後,Debug sidebar 才出現一次字串「已完成安全轉換」。重新 Deploy 或 Add-on restart 都不應自動產生本練習訊息。

核心觀念:msg、節點與 wire

Node-RED runtime 在節點間傳遞 JavaScript object,慣稱 msg。msg.payload 是常用資料欄位,但不是 message 的全部;msg.topic 常用來描述訊息主題,runtime 也會處理 _msgid 等識別資訊。不要假定每個 node 都只讀寫 payload;日後使用節點前要看 Help。

元件本 Flow 的責任不做的事
Inject你按按鈕時建立 msg.payload 字串「原始訊息」不使用 interval、不使用 schedule、不在啟動時自動 inject
Change把 msg.payload設為字串「已完成安全轉換」不執行 JavaScript、不呼叫 Home Assistant
Debug把 msg.payload 顯示在 editor Debug sidebar不輸出 console、不設 node status、不顯示 complete message
Wire定義 Inject → Change → Debug 的訊息路徑畫面左右順序本身不會傳訊

Change node 屬於 Node-RED core function nodes,但本例使用它的視覺化規則,不需要 Function node。這能先把「資料轉換」與「自行撰寫 JavaScript」分開。Debug node 屬於 core common nodes,僅作觀測;Debug 顯示成功不代表未來的設備動作已完成。

手動建立 Inject → Change → Debug

  1. 建立獨立 workspace tab。

    新增一個 flow tab,名稱可設「第一個流程」。不要在既有家庭自動化 tab 內練習,以便把 Modified Flows 的影響限制在新的 flow。

  2. 拖入 Inject 並關閉所有自動觸發。

    從 Palette 拖入 Inject。Payload 類型選 string,值填「原始訊息」;不要設定 repeat 或排程,並確定啟動時自動 Inject 未啟用。名稱可設「手動送出範例訊息」。

  3. 拖入 Change 並設定單一規則。

    建立「Set msg.payload to string 已完成安全轉換」,名稱可設「改寫訊息內容」。不要用環境變數、flow/global context 或真實家庭資料。

  4. 拖入 Debug 並限制輸出。

    Debug 輸出選 msg.payload,啟用 sidebar,關閉 system console 與 node status;名稱可設「檢視轉換結果」。限制欄位可降低不必要資料暴露。

  5. 依序連接 wires。

    從 Inject 的輸出連到 Change 輸入,再從 Change 輸出連到 Debug 輸入。確認沒有支線連到 Action、HTTP Request、File、MQTT 或任何裝置節點。

  6. 在 Deploy 前做差異檢查。

    確認 dirty changes 只有新 tab 與三個節點,Inject 沒有 automatic/interval schedule,Debug 只輸出 payload。選 Modified Flows 前先確認該新 flow 不含其他既有節點。

  7. 受控 Deploy 一次。

    選 Modified Flows,確認影響僅是這個新 flow,再按 Deploy。若 editor 顯示 unknown、invalid 或 unused config 警告,停止並修正,不要忽略。不要連續按 Deploy。

  8. 只手動 Inject 一次並觀察。

    開啟 Debug sidebar,按 Inject 左側按鈕一次。預期看到 msg.payload 的字串值「已完成安全轉換」。記錄結果後可停用 Debug,避免日後累積輸出。

Flow 目標與安全護欄

這條 Flow 模擬日後把感測資料標準化,但刻意不讀取 Home Assistant。它提供四個可驗收目標:

  • 可控制:只有人手按 Inject 才開始,測試時機由你決定。
  • 可預測:無論 Inject 的原始字串為何,固定 Change rule 都把 payload 設成指定字串。
  • 可觀察:Debug 明確只顯示 payload,能把輸入與處理結果分開驗證。
  • 無外部副作用:沒有 Action、HTTP、MQTT、File、Exec、serial 或通知節點;輸出只到 editor sidebar。

「無副作用」在本章是有限定義:runtime 仍會建立 message、執行節點並產生 Debug UI 資料,但不對 Home Assistant entity、外部服務、檔案或實體裝置做動作。Debug 內容本身仍可能成為敏感資料面,因此只用人工字串。

為什麼不用真實門窗或燈光 ID

真實 entity ID 會把範例綁定個人環境,公開匯出時也可能透露空間與設備。更重要的是,一旦接上 HA state 或 Action,就必須處理 connection、unknown、unavailable、目標驗證、權限與回復。這些應在後續章節逐層加入。

逐節點設定與可匯入安全範例

Inject:只允許人工起點

設定 payload type 為 string,值為「原始訊息」。repeat 與 crontab 保持空值,once 為 false。這三項都必須檢查,因為 automatic Inject 會在 runtime 啟動或重新部署後自行送訊息,不符合本章成功定義。topic 可保持空字串。

Change:明確寫入 property 與資料型別

規則是 Set property msg.payload to string「已完成安全轉換」。這不是把整個 msg 換成字串:Change 只更新 object 的 payload property,其他 runtime 欄位仍可能存在。日後若要保留原始資料,應另建 property;本章為了讓結果單純,直接覆寫。

Debug:只看需要欄位

把 output property 設為 msg.payload,sidebar 開啟、console 與 status 關閉。若選 complete msg,可能看到 topic、識別欄位及上游加入的其他資料;本例沒有必要。當你看到單一輸出時,先展開值並確認型別是字串,而不只是視覺文字相同。

安全 Flow JSON

你也可以檢視或匯入站內的 01-first-flow.json。檔案只含一個 tab、手動 Inject、Change 與 Debug,once 為 false,沒有 credentials、真實 IDs 或 endpoints。匯入前仍應先閱讀 JSON,使用 Import 的 new flow 選項隔離內容,確認預覽只有三個 nodes,再依 Deploy 前檢核處理。手動建立仍是本章主要路徑,因為能讓你理解每個欄位。

觀察 msg,而不是只看「成功」

按 Inject 一次後,第一個 message 至少帶有 payload;Change 收到 object 後把 payload 改寫,Debug 取得處理後的同一條邏輯訊息路徑。Debug sidebar 通常會讓你看到 property 名稱、值、資料型別提示與來源節點;UI 細節以目前安裝版本為準,不以未固定截圖宣稱。

觀察點預期若不符代表什麼
觸發時機只在按下 Inject 後若 Deploy 後自行出現,Inject 的 once 或排程可能未關閉
輸出 propertymsg.payload若顯示整個 object,Debug complete 設定不符
值「已完成安全轉換」Change rule、wire 或 Deploy 狀態需檢查
型別string若是 number、boolean 或 object,typed input 選擇錯誤
次數每按一次對應一次多次輸出可能有重複 wires、額外 Inject 或其他 flow

_msgid 等欄位說明 msg 是 object;不要依賴其具體值,也不要把任何 runtime identifier 當成跨流程的永久業務 ID。下一章會更完整討論 payload、topic、物件與陣列。

把觀測與設備完成分開

未來流程可能是「感測器事件 → 條件 → Action → Debug」。Action 後的 Debug 只證明某個訊息走到該處,不一定證明燈具達到預期狀態;可靠設計還要觀察 Home Assistant state、timeout 與錯誤路徑。本章故意不製造這種模糊。

完成檢核、停止與下一步

  • workspace 有一個獨立練習 tab,只有 Inject、Change、Debug 三個 nodes 與兩條 wires。
  • Inject 使用 string「原始訊息」,repeat/crontab 為空,啟動自動 Inject 關閉。
  • Change 只有一條規則:把 msg.payload 設為 string「已完成安全轉換」。
  • Debug 只輸出 msg.payload 到 sidebar,不輸出 system console 或 node status。
  • 部署前差異只有本練習;Modified Flows 的影響範圍已確認,沒有忽略 editor warning。
  • 每按 Inject 一次只出現一次預期字串;Deploy 或 restart 不會自動輸出。
  • 完成後停用 Debug 或至少清空 sidebar;匯出內容不含 secrets、真實 IDs、私人 endpoint 或家庭資料。

若你需要暫停練習,可停用 tab 或 Debug 後依相同部署檢核處理;不要把未部署的編輯狀態誤認為 runtime 已停。下一章會在這個無外部副作用基礎上比較資料型別與完整 msg。

故障排除

  • 按 Inject 沒有 Debug 輸出:確認三個 nodes 已連線、Debug active 且 sidebar tab 開啟;再確認受控 Deploy 成功。不要為了測試連上 Action。
  • 輸出仍是「原始訊息」:檢查 wire 是否繞過 Change、Change rule 是否指定 msg.payload,以及 typed value 是否為 string;修改後重新做 scope 檢核。
  • Deploy 後不用按就出現訊息:立即檢查 Inject 的 once、repeat 與 schedule。全部關閉後再 Deploy;本章禁止 automatic Inject。
  • 一次按鈕出現兩筆以上:檢查重複 wires、另一個相同 Inject、Debug node 重複或其他 tab。可用來源 node 名稱辨識,但不要依賴真實環境資料。
  • Debug 顯示完整 object:把 Debug output 從 complete msg 改為 msg.payload。公開求助前清空既有訊息,避免其他 flow 的敏感內容混入。
  • Import 顯示 unknown node:不要安裝陌生 package。本站 JSON 只用 Node-RED 5.0.2 核心 Inject、Change、Debug;核對匯入檔案與 runtime 基線。
  • Deploy 警告其他 flows 也有變更:停止部署。先分離或回復不屬於本練習的 dirty changes;不要用 Full 把未知變更一併送出。

固定來源與範例

常見問題

為什麼第一條 Flow 不直接控制 Home Assistant 燈具?
先用無外部副作用路徑驗證 msg、wire、Deploy 與 Debug,能把 core runtime 問題和 HA connection/Action 問題分開。
Import 預覽顯示三個正確節點,就可以直接 Deploy 嗎?
還不可以。Import 只把內容放進 editor 的待部署狀態;先確認它位於獨立新 tab、沒有未知節點或額外接線,並檢查 dirty changes 只有本練習。Deploy review 若列出其他 flow,就取消而不是改用 Full。
快速連按兩次 Inject 時,為何看到兩個不同的 _msgid?
每次觸發都建立新的訊息物件,因此追蹤 ID 不同是預期行為。驗收應逐筆核對 payload 與時間,不要把兩則訊息誤判成 Change 重複輸出。
可以直接匯入 JSON 而不手動建立嗎?
可以先閱讀並匯入 安全範例,但仍要在預覽核對三個 core nodes、無自動 Inject,再做 Deploy scope 檢查。手動建立更適合第一次學習。
為何 Debug 只選 payload,不選 complete msg?
本章只驗證一個欄位;限制輸出能降低雜訊與敏感資料暴露。msg 仍是 object,後續章節會安全地觀察其他欄位。
應使用哪個 Deploy scope?
本章建立獨立新 tab,確認只有它有變更後使用 Modified Flows。若差異混有其他 flow,先停止並清理,不要改用 Full。