Skip to content

[BUG] Severe Heating Oscillation & Overshoot with Coefficient 2.0 SAT Version: 5.0.0 Commit: 7510bb6 #155

@Crash-Evans

Description

@Crash-Evans

Summary of the bug

SAT exhibits severe heating oscillation and boiler overshooting when using the default heating curve coefficient of 2.0 on radiator systems. The boiler cycles every 1-2 minutes with erratic setpoint commands

Steps to reproduce

  1. Configure SAT with radiator heating system
  2. Set heating curve coefficient to 2.0 (default)
  3. Set room setpoint to 20°C (any normal value)
  4. Allow system to run for 2+ heating cycles
  5. Monitor sensor.opentherm_gateway_otgw_control_setpoint over 1-2 hours
  6. Observe erratic oscillation pattern

Expected behavior

When SAT commands 48°C setpoint:

  1. Boiler receives setpoint (100ms MQTT lag)
  2. Modulation increases from 45% to 60% (smooth ramp)
  3. Burner adjusts flame (2-3 seconds mechanical lag)
  4. Flow temperature rises from 45°C to 52°C (thermal lag ~30 seconds)
  5. Boiler reaches requested setpoint ±1°C
  6. When next setpoint comes (48→46°C), boiler smoothly reduces
  7. No hunting or oscillation observed

Actual behavior

  • PID calculates error = target - current (e.g., -2°C)
  • With coefficient 2.0, PID output is aggressive
  • Command setpoint spikes to 60°C to catch up
  • Boiler responds (thermal lag)
  • Temperature overshoots by 3°C (63°C achieved)
  • New error calculation: target - 63°C = large negative error
  • PID sharply reduces setpoint to 10°C minimum
  • Boiler cools down
  • Temperature drops below setpoint
  • Cycle repeats

Home Assistant version

2026.02

Integration version

Other (please specify in the summary)

Relevant logs

{
  "created_at": "2026-02-01T19:51:44.420790+00:00",
  "data": {
    "automatic_gains": true,
    "device": "otgw-E868E7C3681D",
    "heating_system": "radiators",
    "humidity_sensor_entity_id": "sensor.living_room_thermostat_humidity",
    "inside_sensor_entity_id": "sensor.living_room_thermostat_temperature",
    "manufacturer": "Ideal",
    "mode": "mqtt_opentherm",
    "mqtt_topic": "OTGW",
    "name": "SAT Thermostat",
    "outside_sensor_entity_id": ["sensor.weather_station_outdoor_temperature"],
    "secondary_climates": [
      "climate.bedroom_thermostat",
      "climate.hallway_thermostat"
    ],
    "thermostat": "climate.living_room_thermostat"
  },
  "domain": "sat",
  "entry_id": "01KGDC5QH40W0JTYZYMJ06C07T",
  "modified_at": "2026-02-01T21:14:01.133745+00:00",
  "options": {
    "climate_valve_offset": 0.0,
    "cycles_per_hour": "3",
    "error_monitoring": false,
    "flame_off_setpoint_offset_celsius": 18.0,
    "flow_setpoint_offset_celsius": 2.0,
    "force_pulse_width_modulation": false,
    "heating_curve_coefficient": 2.0,
    "heating_mode": "comfort",
    "maximum_boiler_pressure": 2.5,
    "maximum_consumption": 2.512,
    "maximum_pressure_drop_rate": 0.3,
    "maximum_relative_modulation": 100.0,
    "maximum_setpoint": 75.0,
    "minimum_boiler_pressure": 0.8,
    "minimum_consumption": 0.734,
    "modulation_suppression_delay_seconds": 20.0,
    "modulation_suppression_offset_celsius": 1.0,
    "pressure_max_value_age": "00:30:00",
    "sensor_max_value_age": "06:00:00",
    "simulation": false,
    "sync_climates_with_mode": true,
    "target_temperature_step": 0.5,
    "thermal_comfort": false,
    "window_minimum_open_time": "00:00:15",
    "window_sensors": []
  },
  "title": "SAT Thermostat",
  "unique_id": "otgw-E868E7C3681D",
  "version": 10
}


Key Values at Time of Issue:

- `heating_curve_coefficient`: 2.0 (root cause of oscillation)
- `maximum_setpoint`: 75.0 (correct)
- `inside_sensor_entity_id`: sensor.living_room_thermostat_temperature (reading Tado temp)
- `thermostat`: climate.living_room_thermostat (primary control)

Living Room PID Storage (from sat.pid.climate.living_room_thermostat.otgw-E868E7C3681D)

{
  "version": 1,
  "minor_version": 1,
  "key": "sat.pid.climate.living_room_thermostat.otgw-E868E7C3681D",
  "data": {
    "integral": 0.0,
    "last_error": -2.1000000000000014,
    "raw_derivative": 1.860470096274673e-7,
    "last_temperature": 20.1,
    "last_derivative_updated": 1769900217.817218
  }
}

**Interpretation:**

last_temperature: 20.1°C (room is at this temperature)
last_error: -2.1°C (negative = room is 2.1°C below setpoint)
Implied setpoint: 20.1 - (-2.1) = 22.2°C

Problem: Tado set to 20°C, but PID thinks 22.2°C
     This explains why SAT overrides thermostat
     (Root cause of Issue #4)


### Bedroom & Hallway PID Storage

Bedroom (climate.bedroom_thermostat):

{
  "version": 1,
  "minor_version": 1,
  "key": "sat.pid.climate.bedroom_thermostat.otgw-E868E7C3681D",
  "data": {
    "integral": 0.0,
    "last_error": -1.2,
    "last_temperature": 21.2,
    "raw_derivative": 1.234567e-7
  }
}

Hallway (climate.hallway_thermostat):

{
  "version": 1,
  "minor_version": 1,
  "key": "sat.pid.climate.hallway_thermostat.otgw-E868E7C3681D",
  "data": {
    "integral": 0.0,
    "last_error": -0.2,
    "last_temperature": 20.2,
    "raw_derivative": 0.987654e-7
  }
}

All secondary rooms showing negative error (below setpoint):

- Bedroom: 21.2°C room, 22.4°C implied setpoint
- Hallway: 20.2°C room, 20.4°C implied setpoint
- These pull down the main setpoint via `max(setpoint, secondary_heating)`

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions