Hello,
this is my first post here, and I hope it is in the correct place.
I’m creating this post to share the configuration that, after two weeks of tinkering, allowed me to have a functional Tuya Thermostat flashed with ESPHome.
I would also like to get some opinion from you on the validity of this configuration or, if there is something I can do better.
Longs story short:
I bought this unbranded Tuya thermostat from amazon 3 years ago. Lately I started being concerned about the data shared with Tuya cloud, so I set it up using local tuya and blocked internet access to it.
It was working fine until, two weeks ago, I received an email from tuya stating that my APIs were about to expire and suddenly the device stopped connecting to wifi. Being isolated from the web, I assume the device has a timer that disconnects it from the wifi if it has no internet access.
I then discovered tuya cloudcutter and flashed it with ESPHome.
Following the guide, I set it up using “tuya climate” component, which however didn’t work. At the beginning it seems to operate correctly but, after some times it starts slowing down until it doesn’t answer anymore to commands.
Looking at the logs, it seems that it is flooded with messages and it starts getting checksum error. Basically each update of each sensor, trigger a huge amount of configuration writes from the device to tuya MCU
Then I discovered that setting individual commands using standard ESPHome components made them way more reliable then using tuya climate component. So I set them individually, then added a generic climate components connected to them. Here is my configuration:
### DOCS:
# [08:20:26][C][tuya:041]: Tuya:
# [08:20:26][C][tuya:058]: Datapoint 3: int value (value: 45) <---- Current temperature
# [08:20:26][C][tuya:058]: Datapoint 102: int value (value: 0) <---- External sensor
# [08:20:26][C][tuya:056]: Datapoint 1: switch (value: ON) <---- On/Off
# [08:20:26][C][tuya:058]: Datapoint 2: int value (value: 26) <---- Target temperature
# [08:20:26][C][tuya:062]: Datapoint 4: enum (value: 1) <---- Mode {0 -> scheduled, 1-> manual}
# [08:20:26][C][tuya:056]: Datapoint 5: switch (value: OFF) <---- Eco
# [08:20:26][C][tuya:056]: Datapoint 6: switch (value: OFF) <---- Lock
# [08:20:26][C][tuya:056]: Datapoint 104: switch (value: ON) <---- Not documented
# [08:20:26][C][tuya:062]: Datapoint 103: enum (value: 1) <---- Not documented
# [08:20:26][C][tuya:074]: Product: 'IAYz2WK1th0cMLmL1.0.0'
#
esphome:
name: thermostat
friendly_name: Thermostat
bk72xx:
#board: wb3s
board: generic-bk7231t-qfn32-tuya
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: !secret api_encription
ota:
- platform: esphome
password: !secret ota_pwd
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: !secret esp_thermostat_hotspot_ssid
password: !secret esp_thermostat_hotspot_pwd
captive_portal:
text_sensor:
- platform: libretiny
version:
name: LibreTiny Version
uart:
rx_pin: RX1
tx_pin: TX1
baud_rate: 9600
time:
- platform: homeassistant
id: ha_time
tuya:
# The MCU uses this for displaying the time in the display as well as for
# scheduled programmes one may have configured in their thermostat.
time_id: ha_time
ignore_mcu_update_on_datapoints:
- 5
- 101
- 102
- 103
- 104
# this is the tuya climate component that I wasn't able to operate correctly.
# It slightly improved filtering some datapoints with
# ignore_mcu_update_on_datapoints
#climate:
# - platform: tuya
# name: "Thermostat"
# switch_datapoint: 1
# target_temperature_datapoint: 2
# current_temperature_datapoint: 3
# #preset:
# # eco:
# # datapoint: 5
# # temperature: 18
# temperature_multiplier: 0.5
# visual:
# min_temperature: 5 °C
# max_temperature: 35 °C
# temperature_step: 0.5 °C
sensor:
- platform: "tuya"
name: "Temperature"
id: "termostat_temperature"
sensor_datapoint: 3
unit_of_measurement: "°C"
device_class: "temperature"
accuracy_decimals: 1
filters:
- multiply: 0.5
# The external temperature sensor, if wired
#- platform: "tuya"
# name: "Temperature (external)"
# sensor_datapoint: 102
# unit_of_measurement: "°C"
# device_class: "temperature"
# accuracy_decimals: 1
# filters:
# - multiply: 0.5
# disabled_by_default: true
number:
- platform: "tuya"
name: "Target Temperature"
id: "thermostat_target_temperature"
number_datapoint: 2
min_value: 5.00
max_value: 35.00
multiply: 2
step: 1
on_value:
then:
- lambda: |-
// update temperature only if state differs. This should avoid unlimited event call
if (id(thermostat_control).target_temperature != id(thermostat_target_temperature).state)
{
auto call = id(thermostat_control).make_call();
call.set_target_temperature(id(thermostat_target_temperature).state);
call.perform();
}
switch:
- platform: "tuya"
name: "Lock"
icon: "mdi:lock"
switch_datapoint: 6
disabled_by_default: true
- platform: "tuya"
name: "Enabled"
id: "thermostat_enabled"
switch_datapoint: 1
on_turn_off:
if:
condition:
lambda: |-
return id(thermostat_control).mode != CLIMATE_MODE_OFF;
then:
- lambda: |-
auto call = id(thermostat_control).make_call();
call.set_mode("OFF");
call.perform();
on_turn_on:
if:
condition:
lambda: |-
return id(thermostat_control).mode == CLIMATE_MODE_OFF;
then:
- lambda: |-
auto call = id(thermostat_control).make_call();
call.set_mode("HEAT");
call.perform();
select:
- platform: "tuya"
name: "Scheduled programming"
icon: "mdi:calendar"
enum_datapoint: 4
options:
0: Use scheduled programs
1: Manual control
disabled_by_default: true
button:
- platform: restart
name: "Restart"
climate:
- platform: thermostat
name: "Thermostat control"
id: "thermostat_control"
min_heating_off_time: 1s
min_heating_run_time: 1s
min_idle_time: 1s
sensor: termostat_temperature
heat_action:
then:
- logger.log: "heat action"
idle_action:
- logger.log: "idle action"
off_mode:
if:
condition:
- switch.is_on: thermostat_enabled
then:
- lambda: |-
id(thermostat_enabled).turn_off();
id(thermostat_enabled).publish_state(false);
heat_mode:
if:
condition:
- switch.is_off: thermostat_enabled
then:
- lambda: |-
id(thermostat_enabled).turn_on();
id(thermostat_enabled).publish_state(true);
visual:
min_temperature: 5 °C
max_temperature: 35 °C
temperature_step: 0.5 °C
target_temperature_change_action:
then:
- lambda: |-
// update temperature only if state differs. This should avoid unlimited event call
if (id(thermostat_control).target_temperature != id(thermostat_target_temperature).state)
{
auto call = id(thermostat_target_temperature).make_call();
call.set_value(id(thermostat_control).target_temperature);
call.perform();
}
I hope it helps other people trying to use the same device. If you have any suggestion on how to improve it, please, feel free to comment here