# Getting started: facility automation You have a box in a cupboard, a handful of sensors that already publish somewhere, and an ambition to make the house do something about them. This page brings up a Fluksio instance you can leave running for years, then wires the first sensor through to a dashboard. Budget an afternoon. Most of it is the browser, which is the point: you will be in this interface a lot, so it is worth learning it properly. ## What you are standing up ```text sensors ──MQTT──▶ ┌──────────────┐ ──▶ InfluxDB (history) │ Fluksio │ HTTP / webhooks ──▶│ flow engine │ ──▶ dashboards (what you look at) │ │ schedules ────────▶└──────────────┘ ──▶ MQTT / HTTP (what you control) │ └── workers on other boxes (optional) ``` One engine holds every flow. Nothing here is a plugin you install separately — the broker client, the time-series writer, the dashboards and the alerting are all part of the same process, editing the same graph. ## Prerequisites - Docker and Compose v2 on the host - A hostname you can point at it. `fluksio.local`, a subdomain, or just `localhost` if you only ever reach it from that machine - Optionally: an MQTT broker and an InfluxDB you already run. If not, the stack can start both for you ## Bring up the stack Clone the app repository and start it: ```sh git clone https://git.stroblme.de/Fluksio/app.git ~/fluksio cd ~/fluksio cp .env.example .env $EDITOR .env # DOMAIN, FIRST_SUPERUSER, ENVIRONMENT=production make up ``` `.env` is the whole configuration. The four settings that matter on day one: | Setting | What it does | |---|---| | `DOMAIN` | the hostname everything is served under; the SPA lands on `app.${DOMAIN}` and the API on `api.${DOMAIN}` | | `FIRST_SUPERUSER` | the account you sign in with | | `FIRST_SUPERUSER_PASSWORD` | leave it as `changethis` and one is generated for you | | `ENVIRONMENT` | `production` closes the interactive API schema; `local` leaves it open | Everything the installation owns — the database, your flows, secrets, artifacts, the packages your node code imports — is on one Docker volume. Backing that volume up is backing up the installation. !!! tip "Reverse proxy" The stack emits Traefik labels and ships a Traefik you can bring up alongside it (`docker/compose.traefik.yml`). If you already run Nginx Proxy Manager or Caddy, attach it to the `proxy` network instead and forward `app.${DOMAIN}` → `fluksio-app:80` and `api.${DOMAIN}` → `fluksio-api:8000`. ??? note "Even smaller: no Docker at all" `pip install fluksio && fluksio serve` gives you the same engine with no containers, keeping its data in `~/.fluksio`. What it does not give you is the web interface, which the SPA container serves — so you would drive it from the API, or [pair it with a portal](../interface/portal.md) that serves the dashboard for you. Good for a Raspberry Pi that only runs flows; less good as your main instance. Open `http://app.${DOMAIN}` and sign in. You should be looking at Home: an empty brain graph, a health summary, and a flow list with nothing in it. ## Your first flow Go to **Flows → New flow** and call it `house`. You land on the canvas. A flow is a set of nodes that talk to each other through **named messages**. You do not draw wires: a node says which messages it needs and which it produces, and the canvas draws the graph that follows from those names. That sounds like a small difference and turns out to be a large one — renaming is safe, fan-in is free, and two flows can share a value by naming it. ### Read a sensor Press **Add node** (or ⌘K / Ctrl-K, which opens the command palette) and pick **MQTT**. In its panel on the right: - **Broker host** — your broker's hostname, `mosquitto` if you are using the one the stack can start - **Topic** — map each output to a topic: `{"living_temperature": "zigbee2mqtt/living/temperature"}` - **Provides** — add one output port named `living_temperature`, type `float` That is a working node. Press **Publish** (⌘S) and the engine picks it up. The canvas now draws your node with a live value on its output as soon as the broker sends one. Click the wire to see the last payload and its history. ### Do something with it Add a **Function** node. This is a Python node — the code editor opens in its panel: ```python def process(living_temperature, comfortable=21.0): """Ask for heat when the room is below the comfort point.""" return {"heat_wanted": living_temperature < comfortable} ``` Declare `living_temperature` as an input (type `float`) and `heat_wanted` as an output (type `bool`). `comfortable` is not a port — it is a **setting**, because it is a constant of this node rather than something the graph carries. It shows up as a field in the node's Settings section. The canvas now draws MQTT → your function, because the message names line up. Nothing else was needed. ### Act on it Add a second **MQTT** node, this time with `heat_wanted` as an *input*, and a topic mapping to whatever your relay listens on. A node with inputs publishes; a node with outputs subscribes. Publish the flow. You have a thermostat. !!! warning "Test before it touches a relay" A flow can be **paused** (it holds messages instead of running them) and **stepped** (release exactly one). Together with the run button — which injects a value by hand — that is how you convince yourself the logic is right before the contactor finds out. Both live on the dock at the bottom of the canvas. ## Store the history A live value is enough to control something and useless for answering "was last February colder?". That is what the **InfluxDB** node is for. Add one, and configure it to write the message you already have: ```json { "url": "http://influxdb:8086", "token": {"$secret": "influx-token"}, "org": "home", "bucket": "sensors", "writes": { "living_temperature": { "measurement": "environment", "field": "temp_c", "tags": {"room": "living"} } } } ``` Give it `living_temperature` as an input and every value that passes gets a point. Note the token. Credentials never sit in a flow: `{"$secret": "influx-token"}` is a reference into an encrypted store, and the editor renders those fields as a secret picker. Add the actual value once under **Secrets**. Flows are a git repository you might well push somewhere — this is what keeps a password out of it. Reading back is the same node with `queries` instead of `writes`, or — for anything a chart asks for — a pair of small Python nodes on either side that build a Flux query and shape its rows. That indirection is deliberate: the database node holds the connection and nothing else, so a dashboard widget never learns which database answered it. ## Put it on a screen **Dashboards → New dashboard**, then drag widgets onto the grid and bind each one to a message. A gauge on `house.living_temperature`, a switch on `house.heat_wanted`, a chart on the history. Widgets are typed the same way ports are: a switch binds to a `bool`, a gauge to a number, an agenda to a `list`. Bind it wrong and the editor says so rather than drawing nothing. Controls work in the other direction — a switch on a dashboard publishes the message it is bound to, exactly as a node would. The canvas draws it as a labelled endpoint feeding the nodes that read it, so a value never appears from nowhere. For a tablet on the wall, see [Dashboards and panels](../interface/dashboards.md): a panel is a named device, it pairs with a six-character code instead of a login, and it can only reach the dashboards you gave it. ## Spread it across machines You now have one box doing everything. Two reasons to change that: something lives on a different network, or something needs hardware the engine's host does not have. The unit of distribution is the **worker**. It runs the code of nodes you mark for it, and it dials *out* to the engine — so the Pi in the shed does not need an inbound route, and the engine does not need to reach it. On the engine, mint a token: ```sh curl -X POST https://api.${DOMAIN}/api/v1/workers/tokens \ -H "Authorization: Bearer $TOKEN" -d '{"name": "shed-pi"}' ``` On the other machine: ```sh pip install fluksio-worker fluksio-worker \ --url wss://api.${DOMAIN}/api/v1/workers/attach \ --token "$WORKER_TOKEN" \ --labels shed,gpio \ --parallel 2 ``` Then mark the node that talks to the shed's GPIO with `device: shed`, and it runs there. Everything else stays where it is. A node bound to a label no attached worker carries simply waits rather than failing, so you can write the flow before the hardware arrives. !!! note "What a worker is not" It is not a second engine. Subscriptions, schedules, webhooks and the dashboards all stay in one process — that is what keeps a value having one definition. A worker executes node bodies, nothing else. Scaling *the engine* to several processes is not supported: run one. ## Make it tell you when something breaks Under **Alerts**, add a channel and a rule. Channels are ntfy (a push notification on your phone), SMTP, a webhook, or a message a dashboard notification widget reads. What you probably want on day one is everything, to ntfy: - add an ntfy channel with your server and topic - add a rule with no events ticked, which means *all of them* The engine deduplicates aggressively — the same node failing every second is one alert, not thirty-six thousand — and caps the total at ten an hour however bad it gets. See [Secrets, modules and alerts](../interface/operations.md). ## Keep it alive - **Back up the data volume.** That is the database, the flows, the secrets and the artifacts. Everything else is rebuildable. - **Watch Home.** The health summary names what is wrong — a quarantined flow, a node that will not load, a stalled queue, a flow that cannot run because its graph does not validate. - **Flows are git.** `git log` inside the flow store is the history of every change anyone made, and reverting one is a revert. ## Where to go next - [Flows, nodes and messages](../concepts/flows.md) — the model, properly - [Node types](../reference/node-types.md) — everything you can put on a canvas without writing Python - [The flow editor](../interface/flow-editor.md) — the canvas, in detail - [Keeping state in a flow](../concepts/state.md) — running totals, debounces, and the one rule that makes them safe - [Accounts and the portal](../interface/portal.md) — reach the installation from outside the house without opening a port