ActionNode#
The ActionNode executes actions on connections, triggering them based on configurable conditions: on startup, when data changes, or when an event is received.
Features#
- Flexible Triggering: Trigger on startup, data change, or incoming event
- Dynamic Options: Action options are validated against the action's own schema
- Input Overrides: Connection ID and action key can be overridden from inputs
- Change Detection: Tracks previous data to only fire when inputs actually change
Usage#
Trigger on Event#
Connect a trigger signal to the trigger input and enable triggerOnEvent. The action fires each time a trigger event arrives.
Trigger on Change#
Enable triggerOnChange. The action fires whenever any input value changes.
Trigger on Startup#
Enable triggerOnStartup. The action fires once when the workflow starts.
Node Structure#
Config Schema#
connectionId (string, optional): ID of the connection to run the action onactionKey (string, optional): Key identifying the action to executetriggerOnEvent (boolean, optional): Fire when a trigger event is receivedtriggerOnStartup (boolean, optional, default: false): Fire on workflow startuptriggerOnChange (boolean, optional, default: false): Fire when inputs change- Additional action-specific option fields
trigger (optional): Trigger signal — fires the action when an event arrivesconnectionId (string, optional): Override the configured connection IDactionKey (string, optional): Override the configured action key- Additional passthrough fields for action options
Output Schema#
Implementation Details#
- Merges input values with config values; inputs take precedence
- Validates action option inputs dynamically against the action definition's schema
- Executes via RPC with a 5000ms timeout
- Subscribes to action feedback after execution
Integration#
The ActionNode is registered in:
NodeRegistry as actionNodeTypeSchema as 'action'
Example Workflow Usage#
- PTZ Camera Control: Connect a button press to
trigger, configure the connection and action key for pan/tilt - Startup Initialisation: Enable
triggerOnStartup to send an init command when the workflow loads - Reactive Control: Enable
triggerOnChange and wire a variable so the action fires whenever the variable changes
The ButtonNode provides a clickable UI button inside the workflow editor. Pressing it emits a trigger event that can be used to kick off downstream logic.
Features#
- UI Interaction: Button is clickable directly in the workflow editor canvas
- Event Emission: Publishes a
trigger event on press, driving downstream nodes - Timestamp Output: Exposes the timestamp of the last press as a state value
Usage#
Place a ButtonNode on the canvas and connect its timestamp output (or trigger event handle) to any node that accepts a trigger input, such as an ActionNode or DelayNode.
Node Structure#
Config Schema#
Output Schema#
timestamp (number): Unix timestamp (ms) of the most recent button press
Implementation Details#
- Responds to the
press UI action - Publishes a
trigger event immediately on press - Calls
workflow.rerenderFromNode() so downstream nodes update synchronously
Integration#
The ButtonNode is registered in:
NodeRegistry as buttonNodeTypeSchema as 'button'
Example Workflow Usage#
- Manual Trigger: Use as a manual start button to kick off a sequence of actions
- Test Harness: Wire a ButtonNode to any chain to manually drive it during development
CaseSwitchNode#
The CaseSwitchNode is a workflow node that acts as a conditional router, passing through input values based on a conditional match. It's similar to a switch statement in programming languages.
Features#
- Conditional Routing: Routes input based on a conditional value match
- Dynamic Input Cases: Supports multiple input cases with configurable conditions
- First Match Wins: Returns the first input case that matches the condition
- No Match Handling: Returns undefined when no condition matches
- Default Fallback: Optional
default input is returned when no case matches
Usage#
Basic Switch#
Conditional Input: "yes"
Cases:
- case1: condition="yes", input="Hello"
- case2: condition="no", input="Goodbye"
Result: "Hello" (first match)
Multiple Cases#
Conditional Input: "red"
Cases:
- case1: condition="red", input="Stop"
- case2: condition="yellow", input="Caution"
- case3: condition="green", input="Go"
Result: "Stop" (matches "red")
No Match (with Default)#
Conditional Input: "blue"
Cases:
- case1: condition="red", input="Stop"
- case2: condition="yellow", input="Caution"
Default: input="Unknown"
Result: "Unknown" (no case matched, returned default)
Node Structure#
conditional (required): The value to match against case conditionscase1, case2, ..., caseN (dynamic): Input values for each casedefault (optional): Fallback value used when no case matches
Config Schema#
label (optional): Custom label for the nodeenabled (default: true): Whether the node is enabledinputVariables (array): Array of case configurationsname (string): Case name (used as input handle ID)description (optional): Case descriptioncondition (string): Condition value to match against
Output Schema#
result: The matched input value (or undefined if no match)matched (boolean): Whether a match was foundmatchedIndex (number, optional): Index of the matched case
Implementation Details#
The CaseSwitchNode works by:
- Converting the conditional input to a string
- Iterating through all configured cases in order
- Comparing each case's condition with the conditional value
- Returning the first matching case's input value
- If no match is found, returning undefined (or the
default input if provided)
Integration#
The CaseSwitchNode is registered in:
NodeRegistry as caseSwitchNodeTypeSchema as 'caseSwitch'
Example Workflow Usage#
- Status Router: Route different status messages based on a status code
- Language Selector: Route content based on language preference
- Error Handler: Route different error responses based on error type
- Menu System: Route to different menu options based on user selection
Comparison with Other Nodes#
- LogicNode: Performs boolean operations, CaseSwitchNode routes based on value matching
- WorkflowRouter: Routes entire workflows, CaseSwitchNode routes individual values
- ExpressionNode: Evaluates expressions, CaseSwitchNode performs conditional routing
ChangeSwitchNode#
The ChangeSwitchNode watches a boolean input and records timestamps whenever the value transitions between true and false. It acts as a change detector with separate outputs for each transition direction.
Features#
- Change Detection: Only updates outputs when the boolean value actually changes
- Dual Timestamp Outputs: Separate outputs for the last
true transition and last false transition - State Preservation: Remembers the last value and timestamps across renders
Usage#
Connect a boolean signal to valueA. The trueResult output updates with the current timestamp each time the value transitions to true, and falseResult updates when it transitions to false.
Input: false → true → true → false
trueResult: [updated] [unchanged]
falseResult: [unchanged] [updated]
Node Structure#
Config Schema#
valueA (boolean, optional): Boolean value to monitor
Output Schema#
trueResult (number): Timestamp (ms) of the last transition to truefalseResult (number): Timestamp (ms) of the last transition to false
Implementation Details#
- Stores
lastValue, lastTrueResult, and lastFalseResult in node state - Compares incoming value to
lastValue; if identical, outputs are unchanged - Does not emit events — use ChangeSwitchNodeV2 if event-driven behaviour is needed
Comparison with ChangeSwitchNodeV2#
Feature | ChangeSwitchNode | ChangeSwitchNodeV2 |
|---|
State-based output | ✓ | ✓ |
Emits events on change | ✗ | ✓ |
Integration#
The ChangeSwitchNode is registered in:
NodeRegistry as changeSwitchNodeTypeSchema as 'changeSwitch'
Example Workflow Usage#
- Edge Detection: Detect rising and falling edges of a boolean feedback value
- Timestamp Logging: Record when a signal last went high or low
ChangeSwitchNodeV2#
The ChangeSwitchNodeV2 is the event-emitting version of ChangeSwitchNode. It watches a boolean input and both updates timestamp outputs and emits events whenever the value transitions between true and false.
Features#
- Change Detection: Only reacts when the boolean value actually changes
- Dual Timestamp Outputs: Separate outputs for the last
true and false transitions - Event Emission: Publishes
trueResult or falseResult events on each transition, enabling reactive downstream processing
Usage#
Connect a boolean signal to valueA. On each true transition a trueResult event fires; on each false transition a falseResult event fires. Both events carry the current timestamp.
Input: false → true → true → false
Events emitted: trueResult falseResult
Node Structure#
Config Schema#
valueA (boolean, optional): Boolean value to monitor
Output Schema#
trueResult (number): Timestamp (ms) of the last transition to truefalseResult (number): Timestamp (ms) of the last transition to false
Output Events#
trueResult: Emitted when value transitions to truefalseResult: Emitted when value transitions to false
Implementation Details#
- Behaves identically to ChangeSwitchNode with the addition of event publishing
- Calls
workflow.rerenderFromNode() on each transition so downstream nodes react immediately - Both output handles are event handles (
isOutputHandleEvent() returns true for both)
Comparison with ChangeSwitchNode#
Feature | ChangeSwitchNode | ChangeSwitchNodeV2 |
|---|
State-based output | ✓ | ✓ |
Emits events on change | ✗ | ✓ |
Integration#
The ChangeSwitchNodeV2 is registered in:
NodeRegistry as changeSwitchV2NodeTypeSchema as 'changeSwitchV2'
Example Workflow Usage#
- Trigger on Rising Edge: Connect the
trueResult event to an ActionNode to fire an action only when the signal goes high - Toggle Animations: Use
trueResult / falseResult events to start/stop different sequences
ClampNode#
The ClampNode constrains a numeric value to stay within a configured minimum and maximum range.
Features#
- Value Clamping: Ensures the output never exceeds
max or falls below min - Configurable Bounds: Both bounds are set in config and validated (
min must be ≤ max) - Type Coercion: String inputs are coerced to numbers before clamping
Usage#
min: 0, max: 100, value: 150 → result: 100
min: 0, max: 100, value: -10 → result: 0
min: 0, max: 100, value: 50 → result: 50
Node Structure#
Config Schema#
min (number, default: 0): Lower boundmax (number, default: 1, must be ≥ min): Upper bound
value (unknown, coerced to number, optional): Value to clamp
Output Schema#
result (number): Clamped value
Implementation Details#
- Uses
Math.min(Math.max(value, min), max) - Validation rejects configs where
min > max
Integration#
The ClampNode is registered in:
NodeRegistry as clampNodeTypeSchema as 'clamp'
Example Workflow Usage#
- Volume Control: Clamp a volume level to 0–100 before sending it to a mixer
- Safe Range: Ensure a position or speed value never exceeds hardware limits
ConnectionVariableNode#
The ConnectionVariableNode reads the current value of a variable from a specific connection and subscribes to live updates whenever that variable changes.
Features#
- Live Variable Subscription: Automatically updates whenever the connection variable changes
- Reactive: Triggers a workflow re-render on each update
- Config-Driven: Connection ID and variable key are set in config and can be changed at runtime
Usage#
Configure the connectionId and variableKey. The value output will reflect the current value of that variable, updating automatically as the connection reports changes.
Node Structure#
Config Schema#
connectionId (string): ID of the connection to read fromvariableKey (string): Key of the variable to read
Output Schema#
value (string | number | boolean | null | undefined): Current value of the variable
Implementation Details#
- Subscribes to variable changes via the system router
- Caches the current config; when config changes, cleans up the old subscription and creates a new one
- Fetches the current value on each render to ensure freshness
- Cleans up the subscription on node unmount
Integration#
The ConnectionVariableNode is registered in:
NodeRegistry as connectionVariableNodeTypeSchema as 'connectionVariable'
Example Workflow Usage#
- Live Status: Read a mixer fader level variable and feed it into a LogicNode or DisplayNode
- Reactive Logic: Drive conditional behaviour based on a connection's current variable state
- Cross-Node Data: Pipe a connection variable into a MathNode or ExpressionNode for further processing
CounterNode#
The CounterNode maintains a persistent numeric counter that can be incremented, decremented, set, or reset through both state inputs and events.
Features#
- Full Counter Operations: Increment by 1, decrement by 1, add an amount, subtract an amount, set to a value, or reset to the initial value
- Event and State Support: Operations can be driven by both state inputs and event signals
- Configurable Initial Value: Set the starting value for the counter and for resets
- UI Actions: Supports direct increment/decrement/set actions from the editor UI
Usage#
initialValue: 0
increaseOne event → value: 1
increaseOne event → value: 2
decreaseOne event → value: 1
set input: 10 → value: 10
reset event → value: 0
Node Structure#
Config Schema#
initialValue (string | number, coerced to number, default: 0): Starting value and reset target
set (string | number, optional): Set the counter to this valuereset (string | number | boolean, optional): Reset to initialValue when truthyincreaseOne (string | number | boolean, optional): Increment by 1 when truthyincreaseAmount (string | number, optional): Add this amount to the counterdecreaseOne (string | number | boolean, optional): Decrement by 1 when truthydecreaseAmount (string | number, optional): Subtract this amount from the counter
increaseOne: Increment by 1decreaseOne: Decrement by 1increaseAmount: Add the event payload valuedecreaseAmount: Subtract the event payload valueset: Set to the event payload valuereset: Reset to initialValue
Output Schema#
value (number): Current counter value
Output Events#
value: Emitted whenever the counter value changes
Implementation Details#
- Tracks
lastSeenInputs to detect state changes between renders - Operations from both state inputs (on change detection) and events are supported
- The counter value is persisted in node state between renders
Integration#
The CounterNode is registered in:
NodeRegistry as counterNodeTypeSchema as 'counter'
Example Workflow Usage#
- Tally Counter: Count button presses or event occurrences
- Loop Index: Use with an IntervalNode to increment a counter each tick and reset at a limit
- Score Tracker: Increment/decrement a score based on feedback events
DelayNode#
The DelayNode passes a payload value through after a configurable time delay. Multiple payloads can be in-flight simultaneously, each delayed by the same duration.
Features#
- Configurable Delay: Set the delay in milliseconds (0 ms – 10 minutes)
- Input Override: Delay duration can be overridden at runtime via the
delay input - Buffered Payloads: Multiple events arriving in quick succession are each delayed independently
- Timeout Management: Pending timeouts are cleared if the delay config changes
Usage#
delay: 1000ms, payload: "hello"
After 1 second → passthrough: "hello"
delay: 500ms, payloads arrive at t=0 and t=200ms
t=500: "first payload" emitted
t=700: "second payload" emitted
Node Structure#
Config Schema#
delay (number, min: 0, max: 600000, default: 1000): Delay in milliseconds
delay (number, optional): Overrides config delay for this renderpayload (unknown, optional): Value to delay
payload: Queues the carried value to be emitted after the delay
Output Schema#
passthrough (unknown): The payload value, emitted after the delay
Implementation Details#
- Each incoming
payload event schedules a setTimeout for the configured delay - When the delay config changes, all pending timeouts are cancelled and the array is cleared
- Timeout handles are stored in an array and cleaned up on node unmount
Integration#
The DelayNode is registered in:
NodeRegistry as delayNodeTypeSchema as 'delay'
Example Workflow Usage#
- Debounce: Add a short delay before an action to avoid triggering on transient signals
- Timed Sequence: Chain multiple DelayNodes to create a timed sequence of actions
- Hold Signal: Delay a feedback value by a fixed offset to synchronise it with another signal
DisplayNode#
The DisplayNode is a simple passthrough node for visualising a value inside the workflow editor. It caches and forwards whatever value it receives, making it useful for debugging and monitoring.
Features#
- Value Visualisation: Displays the current value directly on the node in the editor canvas
- Event Support: Responds to
value events as well as state updates - Passthrough Output: Forwards the received value downstream unchanged
- Configurable Label: The display label can be customised
Usage#
Connect any output to the value input. The current value is shown on the node and is also available as an output for further processing.
Node Structure#
Config Schema#
label (string, optional, default: 'Value'): Label shown on the nodeenabled (boolean, default: true): Whether the node is active
value (unknown): The value to display and pass through
Output Schema#
value (unknown): The input value, forwarded unchanged
Implementation Details#
- Caches the most recent value in an internal property
- Responds to
value events via onEvent, updating the cache and re-rendering - Primarily intended for UI observability during workflow development
Integration#
The DisplayNode is registered in:
NodeRegistry as displayNodeTypeSchema as 'display'
Example Workflow Usage#
- Debug Monitor: Insert between two nodes to inspect intermediate values during development
- Status Display: Wire a connection variable to a DisplayNode to show its current value on the canvas
EventGateNode#
The EventGateNode is an event-driven gate that passes events through only when open, and blocks them when closed. It supports both state-based and event-based control modes.
Features#
- Event Gating: Blocks or passes events based on gate state
- Two Control Modes: Gate state can be driven by a continuous boolean input (state mode) or by discrete open/close/toggle events (event mode)
- Configurable Initial State: The gate can start open or closed
- Pass-through Output: Forwards the last allowed event payload as a state value
Usage#
State Mode#
Set controlMode to 'state' and connect a boolean to the open input. Events pass through whenever open is truthy.
Event Mode#
Set controlMode to 'event' and send open, close, or toggle events to control the gate. An event input event passes a payload through only if the gate is currently open.
Node Structure#
Config Schema#
label (string, optional): Node labelcontrolMode ('state' | 'event', default: 'event'): How the gate is controlledinitiallyOpen (boolean, default: true): Starting open/closed state
open (boolean | string | number, optional): Gate control signal (state mode)
Output Schema#
value (unknown, optional): Last payload that passed through the gateisOpen (boolean): Current open/closed state of the gate
Output Events#
out: Emitted when a payload passes through the gate
open: Open the gateclose: Close the gatetoggle: Toggle the gate statereset: Reset to the configured initial stateevent: Pass a payload through (only if gate is open)
Implementation Details#
- In event mode, gate state persists between renders
isOutputHandleEvent() returns true for the out handle- In state mode, gate state is derived directly from the
open input each render
Integration#
The EventGateNode is registered in:
NodeRegistry as eventGateNodeTypeSchema as 'eventGate'
Example Workflow Usage#
- Conditional Event Routing: Only allow trigger events through when a feedback state is active
- One-shot Gate: Open the gate via an event, let one payload through, then close it
- Arming Circuit: Open the gate only when a specific condition is met, preventing accidental action triggers
EventOrderNode#
The EventOrderNode validates that a set of events arrives in a specific sequential order. It emits a fulfilled event only when all events have been received in the correct sequence.
Features#
- Ordered Sequence Validation: Tracks whether events arrive in the configured order (event1 → event2 → … → eventN)
- Two Modes: Choose between ignoring out-of-order events (Wait) or resetting the sequence (Reset)
- Configurable Sequence Length: Supports sequences of 2–20 events
- Completion Event: Emits
fulfilled when the full sequence is correctly completed
Usage#
Connect event sources to the numbered event inputs in order. Trigger event1 first, then event2, and so on. When the final event in the sequence arrives, the fulfilled event fires.
Sequence: event1 → event2 → event3
Correct: event1 ✓ → event2 ✓ → event3 ✓ → [fulfilled emitted]
Wrong (Reset mode): event1 ✓ → event3 ✗ → [reset, start over]
Wrong (Wait mode): event1 ✓ → event3 ✗ → [ignored, still waiting for event2]
Node Structure#
Config Schema#
mode ('Wait' | 'Reset', default: 'Reset'): Behaviour when an out-of-order event is receivednumberOfInputs (number, min: 2, max: 20, default: 10): Length of the required sequence
event1 … event10 (fixed set): Event inputs in sequence order
Output Schema#
state (boolean): Whether the full sequence has been completednext (number): Index of the next expected event in the sequence
Implementation Details#
- Maintains
expectedEventIndex and currentState in node state - Wait mode: Out-of-order events are silently ignored
- Reset mode: Out-of-order events reset
expectedEventIndex to 0 - On sequence completion, publishes the
fulfilled event and resets for the next cycle
Integration#
The EventOrderNode is registered in:
NodeRegistry as eventOrderNodeTypeSchema as 'eventOrder'
Example Workflow Usage#
- Combination Lock: Require a specific sequence of button presses before triggering an action
- Multi-step Handshake: Validate that a set of network or device events arrive in the correct protocol order
- Safety Interlock: Only allow an action when a required startup sequence has completed in order
EventSwitchNode#
The EventSwitchNode sets its output value by responding to named events. Each event is mapped to a specific value, making it easy to drive a downstream value from discrete event signals.
Features#
- Event-Driven Value Selection: Each named event sets the output to its configured value
- Configurable Event Map: Any number of events can be defined, each with a name and value
- Reset Support: A
reset event returns the output to the initial value - Persistent Value: The current value is stored and survives re-renders
Usage#
Define a set of events and their corresponding values in the config. When any of those events fires, the output value updates to the mapped value.
Events config:
- name: "on", value: true
- name: "off", value: false
Fire "on" → output: true
Fire "off" → output: false
Fire "reset" → output: <initialValue>
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true): Whether the node is activeinitialValue (unknown, optional): Value to use before any event fires, and after a resetevents (array of {name, value}, default: []): The event-to-value mappings
Output Schema#
value (unknown): The current value as set by the most recent event
reset: Reset the output to initialValueevent_<name>: Set the output to the value mapped to <name>
Implementation Details#
- Caches
currentValue between renders - On
onConfigChange, applies any change to initialValue - Event names in the config are prefixed with
event_ internally
Integration#
The EventSwitchNode is registered in:
NodeRegistry as eventSwitchNodeTypeSchema as 'eventSwitch'
Example Workflow Usage#
- State Machine: Drive a string output through a set of named states via events
- Scene Selector: Map scene-select button presses to scene name strings
- Mode Toggle: Cycle between operating modes by firing named events
ExpressionNode#
The ExpressionNode is a workflow node that evaluates expressions using the companion expression library. It mimics the LogicNode structure but provides more powerful expression evaluation capabilities with configurable inputs.
Features#
- Expression Evaluation: Supports complex mathematical and logical expressions
- Variable Injection: Can use variables from the workflow context
- Configurable Inputs: Supports 1-20 dynamic input variables
- Expression Mode: Uses the TextWithVariableInput component in expressionable mode
- Error Handling: Provides clear error messages for invalid expressions
Usage#
Basic Expression#
_EXP_(1 + 2)
Result: 3
_EXP_($(var1) + $(var2))
With input variables: var1: 5, var2: 3
Result: 8
_EXP_($(var1) > $(threshold) ? 'High' : 'Low')
With input variables: var1: 15, threshold: 10
Result: 'High'
String Interpolation#
Hello $(var1)!
With input variables: var1: 'World'
Result: Hello World!
Node Structure#
expression (optional): The expression string to evaluatevariables (optional): Variables available to the expressionvar1, var2, ..., varN (dynamic): Input variables based on numOfInputs configuration
Config Schema#
label (optional): Custom label for the nodeenabled (default: true): Whether the node is enablednumOfInputs (default: 1, min: 1, max: 20): Number of dynamic input variablesexpression (default: '_EXP_()'): The default expression
Output Schema#
result: The evaluated result (can be any type)
Implementation Details#
The ExpressionNode uses the companion expression library:
ParseExpression: Parses the expression string into an ASTResolveExpression: Evaluates the parsed expression with variablesExpressionFunctions: Provides built-in functions for expressions
The node supports two modes:
- Expression Mode: When the input starts with
_EXP_( and ends with ) - Variable Injection Mode: For simple string interpolation with
$(variable) syntax
Integration#
The ExpressionNode is registered in:
NodeRegistry as expressionNodeTypeSchema as 'expression'
Example Workflow Usage#
- Simple Calculator: Set numOfInputs to 2, expression to
_EXP_($(var1) + $(var2)) - Conditional Logic: Set numOfInputs to 3, expression to
_EXP_($(var1) > $(var2) ? $(var3) : 'default') - String Formatting: Set numOfInputs to 1, expression to
Hello $(var1), the time is $(time.hh:mm:ss)!
FeedbackNode#
The FeedbackNode monitors the state of a feedback source on a connection and exposes it as a boolean output. It subscribes to live updates, so the output reflects the current state at all times.
Features#
- Live Subscription: Automatically updates when the feedback state changes on the connection
- Boolean Output: Normalises feedback state to
true, false, or null - Dynamic Options: Feedback options are validated against the feedback definition's schema
- Config-Driven Resubscription: Cleanly resubscribes when connection or feedback key changes
Usage#
Configure the connection ID, feedback key, and any required options. The state output will reflect the current boolean state of that feedback source.
Node Structure#
Config Schema#
connectionId (string): ID of the connection to monitorfeedbackKey (string): Key identifying the specific feedback to watchoptions (record of string → string | number): Feedback-specific options
Output Schema#
state (boolean | null): Current state of the feedback (null if not yet received)
Implementation Details#
- Subscribes to feedback state changes via the system router
- Validates options against the feedback definition schema on each config change
- Unsubscribes from the previous feedback before resubscribing when config changes
- Cleans up all subscriptions on node unmount
Integration#
The FeedbackNode is registered in:
NodeRegistry as feedbackNodeTypeSchema as 'feedback'
Example Workflow Usage#
- Mute Indicator: Monitor an audio console mute feedback and drive a logic node based on its state
- Conditional Action: Wire
state into a LogicNode to only execute an action when a specific feedback is active - Display Status: Connect
state to a DisplayNode to visualise a connection's current feedback state
InternalActionNode#
The InternalActionNode executes internal system actions — actions that are not tied to a specific device connection. It supports the same triggering modes as ActionNode.
Features#
- Internal Actions: Targets the built-in system action registry rather than a connection
- Flexible Triggering: Trigger on startup, data change, or incoming event
- Dynamic Options: Action options are validated against the action's own schema
- Input Overrides: Action key can be overridden from inputs
Usage#
Select an internal action key in the config and choose a trigger mode. Connect a trigger signal to the trigger input for event-driven execution.
Node Structure#
Config Schema#
actionKey (string, optional): ID of the internal action to executetriggerOnEvent (boolean, optional): Fire when a trigger event is receivedtriggerOnChange (boolean, optional): Fire when inputs change- Additional action-specific option fields
trigger (optional): Trigger signal — fires the action when an event arrivesactionKey (string, optional): Override the configured action key- Additional passthrough fields for action options
Output Schema#
Implementation Details#
- Uses the
internalActions registry to resolve and validate the action - Executes via
rpcClient.orchestrator.runInternalAction with a 5000ms timeout - Otherwise behaves identically to ActionNode
Integration#
The InternalActionNode is registered in:
NodeRegistry as internalActionNodeTypeSchema as 'internalAction'
Example Workflow Usage#
- Page Navigation: Trigger an internal page-change action from a button press
- System Reset: Fire a system-level reset action on workflow startup
InternalFeedbackNode#
The InternalFeedbackNode monitors the state of an internal system feedback source (not tied to a device connection) and exposes its state as a boolean output.
Features#
- Internal Feedback: Targets the built-in internal feedback registry
- Live Subscription: Updates automatically when the feedback state changes
- Boolean Output: Normalises feedback state to
true, false, or null - Mapping Support: Decodes option mappings from JSON strings when needed
Usage#
Select an internal feedback key and configure any required options. The state output will reflect the current boolean state.
Node Structure#
Config Schema#
feedbackKey (string): ID of the internal feedback to monitoroptions (record of string → string | number | boolean | array): Feedback-specific options
Output Schema#
state (boolean | null): Current state of the feedback
Implementation Details#
- Uses the
internalFeedbacks registry to resolve and validate the feedback - Decodes option values that are JSON strings (e.g. mappings stored as serialised arrays)
- Cleans up subscriptions on node unmount
Integration#
The InternalFeedbackNode is registered in:
NodeRegistry as internalFeedbackNodeTypeSchema as 'internalFeedback'
Example Workflow Usage#
- Page State: Monitor which page is currently active and use the result to drive conditional logic
- System Status: Expose an internal system state as a boolean for downstream processing
IntervalNode#
The IntervalNode fires a trigger event repeatedly at a fixed interval, like a clock tick. It is useful for polling, periodic actions, and animation loops.
Features#
- Periodic Triggering: Emits a
trigger event at every configured interval - Configurable Rate: Set interval from 100ms to 10 minutes
- Auto-Restart: Clears and restarts the interval whenever the config changes
- Timestamp Output: Exposes the timestamp of the most recent tick
Usage#
Set the interval in milliseconds. The node will emit a trigger event and update timestamp on every tick.
interval: 1000ms → trigger event fires every second
Node Structure#
Config Schema#
interval (number, min: 100, max: 600000, default: 1000): Interval in milliseconds
Output Schema#
timestamp (number): Unix timestamp (ms) of the most recent tick
Output Events#
trigger: Emitted on every interval tick
Implementation Details#
- Uses
setInterval internally - Restarts the interval when the config changes to apply the new duration immediately
- Calls
workflow.rerenderFromNode() on each tick to propagate the updated timestamp - Cleans up the interval on node unmount
Integration#
The IntervalNode is registered in:
NodeRegistry as intervalNodeTypeSchema as 'interval'
Example Workflow Usage#
- Polling Loop: Trigger a feedback read or variable check every second
- Animation Tick: Drive an animation or counter update at a regular cadence
- Heartbeat: Send a periodic keep-alive action to a connected device
LogicNode#
The LogicNode evaluates a logical or comparison operation between two values and outputs a boolean result. It supports numeric comparisons, strict equality, and boolean logic operators.
Features#
- Comparison Operators:
==, !=, >, <, >=, <=, ===, !== - Boolean Operators:
AND, OR, XOR, NOT - Input and Config Values: Both values can be supplied via config (static) or inputs (dynamic)
- Type Coercion: Numeric comparisons coerce string inputs to numbers
Usage#
Comparison#
valueA: 10, operator: ">", valueB: 5 → result: true
valueA: "on", operator: "==", valueB: "on" → result: true
Boolean Logic#
valueA: true, operator: "AND", valueB: false → result: false
valueA: true, operator: "NOT" → result: false
Node Structure#
Config Schema#
valueA (string | number | boolean, optional): Left-hand operandoperator (enum, optional): One of == != > < >= <= === !== AND OR XOR NOTvalueB (string | number | boolean, optional): Right-hand operand
valueA (string | number | boolean, optional): Overrides config valueAoperator (enum, optional): Overrides config operatorvalueB (string | number | boolean, optional): Overrides config valueB
Output Schema#
result (boolean): Result of the logical operation
Implementation Details#
- Default operator is
== when none is configured NOT is a unary operator — only valueA is used- Numeric operators (
>, <, >=, <=) cast both values to numbers before comparing - Input values take precedence over config values
Integration#
The LogicNode is registered in:
NodeRegistry as logicNodeTypeSchema as 'logic'
Example Workflow Usage#
- Threshold Check:
valueA = sensor reading, operator = >, valueB = threshold constant - Gate Condition: Feed
result into a StateGateNode or EventGateNode - Inverter: Use
NOT to invert a boolean feedback state
MathNode#
The MathNode performs a basic arithmetic operation on two numbers and outputs the result.
Features#
- Four Arithmetic Operations: Addition, subtraction, multiplication, division
- Type Coercion: String inputs are coerced to numbers automatically
- Input and Config Values: Both operands can come from config (static) or inputs (dynamic)
Usage#
numberA: 10, operation: "add", numberB: 5 → result: 15
numberA: 10, operation: "subtract", numberB: 3 → result: 7
numberA: 4, operation: "multiply", numberB: 6 → result: 24
numberA: 20, operation: "divide", numberB: 4 → result: 5
Node Structure#
Config Schema#
numberA (string | number, coerced to number, optional): Left-hand operandoperation ('add' | 'subtract' | 'multiply' | 'divide', optional): Operation to performnumberB (string | number, coerced to number, optional): Right-hand operand
numberA (string | number, coerced to number, optional): Overrides config numberAoperation (enum, optional): Overrides config operationnumberB (string | number, coerced to number, optional): Overrides config numberB
Output Schema#
result (number): Result of the arithmetic operation
Implementation Details#
- Default operation is
add - Input values take precedence over config values
- Division does not guard against divide-by-zero; the result will be
Infinity or NaN
Integration#
The MathNode is registered in:
NodeRegistry as mathNodeTypeSchema as 'math'
Example Workflow Usage#
- Offset: Add a fixed offset to a sensor value before sending it downstream
- Scale: Multiply a normalised (0–1) value by 100 to get a percentage
- Counter Arithmetic: Combine with CounterNode to compute derived values
NFCNode#
The NFCNode listens for NFC card read events from a specific surface position (or all positions) and exposes the tag ID, surface, position, and timestamp.
Features#
- NFC Event Subscription: Reacts to NFC card reads from physical surface positions
- Position Filtering: Can listen to all positions or a specific position ID
- Event Emission: Publishes an
nfcRead event with the full read payload - Persistent Last Read: Stores the most recent read so state values are always current
Usage#
Leave positionId as null to receive reads from any position, or configure a specific position ID to filter to a single position.
Node Structure#
Config Schema#
positionId (string | null, default: null): Position to listen to, or null for all positions
Output Schema#
nfcId (string | null): ID of the NFC tag that was readsurfaceId (string | null): Surface where the tag was readpositionId (string | null): Position where the tag was readtimestamp (number | null): Timestamp of the read event
Output Events#
nfcRead: Emitted on each NFC read, carrying the full payload
Implementation Details#
- Subscribes to position NFC events via the system router
- Filters by
positionId if one is configured - Calls
workflow.rerenderFromNode() on each read to update downstream nodes - Cleans up the subscription on node unmount
Integration#
The NFCNode is registered in:
NodeRegistry as nfcNodeTypeSchema as 'nfc'
Example Workflow Usage#
- Access Control: Trigger an action when a specific NFC tag is scanned at a position
- Asset Tracking: Log or display the ID of the last NFC tag read
- Multi-Position Monitor: Listen to all positions and route based on
positionId output
OscListenerNode#
The OscListenerNode starts an OSC (Open Sound Control) UDP server and listens for incoming messages on configured paths. Each matched path emits a named event carrying the message parameters.
Features#
- OSC Server: Hosts a UDP-based OSC listener on a configurable port
- Path Matching: Supports exact paths, named params (
:name), wildcards (*, **), and glob patterns - Named Endpoints: Each endpoint has a stable ID that becomes the event handle name
- Parameter Extraction: Named route parameters are extracted and available as outputs
- Legacy Path Support: Backwards-compatible with the older array-of-strings path format
Usage#
Configure endpoints with OSC paths. When a matching OSC message arrives, the corresponding event fires with the message parameters.
Endpoint: id="scene", path="/scene/:name"
Incoming: /scene/main → event "scene" fires, params.name = "main"
Endpoint: id="fader", path="/fader/*"
Incoming: /fader/1 → event "fader" fires
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)port (number, min: 1024, max: 65535): UDP port to listen onendpoints (array of {id, path}, default: []): Named endpoint definitionspaths (array of string, default: []): Legacy path list (deprecated)
- Dynamic fields matching endpoint IDs (for state-based access)
Output Schema#
- Dynamic fields for each endpoint: carries the last received parameters and payload
params (object): Named route parameters extracted from the path pattern
Output Events#
- One event per endpoint ID, fired when a matching message is received
Implementation Details#
- Starts an OSC UDP server on mount and restarts it on config changes
- Path patterns are compiled to regex for efficient matching
- Named params use
:paramName syntax; wildcards use * (single segment) or ** (multi-segment) - Results are cached per endpoint ID so state values are always populated
Integration#
The OscListenerNode is registered in:
NodeRegistry as oscNodeTypeSchema as 'osc'
Example Workflow Usage#
- Mixing Console: Listen for
/channel/:num/fader messages and route them to connection actions - Lighting Control: Receive OSC cues from a lighting console and trigger companion actions
- Multi-Parameter: Use named params to extract values directly from the OSC path
PriorityStackNode#
The PriorityStackNode selects a value from a set of inputs based on a configurable priority rule. When multiple inputs are active, the priority mode determines which one wins.
Features#
- Multiple Priority Modes: top-to-bottom, bottom-to-top, last-changed, first-changed
- Fallback Value: Returns a configurable fallback when no inputs are active
- Active Input Tracking: Maintains a set of currently active inputs and their change timestamps
- Ordered Output: Also exposes the full ordered list of active input IDs
Usage#
Add named inputs in the config. When those inputs receive truthy values, they are considered "active". The node selects the value of the highest-priority active input according to the chosen mode.
Inputs config: [A, B, C] (priority mode: topToBottom)
A = "hello" (truthy), B = false, C = "world" (truthy)
Result: "hello" (A has higher priority than C)
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)fallbackValue (unknown, optional): Value returned when no inputs are activeinputs (array of {id, label}, default: []): The named inputs to managepriorityMode ('topToBottom' | 'bottomToTop' | 'lastChange' | 'firstChange', default: 'topToBottom'): How to select the winner
- Dynamic inputs matching the configured input IDs (boolean | string | number)
Output Schema#
value (unknown, optional): Value of the highest-priority active input, or fallbackValueordered (string[]): Ordered list of active input IDs from highest to lowest priority
Implementation Details#
- An input is active if its value is truthy (non-zero, non-empty, non-false, non-null)
topToBottom / bottomToTop use the position of the input in the inputs config arraylastChange / firstChange use the timestamp of when each input last became active- Change timestamps are stored per input ID in
lastChangeTs
Integration#
The PriorityStackNode is registered in:
NodeRegistry as priorityStackNodeTypeSchema as 'priorityStack'
Example Workflow Usage#
- Layer Priority: Show the most recently activated overlay source
- Fallback Input: Select from a ranked list of video sources, showing the highest-priority active one
- Last-In-Wins: Use
lastChange mode so the most recently activated input always wins
RestNode#
The RestNode hosts a Fastify HTTP server with configurable REST API endpoints. GET endpoints return current input values; POST endpoints emit events and cache received request bodies.
Features#
- HTTP Server: Spins up a Fastify server on a configurable port
- GET and POST Endpoints: GET returns live state data; POST captures body data and fires an event
- Bearer Token Auth: Optional token-based authentication per request
- Rate Limiting: Configurable request rate limits via
@fastify/rate-limit - Named Endpoints: Each endpoint has a stable ID used as the event handle
- Auto-Restart: Server restarts automatically on config changes
Usage#
GET Endpoint#
Configure a GET endpoint with path /status. The response will include the current value of the status input.
POST Endpoint#
Configure a POST endpoint with path /trigger. When a POST request arrives, a trigger event fires with the request body as its payload.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)port (number, min: 1024, max: 65535): HTTP portrateLimit (optional): {max: number, timeWindow: number} — request rate limitbearerTokens (array of {id, token}, default: []): Allowed bearer tokensendpoints (array of {id, path, method}, default: []): Configured endpoints
- Dynamic fields matching GET endpoint IDs (data returned in GET responses)
Output Schema#
- Dynamic fields matching POST endpoint IDs (last received body data)
Output Events#
- One event per POST endpoint ID, fired on each request
Implementation Details#
- Uses Fastify with
@fastify/rate-limit plugin - Bearer token authentication checks the
Authorization: Bearer <token> header - POST endpoints cache the request body and emit an event
- GET endpoints return the current input value for that endpoint ID
- Server is fully restarted on any config change
Integration#
The RestNode is registered in:
NodeRegistry as restNodeTypeSchema as 'rest'
Example Workflow Usage#
- Webhook Receiver: Receive webhook POSTs from external services and trigger workflow actions
- Status API: Expose current workflow state as a GET endpoint for external monitoring
- Remote Control: Trigger companion actions via HTTP requests from a control surface or script
SchedulerNode#
The SchedulerNode triggers on a cron schedule. It emits a trigger event and updates a timestamp output each time the cron expression fires.
Features#
- Cron Scheduling: Uses standard cron expressions for flexible scheduling
- Event Emission: Publishes a
trigger event on each scheduled tick - Auto-Restart: Stops and restarts the cron task when the expression changes
- Comprehensive Logging: Logs cron lifecycle events for debugging
Usage#
Set a valid cron expression in cronExpression. The node fires at each matching time.
cronExpression: "*/5 * * * *" → fires every 5 minutes
cronExpression: "0 9 * * 1-5" → fires at 9am on weekdays
cronExpression: "0 0 * * *" → fires at midnight every day
Node Structure#
Config Schema#
cronExpression (string): A valid cron expression
Output Schema#
timestamp (number): Unix timestamp (ms) of the most recent cron tick
Output Events#
trigger: Emitted on each cron tick
Implementation Details#
- Uses the
node-cron library - Manages a single
ScheduledTask instance, replacing it when the expression changes - Logs
scheduled, started, stopped, and destroyed lifecycle events - Cleans up the task on node unmount
Integration#
The SchedulerNode is registered in:
NodeRegistry as schedulerNodeTypeSchema as 'scheduler'
Example Workflow Usage#
- Daily Report: Trigger a sequence of actions every morning at a specific time
- Business Hours: Fire events only during working hours using day-of-week and hour ranges
- Periodic Sync: Refresh connection state or send a sync command at regular intervals
StateGateNode#
The StateGateNode passes a payload value through to its output when the gate is open, and blocks it when closed. Unlike EventGateNode it operates on continuous state values rather than events.
Features#
- State Gating: Passes
payload through when open; returns fallback or cached value when closed - Two Control Modes: Gate state driven by a boolean input (state mode) or by discrete events (event mode)
- Fallback Value: Optional fallback output when the gate is closed
- Value Caching: Remembers the last payload seen while open
Usage#
State Mode#
Connect a boolean to the open input. While truthy, payload is forwarded to value. While falsy, value returns the fallback (or the last cached payload).
Event Mode#
Send open, close, or toggle events to control the gate. The payload input is the value being gated.
Node Structure#
Config Schema#
label (string, optional): Node labelcontrolMode ('state' | 'event', default: 'state'): How the gate is controlledinitiallyOpen (boolean, default: true): Starting stateopen (boolean, optional): Static open state (used as config default in state mode)
payload (unknown, optional): Value to gateopen (boolean | string | number, optional): Gate control signal (state mode)fallback (unknown, optional): Value returned when gate is closed
Output Schema#
value (unknown, optional): Gated payload or fallbackisOpen (boolean): Current gate state
open: Open the gateclose: Close the gatetoggle: Toggle the gate statereset: Reset to initiallyOpen state
Implementation Details#
- In state mode, gate state is computed from the
open input on each render - In event mode, gate state persists and is modified by events
- When closed: returns
fallback if provided, otherwise returns the last cached payload value
Comparison with EventGateNode#
Feature | StateGateNode | EventGateNode |
|---|
Gates continuous values | ✓ | ✗ |
Gates events | ✗ | ✓ |
State and event control modes | ✓ | ✓ |
Integration#
The StateGateNode is registered in:
NodeRegistry as stateGateNodeTypeSchema as 'stateGate'
Example Workflow Usage#
- Hold Last Value: Gate a sensor reading; when the gate closes, hold the last known value
- Conditional Display: Only pass a value to a display when a condition is met
- Safety Interlock: Block a value from reaching an ActionNode until an enable signal is asserted
StaticValueNode#
The StaticValueNode outputs a fixed, configured value of a chosen type. It is useful for injecting constants into a workflow.
Features#
- Type Selection: Choose between string, number, or boolean output types
- Type Coercion: The configured value is cast to the selected type at runtime
- Enabled Toggle: When disabled, the node returns a safe default for the selected type
Usage#
type: "string", value: "hello" → result: "hello"
type: "number", value: "42" → result: 42
type: "boolean", value: true → result: true
Defaults when disabled:
- string →
"" - number →
0 - boolean →
false
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)type ('string' | 'number' | 'boolean', default: 'string'): Output typevalue (string | number | boolean, default: ''): The static value to output
Output Schema#
result (string | number | boolean): The configured static value cast to the selected type
Implementation Details#
- Type casting is done via a
castValue method - When disabled, the type default is returned instead of the configured value
Integration#
The StaticValueNode is registered in:
NodeRegistry as staticValueNodeTypeSchema as 'staticValue'
Example Workflow Usage#
- Constant Threshold: Provide a fixed number threshold to a LogicNode
- Label String: Inject a fixed string label into a StringMergeNode
- Feature Flag: Use a boolean constant to enable/disable a branch of logic
StringMergeNode#
The StringMergeNode concatenates multiple named input values into a single string, joined by a configurable separator.
Features#
- Dynamic Inputs: Any number of named input variables can be defined
- Configurable Separator: Choose any separator string (empty by default)
- Null/Undefined Skipping: Missing or undefined inputs are omitted from the result
Usage#
separator: " ", var1: "Hello", var2: "World" → result: "Hello World"
separator: "-", var1: "2024", var2: "01", var3: "15" → result: "2024-01-15"
separator: "", var1: "foo", var2: undefined → result: "foo"
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)separator (string, default: ''): String placed between each valueinputVariables (array of {name, description}, default: []): Named inputs to merge
- Dynamic fields for each configured input variable name
Output Schema#
result (string): Concatenated string, or '' if disabled
Implementation Details#
- Iterates over
inputVariables in order, collecting non-null/undefined values - Joins with
separator using Array.prototype.join - Returns
'' when disabled
Integration#
The StringMergeNode is registered in:
NodeRegistry as stringMergeNodeTypeSchema as 'stringMerge'
Example Workflow Usage#
- Date Formatter: Merge year, month, and day with
- separator - Message Builder: Combine a fixed prefix with a dynamic variable value
- CSV Row: Join multiple values with
, to build a CSV line
TcpClientNode#
The TcpClientNode connects to a remote TCP server, sends messages, and receives data. It automatically reconnects on disconnect and supports delimiter-based message framing.
Features#
- Auto-Reconnect: Reconnects automatically at a configurable interval after disconnection
- Idle Disconnect: Optionally disconnects after a period of inactivity
- Delimiter Framing: Buffer incoming data until a delimiter (LF, CRLF, CR, null byte) before emitting
- JSON Parsing: Attempts to JSON-parse each received message and exposes the result
- Connection Events: Optionally emits a
connection event on (re)connect - Send on Change: Sends the
send_state value whenever it changes
Usage#
Configure the host and port. Wire a string to send_state to send it whenever the value changes. Incoming messages appear on lastMessage and lastObject.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)host (string, min length: 1): Remote hostname or IP addressport (number, min: 1, max: 65535): Remote portreconnectInterval (number, default: 5000): Reconnect delay in msdisconnectWhenIdle (boolean, default: false): Disconnect after idle periodidleDisconnectDelay ('0' | '200' | '1000' | '10000' | '60000', default: '1000'): Idle timeout in msemitOnConnect (boolean, default: false): Emit connection event on connectdelimiter ('none' | 'null' | 'lf' | 'crlf' | 'cr', default: 'none'): Message delimiterbufferUntilDelimiter (boolean, default: false): Buffer data until delimiter is foundmaxBufferSize (number, default: 65536): Max buffer size before truncation
send_state (string, optional): Message to send to the remote server
Output Schema#
isConnected (boolean): Whether the TCP connection is activelastMessage (string): Most recently received messagelastObject (unknown): JSON-parsed version of the last message (if parseable)
Output Events#
message: Emitted on each received messageconnection: Emitted on connect (if emitOnConnect is enabled)
Integration#
The TcpClientNode is registered in:
NodeRegistry as tcpClientNodeTypeSchema as 'tcpClient'
Example Workflow Usage#
- Device Control: Send commands to a device over TCP and process its responses
- Telnet-Style Interface: Connect to a text-based TCP service and parse responses
- Raw Protocol: Use no delimiter for binary/streaming protocols
TcpServerNode#
The TcpServerNode listens for incoming TCP connections, broadcasts messages to all connected clients, and exposes data received from any client.
Features#
- Multi-Client Support: Accepts and manages multiple simultaneous TCP connections
- Broadcast: Sends the
send_state value to all currently connected clients - Delimiter Framing: Per-client message buffering with configurable delimiters
- JSON Parsing: Attempts to parse received messages as JSON
- Client Tracking: Exposes connection count, last sender IP/port, and last received message
- Error Reporting: Surfaces server errors via the
errorMessage output
Usage#
Configure a port to listen on. Wire a string to send_state to broadcast it to all clients. Incoming messages from any client appear on the lastMessage and related outputs.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)port (number, min: 1024, max: 65535): TCP port to listen ondelimiter ('none' | 'null' | 'lf' | 'crlf' | 'cr', default: 'none'): Message delimiterbufferUntilDelimiter (boolean, default: false): Buffer per-client data until delimitermaxBufferSize (number, default: 65536): Max per-client buffer size
send_state (string, optional): Message to broadcast to all connected clients
Output Schema#
isConnected (boolean): Whether at least one client is connectedconnectionCount (number): Number of currently connected clientslastMessage (string): Most recently received message (from any client)lastObject (unknown): JSON-parsed version of the last messagelastTimestamp (number): Timestamp of the last received messagelastFromIp (string): IP address of the last senderlastFromPort (number): Port of the last sendererrorMessage (string): Last server error message
Integration#
The TcpServerNode is registered in:
NodeRegistry as tcpServerNodeTypeSchema as 'tcpServer'
Example Workflow Usage#
- Control Surface Server: Accept connections from multiple control surfaces and broadcast state
- Data Aggregator: Collect incoming data from multiple TCP clients and process it centrally
- Echo Server: Receive messages and send responses back to all clients
UdpReceiverNode#
The UdpReceiverNode binds a UDP socket on a configured port and receives datagrams from any sender. It supports delimiter-based message buffering and exposes sender metadata alongside the received data.
Features#
- UDP Listener: Binds a UDP socket and receives datagrams from multiple senders
- Per-Sender Buffering: Message buffering tracked per source address
- Delimiter Framing: Buffer data until a delimiter before treating it as a complete message
- JSON Parsing: Attempts to parse each message as JSON
- Sender Metadata: Exposes the IP address and port of the last sender
- Event Emission: Emits a
message event on each received message
Usage#
Configure the port. Incoming UDP datagrams are available on lastMessage and lastObject. The message event fires on each datagram.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)port (number, min: 1024, max: 65535): UDP port to bind ondelimiter ('none' | 'null' | 'lf' | 'crlf' | 'cr', default: 'none'): Message delimiterbufferUntilDelimiter (boolean, default: false): Buffer per-sender data until delimitermaxBufferSize (number, default: 65536): Max buffer size per sender
Output Schema#
lastMessage (string): Most recently received messagelastObject (unknown): JSON-parsed version of the last messagelastAddress (string): IP address of the last senderlastPort (number): Port of the last senderlastTimestamp (number): Timestamp of the last received messageerrorMessage (string): Last socket error message
Output Events#
message: Emitted on each received datagram
Integration#
The UdpReceiverNode is registered in:
NodeRegistry as udpReceiverNodeTypeSchema as 'udpReceiver'
Example Workflow Usage#
- OSC Alternative: Receive raw UDP messages from devices that don't support OSC framing
- Sensor Data: Ingest UDP telemetry from sensors or embedded devices
- Broadcast Receiver: Listen for UDP broadcast packets on the local network
UdpSenderNode#
The UdpSenderNode sends UDP datagrams to a configured host and port. The destination can be overridden at runtime via input values.
Features#
- UDP Sending: Sends datagrams to a configured or dynamically specified host/port
- Input Overrides: Host and port can be overridden per-render via
host_input and port_input - Message Delimiter: Optionally appends a delimiter to each outgoing message
- Local Port Binding: Can bind to a specific local port, or use 0 for auto-assignment
- Send Confirmation: Emits a
sent event and updates last-sent metadata after each send
Usage#
Configure the default host and port. Wire a string to send_state to send it. Override the destination dynamically using host_input and port_input.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)host (string, optional): Default destination hostname or IPport (number, optional): Default destination portlocalPort (number, optional): Local port to bind (0 = auto)delimiter ('none' | 'null' | 'lf' | 'crlf' | 'cr', default: 'none'): Appended delimiter
send_state (string, optional): Message to sendhost_input (string, optional): Overrides config hostport_input (number, optional): Overrides config port
Output Schema#
lastSentMessage (string): Last message that was sentlastSentAddress (string): Destination address of the last sendlastSentPort (number): Destination port of the last senderrorMessage (string): Last send error message
Output Events#
sent: Emitted after each successful send
Integration#
The UdpSenderNode is registered in:
NodeRegistry as udpSenderNodeTypeSchema as 'udpSender'
Example Workflow Usage#
- Device Command: Send UDP control commands to a device that accepts UDP input
- Dynamic Routing: Use
host_input and port_input to route messages to different destinations based on workflow state - Broadcast: Send to the subnet broadcast address to reach all devices on the local network
WebSocketClientNode#
The WebSocketClientNode connects to a remote WebSocket server, sends messages, and receives data. It automatically reconnects on disconnect and supports idle-based disconnection.
Features#
- Auto-Reconnect: Reconnects automatically at a configurable interval
- Idle Disconnect: Optionally disconnects after a period without outgoing messages
- JSON Parsing: Attempts to parse each received message as JSON
- Connection Events: Optionally emits a
connection event on (re)connect - Send on Change: Sends the
send_state value whenever it changes
Usage#
Configure the WebSocket url. Wire a string to send_state to send messages. Received messages appear on lastMessage and lastObject.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)url (string, min length: 1): WebSocket URL to connect to (e.g. ws://host:port/path)reconnectInterval (number, default: 5000): Reconnect delay in msdisconnectWhenIdle (boolean, default: false): Disconnect after idle periodidleDisconnectDelay ('0' | '200' | '1000' | '10000' | '60000', default: '1000'): Idle timeout in msemitOnConnect (boolean, default: false): Emit connection event on connect
send_state (string, optional): Message to send to the server
Output Schema#
isConnected (boolean): Whether the WebSocket is currently connectedlastMessage (string): Most recently received messagelastObject (unknown): JSON-parsed version of the last message
Output Events#
message: Emitted on each received messageconnection: Emitted on connect (if emitOnConnect is enabled)
Integration#
The WebSocketClientNode is registered in:
NodeRegistry as webSocketClientNodeTypeSchema as 'webSocketClient'
Example Workflow Usage#
- API Integration: Connect to a WebSocket API and react to incoming messages
- Real-Time Data: Subscribe to a live data feed and pass values downstream
- Bidirectional Control: Send commands and receive state updates over a single WebSocket connection
WebSocketServerNode#
The WebSocketServerNode hosts a WebSocket server that accepts client connections, broadcasts messages to all connected clients, and exposes data received from any client.
Features#
- Multi-Client Support: Accepts and manages multiple simultaneous WebSocket connections
- Broadcast: Sends the
send_state value to all connected clients - Client ID Tracking: Each connecting client receives an auto-incremented ID
- JSON Parsing: Attempts to parse received messages as JSON
- Connection Events: Optionally emits a
connection event when a new client connects - Error Reporting: Surfaces server errors via the
errorMessage output
Usage#
Configure a port. Clients connect via ws://host:port. Wire a string to send_state to broadcast it. Incoming messages appear on lastMessage and related outputs.
Node Structure#
Config Schema#
label (string, optional): Node labelenabled (boolean, default: true)port (number, min: 1024, max: 65535): WebSocket port to listen onemitOnConnect (boolean, default: false): Emit connection event when a client connects
send_state (string, optional): Message to broadcast to all connected clients
Output Schema#
isConnected (boolean): Whether at least one client is connectedconnectionCount (number): Number of currently connected clientslastMessage (string): Most recently received message (from any client)lastObject (unknown): JSON-parsed version of the last messagelastTimestamp (number): Timestamp of the last received messagelastFromId (string): Client ID of the last sendererrorMessage (string): Last server error message
Output Events#
connection: Emitted when a client connects (if emitOnConnect is enabled)
Integration#
The WebSocketServerNode is registered in:
NodeRegistry as webSocketServerNodeTypeSchema as 'webSocketServer'
Example Workflow Usage#
- Browser Dashboard: Serve real-time state to a web dashboard connected via WebSocket
- Control Protocol: Accept commands from custom WebSocket clients and trigger workflow actions
- State Broadcast: Push workflow state updates to all listening clients whenever values change
WorkflowRouterNode#
The WorkflowRouterNode is a dynamic signal router that maps configurable sources to destinations across one or more levels. Routes are persisted to the database and survive restarts.
Features#
- Dynamic Routing: Connects any source to any destination at runtime
- Multi-Level: Supports up to 10 independent routing levels (e.g. for video, audio, data)
- Scalable: Up to 1000 sources and 1000 destinations per router
- Persistent Routes: Routes are saved to the database with a 5-second debounce
- Cross-Workflow Validation: Validates that source and destination nodes belong to the same workflow
- Dynamic I/O: Source and destination counts can be changed without losing existing routes
Usage#
Configure the number of sources, destinations, and levels. Use the editor UI to assign routes. The output_<dest> values reflect the current value of whatever source is routed to each destination. An unrouted_<dest> value appears when no source is assigned.
Node Structure#
Config Schema#
label (string, default: 'Router'): Node labelsourceCount (number, min: 1, max: 1000, default: 10): Number of source inputsdestinationCount (number, min: 1, max: 1000, default: 10): Number of destination outputslevels (number, min: 1, max: 10, default: 1): Number of independent routing levels
- Dynamic inputs for each source (boolean | string | number | null)
Output Schema#
output_<N> for each destination: Current value of the routed source, or fallback if unroutedunrouted_<N> (boolean): Whether destination N is currently unroutedordered (array): Ordered list of active source IDs by destination
Implementation Details#
- Built with the Effect library for typed error handling and resource management
- Routes are stored in the database with a 5-second write debounce to reduce I/O
- In-memory cache is the source of truth; the database is used only for persistence and initial load
- Orphaned ports (from reduced source/destination counts) are cleaned up on config change
- Cross-workflow routing is blocked with a validation error
Integration#
The WorkflowRouterNode is registered in:
NodeRegistry as workflowRouterNodeTypeSchema as 'workflowRouter'
Example Workflow Usage#
- Video Router: Route camera feeds to output destinations with a visual matrix UI
- Audio Routing: Assign audio sources to mix bus destinations across multiple levels
- Signal Matrix: General-purpose N×M routing matrix for any type of signal value
WorkflowVariableNode#
The WorkflowVariableNode stores a typed value scoped to the current workflow. It acts as a shared memory cell: other nodes can write to it and read from it. Values can optionally be persisted to the database.
Features#
- Typed Storage: Stores a string, number, or boolean value with type enforcement
- Read and Write: Exposes both a
write input and a read output on the same node - Optional Persistence: Can save the value to the database so it survives restarts
- Live Sync: Broadcasts changes via Redis so all nodes reacting to this variable update immediately
- Controlled/Uncontrolled Modes: When
write is connected, the node is controlled by that input; when disconnected, it manages its own state
Usage#
Connect a value to write to update the variable. Read the current value from the read output. Other nodes in the same workflow can reference this variable by its variableKey.
Node Structure#
Config Schema#
schema ({type: 'string' | 'number' | 'boolean'}): The type of the variableinitialValue (unknown): Value used on first creationvariableKey (string): Unique identifier for this variable within the workflowpersist (boolean, default: false): Whether to persist the value to the database
write (string | number | boolean | object | null, optional): Value to write to the variable
Output Schema#
read (string | number | boolean | object | null, optional): Current value of the variable
Implementation Details#
- Initialises from Redis, the persisted database value, or
initialValue (in that priority order) - Persistence is throttled to a maximum of once every 30 seconds
- Type casting produces a friendly error message if the written value cannot be cast to the configured type
- Cross-node reactivity via Redis pub/sub
Integration#
The WorkflowVariableNode is registered in:
NodeRegistry as workflowVariableNodeTypeSchema as 'workflowVariable'
Example Workflow Usage#
- Shared State: Store a mode value that multiple branches of the workflow read
- Persisted Counter: Track a count that survives workflow restarts
- Cross-Node Communication: Write from one part of the workflow and read in another without direct connections
WorkflowVariableReadNode#
The WorkflowVariableReadNode reads the current value of a variable from another workflow. It subscribes to live updates so the output always reflects the latest value.
Features#
- Cross-Workflow Read: Accesses variables defined in a different workflow
- Live Subscription: Automatically updates when the target variable changes
- Schema Validation: Validates the received value against the target variable's type schema
- Reactive: Triggers a re-render when the value changes
Usage#
Configure the workflowId of the workflow that owns the variable, and the variableKey of the variable to read. The read output reflects its current value.
Node Structure#
Config Schema#
workflowId (string): ID of the workflow that owns the variablevariableKey (string): Key of the variable to read
Output Schema#
read (string | number | boolean | object | null, optional): Current value of the variable
Implementation Details#
- Subscribes to variable change notifications for the target workflow/key pair
- Validates the incoming value against the target variable's type schema
- Cleans up the subscription on node unmount or config change
Comparison with WorkflowVariableNode#
Feature | WorkflowVariableNode | WorkflowVariableReadNode |
|---|
Same-workflow variable | ✓ | ✗ |
Cross-workflow read | ✗ | ✓ |
Write support | ✓ | ✗ |
Integration#
The WorkflowVariableReadNode is registered in:
NodeRegistry as workflowVariableReadNodeTypeSchema as 'workflowVariableRead'
Example Workflow Usage#
- Global Config: Read a configuration value maintained in a dedicated "globals" workflow
- Shared Counter: Display or use a counter that is owned and managed by another workflow
- Cross-Workflow Sync: Synchronise state between two workflows without direct connections
WorkflowVariableWriteNode#
The WorkflowVariableWriteNode writes a value to a variable defined in another workflow. It validates the written value against the target variable's type schema before writing.
Features#
- Cross-Workflow Write: Updates a variable in a different workflow
- Type Validation: Strictly validates the value against the target variable's schema (no coercion)
- Reactive: Re-validates and writes whenever the
write input changes
Usage#
Configure the workflowId of the target workflow and the variableKey. Connect the value to write to the write input. The value will be written to the target variable whenever it changes.
Node Structure#
Config Schema#
workflowId (string): ID of the workflow that owns the variablevariableKey (string): Key of the variable to write to
write (string | number | boolean | object | null, optional): Value to write
Output Schema#
Implementation Details#
- Validates the value against the target variable's type schema before writing
- Uses strict type validation — values are not coerced to the target type
- Subscribes to the target schema to stay up to date with type changes
- Cleans up subscriptions on node unmount
Comparison with WorkflowVariableNode#
Feature | WorkflowVariableNode | WorkflowVariableWriteNode |
|---|
Same-workflow variable | ✓ | ✗ |
Cross-workflow write | ✗ | ✓ |
Read support | ✓ | ✗ |
Integration#
The WorkflowVariableWriteNode is registered in:
NodeRegistry as workflowVariableWriteNodeTypeSchema as 'workflowVariableWrite'
Example Workflow Usage#
- Remote Update: Write a computed value from one workflow into a variable owned by another
- Coordinator Pattern: Have a "controller" workflow update shared variables consumed by multiple "view" workflows