Files
app/docs/reference/node-types.md
T
Melvin Strobl 0d4ff89302
Docs / docs (push) Successful in 24s
Playwright Tests / test-playwright (1, 2) (push) Failing after 32s
Playwright Tests / test-playwright (2, 2) (push) Failing after 21s
pre-commit / pre-commit (push) Failing after 2m56s
Test Backend / test-backend (3.12) (push) Failing after 4m53s
Test Backend / test-backend (3.14) (push) Failing after 4m37s
Compose Smoke Test / test-compose (push) Failing after 24s
Playwright Tests / merge-reports (push) Failing after 2m47s
wren node takes switches (plug, socket, appliances), refused when the model lacks the kind
2026-10-22 17:48:41 +02:00

25 KiB
Raw Blame History

Node types

Built-in nodes cover common integrations, timing, and logic. Configure their settings in the node panel; use a Function node for your own Python.

GET /flows/node-types returns this list with each type's full parameter schema, including any connectors installed on your instance.

MQTT and InfluxDB need their extras: mqtt and influx in the image's EXTRAS, or pip install 'fluksio[mqtt,influx]' on a Python installation. Run the broker and database separately.

Function

python — run your own code when its triggering inputs arrive.

The one type with a source file. Its arguments are its input ports and its own settings; its return value is a dict keyed by output ports. See Writing node code.

Settings on a Function node are free-form: you add them by name, and they arrive as keyword arguments.

Integrations

MQTT

mqtt — listen on topics, or send to them.

The ports pick the direction: outputs only make a listener, inputs a sender. A listener subscribes to its topics, a sender publishes its inputs to them.

Setting Default Notes
topic * one topic for every port, or {"message": "some/topic"} per port
broker_host localhost
broker_port 1883
username / password — password takes a secret reference
client_id —
qos 0 0, 1 or 2
retain false on published messages
keepalive 60 seconds
timeout 10.0 seconds before a broker operation is given up on
send_queue_size 256 payloads that may wait for the broker; past this the oldest is dropped and the node reports degraded
json_key — key to lift out of an object payload; one for every port, or {"port": "key"}

A topic may be a filter: + matches one level, # the rest. Everything a filter matches lands on the same port, so use one port per topic where the difference matters.

Set an explicit topic: the default * is a literal topic, not MQTT's match-all filter.

For {"value": 47}, set json_key to value. Without it, an object is unwrapped only if it contains the port's name as a key.

HTTP

http — listen on a webhook, or send to a URL and pass the reply on.

The ports pick the direction: outputs only make a listener, inputs a sender. A listener is a webhook: the engine mounts a route at /hooks/{flow}/{url}/{secret} while the flow runs. A sender requests url and publishes a JSON object response on its outputs, one key per output port. Keys no port declares are dropped, and a response that is not an object publishes nothing.

Setting Default Notes
url — the route path for a webhook, the full URL for a sender
method POST GET or POST
timeout 30 seconds, sender only
headers {} request headers, sender only; a value may be a secret reference
query {} fixed query parameters, sender only; a value may be a secret reference
send_inputs true off when the inputs only trigger the request
secret — shared secret appended to the webhook URL; takes a secret reference

!!! warning "A webhook with no secret is open to anyone who can reach the host."

The canvas flags this as an advisory issue rather than stopping the flow,
because a webhook on a private network is a legitimate thing to want.
Through the [portal](../interface/portal.md#hooks-and-agents-through-the-portal)
a webhook or served socket answers only with a secret.

WebSocket

websocket — listen on a socket, or send to it, as a client or a server.

The URL picks the role: ws://… or wss://… connects to a server and reconnects when it drops; a path such as /ticker is served at /hooks/{flow}/{url}/{secret} while the flow runs. The ports pick the direction: outputs only make a listener, inputs a sender. A served sender sends to every connected client.

Setting Default Notes
url — ws:// or wss:// URL, or the path to serve
headers {} handshake headers, client only; a value may be a secret reference
on_connect [] frames sent after every (re)connect, client only — a subscribe or auth message; an item may be a secret reference
secret — shared secret appended to a served socket's URL; takes a secret reference
timeout 10.0 seconds for the handshake, and for a send to one client of a served socket
send_queue_size 256 messages that may wait to be sent; past this the oldest is dropped and the node reports degraded
json_key — as on mqtt

Frames are text. An incoming frame is parsed as JSON: an object feeds the ports it names, and a node with a single output takes any other frame whole. A single input is sent as it stands — a string raw, anything else as JSON — and several go as one object keyed by port.

InfluxDB

influxdb — write measurements to a bucket, or read them back.

Setting Notes
url, token, org, bucket connection; token takes a secret reference
write_precision ns, us, ms (default) or s
query_range default window for queries, e.g. -1h
timeout operation timeout in seconds; default 10.0
writes per-input: measurement, field, tags
queries per-output: measurement, field, tags, range, aggregation
{
  "writes": {
    "living_temperature": {
      "measurement": "environment",
      "field": "temp_c",
      "tags": {"room": "living"}
    }
  }
}

Query passthrough. An incoming object with a flux key supplies the query. The first output receives an object with rows, each containing ts, value, field, measurement, and tags. Use Function nodes to build queries and shape results for charts.

Notification

ntfy — push an incoming value to a phone through ntfy.

Setting Default Notes
server https://ntfy.sh
topic — required
title —
priority default min, low, default, high, urgent
tags — comma-separated ntfy tags
token — for a protected topic; takes a secret reference
timeout 10.0 request timeout in seconds

For engine failures rather than flow messages, configure Alerts.

Timing

Inject

inject — emit a value on request, on a timer, or when the flow starts.

Setting Default Notes
payload current time what to emit
payloads {} per-output-port payloads, keyed by port
interval 0 emit every n seconds; 0 means never on its own
cron — a five-field cron expression
at_start false emit once when the flow starts
start_delay 1.0 how long to wait before that first emission

Use cron for scheduled emissions, or trigger the node by hand for testing.

Delay & schedule

delay — hold messages back, limit their rate, or emit on a schedule.

Setting Default Notes
delay 0 seconds to hold each message, fractional
interval 0 minimum seconds between forwards, per output
mapping {} input port → output port; paired in order when empty
cron — five-field expression

Order of operations: delay → rate check → forward. A message arriving inside the interval window is held, and when the window ends the latest one held is sent. With a cron and no inputs it emits the current time on each tick; with inputs it emits the last value it received.

Timers wait until their deadline; busy workers can delay execution.

!!! note "Not in a batch flow"

Batch flows reject rate-limited non-streaming ports. Runs have no live
timer to release a held value.

Debounce

debounce — send one value now and another once things go quiet. A debounce and hold rather than a scheduler: everything it sends starts from a value arriving. For a cron tick, see inject. Node-RED calls this node trigger.

Setting Default Notes
first true sent as soon as a value arrives
then false sent when the wait expires; empty sends nothing
wait 60 seconds of quiet before the second value
extend true a value arriving during the wait starts it over
passthrough false send the incoming value instead of first
wait_port — an input carrying the wait, when it differs per message

For example: turn a light on when the door opens, then off after two quiet minutes.

Use wait_port when the delay comes from an input. A wait of zero or less cancels the pending second value and schedules nothing new.

Logic

Switch

switch — send a value down one branch or another, by rule.

Each rule names an output port; a matching value leaves through that port. Comparisons: eq, ne, gt, gte, lt, lte, contains, between.

Setting Default Notes
rules [] checked in order; each names the output it routes to
stop_at_first true leave through the first matching rule only
otherwise — output for a value that matched nothing

Comparing a string to a number is a mistake in the rule, not a reason to take the flow down: the rule simply does not match.

Change

change — scale, offset, round or map a value on its way past.

Setting Default Notes
scale 1.0 multiply numbers by this
offset 0.0 then add this
round_to — decimal places
mapping {} replace a value with another, looked up as text
default — value when the lookup misses; empty passes it through

Filter unchanged

rbe — pass a value on only when it has actually changed.

Setting Default Notes
deadband 0.0 ignore numeric changes smaller than this
deadband_percent false read the deadband as a percentage

The cheapest fix for a sensor that reports every second and changes every hour.

Join

join — gather several inputs into one object or list.

Setting Default Notes
mode object object or array

System

Command

exec — run a command where the engine runs and read its output. Adding one or changing its settings requires a superuser.

Setting Default Notes
command — the command to run
append_payload false add the incoming value as one argument
timeout 30 seconds
fail_on_error false treat a non-zero exit as a node failure rather than output

Outputs the command's stdout, stderr and exit code.

In Docker, the command runs inside the backend container. Mount host files explicitly if the command needs them.

Service

service — keep a server running on a remote worker for as long as its flow runs, and publish the url it listens on. Adding one or changing its settings requires a superuser.

Setting Default Notes
command — the server, such as parrot serve --host 0.0.0.0 --port {port}
port 8100 where the server listens, {port} in the command
worker — label of the worker to run it on, such as gpu
health /health path that answers 200 once the server is ready
env {} environment variables for the server
weights — an artifact digest, fetched to the worker as {weights}

The flow starts it, so it takes no inputs. Every output port carries http://<worker>:<port> once the health check answers 200, and an empty string the moment the server stops. When it stops, it starts again after a backoff. Its health reads degraded while it starts, restarts or waits for a worker, down after three starts in a row that never got healthy, and ok while it serves.

File

file — read a file into the flow, or write one out of it.

Setting Default Notes
path — relative to the engine's files directory
mode read read, write or append
format text text or json
newline true end each written record with a newline

Paths stay inside the engine's files directory.

Numeric

Perceptron

mlp — a small neural layer over its numeric inputs.

output = weights @ inputs + biases, with weights drawn from seed so a node reloads identically.

Setting Default Notes
seed 0

A worked example of numeric logic, not a modelling tool. To train something, use a batch flow and a Function node.

Language

Wren

wren — turn a typed command into values for the devices wired to it.

Wren is an optional local language model for device commands. Install it in the engine's environment with pip install git+https://github.com/stroblme/wren@eae4d6ff9ded68e36624972fd3f4a8f8d210b0c2. The wren package on PyPI is an unrelated one. Without the model the node reports a missing dependency.

Input Type Notes
prompt str the command, and the only input that runs the node
presence str the area the user is in; anything else reads as away
outdoor float or int °C; 20 without it, clamped to −5…30
dark bool without it, dark before 07:00 and from 18:00
<area>_temperature float or int °C, read when the command asks how warm the area is

The outputs are actions, a list of records, reply, unhandled, and any number of targets. A target is an output named <area>_<kind> and bound to the message its device listens on: living_room_setpoint bound to home.setpoint is the living room's heating. The wiring is the allowlist, so the model can write nothing that is not wired to the node.

  • area: living_room, kitchen, bathroom, bedroom, office, garden, patio, garage, driveway, balcony
  • kind: light, lamp, brightness for a light; setpoint, heating, heat, boost for heating; media, music, tv; shutter, blind, cover; water, watering, sprinkler; switch, plug, socket, appliances for a switch, which only wren's s10 model knows

A port naming anything else, or a kind the installed model lacks, is refused when the flow is built. A port is named after its message unless it is given a name of its own, in the node panel's port name field or as {"name": "setpoint", "port": "living_room_setpoint"} through the flow API.

Targets of one area and kind are one device to the model, and each takes what a call means for its type. A target's current value is the state the model reads.

Call bool number str
lights.on / off true / false 100 / 0 on / off
media.play / stop true / false — play / stop
heating.set — the temperature —
heating.boost / off true / false — boost / off
shutters.up / down true / false 100 / 0 up / down
shutters.stop — — stop
watering.start / stop true / false — on / off
switches.on / off true / false 1 / 0 on / off
Setting Default Notes
apply false write to the targets; off, the node only reports
min_confidence 0.93 below this nothing is written. wren's s10 model is less sure of itself, use 0.8 with it
aliases empty the house's own words, or words the recogniser hears wrong, in the model's: {"traverse": "living room light", "page too": "patio"}

Each record in actions contains message, value, applied, delay_s, call, and confidence. Unwired calls have an empty message and are not applied. Keep apply=false while checking how the model interprets commands.

reply is one sentence saying what was applied, such as "Kitchen light on." or "Garage light on in 5 minutes.". When nothing was applied, unhandled carries the prompt instead, for something else to answer, such as an Assistant. When no node reads unhandled, reply says "Sorry, I can't do that yet." instead, so a spoken command never goes unanswered.

A call with a delay ("in 10m") is held by the engine's work queue, like the Debounce node's second value: it survives a restart where the queue is in Redis, and not otherwise.

The model sees every area, wired or not, each with at least a light, so "the bathroom light" is not taken for a light that is wired elsewhere. Such a call is reported but not applied, and goes on to unhandled. So is a call on an area the command does not name, unless it is the user's area or, with no area named, the living room: "switch on the appliances" is a word the pinned model never learned, and it lands on whichever light it was shown. The command is read in lower case without sentence marks and with number words as digits ("in five minutes" as "in 5 minutes"), as the model was trained. aliases are applied before, whole words in any case, and unhandled carries the prompt with them.

A command for all of a kind, such as "turn off all lights", "switch every light on in 5 minutes" or "everything off in the office", is not the model's. The node applies it to every wired target of that kind, within the areas it names, and replies "All lights off." or "Lights off in the office.". A question ("are all lights off") or an exception ("all but the kitchen") goes to the model as any other command.

A question for an area's temperature ("how warm is it in the kitchen") is answered from its <area>_temperature input, or from outdoor for an area outside, as "It's 8 degrees outside.". Without a reading it goes on to unhandled.

!!! note "What the current model cannot do"

Commands are English, such as "turn on the lights in the kitchen".
The model uses the areas above, one device per kind and area, on/off
actions, and whole-minute delays. It does not dim lights or set colors.

The chat's Control tab sends commands by hand.

Assistant

assistant — answer a request with the instance's AI provider, acting only on what is wired to it.

Input Type Notes
prompt str the request, such as a Wren node's unhandled; it runs the node
listening bool optional; turning true opens the connection and loads the model while the request is spoken
any other any read as context when a request arrives, as outside_temperature: 12.5
Output Type Notes
reply str what was set, as "Kitchen light off.", or the answer, one sentence at a time while the model writes it
actions list one record per value set: message, value, applied, delay_s, call
any other bool, int, float or str a target: one tool, named after its port, taking a value of its type
Setting Default Notes
model empty the model to ask; empty asks the instance's
extra_body {"temperature": 0, "reasoning_effort": "none", "keep_alive": "30m"} added to every request as it is
values empty for a str target, by port: the only values it takes and what the reply says for each, as {"canopy": {"UP": "open", "DOWN": "closed"}}

It asks the provider set under Settings → Assistant. As for Wren, the wiring is the allowlist: the model is offered the targets and nothing else, never the tools the chat's assistant uses. Its instructions list the context inputs and what each target holds now. A target is set the moment the model calls its tool. The reply then names what was set ("Living room light on, living room setpoint to 22."), so the model is asked once per request. A target with values is offered only those, each explained to the model, and confirmed in its word ("Canopy open."). Any other value is refused.

Every request ends in a reply:

  • When no tool fits or the request makes no sense, the model says so, as "Sorry, I can't do that.".
  • When no provider is set up, the connection is refused, it takes over 3 seconds to connect or 15 seconds to answer, the reply is "The assistant isn't available right now." and the node reads degraded. A model that has to be loaded first can take about 10 seconds.

The default extra_body is for Ollama. temperature makes the same request do the same thing each time. reasoning_effort turns thinking off through Ollama's OpenAI-compatible API, which ignores Ollama's own think. keep_alive keeps the model loaded for 30 minutes after the last request instead of 5. Drop a field the provider refuses.

In a voice flow, stt writes the prompt of a Wren node, Wren's unhandled is the assistant's prompt, both reply outputs go to tts, and the device's listening warms the assistant.

Speech to text

stt — turn a microphone's audio into text, through a speech server.

Input Type Notes
audio audio 16 kHz mono PCM16 chunks (audio/L16), or a whole clip such as a WAV
listening bool true while the device listens; false ends the utterance
url str the speech server, from a Service node; read, never waited for
Output Type Notes
partial str the words agreed on so far, as they come, then the final text
text str what was said, once per utterance

The server is any that speaks OpenAI's audio API, such as parrot serve. Each utterance is one realtime transcription session ({url}/v1/realtime), opened when listening turns true or at the first chunk and committed when it turns false. A clip is sent whole to {url}/v1/audio/transcriptions. The node returns at once and publishes when the server answers, since a device's next chunk waits for it. An utterance that comes back empty, fails, or gets no chunk for 10 seconds publishes "Didn't catch that." on partial alone, so a caption shows it and nothing speaks it. A lost one is logged.

listening has to hold a value before a chunk wakes the node. Declare it as a flow input with the initial value false when a device may never send it, such as one that sends whole clips.

Text to speech

tts — speak text as audio for a speaker, through a speech server.

Input Type Notes
text str what to say, a whole reply or one sentence at a time
url str the speech server, as for Speech to text
Output Type Notes
speech audio one MP3 reference per sentence
Setting Default Notes
voice af_heart one of Kokoro's voices, such as bm_george

Each sentence is one call to {url}/v1/audio/speech and published as soon as it is spoken. Units after a number and clock times are written out first, since a speech model reads them letter by letter: "22 °C" is said "22 degrees", "3.4 kWh" "3.4 kilowatt hours" and "07:30" "7 30". How a word itself is pronounced is the server's: parrot serve --lexicon gives words their phonemes. Calls to one server share a connection, which Speech to text opens when an utterance starts, so a reply's first sentence does not wait for one. A sentence spoken before in the same voice is reused rather than spoken again, for the last 256 sentences. A reference carries "append": true while the sentences before it are still playing, which queues it on a panel's speaker. The first sentence of a reply carries none and cuts off whatever was playing.

Devices

Device

device — a paired device's own ports: what it publishes, what it follows.

The engine never runs it. Its outputs are the device's in ports, published by the device itself, and its inputs the out ports the device follows, which never wake anything. Each port is named by the device, such as gpio.4 or backlight. See the device's own ports.

Setting Default Notes
device the device's id
config {} per port, a JSON object handed to the device unread; a reported port's settings get a form

The node is degraded, Offline, while the device is not connected: amber on the canvas and a warning on Home, never a fault.

Connectors

Installed connector packages add types through the fluksio.node_types entry point group. The editor shows the source package. Incompatible contract versions are skipped; installing or upgrading a connector requires an engine restart.

The contract itself is in The connector contract, and Writing a connector walks through building one.

See also