v0.4.0 + Physics-Based Simulation Framework
Two updates to share — v0.4.0 focused on cleaning up the internals and improving diagnostic visibility, and a simulation framework that landed right after to support upcoming control algorithm work.
What changed in v0.4.0
The main user-facing additions are better diagnostic sensors. The controller now exposes zone counting sensors (zones_flowing, zones_heating, zones_window) so you can see at a glance how many zones are in each state. The heat request signal moved from per-zone tracking to a single controller-level binary sensor, which better reflects how the system actually works — one aggregated signal to the boiler.
Each zone also gets a new heat state (flow confirmed AND supply coefficient above 10%) and a remaining duration sensor showing how much time is left in the current valve run. Useful for dashboards and for understanding why a zone just turned off.
On the reliability side, the controller now defers initialization until all watched entities (valves, DHW sensor, supply/outdoor temp sensors) have reported valid state. This prevents the controller from issuing commands to entities that haven’t loaded yet after an HA restart — something that could cause brief spurious valve actions on slower systems. After 120 seconds it proceeds anyway and logs a warning, so a dead sensor won’t block startup forever.
Breaking change: Several entity IDs were renamed for consistency. If you have automations or dashboard cards referencing zone-level heat_request or blocked entities, you’ll need to update them — see the full changelog for the mapping.
Physics-based simulation framework
This is the part I’m most excited about. The integration now includes a thermal simulation test suite that runs the real HeatingController code — the exact same code running in production — against a lumped-capacitance room model. No HA dependencies, no mocks of the control logic itself. Each simulated room computes heat gain from the floor circuit minus envelope losses at 60-second timesteps, with a realistic valve actuator model (3-minute open ramp, 85% threshold before heat delivery).
Four room archetypes cover the range from Passivhaus (~60h thermal time constant) to pre-1960s uninsulated buildings (~13h), with parameters derived from EN 12831, ISO 13790, and EN 1264. The test suite validates:
- Steady-state convergence — does the controller actually reach and hold the setpoint across different building types and outdoor temperatures?
- Anti-windup behavior — does the integral term clamp correctly when a setpoint is physically unreachable?
- Disturbance recovery — window openings, setpoint changes, and outdoor temperature drops mid-simulation
- Multi-zone fairness — do zones with different demands get proportional heating time without starving each other?
- Borderline duty cycles — stable behavior at the minimum run time threshold where the valve might skip periods entirely
The full suite of 47 simulation tests runs in about two seconds. Seven tests are intentionally marked as expected failures to document genuine controller limitations (cold-start overshoot from integral windup, oscillation in leaky rooms outside the UFH design envelope) without breaking CI.
Why this matters for what’s coming next
@mroggi asked about PID parameter optimization earlier — the simulation framework is the foundation for that work. It lets me verify control algorithm changes against realistic thermal conditions before they reach anyone’s house.
The immediate next step is supply flow temperature limits — letting you define upper and lower bounds to keep the boiler operating in its efficient condensing range. But that feature depends on integral back-calculation corrections so the PID doesn’t wind up when flow constraints override what the controller wants to do. And verifying that back-calculation behavior across different building types and operating conditions is exactly what the simulation framework was built for.
So the sequence was: build the simulation harness → verify current behavior → safely implement constrained-output PID → expose flow limits to users. I’d rather take this in the right order than ship a feature that causes overshoot in edge cases.
If you’re curious about the simulation details, there’s a full write-up in the docs.
Install via HACS (custom repository): https://github.com/lnagel/hass-ufh-controller
Docs: Full documentation index