The coordinator runs every 60 seconds and performs:
- PID Update (per zone)
- Historical State Query (per zone)
- Zone Decision (per zone)
- Pump & Heat Request Aggregation
- Safety Lockout
- Boiler Mode Management
The PID controller calculates a duty cycle (0-100%) from the temperature error (setpoint minus current temperature).
Calculation:
- Proportional term: Kp × error (immediate response to temperature deviation)
- Integral term: Ki × accumulated error over time (eliminates steady-state error)
- Derivative term: Kd × rate of error change (damping, typically disabled for slow hydronic systems)
Anti-windup protection: The integral term is clamped between configurable limits (default 0-100%) to prevent unbounded accumulation when the system cannot reach setpoint.
Output clamping: The final duty cycle is clamped to 0-100%.
To prevent integral windup during periods when heating is blocked or irrelevant, the PID controller's update() method is skipped (integration paused) when any of the following conditions are true:
| Condition | Reason |
|---|---|
| Temperature unavailable | Cannot calculate meaningful error without current temperature |
Controller mode ≠ heat |
PID control only applies in heat mode |
| Zone disabled | Disabled zones don't participate in heating |
| Window open (above threshold) | Heating blocked, would cause integral windup |
When paused:
- The integral term is frozen at its current value
- The duty cycle is maintained at its last calculated value
- The error term is still updated (for UI display purposes)
This prevents the common problem of integral windup where the integral term accumulates while the room temperature is unstable after a window opening, which would cause overshoot when normal control resumes.
| Window | Duration | Calculation |
|---|---|---|
| Observation Period | 2 hours (default) | Aligned to midnight (00:00, 02:00, 04:00...) |
| Valve Position Estimation | 7 minutes (default) | valve_open_time + valve_close_time (physical position estimation) |
Heat accounting tracks how much of its quota each zone has consumed during the observation period, with optional supply-temperature normalization to adjust for actual heating conditions.
Key concepts:
used_durationaccumulates only whenflow=True(valve confirmed open)- When a supply temperature sensor is configured, accumulation is weighted by the supply coefficient
- At period boundaries, all zones reset to fresh quota
See Heat Accounting for detailed documentation.
The zone evaluation follows a priority-based decision tree:
-
Flush circuit priority: If flush is enabled and DHW has recently ended with no regular circuits currently running, flush circuits turn on to capture latent heat from the boiler.
-
End-of-period freeze: When less than
min_run_timeremains in the observation period, valve positions are frozen to prevent unnecessary cycling at period boundaries. -
Quota-based scheduling: For zones that haven't met their quota:
- If valve is already on: stay on (commands are re-sent to prevent relay timeout)
- If remaining quota is less than
min_run_time: stay off (not worth a short run) - If DHW is active and this is a regular circuit currently off: stay off (DHW priority)
- Otherwise: turn on
-
Quota met: For zones that have met their quota:
- If valve is on: turn off
- If valve is off: stay off
Note: Window blocking affects PID integration (pausing accumulation), not valve control directly. Valves follow quota-based scheduling regardless of window state.
The controller computes a single heat request signal from all zones with active flow:
- Only zones with
flow=True(valve confirmed fully open) are considered - A zone contributes to the heat request when its
remaining_durationexceeds theclosing_warning_duration(zone won't close imminently) - If any qualifying zone needs heat, the boiler heat request is enabled
When a summer mode entity is configured and the controller is in automatic mode:
- If heat is requested and summer mode is not "winter": switch to "winter" (enables heating circuit)
- If no heat is requested and summer mode is not "summer": switch to "summer" (disables heating circuit, saves energy)