Files
app/docs/concepts/state.md
T
2026-10-13 10:02:20 +02:00

57 lines
1.9 KiB
Markdown

# Keeping state in a flow
Need a running total or a last-seen value? Make it a message. Function nodes
have no hidden `context` or global flow store.
## Read and write the same message
Declare `reading` and `total` as inputs, and `total` as an output:
```python
def process(reading, total):
return {"total": total + reading}
```
Add `total` to the flow's **Inputs** with a starting value of `0.0`. Each
execution reads the previous total and publishes the next one.
- A node is never downstream of itself, so its own publication does not run
it again.
- A triggering input needs a starting value. Without one, validation reports
`self_loop_needs_initial`; a Python default alone does not supply the message.
## Feedback between two nodes
A controller may read an estimate that depends on its own previous output.
Ordinary triggering inputs would form a cycle. Mark the feedback input
**non-triggering** in the flow definition instead:
```json
{"name": "estimate", "port": "estimate", "dtype": "float", "trigger": false}
```
It creates no dependency and does not wake the node. Whenever another input
runs the node, it reads the latest estimate. If no estimate exists yet, that
argument is omitted, so give it a Python default:
```python
def process(reading, estimate=0.0):
return {"command": reading - estimate}
```
Use this on the feedback input; keep the inputs that should wake the node
triggering. Configure `trigger` through the API or flow JSON; the node panel
does not currently expose it.
## Built-in state
Built-in nodes such as delay, rate limit and filter-on-change keep their own
timing or comparison state in the engine. Storage and device nodes also manage
the state needed for their connections. Your Function code still exchanges
state through declared messages.
## See also
- [Where a node's values come from](values.md)
- [Node types](../reference/node-types.md)