Seed the aircon write-path rig

Four controls and the unit's own answer beside them. Two catches, both on:
the flow is seeded stopped and the node's commands setting is off. The initial
values are read off the unit when the script runs, so starting the flow asks
for what it was already doing rather than commanding it to something else.
This commit is contained in:
2026-08-20 22:43:03 +02:00
parent 044bdc662a
commit 37f0c55a8d
3 changed files with 19 additions and 4 deletions
+4 -1
View File
@@ -3,7 +3,7 @@
# The workspace root delegates to these (see ../Makefile).
.PHONY: dev-utils dev dev-local up down update install dev-backend dev-frontend \
generate-client seed-example seed-demo seed-house seed-hosted-demo test test-backend test-frontend soak bench-startup lint lint-backend \
generate-client seed-example seed-demo seed-house seed-aircon seed-hosted-demo test test-backend test-frontend soak bench-startup lint lint-backend \
lint-frontend umami clean help
COMPOSE_ROOT := $(CURDIR)
@@ -100,6 +100,9 @@ seed-demo: ## Seed the training-run example: a batch flow and its dashboard
seed-house: ## Seed the house write-path rig (needs the real broker reachable)
cd backend && uv run python ../scripts/seed_house_control.py
seed-aircon: ## Seed the aircon write-path rig (needs the unit reachable)
cd backend && uv run python ../scripts/seed_aircon_control.py
# Operators of the hosted demo only — NOT part of any deployment, and nothing a
# self-hosted instance needs. It wipes and recreates its three flows and its
# dashboard, so re-running it is how the public demo is reset.
+4 -1
View File
@@ -69,7 +69,10 @@ end against the house broker. The rig is the `house_control` flow and its
dashboard, seeded by `make -C app seed-house`.
- FEAT/NODE: Art-Net against the real fixtures is still untried. The house's own dmxnet sender re-emits universe 1 every 1000 ms, so fluksio and Node-RED overwrite each other; the test needs Node-RED's Art-Net sender stopped, and while it is stopped every channel fluksio does not set is dark.
- FEAT/NODE: the WF-RAC write path stays gated. `setAirconStat` needs an operatorId registered with the unit first, which is itself a write, and the aircon is not on the safe-to-control list.
- FEAT/NODE: the aircon has never been commanded for real. The encoder round-trips the unit's own live reading field for field and the dry run logs the right command, but nothing has been posted to it yet. `commands` is the switch.
- CHORE/NODE: the operatorId worry was unfounded — the reference Node-RED `setstat` node for this unit is configured with an empty operatorId and deviceId, so a command needs no registration. The second unit may still differ.
- PERF/NODE: a `wfrac` command is two round trips (read, then set) on the scheduler's thread, so at the default timeout a command can hold a cascade for several seconds. Fine for a person pressing a button; a flow commanding it on a schedule would want the work off that thread.
- CHORE/NODE: `wfrac` writes carry the unit's whole state, so two flows commanding one unit will each undo whatever the other set between their read and their write. One writer per unit, the same rule Art-Net has for a universe.
- FEAT/NODE: the second WF-RAC unit (the one Node-RED addresses with operatorId "0") closes the connection on an anonymous read. It likely wants an account registered; the first unit answers without one.
- CHORE/NODE: `wfrac` reports `mode` as "unknown" while the unit is off, because the mode bits hold a value outside the known set. Faithful to the reference decoder, but "off" would read better.
- PERF/NODE: `ArtNetOut.write` sends one frame per input port, so a node with two ports emits two frames per run. The last one carries both channels, so the end state is right; folding them into one send would halve the traffic.
+11 -2
View File
@@ -73,8 +73,9 @@ external interfaces. See `docs/architecture/structure.canvas` → *Backend M
`connector-skeleton/`. The registry follows later
- [x] First real connectors written against that contract from outside the engine:
WF-RAC aircon, calendar, UniFi presence and Art-Net, in `connectors/`.
Built by `make connectors` and installed into the image. Three of the four
read only; the aircon package still cannot produce a command
Built by `make connectors` and installed into the image. Calendar and
UniFi read only; Art-Net and the aircon write, each behind a setting that
starts off
- [x] The other direction of the contract: `ConnectorNode.write` receives the
node's input ports, so a connector can command something rather than only
read it. Additive, so `CONTRACT_VERSION` stays at 1 — before this the base
@@ -82,6 +83,14 @@ external interfaces. See `docs/architecture/structure.canvas` → *Backend M
packet builder unreachable. Art-Net now sends: a per-port `channels` map
puts each input on its own DMX channel, `transmit` still gates the socket,
and one node owns one universe because a frame carries all 512 levels
- [x] The aircon writes too: power, mode, setpoint and fan speed, behind a
`commands` setting that starts off. A WF-RAC command carries the whole
state, so the node reads the unit and applies the change on top — the
encoder is a port of the same reference the decoder came from, and it
round-trips the unit's own live reading field for field. No operatorId
registration turned out to be needed: the unit this instance talks to
accepts an anonymous command, which is what the reference Node-RED node
does as well
- [x] Node lifecycle as a protocol (`start`/`stop`/`report_health` on `Node`),
replacing the controller's per-type isinstance chains — the same hooks a
connector implements, validated on the built-in nodes first