Three ways in
Try the controller
The real interface, running on invented data with no backend. Switch to spa, call for heat, edit a schedule. Every reading is mock — it says so at the bottom of the screen.
Drawings and partsThe panel build
Every component photographed, where each sits on the backplate, and what connects to what. The figures are generated from one shared geometry so they cannot disagree.
Source and documentsThe repository
Requirements, architecture, the bring-up procedure, and the bench notes. MIT licensed. Issues are open; pull requests are not, yet.
Why the measurements are the point
A rule this project runs on: every number needs a source. If a figure cannot say where it came from, it is treated as fiction. That rule keeps earning its place.
Its product page names the 8relay-rpi driver.
The routing is 8relind-rpi's. That driver's channels
close relays 1, 2, 8, 7, 3, 4, 5, 6 — so writing channel 8, the
nominal spare, starts the blower. Mapped by hand on the bench.
The same page advertises a hardware watchdog.
The I²C bus holds a single port expander at 0x27 and
nothing else — no microcontroller, no watchdog. It had to be
built in software instead. It buys recovery, not safety: the
expander latches, so a wedged Pi leaves every relay where it was.
Measured — relays held through all 51 s of a deliberate wedge.
Two figures sat in the requirements document justifying design decisions.
Both were invented outright — a pool-heating duration and the exchanger pressure drop that justified automating a valve. Found by auditing every number for a citation.
Several decisions rested on what njsPC was assumed to do.
Four were reversed once someone read its source. It creates the bodies, circuits and valves unprompted, drives them from its own timers, and cannot be made passive — so this project supervises it rather than replacing it.
Unit tests passed on interlocks that were wrong in the same way three times.
Integration suites — a relay card faked at the process boundary, an njsPC answering real HTTP — found that the bypass, the pool heat call and the blower were each enforced only when an intent was what moved. njsPC takes the body without asking. Every one had the right end state and the wrong route through it.
The afternoon it was real
Everything above is a discovery that something was wrong. Here is the other half. On 30 August 2026 a temporary rig — lever connectors, pluggable blocks, no gland and no permanent cable — put the heat pump on two relay channels for one afternoon.
The interesting line is the first one. Resting state was
0x40 — the bypass valve held around the heater. The call
is 0x10. So the bypass released and the heat call closed
in a single write to a single byte: there is no
instant at which the heater is firing into an exchanger that has no
water going through it. That interlock is not enforced by getting the
order of two writes right. The bad combination cannot be represented.
The third line is the other half of the same idea. Ending a call does not isolate the exchanger — it keeps water moving through it for three minutes first, and only then lets the valve swing. A spa call ran the same way on the other channel.
Then the wires came off and the heater went back to its own thermostat. It is not installed. What survives is measurements: a flow floor that had been invented, a purge that had never run, and a three-wire control model that had been read from a manual and was wrong about which terminals it used.
Where it has got to
| Part | State | Notes |
|---|---|---|
| Supervisor and UI on the Pi | running | Both systemd services, verified across a reboot, watchdog restarts a wedged process |
| Relay driving | done | Channel map measured, not inherited; written to the expander directly |
| Interlocks | done | Bypass policy, heat-conditional pump floor, purge, targets as cutoffs |
| Commissioning checks | done | Ten checks comparing njsPC's settings against what this repo believes. It reports; it never corrects |
| Equipment on the bus | not yet | No serial port, so every reading is null — which is the state the tests are written against |
| Valve travel modelling | blocked | Needs a stopwatch on a real actuator. Until then njsPC's own delay clock is displayed, and said to be its |
| Scheduled preheat | blocked | Wants a real water temperature. Refuses with a reason rather than doing nothing |
| TLS | deferred | Deliberately. The reasoning is in the requirements document |
⚠︎ Safety
This documents a system switching 240 V pool equipment — a heat pump, a variable-speed pump and a 1.5 HP blower — in a wet environment.
- The 240 V side, including NEC 680 equipotential bonding, is work for a licensed electrician and in most places needs a permit and an inspection. This is not a wiring guide.
- The heater keeps its own thermostat and its own hard caps, in its own firmware. That is deliberate, and it is why no software bug here can produce a scalding spa.
- Everything is specific to one site. Valve travels, relay wiring and flow thresholds do not transfer. Survey your own system.