veli
February 6, 2023, 9:29pm
1
I have some Tuya-based thermostats, modded with esp chip and installed ESPHome.
Problem
The Tuya Climate component exposes data points, but in Home Assistant it only provides 2 modes: heat (on) and off . No custom operation modes or presets.
But the thermostat provides them and I have them as switches & selects in ESPHome:
What others do
for example wthermostatbeca software installed onto the ESP can automatically create some modes or presets that are available on the device.
My current solution in ESPHome
I made the climate:tuya as internal (hidden), a template thermostat climate with presets and a lot of automations that would turn on/off the switches, pick correct options from the internal input selects, etc.
Question
The only thing missing is creating custom icons that would be visible in the UI.
This seems impossible currently. But somehow WthermostatBeca software (using MQTT integration) is capable of doing it.
To get the Auto icon (Thermostat Climate Controller — ESPHome ), i the thermostat would also need a cooling action, which creates a cool icon and dual gauges… But this thermostat is only for heating.
i am aware of lovelace simple-thermostat and all plugins, i just would like to provide a native way from esphome to home-assistant to provide a mode like Auto or Eco that would work natively like with other thermostats.
GaaD
(Wojtek)
January 9, 2025, 8:26pm
2
Hi @veli
I have a similar thermostat.
Can you share your esp code?
Wojtek
veli
January 14, 2025, 9:05pm
3
The back-and forth syncing sometimes caused an infinite loop so i reverted to basic sad heat/no_heat options. Basically configure the tuya to work as it should, then make a dummy clone that has all the fun stuff, and sync it with tuya actions, and sync it back so its actions go to tuya, but wont loop back infinitely .
climate:
# the original tuya
- platform: tuya
disabled_by_default: true
id: ${device_name}_tuya
# internal: true
entity_category: diagnostic
name: Tuya
icon: mdi:thermostat-auto
switch_datapoint: 1
target_temperature_datapoint: 2
current_temperature_datapoint: 3
temperature_multiplier: 0.5
active_state_datapoint: 104
active_state_heating_value: ON
eco_datapoint: 5
eco_temperature: 15
visual:
min_temperature: ${default_target_temperature_low}
max_temperature: ${default_target_temperature_high}
temperature_step:
target_temperature: 1
current_temperature: 0.5
# https://esphome.io/components/climate/thermostat
- platform: thermostat
id: ${device_name}
name: None
icon: mdi:thermostat-auto
sensor: ${device_name}_current_temperature
min_heating_off_time: ${min_heating_off_time}
min_heating_run_time: ${min_heating_off_time}
min_idle_time: ${min_idle_time}
heat_action:
- logger.log: id(${device_name}).action
# - switch.turn_on: hvac_action
idle_action:
- logger.log: id(${device_name}).action
# - switch.turn_off: hvac_action
heat_mode:
- logger.log: id(${device_name}).mode
- switch.turn_on: ${device_name}_power
off_mode:
- logger.log: id(${device_name}).mode
- switch.turn_off: ${device_name}_power
# min_cooling_off_time: 300s
# min_cooling_run_time: 300s
# cool_mode:
# - logger.log: id(${device_name}).preset
# auto_mode:
# - switch.turn_on: power
# cool_action:
# - logger.log: id(${device_name}).preset
default_preset: none
preset:
- name: none # auto, no eco
default_target_temperature_low: ${default_target_temperature_none}
# default_target_temperature_high: ${default_target_temperature_high}
- name: eco # auto, eco
default_target_temperature_low: ${default_target_temperature_eco}
# default_target_temperature_high: ${default_target_temperature_high}
mode: HEAT
- name: away # manual, eco
default_target_temperature_low: ${default_target_temperature_away}
# default_target_temperature_high: ${default_target_temperature_high}
mode: HEAT
- name: home # manual, no eco
default_target_temperature_low: ${default_target_temperature_home}
# default_target_temperature_high: ${default_target_temperature_high}
target_temperature_change_action:
- script.execute: tuya_set_target_temperature
preset_change:
- script.stop: hvac_preset_change
- script.execute: hvac_preset_change
visual:
min_temperature: ${default_target_temperature_low}
max_temperature: ${default_target_temperature_high}
temperature_step: ${temperature_step}
script:
- id: hvac_preset_change
mode: restart
then:
- script.stop: tuya_set_target_temperature
- script.stop: hvac_set_target_temperature
- delay: 0.2s
- script.execute: sync
- delay: 0.2s
- script.execute: tuya_set_target_temperature
- id: tuya_set_target_temperature
then:
- script.stop: hvac_set_target_temperature
- lambda: |-
auto set_target_temperature = id(${device_name}).target_temperature;
auto get_target_temperature = id(${device_name}_tuya).target_temperature;
if ( set_target_temperature != get_target_temperature ){
auto climate = id(${device_name}_tuya).make_call();
climate.set_target_temperature(set_target_temperature);
climate.perform();
}
- id: hvac_set_target_temperature
mode: restart
then:
- script.stop: tuya_set_target_temperature
- lambda: |-
auto set_target_temperature = id(${device_name}).target_temperature;
auto get_target_temperature = id(${device_name}_target_temperature).state;
if ( set_target_temperature != get_target_temperature ){
auto climate = id(${device_name}).make_call();
climate.set_target_temperature(get_target_temperature);
climate.perform();
}
- id: sync
mode: restart
then:
- script.stop: boot
- lambda: |-
auto hvac_preset = id(${device_name}).preset;
auto set_preset_mode = id(${device_name}_preset_mode).make_call();
auto climate = id(${device_name}).make_call();
auto preset = id(${device_name}_preset_mode).state;
auto set_power = id(${device_name}_power).state;
auto set_eco = id(${device_name}_eco).state;
auto set_current_temperature = id(${device_name}_current_temperature).state;
auto set_target_temperature = id(${device_name}_target_temperature).state;
auto set_away_temperature = id(${device_name}_away_temperature).state;
auto set_max_temperature = id(${device_name}_max_temperature).state;
auto set_min_temperature = id(${device_name}_min_temperature).state;
// if (eco) {
// climate.set_preset(CLIMATE_PRESET_ECO);
// } else {
// // climate.set_preset(CLIMATE_MODE_OFF);
// }
if (CLIMATE_PRESET_ECO == hvac_preset){
set_preset_mode.set_option("Auto");
id(${device_name}_eco).turn_on();
climate.set_target_temperature(set_min_temperature);
}
else {
id(${device_name}_eco).turn_off();
if (CLIMATE_PRESET_HOME == hvac_preset){
set_preset_mode.set_option("Manual");
climate.set_target_temperature((set_max_temperature+set_min_temperature)/2);
}
if (CLIMATE_PRESET_NONE == hvac_preset){
set_preset_mode.set_option("Auto");
climate.set_target_temperature(set_current_temperature);
}
if (CLIMATE_PRESET_AWAY == hvac_preset){
id(${device_name}_eco).turn_on();
set_preset_mode.set_option("Manual");
climate.set_target_temperature(set_away_temperature);
}
}
climate.perform();
set_preset_mode.perform();
- delay: 3s
- lambda: |-
auto hvac_preset = id(${device_name}).preset;
auto climate = id(${device_name}).make_call();
if (CLIMATE_PRESET_BOOST == hvac_preset){
id(${device_name}_lock).turn_on();
}
else {
id(${device_name}_lock).turn_off();
}
climate.perform();
- id: boot
mode: single
then:
- script.stop: sync
- lambda: |-
auto preset = id(${device_name}_preset_mode).state;
// auto set_action = id(${device_name}_tuya).action;
auto set_power = id(${device_name}_power).state;
auto set_eco = id(${device_name}_eco).state;
auto set_target_temperature = id(${device_name}_target_temperature).state;
auto set_away_temperature = id(${device_name}_away_temperature).state;
auto set_max_temperature = id(${device_name}_max_temperature).state;
auto set_min_temperature = id(${device_name}_min_temperature).state;
auto climate = id(${device_name}).make_call();
if (set_power) {
climate.set_mode(CLIMATE_MODE_HEAT);
if (set_eco) {
climate.set_preset(CLIMATE_PRESET_ECO);
climate.set_target_temperature(set_min_temperature);
} else {
if ("Manual" == preset){
climate.set_preset(CLIMATE_PRESET_NONE);
climate.set_target_temperature(set_target_temperature);
}
if ("Auto" == preset){
climate.set_preset(CLIMATE_PRESET_AWAY);
climate.set_target_temperature(set_away_temperature);
}
}
} else {
climate.set_mode(CLIMATE_MODE_OFF);
}
climate.perform();
Happy to hear back if you make something reliable with my old codebase, could update them over a few years
GaaD
(Wojtek)
January 17, 2025, 6:20pm
4
Hi
I’m not a programmer but I managed to do something like this:
esphome:
name: termostat-3
friendly_name: termostat-3
esp8266:
# board: esp01_1m
board: esp12e
# Enable logging
logger:
level: debug
logs:
component: error
baud_rate: 0
# Enable Home Assistant API
api:
encryption:
key: "**********************"
ota:
- platform: esphome
password: "************************"
wifi:
networks:
# - ssid: **********
# password: !secret wifi_password_1
- ssid: ******
password: !secret wifi_password_2
- ssid: **********
password: !secret wifi_password_3
- ssid: **********
password: !secret wifi_password_4
manual_ip:
static_ip: 192.168.1.82
gateway: 192.168.1.1
subnet: 255.255.255.0
dns1: 192.168.1.1
dns2: 8.8.8.8
# power_save_mode: NONE
# output_power: 17dB
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: !secret wifi_ap_ssid
password: !secret wifi_ap_password
web_server:
port: 80
version: 3
auth:
username: !secret web_username
password: !secret web_password
time:
- id: time_provider
platform: homeassistant
on_time_sync:
- logger.log: "Synchronized system clock"
captive_portal:
uart:
rx_pin: GPIO3
tx_pin: GPIO1
baud_rate: 9600
id: mcu_uart
tuya:
time_id: time_provider
climate:
- platform: tuya
name: "Termostat"
icon: mdi:heating-coil
switch_datapoint: 1
target_temperature_datapoint: 2
current_temperature_datapoint: 3
supports_heat: True
supports_cool: False
temperature_multiplier: 0.1
active_state:
datapoint: 5
heating_value: 1
visual:
min_temperature: 10 °C
max_temperature: 30 °C
temperature_step: 0.1 °C
switch:
- platform: tuya
name: "ON/OFF"
icon: mdi:power
switch_datapoint: 1
restore_mode: ALWAYS_ON
device_class: "switch"
- platform: tuya
name: "Sound"
icon: mdi:volume-off
switch_datapoint: 109
device_class: "switch"
- platform: tuya
name: "Out inverse"
entity_category: config
icon: mdi:select-inverse
switch_datapoint: 108
device_class: "switch"
- platform: tuya
name: "Anti frost"
entity_category: config
icon: mdi:snowflake
switch_datapoint: 103
device_class: "switch"
- platform: tuya
name: "Reset to default"
entity_category: config
icon: mdi:alert
switch_datapoint: 104
device_class: "switch"
sensor:
- platform: tuya
name: "Temperatura"
device_class: "temperature"
sensor_datapoint: 3
unit_of_measurement: "°C"
accuracy_decimals: 1
filters:
- multiply: 0.1
- platform: wifi_signal
name: "WiFi Signal dB"
id: wifi_signal_db
update_interval: 60s
entity_category: "diagnostic"
text_sensor:
- platform: wifi_info
ip_address:
name: ESP IP Address
ssid:
name: ESP Connected SSID
mac_address:
name: ESP Mac Wifi Address
scan_results:
name: ESP Latest Scan Results
select:
- platform: "tuya"
name: "Program Mode"
icon: mdi:view-list
enum_datapoint: 4
optimistic: true
options:
0: Manual
1: Program
2: Temporary program
3: Leave home
- platform: "tuya"
name: "Backlight brightness"
icon: mdi:lightbulb-on-30
enum_datapoint: 106
optimistic: false
options:
0: Dark
1: Low
2: Middle
3: High
- platform: "tuya"
name: "Sensor selection"
icon: mdi:wrench
entity_category: config
optimistic: true
enum_datapoint: 110
options:
0: Internal
1: External
2: Both
- platform: "tuya"
name: "Working day setting"
icon: mdi:briefcase
entity_category: config
optimistic: true
enum_datapoint: 107
options:
0: Not active
1: 5+2 days
2: 6+1 days
3: 7 days
- platform: "tuya"
name: "Error"
icon: mdi:alert-circle
entity_category: diagnostic
enum_datapoint: 11
options:
0: No errors
1: Err 1-Ext. sensor error
2: Err 2
3: Err 3
number:
- platform: "tuya"
name: "Precision"
entity_category: config
mode: box
number_datapoint: 101
min_value: 0.5
max_value: 10.0
step: 0.5
multiply: 10
- platform: "tuya"
name: "Korekta T"
entity_category: config
mode: box
number_datapoint: 19
min_value: -10
max_value: 10
step: 0.1
multiply: 10
- platform: "tuya"
name: "Max set. temperature"
entity_category: config
mode: box
number_datapoint: 15
min_value: 15
max_value: 95
step: 1
multiply: 1
- platform: "tuya"
name: "Max ext. sensor temp."
entity_category: config
mode: box
number_datapoint: 102
min_value: 35
max_value: 60
step: 1
multiply: 1
I don’t know how to make a weekly programmer. Each working day can be divided into 6 periods with a different temperature and days off - two periods. I have no idea how to do this in ESPHome.
To make it similar to WThermostatBeca.