ADR-0004: TRV calibration strategies, and model-predictive valve control¶
Status: Accepted Date: 2026-06 (retrospective)
Context¶
A radiator valve's built-in temperature sensor sits on the hot radiator, so it
reads warmer than the room and closes the valve before the room is actually
warm. Three things are true at once: (1) users have wildly different TRV
hardware and trust levels, (2) some valves expose a local_temperature_calibration
number, some expose a raw valve_opening_degree, some expose neither, and (3)
a radiator + room is a slow, lagged thermal system where naive bang-bang control
overshoots and oscillates.w
We need a way to drive valves well without assuming any one capability is present, and without forcing every user onto a complex control mode.
Decision¶
Offer three calibration modes, selectable per install (the
TRV calibration mode select), in increasing order of capability and ambition:
target(default, safe). Command the valve to the band's heat target and trust its own loop. Works on any climate entity; inherits the TRV's sensor bias. The recommended starting point.offset. Writelocal_temperature_calibration = room − TRV's own readingso the valve's internal loop regulates to the room temperature. Needs a calibration number.mpc. Drive thevalve_opening_degreedirectly with model-predictive control: a first-order per-room thermal model (dT/dt = gain·valve − loss·(room − outdoor)) whosegain/lossare learned online by least-squares system identification (SciPy), a Kalman filter to smooth the slow/noisy sensor between updates, and a short look-ahead optimiser (which also consumes the weather forecast for preconditioning).
Default to the mode that always works; let users opt up.
For why MPC rather than a PID: the plant is slow and lagged with a large
measurable disturbance (outdoor temperature). A learned first-order model with
look-ahead handles the lag and feed-forwards the disturbance directly; a PID
would need careful per-room retuning and still lacks feed-forward. The model is
also introspectable — gain/loss surface as diagnostics — where PID gains are
opaque.
Options Considered¶
| Option | Pros | Cons |
|---|---|---|
Single target mode only |
Trivial; universal | Inherits TRV sensor bias; no path to better control |
target + offset only |
Fixes the bias cheaply | Still bang-bang; needs a calibration number |
Add mpc (chosen) |
Handles lag + outdoor feed-forward; introspectable; best comfort | SciPy dependency; learning code to maintain and harden |
| PID instead of MPC | Familiar | Per-room tuning; no feed-forward; opaque gains |
Consequences¶
- Easier: users get a safe default and a clear upgrade path; MPC's learned parameters are observable and persisted across restarts.
- Harder: the MPC path carries real numerical risk (system identification, matrix conditioning) — mitigated by the pure-core test discipline (ADR-0002), Hypothesis properties, and explicit numerical hardening; SciPy runs in an executor to keep the event loop free.
- Discovery of the per-mode
numberentities is by name hint, now folded into device profiles (ADR-0001). - Known limitation / to revisit: the model learns a single
gain, so it assumes a constant radiator output and is mis-specified for weather-compensated hydronic systems (district heating, outdoor-reset boilers), where the same valve opening emits more heat in colder weather. Comfort still holds (closed-loop, rolling re-fit), but the fit degrades; a poor-fit repair flags it andoffsetmode is the recommended fallback. The proper fix is an optional supply-temperature input changing the heat term tok·valve·(supply − room), deferred until there's demand and a sensor to drive it.