Underfloor Heating Controller

The first Home Assistant integration purpose-built for hydronic underfloor heating systems.

While existing thermostats adapt radiator/TRV logic to UFH, this integration addresses UFH’s unique requirements: high thermal mass, slow response times, multi-zone coordination through shared heat sources, and valve scheduling to prevent rapid cycling.

Highlights

PID-Based Temperature Control

  • Tuned for concrete screed thermal response
  • Per-zone PID parameters (Kp, Ki, Kd) with anti-windup protection
  • EMA smoothing handles noisy wireless sensors (Zigbee, WiFi)

Multi-Zone Coordination

  • Zones aggregate demands for efficient boiler firing
  • Observation period scheduling (2-hour windows) prevents rapid valve cycling
  • Quota-based valve management allocates time based on PID duty cycle

Native Boiler Integration

  • Smart heat request signaling - waits for valves to fully open before firing
  • DHW priority handling - blocks new heating during hot water
  • Latent heat capture - flush circuits capture residual boiler heat after DHW
  • Summer mode control - enables/disables heating circuit based on demand
  • Compatible with Bosch, Buderus, Nefit, Junkers, Worcester via EMS-ESP

Zone Fault Isolation

  • Independent zone failures - one failing sensor doesn’t affect other zones
  • Graceful degradation - failed zones use last-known demand for 1 hour before fail-safe
  • Safe initialization - no valve actions until all zones have valid readings
  • Automatic recovery when sensors reconnect

More details in the GitHub release

See also the documentation

1 Like

Hi,

Does this controller also account for hybrid systems with some rooms in the house being heated by radiators instead of UFH and thus requiring a different or possibly higher temperature of the boiler supply temp?

This integration does not control boiler supply temperatures, that falls outside the scope here. But in general, the control logic can be used for radiators if you wish.

Amazing project, thank you for sharing. I am testing this right now with one room.
Are you planning to implement more advanced optimization of PID paraameters based on historic performance? I am not sure how to manually optimize PID parameters to get to optimized results.

What’s Changed in v0.2.0 since v0.1.0

  • Hide zone analysis entities by default
  • Set flush request timing to activate only after DHW ends
  • Centralize version management in const module
  • Fix observation period alignment to respect configured value
  • Add force-update mechanism for external dead-man-switch support
  • Watch controller entities for external state changes
  • Add Tasmota relay configuration documentation
  • Add valve unavailability as degraded/fail-safe trigger
  • Add shorter initializing timeout for faster fail-safe reporting
  • Remove circulation_entity configuration option

Full Changelog: Comparing v0.1.0...v0.2.0 · lnagel/hass-ufh-controller · GitHub

I’m planning to add heat accounting based on supply temperature. More details and opportunity to comment in GitHub. Any comments and challenges welcome.

v0.3.0 released

New Features

  • Heating curve for outdoor temperature compensation (#76) — Dynamically calculates supply target temperature based on outdoor conditions using two-point linear interpolation. When an outdoor temperature sensor is configured, the system adjusts the supply target between warm and cold design points instead of using a fixed value. Includes a new controller-level supply target sensor entity.
  • Heat accounting with supply temperature normalization (#64) — Zones now accumulate quota weighted by actual heat delivery via a supply coefficient. When supply temperature is below target, zones consume quota slower and stay open longer. Includes a new supply coefficient sensor per zone and a flow binary sensor per zone. Configurable via a new “Heat Accounting” options flow step.
  • Quickstart guide (#68) — New task-oriented guide (docs/quickstart.md) walking users from installation to a working first zone.

Bug Fixes

  • Fix crash on config entry unload after in-place config reload (#65) — Stale listener callbacks caused ValueError: list.remove(x): x not in list on entry unload. Fixed by adding a dedicated shutdown() method registered once during setup.
  • Fix flaky test due to EMA floating-point precision (#81, #86) — Use pytest.approx() for float assertions where values pass through EMA smoothing.

Improvements

  • PID error icons changed to plus/minus (#66) — Replaced chevron icons (too similar to check icon) with mdi:thermometer-plus (cold) and mdi:thermometer-minus (warm).
  • Zone diagnostic entities hidden from dashboards — Zone sensors and binary sensors now have entity_registry_visible_default = False. Mode select and flush switch remain visible.
  • Entity recreation on config change — Changing supply temp, outdoor temp, or DHW entity in config now triggers a
    full reload to recreate affected entities.

Refactoring / Internal

  • Storage format V2 (#78) — Coordinator state restructured with controller data nested under a controller key. PID keys renamed for consistency. Includes automatic V1 → V2 migration.
  • Entity declarations refactored to description-based patterns (#77) — All entity metadata and behavior defined in entity descriptions with generic entity classes using callbacks.
  • PIDState fields renamed (#82) — p_termproportional, i_termintegral, d_termderivative.
  • TimingParams renamed to TimingConfig throughout codebase.
  • Test reorganization (#63) — Moved pure controller/zone tests from integration/ to unit/.

Full Changelog: Comparing v0.2.1...v0.3.0 · lnagel/hass-ufh-controller · GitHub