🧠 Auto TPI: In-Depth Technical Guide
NOTE
This document is intended for advanced users who want to understand the Auto TPI algorithm in detail. For a more accessible introduction, see the Auto TPI User Guide.
Table of Contents
- The TPI Algorithm
- Detailed Learning Cycle
- Thermal Capacity Calibration
- Coefficient Calculation Algorithms
- Automatic Correction Mechanisms
- Advanced Parameters and Constants
- Services and API
- Advanced Diagnostics and Troubleshooting
The TPI Algorithm
Fundamental Principle
The TPI (Time Proportional & Integral) algorithm calculates a power percentage at each cycle. This percentage determines how long the heater will be active during the cycle (e.g., 60% on a 10-minute cycle = 6 minutes of heating).
Basic Formula
Power = (Kint × ΔT_indoor) + (Kext × ΔT_outdoor)
Where:
- Kint (
tpi_coef_int): Indoor coefficient, reacts to the setpoint gap - Kext (
tpi_coef_ext): Outdoor coefficient, compensates for thermal losses - ΔT_indoor = Setpoint − Indoor Temperature
- ΔT_outdoor = Setpoint − Outdoor Temperature
graph LR
subgraph Inputs
A[Indoor Temperature]
B[Outdoor Temperature]
C[Setpoint]
end
subgraph TPI Calculation
D["ΔT_int = Setpoint - T_int"]
E["ΔT_ext = Setpoint - T_ext"]
F["Power = Kint×ΔT_int + Kext×ΔT_ext"]
end
subgraph Output
G["Power % (0-100%)"]
H["ON/OFF Time"]
end
A --> D
C --> D
B --> E
C --> E
D --> F
E --> F
F --> G
G --> H
Role of Coefficients
| Coefficient | Role | Learning Situation |
|---|---|---|
| Kint | Controls reactivity: the higher it is, the faster heating reacts to gaps | During temperature rise (gap > 0.05°C, power < 99%) |
| Kext | Compensates for thermal losses: the higher it is, the more heating anticipates cooling | During stabilization around the setpoint (gap < 0.5°C) |
Detailed Learning Cycle
Flow Overview
flowchart TD
subgraph Initialization
A[Start Session] --> B{Heating Rate = 0?}
B -->|Yes| C[Historical Pre-calibration]
B -->|No| G[Active Learning]
C --> D{Reliability >= 20%?}
D -->|Yes| G
D -->|No| E[Bootstrap Mode]
E -->|3 aggressive cycles| F[Estimated Capacity]
F --> G
end
subgraph "Learning Loop"
G --> H[Start TPI Cycle]
H --> I[Snapshot Initial State]
I --> J[Execute ON/OFF Heating]
J --> K[End Cycle: Measure ΔT]
K --> L{Valid Conditions?}
L -->|No| M[Skip Learning]
L -->|Yes| N{Analyze Situation}
N -.->|Overshoot| O[🔸 Kext Correction<br/>optional]
N -.->|Stagnation| P[🔸 Kint Boost<br/>optional]
N -->|T° Rising| Q[Kint Learning]
N -->|Stabilization| R[Kext Learning]
O -.-> S[Update Coefficients]
P -.-> S
Q --> S
R --> S
M --> H
S --> H
end
subgraph Finalization
S --> T{50 cycles Kint AND Kext?}
T -->|No| H
T -->|Yes| U[Save to Config]
U --> V[End Notification]
end
style O fill:#fff3cd,stroke:#ffc107,stroke-width:2px
style P fill:#fff3cd,stroke:#ffc107,stroke-width:2px
NOTE
Yellow boxes with dashed lines (🔸) represent optional correction mechanisms. They must be explicitly enabled via set_auto_tpi_mode service parameters.
Cycle Snapshot Details
At each cycle start, the algorithm captures the current state:
| Captured Data | Usage |
|---|---|
last_temp_in | Indoor temperature at cycle start |
last_temp_out | Outdoor temperature at cycle start |
last_order | Setpoint at cycle start |
last_power | Calculated power for this cycle (0.0 to 1.0) |
last_state | HVAC mode (heat/cool) |
At cycle end, these values are compared with current measurements to calculate progression.
Cycle Validation Conditions
A cycle is ignored for learning if:
| Condition | Reason |
|---|---|
| Power = 0% or 100% | Saturation: no exploitable efficiency information |
| Setpoint modified | Target changed mid-cycle |
| Power shedding active | Heating was forced OFF by Power Manager |
| Failure detected | Anomaly detected (ineffective heating) |
| Central boiler OFF | Thermostat requests but boiler doesn't respond |
| First cycle after restart | No valid reference data |
Thermal Capacity Calibration
Definition
Thermal capacity (or heating rate) represents the maximum temperature rise speed of your system, expressed in °C per hour (°C/h).
Example: A capacity of 2.0 °C/h means your radiator can raise the temperature by 2°C in one hour at full power (under ideal adiabatic conditions).
Determination Methods
graph TD
A[Heating Rate = 0?] -->|Yes| B[Pre-calibration]
A -->|No| C[Use Configured Value]
B --> D{History Available?}
D -->|Yes| E[History Analysis]
D -->|No| F[Bootstrap Mode]
E --> G{Reliability >= 20%?}
G -->|Yes| H[Calibrated Capacity]
G -->|No| F
F --> I[3 aggressive cycles Kint=1.0 Kext=0.1]
I --> J[Measure Actual Rise]
J --> K[Estimated Capacity]
H --> L[Kint/Kext Learning]
K --> L
C --> L
Pre-calibration via History Analysis
The auto_tpi_calibrate_capacity service analyzes sensor history:
- Retrieval of
temperature_slopeandpower_percentdata over 30 days - Filtering: only keeps points where
power >= 95% - Outlier elimination using IQR (Interquartile Range) method
- 75th percentile calculation of slopes (more representative than median)
- Adiabatic correction:
Capacity = P75 + Kext × ΔT - Safety margin application: 20% by default
Bootstrap Mode
If history is insufficient (reliability < 20%), the system enters bootstrap mode:
- Aggressive coefficients: Kint = 1.0, Kext = 0.1
- Duration: minimum 3 cycles
- Goal: Trigger significant temperature rise to measure actual capacity
- Timeout safety: If failure after 5 cycles, default capacity = 0.3 °C/h (slow systems)
Coefficient Calculation Algorithms
Kint Learning (Indoor Coefficient)
The algorithm adjusts Kint when temperature rises toward the setpoint.
Detailed Formula
flowchart LR
subgraph "1. Effective Capacity"
A["C_eff = C_ref × (1 - Kext × ΔT_ext)"]
end
subgraph "2. Max Possible Rise"
B["max_rise = C_eff × cycle_duration × efficiency"]
end
subgraph "3. Adjusted Target"
C["target = min(setpoint_gap, max_rise)"]
end
subgraph "4. Ratio"
D["ratio = (target / actual_rise) × aggressiveness"]
end
subgraph "5. New Kint"
E["Kint_new = Kint_old × ratio"]
end
A --> B --> C --> D --> E
Variables Used
| Variable | Description | Typical Value |
|---|---|---|
C_ref | Calibrated reference capacity | 1.5 °C/h |
Kext | Current outdoor coefficient | 0.02 |
ΔT_ext | Indoor/outdoor temp difference | 15°C |
cycle_duration | In hours | 0.167 (10 min) |
efficiency | Power percentage used | 0.70 |
aggressiveness | Moderation factor | 0.9 |
Kext Learning (Outdoor Coefficient)
The algorithm adjusts Kext when temperature is close to setpoint (|gap| < 0.5°C).
Formula
Correction = Kint × (indoor_gap / outdoor_gap)
Kext_new = Kext_old + Correction
- If indoor_gap negative (overshoot) → Negative correction → Kext decreases
- If indoor_gap positive (undershoot) → Positive correction → Kext increases
Smoothing Methods
Two methods are available to smooth new values:
Weighted Average ("Discovery" mode)
Kint_final = (Kint_old × count + Kint_new) / (count + 1)
| Cycle | Old Weight | New Weight | New Value Impact |
|---|---|---|---|
| 1 | 1 | 1 | 50% |
| 10 | 10 | 1 | 9% |
| 50 | 50 | 1 | 2% |
The counter is capped at 50 to maintain minimum reactivity.
EWMA ("Fine Tuning" mode)
Kint_final = (1 - α) × Kint_old + α × Kint_new
α(n) = α₀ / (1 + decay_rate × n)
| Parameter | Default | Description |
|---|---|---|
α₀ (initial alpha) | 0.08 | Initial weight of new values |
decay_rate | 0.12 | Alpha decrease speed |
Continuous Kext Learning
This mechanism allows for long-term adaptation of without an active learning session.
Eligibility Conditions
A cycle is used for continuous learning only if:
- Feature enabled:
auto_tpi_continuous_kextis set totrue. - Bootstrapped: At least one outdoor learning cycle has been completed previously for the current mode.
- Non-saturated power: .
- Stable system: No cycle interruptions, no boiler off, no heating failure, and no excessive consecutive failures.
- Significant outdoor delta: .
- No setpoint change: The target temperature did not change during the cycle.
Continuous Learning Formula
The correction is calculated similarly to the standard learning:
Then, it is applied using an EWMA with a specific alpha:
By default, .
Automatic Correction Mechanisms
Overshoot Correction (Kext Deboost)
Activation:
allow_kext_compensation_on_overshootparameter inset_auto_tpi_modeservice
Detects and corrects when temperature exceeds the setpoint without coming back down.
flowchart TD
A{T° > Setpoint + 0.2°C?} -->|Yes| B{Power > 5%?}
B -->|Yes| C{T° not falling?}
C -->|Yes| D[Kext Correction]
A -->|No| E[No Correction]
B -->|No| E
C -->|No| E
D --> F["reduction = overshoot × Kint / ΔT_ext"]
F --> G["Kext_target = max(0.001, Kext - reduction)"]
G --> H[Apply with alpha boost ×2]
Stagnation Correction (Kint Boost)
Activation:
allow_kint_boost_on_stagnationparameter inset_auto_tpi_modeservice
Detects and corrects when temperature stagnates despite significant gap.
flowchart TD
A{Gap > 0.5°C?} -->|Yes| B{Progress < 0.02°C?}
B -->|Yes| C{Power < 99%?}
C -->|Yes| D{Consecutive boosts < 5?}
D -->|Yes| E[Kint Boost]
A -->|No| F[No Correction]
B -->|No| F
C -->|No| F
D -->|No| G[Undersized Heating Alert]
E --> H["boost = 8% × min(gap/0.3, 2.0)"]
H --> I["Kint_target = Kint × (1 + boost)"]
Advanced Parameters and Constants
Internal Constants (Non-configurable)
| Constant | Value | Description |
|---|---|---|
MIN_KINT | 0.01 | Kint floor to maintain reactivity |
OVERSHOOT_THRESHOLD | 0.2°C | Overshoot threshold to trigger correction |
OVERSHOOT_POWER_THRESHOLD | 5% | Min power to consider overshoot as Kext error |
OVERSHOOT_CORRECTION_BOOST | 2.0 | Alpha multiplier during correction |
NATURAL_RECOVERY_POWER_THRESHOLD | 20% | Max power to skip learning in natural recovery |
INSUFFICIENT_RISE_GAP_THRESHOLD | 0.5°C | Min gap to trigger Kint boost |
MAX_CONSECUTIVE_KINT_BOOSTS | 5 | Limit before undersizing alert |
MIN_PRE_BOOTSTRAP_CALIBRATION_RELIABILITY | 20% | Min reliability to skip bootstrap |
Configurable Parameters
| Parameter | Type | Default | Range |
|---|---|---|---|
| Aggressiveness | Slider | 1.0 | 0.5 - 1.0 |
| Heating Time | Minutes | 5 | 1 - 30 |
| Cooling Time | Minutes | 7 | 1 - 60 |
| Heating Rate | °C/h | 0 (auto) | 0 - 5.0 |
| Initial Weight (Discovery) | Integer | 1 | 1 - 50 |
| Alpha (Fine Tuning) | Float | 0.08 | 0.01 - 0.3 |
| Decay Rate | Float | 0.12 | 0.0 - 0.5 |
Services and API
versatile_thermostat.set_auto_tpi_mode
Controls learning start/stop.
service: versatile_thermostat.set_auto_tpi_mode
target:
entity_id: climate.my_thermostat
data:
auto_tpi_mode: true # true = start, false = stop
reinitialise: true # true = full reset, false = resume
allow_kint_boost_on_stagnation: false # Boost Kint on stagnation
allow_kext_compensation_on_overshoot: false # Kext correction on overshoot
versatile_thermostat.auto_tpi_calibrate_capacity
Calibrates thermal capacity from history.
service: versatile_thermostat.auto_tpi_calibrate_capacity
target:
entity_id: climate.my_thermostat
data:
start_date: "2024-01-01T00:00:00+00:00" # Optional
end_date: "2024-02-01T00:00:00+00:00" # Optional
min_power_threshold: 95 # Min power %
capacity_safety_margin: 20 # Safety margin %
save_to_config: true # Save to config
Service Returns:
| Key | Description |
|---|---|
max_capacity | Calculated raw capacity (°C/h) |
recommended_capacity | Capacity after margin (°C/h) |
reliability | Reliability index (%) |
samples_used | Number of samples |
outliers_removed | Number of outliers removed |
Advanced Diagnostics and Troubleshooting
Diagnostic Sensor
Entity: sensor.<name>_auto_tpi_learning_state
| Attribute | Description |
|---|---|
active | Learning in progress |
heating_cycles_count | Total observed cycles |
coeff_int_cycles | Validated Kint cycles |
coeff_ext_cycles | Validated Kext cycles |
model_confidence | Confidence 0.0 - 1.0 |
calculated_coef_int | Current Kint |
calculated_coef_ext | Current Kext |
last_learning_status | Last cycle reason |
capacity_heat_status | learning or learned |
capacity_heat_value | Current capacity (°C/h) |
Common Learning Statuses
| Status | Meaning | Suggested Action |
|---|---|---|
learned_indoor_heat | Kint updated successfully | Normal |
learned_outdoor_heat | Kext updated successfully | Normal |
power_out_of_range | Power at 0% or 100% | Wait for non-saturated cycle |
real_rise_too_small | Rise < 0.01°C | Check sensor or cycle duration |
setpoint_changed_during_cycle | Setpoint modified | Avoid touching setpoint |
no_capacity_defined | No calibrated capacity | Wait for calibration/bootstrap |
corrected_kext_overshoot | Overshoot correction applied | Normal if Kext too high |
corrected_kint_insufficient_rise | Kint boost applied | Normal if Kint too low |
max_kint_boosts_reached | 5 consecutive boosts | Undersized heating |
Diagnostic Decision Tree
flowchart TD
A[Problem Detected] --> B{Kint or Kext?}
B -->|Kint too low| C[T° rises slowly]
C --> D{After 10 cycles?}
D -->|Yes| E[Check heating/cooling times]
D -->|No| F[Wait for convergence]
B -->|Kint too high| G[T° oscillations]
G --> H[Reduce aggressiveness]
B -->|Kext too low| I[T° drops below setpoint]
I --> J[Check outdoor T° sensor]
B -->|Kext too high| K[Persistent overshoot]
K --> L[Enable allow_kext_compensation]
A --> M{No learning?}
M -->|power_out_of_range| N[Saturated heating]
N --> O[Wait for favorable conditions]
M -->|no_capacity_defined| P[No calibration]
P --> Q[Check history or force value]
Persistence File
Location: .storage/versatile_thermostat_{unique_id}_auto_tpi_v2.json
This file contains the complete learning state and is restored on Home Assistant restart. It can be deleted to force a complete reset (not recommended).
Startup Synchronization
At each startup, if Continuous Kext Learning is enabled, the system performs an Alignment between the stored data (JSON) and the Home Assistant configuration (ConfigEntry):
- Clamping: Loaded coefficients are immediately capped to the
max_coef_intlimit (standard safety). - Kext and Capacity Configuration Sync: If the or the heating/cooling capacity in the configuration differs from the learned value in storage (captured through background adaptation without integration reload), the system performs an atomic update of the configuration. This ensures that the user interface and the YAML/UI configuration remain synchronized with the most accurate building model.
Appendices
Recommended Value References
| Heating Type | Heating Time | Cooling Time | Typical Capacity |
|---|---|---|---|
| Electric convector | 2-5 min | 3-7 min | 2.0-3.0 °C/h |
| Inertia radiator | 5-10 min | 10-20 min | 1.0-2.0 °C/h |
| Underfloor heating | 15-30 min | 30-60 min | 0.3-0.8 °C/h |
| Central boiler | 5-15 min | 10-30 min | 1.0-2.5 °C/h |
Complete Mathematical Formulas
Effective Capacity:
Adaptive Alpha (EWMA):
Calibration Reliability:
Where CV = Coefficient of Variation (standard deviation / mean)