docs
Docs / docs (push) Successful in 26s
Playwright Tests / test-playwright (1, 2) (push) Failing after 4m7s
Playwright Tests / test-playwright (2, 2) (push) Failing after 2m30s
pre-commit / pre-commit (push) Failing after 2m42s
Test Backend / test-backend (push) Failing after 3m3s
Compose Smoke Test / test-compose (push) Successful in 32s
Playwright Tests / merge-reports (push) Successful in 1m51s
Docs / docs (push) Successful in 26s
Playwright Tests / test-playwright (1, 2) (push) Failing after 4m7s
Playwright Tests / test-playwright (2, 2) (push) Failing after 2m30s
pre-commit / pre-commit (push) Failing after 2m42s
Test Backend / test-backend (push) Failing after 3m3s
Compose Smoke Test / test-compose (push) Successful in 32s
Playwright Tests / merge-reports (push) Successful in 1m51s
This commit is contained in:
@@ -1,17 +1,18 @@
|
||||
# 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.
|
||||
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](#connectors) installed on your instance.
|
||||
|
||||
MQTT and InfluxDB need `pip install 'fluksio[server]'` on a Python installation.
|
||||
The Docker image includes these dependencies. Run the broker and database
|
||||
separately, or use the development stack's test services.
|
||||
|
||||
## Function
|
||||
|
||||
**`python`** — your own code, run on every incoming message.
|
||||
**`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
|
||||
@@ -46,9 +47,11 @@ 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.
|
||||
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
|
||||
|
||||
@@ -81,6 +84,7 @@ Outputs only makes it a **webhook**: the engine mounts a route at
|
||||
| `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` |
|
||||
|
||||
@@ -96,12 +100,10 @@ Outputs only makes it a **webhook**: the engine mounts a route at
|
||||
}
|
||||
```
|
||||
|
||||
**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.
|
||||
**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
|
||||
|
||||
@@ -115,10 +117,10 @@ Building a query and shaping its rows are Function nodes on either side.
|
||||
| `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 |
|
||||
|
||||
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.
|
||||
For engine failures rather than flow messages, configure
|
||||
[Alerts](../interface/operations.md#alerts).
|
||||
|
||||
## Timing
|
||||
|
||||
@@ -135,9 +137,7 @@ thing configured elsewhere.
|
||||
| `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.
|
||||
Use `cron` for scheduled emissions, or trigger the node by hand for testing.
|
||||
|
||||
### Delay & schedule
|
||||
|
||||
@@ -154,17 +154,12 @@ Order of operations: rate check → delay → forward. With a `cron` and no inpu
|
||||
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.
|
||||
Timers wait until their deadline; busy workers can delay execution.
|
||||
|
||||
!!! 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.
|
||||
Batch flows reject rate-limited non-streaming ports. Runs have no live
|
||||
timer to release a held value.
|
||||
|
||||
### Trigger
|
||||
|
||||
@@ -181,14 +176,11 @@ starts from a value arriving. For a cron tick, see [`inject`](#inject).
|
||||
| `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.*
|
||||
For example: turn a light on when the door opens, then off after two quiet
|
||||
minutes.
|
||||
|
||||
`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.
|
||||
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
|
||||
|
||||
@@ -243,8 +235,8 @@ The cheapest fix for a sensor that reports every second and changes every hour.
|
||||
|
||||
### Command
|
||||
|
||||
**`exec`** — run a command in the engine's container and read its output.
|
||||
Adding one or changing its settings is a superuser's, since the command is code.
|
||||
**`exec`** — run a command where the engine runs and read its output.
|
||||
Adding one or changing its settings requires a superuser.
|
||||
|
||||
| Setting | Default | Notes |
|
||||
|---|---|---|
|
||||
@@ -255,11 +247,8 @@ Adding one or changing its settings is a superuser's, since the command is code.
|
||||
|
||||
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.
|
||||
In Docker, the command runs inside the backend container. Mount host files
|
||||
explicitly if the command needs them.
|
||||
|
||||
### File
|
||||
|
||||
@@ -272,8 +261,7 @@ Outputs the command's stdout, stderr and exit code.
|
||||
| `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.
|
||||
Paths stay inside the engine's files directory.
|
||||
|
||||
## Numeric
|
||||
|
||||
@@ -297,17 +285,15 @@ use a [batch flow](../concepts/runs.md) and a Function node.
|
||||
|
||||
**`wren`** — turn a typed command into values for the devices wired to it.
|
||||
|
||||
[Wren](https://github.com/stroblme/wren) is a small language model that runs
|
||||
on the engine's CPU: about 40 MB, a fifth of a second per command. It reads the
|
||||
house as text, the areas, the devices in each and their states, and answers a
|
||||
command with calls on those devices. It is a separate package, installed into
|
||||
the engine with `pip install wren`; without it the node says so on the canvas.
|
||||
[Wren](https://github.com/stroblme/wren) is an optional local language model
|
||||
for device commands. Install it in the engine's environment with
|
||||
`pip install wren`; the node reports a missing dependency if it is absent.
|
||||
|
||||
| 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` | °C; 20 without it |
|
||||
| `outdoor` | `float` or `int` | °C; 20 without it, clamped to −5…30 |
|
||||
| `dark` | `bool` | without it, dark before 07:00 and from 18:00 |
|
||||
|
||||
The outputs are `actions`, a list of records, and any number of **targets**. A
|
||||
@@ -346,43 +332,31 @@ reads.
|
||||
| `apply` | `false` | write to the targets; off, the node only reports |
|
||||
| `min_confidence` | `0.93` | below this nothing is written |
|
||||
|
||||
Each record in `actions` is `{message, value, applied, delay_s, call,
|
||||
confidence}`, one per call and target. A call nothing is wired for, such as
|
||||
`sensors.temperature` or a light in a room without one, gets a record with an
|
||||
empty `message`. The confidence is the lowest probability the model gave any
|
||||
part of its answer: at the default threshold, nine in ten right answers pass,
|
||||
and 38 of the 40 wrong ones in the model's own evaluation do not.
|
||||
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.
|
||||
|
||||
A call with a delay ("in 10m") is held by the engine's work queue, like the
|
||||
Trigger node's second value: it survives a restart where the queue is in
|
||||
Redis, and not otherwise.
|
||||
|
||||
Every house the model was trained on had a living room with a light, media
|
||||
and heating, and a light in every other area. The node fills in what is not
|
||||
wired, so the model reads a house it knows; a call on one of those is
|
||||
reported, never applied.
|
||||
The node adds default devices to the model's view for missing expected devices.
|
||||
They are reported but cannot receive writes unless wired.
|
||||
|
||||
!!! note "What the current model cannot do"
|
||||
|
||||
No levels: a light is on or off, never dimmed, coloured or half open.
|
||||
Delays are whole minutes. Only one device per kind and area. Only the ten
|
||||
areas above: one it was not trained on is answered confidently and wrongly,
|
||||
which is why such a port is refused rather than tried. Commands are English,
|
||||
phrased roughly like "turn on the lights in the kitchen".
|
||||
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](../code/agents.md#control) sends commands by hand.
|
||||
|
||||
## 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.
|
||||
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](connector-contract.md), and
|
||||
[Writing a connector](../code/connectors.md) walks through building one.
|
||||
|
||||
Reference in New Issue
Block a user