From 500593836e74700fe1d75b84da6bfbab76c51647 Mon Sep 17 00:00:00 2001
From: bcbetterninja <327058824+bcbetterninja@users.noreply.github.com>
Date: Tue, 15 Sep 2026 09:58:12 +0000
Subject: [PATCH] Add source filtering to Node-RED layout trigger
---
nodered/README.md | 8 ++++
nodered/src/bf-trigger-layout-changed.html | 19 +++++++-
nodered/src/bf-trigger-layout-changed.js | 8 +++-
server/tests/nodered-events.test.ts | 55 ++++++++++++++++++++++
4 files changed, 87 insertions(+), 3 deletions(-)
diff --git a/nodered/README.md b/nodered/README.md
index 2ba45a08..f7477264 100644
--- a/nodered/README.md
+++ b/nodered/README.md
@@ -80,6 +80,14 @@ Each trigger node also offers an optional ID filter (display_id / kiosk_id /
camera_id) so you can drop one node per entity without a downstream switch.
Tenant scoping happens before those ID filters.
+The Layout Changed trigger also has a **Source** dropdown: **All (not set)**
+(the default, including existing flows), **Server**, or **Kiosk**. Server events
+report server-issued switches; kiosk events report kiosk-side layout changes,
+including local switches and idle returns. All sources can produce two events
+for one server-issued switch. Select a source to receive just that side.
+The output preserves `msg.payload.source` (`null` when absent); events without
+a source pass only when the dropdown is unset.
+
## Installation
### Dev (single-host BetterFrame install)
diff --git a/nodered/src/bf-trigger-layout-changed.html b/nodered/src/bf-trigger-layout-changed.html
index de77fdfe..aa11da99 100644
--- a/nodered/src/bf-trigger-layout-changed.html
+++ b/nodered/src/bf-trigger-layout-changed.html
@@ -6,6 +6,7 @@
name: { value: "" },
config: { value: "", type: "bf-server-config", required: true },
display_id: { value: "" },
+ source: { value: "" },
},
inputs: 0,
outputs: 1,
@@ -30,19 +31,33 @@
+
+
+
+
Fires when a display's active layout changes.
Listens on POST /api/internal/layout.changed internally — no upstream
http in node required.
- Emits msg.payload = {display_id, kiosk_id, layout_id, layout_name} for the selected tenant.
+ Emits msg.payload = {display_id, kiosk_id, layout_id, layout_name, source} for the selected tenant.
Leave Display ID blank to receive events from all displays.
+ Leave Source unset to receive all events, or select Server or Kiosk.