Climate control issue: Tuya heater not updating current temperature

Hi all,

I’m having some troubles converting my Eurom Alutherm 1500 WiFi heater, which contains an ESP8266. The Tuya datapoints are as follows (based on what I found on the internet):

1: switch, on or off
2: target temperature, int value
3: current temperature, int value
4: power mode, enum (0 = manual, 1 = program)
101: power intensity, enum (0:3 = (low, medium, high, off))
102: eco switch, on or off

I flashed it successfully with ESPHome, and am using the following climate control:

climate:
  - platform: tuya
    name: "${upper_devicename} automatic control"
    id: raw_climate
    switch_datapoint: 1
    active_state_datapoint: 1
    target_temperature_datapoint: 2
    current_temperature_datapoint: 3
    eco_datapoint: 102
    eco_temperature: 16 °C
    visual:
      min_temperature: 10
      max_temperature: 25
      temperature_step: 1
    on_state:
      then:
        - lambda: |-
            // make sure we're in programmed mode, so that the heater can
            // vary the power required to reach the requested temperature.
            static uint8_t previous_mode = CLIMATE_MODE_OFF;
            uint8_t mode = id(raw_climate).mode;
            if (mode != CLIMATE_MODE_OFF && mode != previous_mode)
              tuya_tuya->set_enum_datapoint_value(4, 0x1);
            previous_mode = mode;

I would like the heater to be able to vary its power output automatically, so I’m force-enabling the program mode whenever a setting is changed. This all works as expected, however, I’m often running into the issue that the current temperature is not updated (and thus the heater continues to heat, or to remain disabled) until I manually trigger a state change, e.g., by changing the target temperature. FWIW, when I do that the display of the heater temporarily enables, so maybe it’s related to the device being in sleep mode or something?

To illustrate, here’s some logs. This is the starting situation:

[09:49:17][I][app:102]: ESPHome version 2023.10.6 compiled on Dec  4 2023, 13:14:50
[09:49:17][C][logger:416]: Logger:
[09:49:17][C][logger:417]:   Level: DEBUG
[09:49:17][C][logger:418]:   Log Baud Rate: 0
[09:49:17][C][logger:420]:   Hardware UART: UART0
[09:49:17][C][uart.arduino_esp8266:102]: UART Bus:
[09:49:17][C][uart.arduino_esp8266:103]:   TX Pin: GPIO15
[09:49:17][C][uart.arduino_esp8266:104]:   RX Pin: GPIO13
[09:49:17][C][uart.arduino_esp8266:106]:   RX Buffer Size: 256
[09:49:17][C][uart.arduino_esp8266:108]:   Baud Rate: 9600 baud
[09:49:17][C][uart.arduino_esp8266:109]:   Data Bits: 8
[09:49:17][C][uart.arduino_esp8266:110]:   Parity: NONE
[09:49:17][C][uart.arduino_esp8266:111]:   Stop bits: 1
[09:49:17][C][uart.arduino_esp8266:113]:   Using hardware serial interface.
[09:49:17][C][tuya.climate:151]: Tuya Climate 'Heater automatic control'
[09:49:17][C][tuya.climate:153]:   Switch has datapoint ID 1
[09:49:17][C][tuya.climate:156]:   Active state has datapoint ID 1
[09:49:17][C][tuya.climate:159]:   Target Temperature has datapoint ID 2
[09:49:17][C][tuya.climate:162]:   Current Temperature has datapoint ID 3
[09:49:17][C][tuya.climate:167]:   Eco has datapoint ID 102
[09:49:17][C][tuya.sensor:028]: Tuya Sensor 'Heater temperature'
[09:49:17][C][tuya.sensor:028]:   Device Class: 'temperature'
[09:49:17][C][tuya.sensor:028]:   State Class: 'measurement'
[09:49:17][C][tuya.sensor:028]:   Unit of Measurement: '°C'
[09:49:17][C][tuya.sensor:028]:   Accuracy Decimals: 0
[09:49:17][C][tuya.sensor:028]:   Icon: 'mdi:thermometer'
[09:49:17][C][tuya.sensor:029]:   Sensor has datapoint ID 3
[09:49:17][C][tuya:041]: Tuya:
[09:49:17][C][tuya:058]:   Datapoint 2: int value (value: 12)
[09:49:17][C][tuya:058]:   Datapoint 3: int value (value: 11)
[09:49:17][C][tuya:062]:   Datapoint 4: enum (value: 1)
[09:49:17][C][tuya:062]:   Datapoint 101: enum (value: 3)
[09:49:17][C][tuya:056]:   Datapoint 102: switch (value: OFF)
[09:49:17][C][tuya:064]:   Datapoint 12: bitmask (value: 0)
[09:49:17][C][tuya:056]:   Datapoint 1: switch (value: OFF)

I have an additional sensor for debugging the current temperature, simply exposing datapoint 3.

Let’s enable the heat:

[09:49:32][D][climate:011]: 'Heater automatic control' - Setting
[09:49:32][D][climate:015]:   Mode: HEAT
[09:49:32][D][tuya:597]: Setting datapoint 1 to 1
[09:49:32][D][tuya:308]: Datapoint 1 update to ON
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: IDLE
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][tuya:597]: Setting datapoint 4 to 1
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: HEATING
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][tuya:308]: Datapoint 1 update to ON
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: HEATING
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: HEATING
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][tuya:316]: Datapoint 2 update to 12
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: HEATING
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][tuya:316]: Datapoint 3 update to 11
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: HEATING
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][sensor:093]: 'Heater temperature': Sending state 11.00000 °C with 0 decimals of accuracy
[09:49:32][D][tuya:328]: Datapoint 4 update to 1
[09:49:32][D][tuya:328]: Datapoint 101 update to 2
[09:49:32][D][sensor:093]: 'raw_power': Sending state 2.00000  with 0 decimals of accuracy
[09:49:32][D][sensor:093]: 'Heater power': Sending state 1500.00000 W with 0 decimals of accuracy
[09:49:32][D][sensor:093]: 'Heater total daily energy': Sending state 2.80584 kWh with 2 decimals of accuracy
[09:49:32][D][tuya:308]: Datapoint 102 update to OFF
[09:49:32][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:32][D][climate:383]:   Mode: HEAT
[09:49:32][D][climate:385]:   Action: HEATING
[09:49:32][D][climate:394]:   Preset: NONE
[09:49:32][D][climate:403]:   Current Temperature: 11.00°C
[09:49:32][D][climate:409]:   Target Temperature: 12.00°C
[09:49:32][D][tuya:345]: Datapoint 12 update to 00000000
[09:49:33][D][tuya:308]: Datapoint 1 update to ON
[09:49:33][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:33][D][climate:383]:   Mode: HEAT
[09:49:33][D][climate:385]:   Action: HEATING
[09:49:33][D][climate:394]:   Preset: NONE
[09:49:33][D][climate:403]:   Current Temperature: 11.00°C
[09:49:33][D][climate:409]:   Target Temperature: 12.00°C
[09:49:33][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:33][D][climate:383]:   Mode: HEAT
[09:49:33][D][climate:385]:   Action: HEATING
[09:49:33][D][climate:394]:   Preset: NONE
[09:49:33][D][climate:403]:   Current Temperature: 11.00°C
[09:49:33][D][climate:409]:   Target Temperature: 12.00°C
[09:49:33][D][tuya:316]: Datapoint 2 update to 12
[09:49:33][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:33][D][climate:383]:   Mode: HEAT
[09:49:33][D][climate:385]:   Action: HEATING
[09:49:33][D][climate:394]:   Preset: NONE
[09:49:33][D][climate:403]:   Current Temperature: 11.00°C
[09:49:33][D][climate:409]:   Target Temperature: 12.00°C
[09:49:33][D][tuya:316]: Datapoint 3 update to 11
[09:49:33][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:33][D][climate:383]:   Mode: HEAT
[09:49:33][D][climate:385]:   Action: HEATING
[09:49:33][D][climate:394]:   Preset: NONE
[09:49:33][D][climate:403]:   Current Temperature: 11.00°C
[09:49:33][D][climate:409]:   Target Temperature: 12.00°C
[09:49:33][D][sensor:093]: 'Heater temperature': Sending state 11.00000 °C with 0 decimals of accuracy
[09:49:33][D][tuya:328]: Datapoint 4 update to 1
[09:49:33][D][tuya:328]: Datapoint 101 update to 2
[09:49:33][D][sensor:093]: 'raw_power': Sending state 2.00000  with 0 decimals of accuracy
[09:49:33][D][sensor:093]: 'Heater power': Sending state 1500.00000 W with 0 decimals of accuracy
[09:49:33][D][sensor:093]: 'Heater total daily energy': Sending state 2.80625 kWh with 2 decimals of accuracy
[09:49:33][D][tuya:308]: Datapoint 102 update to OFF
[09:49:33][D][climate:380]: 'Heater automatic control' - Sending state:
[09:49:33][D][climate:383]:   Mode: HEAT
[09:49:33][D][climate:385]:   Action: HEATING
[09:49:33][D][climate:394]:   Preset: NONE
[09:49:33][D][climate:403]:   Current Temperature: 11.00°C
[09:49:33][D][climate:409]:   Target Temperature: 12.00°C
[09:49:33][D][tuya:345]: Datapoint 12 update to 00000000

Not sure why that’s so chatty, but anyway.

At some point, it reaches the target temperature, but heating does not stop…

[10:19:32][D][tuya:316]: Datapoint 3 update to 12
[10:19:32][D][climate:380]: 'Heater automatic control' - Sending state:
[10:19:32][D][climate:383]:   Mode: HEAT
[10:19:32][D][climate:385]:   Action: HEATING
[10:19:32][D][climate:394]:   Preset: NONE
[10:19:32][D][climate:403]:   Current Temperature: 12.00°C
[10:19:32][D][climate:409]:   Target Temperature: 12.00°C
[10:19:32][D][sensor:093]: 'Heater temperature': Sending state 12.00000 °C with 0 decimals of accuracy
[10:19:32][D][tuya:328]: Datapoint 4 update to 1
[10:19:32][D][tuya:328]: Datapoint 101 update to 2
[10:19:32][D][sensor:093]: 'raw_power': Sending state 2.00000  with 0 decimals of accuracy
[10:19:32][D][sensor:093]: 'Heater power': Sending state 1500.00000 W with 0 decimals of accuracy
[10:19:32][D][sensor:093]: 'Heater total daily energy': Sending state 3.55557 kWh with 2 decimals of accuracy
[10:19:32][D][tuya:308]: Datapoint 102 update to OFF
[10:19:32][D][climate:380]: 'Heater automatic control' - Sending state:
[10:19:32][D][climate:383]:   Mode: HEAT
[10:19:32][D][climate:385]:   Action: HEATING
[10:19:32][D][climate:394]:   Preset: NONE
[10:19:32][D][climate:403]:   Current Temperature: 12.00°C
[10:19:32][D][climate:409]:   Target Temperature: 12.00°C
[10:19:32][D][tuya:345]: Datapoint 12 update to 00000000

Maybe that’s OK though, I guess the device could decide to make sure the temperature is stable before stopping the heating.

Anyway, the problem is that if I wait here, the current temperature seems stuck at 12 degrees. For example, if some time later I change the target temperature to 13 degrees, the current temperature immediately updates too!

[10:20:06][D][climate:011]: 'Heater automatic control' - Setting
[10:20:06][D][climate:040]:   Target Temperature: 13.00
[10:20:06][D][tuya:597]: Setting datapoint 2 to 13
[10:20:06][D][tuya:316]: Datapoint 2 update to 13
...
[10:20:07][D][tuya:316]: Datapoint 3 update to 13
[10:20:07][D][climate:380]: 'Heater automatic control' - Sending state:
[10:20:07][D][climate:383]:   Mode: HEAT
[10:20:07][D][climate:385]:   Action: HEATING
[10:20:07][D][climate:394]:   Preset: NONE
[10:20:07][D][climate:403]:   Current Temperature: 13.00°C
[10:20:07][D][climate:409]:   Target Temperature: 13.00°C
[10:20:07][D][sensor:093]: 'Heater temperature': Sending state 13.00000 °C with 0 decimals of accuracy

This is surprising, and happens often, sometimes resulting in the current temperature jumping multiple degrees after a target temperature change. That of course makes the heater unreliable, and I’m considering simply exposing the heating preset and controlling everything from Home Assistant based on a different sensor in the room. That’s not ideal though, and I would prefer to fix the device itself. Any thoughts, or ideas for a workaround?

If it is anything like my Tuya heater the thermostat is actually in the remote control … I hope yours isn’t the same.

Mine definitely is in the heater body; it even has an extension cord to put the temperature sensor further away from the heater.

I tried forcibly updating the target temperature, which seemed to make the temperature sensor actually update:

interval:
 - interval: 5min
   then:
     - lambda: |-
         auto target_temp = id(heater_climate).target_temperature;
         ESP_LOGD("custom", "Reapplying target temperature: %d", target_temp);
         tuya_tuya->set_integer_datapoint_value(2, target_temp);

… However, even with the temperature datapoint significantly below / above the setpoint, the heater didn’t turn on / off. I’m not sure whether the device is to blame here (with the program mode not working as expected), or the Tuya climate platform in ESPHome, but I’ve switched to manual mode + PID climate platform (additionally relying on an external temperature sensor with greater precision):

output:
  - platform: template
    id: heat_output
    type: float
    write_action:
      - lambda: |-
          // put the heater in manual mode
          id(heater_tuya).set_enum_datapoint_value(4, 0);

          if (state > 0.60) { // High power
            id(heater_tuya).set_enum_datapoint_value(101, 2);
          } else if (state > 0.40) { // Medium power
            id(heater_tuya).set_enum_datapoint_value(101, 1);
          } else if (state > 0) { // Low power
            id(heater_tuya).set_enum_datapoint_value(101, 0);
          } else { // Off
            id(heater_tuya).set_enum_datapoint_value(101, 3);
          }

climate:
  - platform: pid
    name: "${upper_devicename} automatic control"
    id: heater_climate
    sensor: external_temperature
    default_target_temperature: 13 °C
    heat_output: heat_output
    control_parameters:
      kp: 0.6

sensor:
  - platform: homeassistant
    name: "${upper_devicename} external temperature"
    id: external_temperature
    entity_id: sensor.whatever
    force_update: true

This isn’t great, but at least my heater works as expected now…