# Node types Every type the canvas can place. Each is configured by filling in a form the editor generates from its parameter schema, so they all behave the same way. Anything here can also be written as a **Function** node. These types cover the shapes that come up most often, as a form to fill in rather than repeated code. `GET /flows/node-types` returns this list with each type's full parameter schema, including any [connectors](#connectors) installed on your instance. ## Function **`python`** — your own code, run on every incoming message. 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](../code/nodes.md). Settings on a Function node are free-form: you add them by name, and they arrive as keyword arguments. ## Integrations ### MQTT **`mqtt`** — subscribe to topics, or publish what arrives on its inputs. A node with *outputs only* subscribes; a node with *inputs* publishes. | 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 | | `publish_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. `json_key` is for a device that wraps its reading: Victron publishes `{"value": 47}` on every path. Without it, a payload object is unwrapped only when it happens to carry the port's own name as a key. ### HTTP **`http`** — receive data on a webhook, or send it to a URL. Outputs only makes it a **webhook**: the engine mounts a route at `/hooks/{flow}/{url}/{secret}` while the flow runs. Inputs make it a **sender**. | Setting | Default | Notes | |---|---|---| | `url` | — | the route path in webhook mode, the full URL in sender mode | | `method` | `POST` | `GET` or `POST` | | `timeout` | `30` | seconds, sender mode | | `headers` | `{}` | | | `query` | `{}` | fixed query parameters; 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. ### 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` | | `writes` | per-input: `measurement`, `field`, `tags` | | `queries` | per-output: `measurement`, `field`, `tags`, `range`, `aggregation` | ```json { "writes": { "living_temperature": { "measurement": "environment", "field": "temp_c", "tags": {"room": "living"} } } } ``` **Query passthrough.** An incoming message holding a `flux` key is run as written, and the rows come back on the first output port as `{"rows": [{ts, value, field, measurement, tags}], ...}`. A database node holds the credentials and the connection, nothing else. Building a query and shaping its rows are Function nodes on either side. ### Notification **`ntfy`** — push an incoming value to a phone through [ntfy](https://ntfy.sh). | 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 | This is a *flow* deciding something is worth saying. The engine reporting that it broke is [Alerts](../interface/operations.md#alerts), which is a different thing configured elsewhere. ## 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 | The scheduler: a `cron` expression here is what makes a flow run by the clock. It is also the most-placed node in a real instance, mostly as a button someone presses. ### 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 | | `mapping` | `{}` | input port → output port; paired in order when empty | | `cron` | — | five-field expression | Order of operations: rate check → delay → forward. With a `cron` and no inputs it emits the current time on each tick; with inputs it emits the last value it received. The engine sleeps until a delay is due rather than polling for it, so a `delay` fires within a few milliseconds of its deadline on an idle engine, and a due timer is taken off the queue before work that is merely waiting. It is not a real-time guarantee: the wait ends on a normal thread, and a node that then needs a busy cascade slot waits for one. !!! note "Not in a batch flow" A rate limit holds a value back for a timer to release, and a run has no timer, since the value would be dropped rather than delayed. Submitting a batch flow with a rate-limited port is refused instead. ### Trigger **`trigger`** — send one value now and another once things go quiet. Despite the name, a debounce and hold rather than a scheduler: everything it sends starts from a value arriving. For a cron tick, see [`inject`](#inject). | 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 | The shape this exists for: *the door opened, so turn the light on, and off again in two minutes unless it opens again.* `wait_port` covers the case where how long to wait is itself a value: a rollershutter takes 26 seconds up and 28 down, so the node that decides the direction says how long to run for as well. A wait of zero or less sends nothing afterwards, and still cancels whatever the last message scheduled, which is how a *stop* is commanded exactly once. ## 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 in the engine's container and read its output. | 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. !!! warning "Inside the container, not on the host" A flow ported from something that read the host's journal, or poked a host script, needs either a mount or a small listener on the host side. This node cannot see the host. ### 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 | Confined to a directory the engine owns. A flow that could name any path would be a way to read the secrets store or overwrite a node's source. ## 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](../concepts/runs.md) and a Function node. ## Connectors Anything else in the palette came from an installed **connector** package: a node type written against a published contract and discovered through the `fluksio.node_types` entry point group. The editor shows which package supplied it. A connector declares its contract version, and one written for a version this engine does not speak is ignored rather than half-loaded. Installing or upgrading one takes effect on the next engine restart, because Python does not re-import a changed module and a rescan would promise more than it delivers. The contract itself is in [The connector contract](connector-contract.md), and [Writing a connector](../code/connectors.md) walks through building one. ## See also - [Payload types](payload-types.md) — what a port may carry - [Writing node code](../code/nodes.md) — the Function node - [The flow editor](../interface/flow-editor.md) — placing and configuring them