# https://esphome.io/components/climate/thermostat.html substitutions: thermostat_name: "thermostat" thermostat_temperature_entity: "sensor.outside_temperature" thermostat_entity: "climate.smart_radiator_thermostat_x_2" heating_icon: "\U0000e936" temperature_icon: "\U0000e939" pm_home_icon: "\U000F0826" pm_away_icon: "\U000F1A46" climate: - platform: thermostat name: "${thermostat_name}" id: climate_thermostat sensor: thermostate_temperature_sensor on_boot_restore_from: memory min_heating_off_time: 5s min_heating_run_time: 5s min_idle_time: 5s startup_delay: true heat_deadband: 0.5 heat_overrun: 0.5 visual: min_temperature: 10 max_temperature: 30 temperature_step: 0.5 heat_action: - delay: 500ms idle_action: - delay: 500ms default_preset: Home preset: - name: Home default_target_temperature_low: 22 °C - name: Away default_target_temperature_low: 18 °C # switch: # - platform: gpio # id: thermostat_relay # name: "Heating Relay" # pin: GPIO40 # on_turn_on: # - lvgl.meter.update: # id: thermostat_meter # text_color: color_deep_orange # - lvgl.label.update: # id: heating_status # text_color: color_deep_orange # - lvgl.label.update: # id: thermostat_state_icon # text_color: color_deep_orange # - lvgl.label.update: # id: thermostat_target_temperature_icon # text_color: color_deep_orange # - lvgl.label.update: # id: thermostat_target_temperature_value # text_color: color_deep_orange # - lvgl.label.update: # id: thermostat_target_temperature_whole # text_color: color_deep_orange # - lvgl.label.update: # id: thermostat_target_temperature_fraction # text_color: color_deep_orange # - lvgl.label.update: # id: thermostat_target_temperature_measurement # text_color: color_deep_orange # on_turn_off: # - lvgl.meter.update: # id: thermostat_meter # text_color: color_steel_blue # - lvgl.label.update: # id: heating_status # text_color: color_steel_blue # - lvgl.label.update: # id: thermostat_state_icon # text_color: color_steel_blue # - lvgl.label.update: # id: thermostat_target_temperature_icon # text_color: color_steel_blue # - lvgl.label.update: # id: thermostat_target_temperature_value # text_color: color_steel_blue # - lvgl.label.update: # id: thermostat_target_temperature_whole # text_color: color_steel_blue # - lvgl.label.update: # id: thermostat_target_temperature_fraction # text_color: color_steel_blue # - lvgl.label.update: # id: thermostat_target_temperature_measurement # text_color: color_steel_blue # restore_mode: RESTORE_DEFAULT_ON sensor: # Thermostat temperature sensor - platform: homeassistant id: thermostate_temperature_sensor entity_id: "${thermostat_temperature_entity}" # Thermostat target temperature - platform: homeassistant id: thermostat_sensor_target_temp entity_id: "${thermostat_entity}" attribute: temperature on_value: then: - lvgl.arc.update: id: thermostat_arc_main value: !lambda return x; - lvgl.label.update: id: thermostat_target_temperature_whole text: !lambda |- static char buf[10]; int whole_part = static_cast(id(thermostat_sensor_target_temp).state); snprintf(buf, 10, "%d", whole_part); return buf; - lvgl.label.update: id: thermostat_target_temperature_fraction text: !lambda |- static char buf[10]; int whole_part = static_cast(id(thermostat_sensor_target_temp).state); int fractional_part = static_cast((id(thermostat_sensor_target_temp).state - whole_part) * 10); snprintf(buf, 10, ".%01d", fractional_part); return buf; # Thermostat current temperature - platform: homeassistant id: thermostat_sensor_current_temp entity_id: "${thermostat_entity}" attribute: current_temperature on_value: - lvgl.label.update: id: thermostat_target_temperature_value text: !lambda |- char buf[8]; sprintf(buf, "%.1f", x); return std::string(buf); - lvgl.arc.update: id: thermostat_arc_current_temp value: !lambda return x; text_sensor: # Thermostat name - platform: homeassistant id: thermostat_sensor_name entity_id: "${thermostat_entity}" attribute: friendly_name on_value: - lvgl.label.update: id: thermostat_name_label text: !lambda return x; # Thermostat preset mode - platform: homeassistant id: thermostat_sensor_preset_mode entity_id: "${thermostat_entity}" attribute: preset_mode on_value: - if: condition: lambda: 'return id(thermostat_sensor_preset_mode).state == "home";' then: - lvgl.obj.update: id: thermostat_preset_mode_bg bg_color: color_green - lvgl.label.update: id: thermostat_preset_mode_icon text: "${pm_home_icon}" text_color: color_green else: - lvgl.obj.update: id: thermostat_preset_mode_bg bg_color: color_red - lvgl.label.update: id: thermostat_preset_mode_icon text: "${pm_away_icon}" text_color: color_red # Thermostat hvac mode - platform: homeassistant id: thermostat_sensor_hvac_mode entity_id: "${thermostat_entity}" attribute: hvac_action on_value: - script.execute: id: thermostat_get_and_set_translated_state state_str: !lambda 'return id(thermostat_sensor_hvac_mode).state;' # Thermostat state - platform: homeassistant id: thermostat_sensor_state entity_id: "${thermostat_entity}" on_value: - if: condition: lambda: 'return id(thermostat_sensor_state).state == "off";' then: - lvgl.obj.update: id: thermostat_power_bg bg_color: color_misty_blue - lvgl.label.update: id: thermostat_power_icon text_color: color_misty_blue else: - lvgl.obj.update: id: thermostat_power_bg bg_color: color_deep_orange - lvgl.label.update: id: thermostat_power_icon text_color: color_deep_orange lvgl: gradients: - id: thermostat_temp_gradient direction: ver dither: none stops: - color: 0xFF0000 position: 0 - color: 0xFF3300 position: 32 - color: 0xFF6600 position: 64 - color: 0xFF8000 position: 96 - color: 0xFF9900 position: 128 - color: 0xFFB300 position: 160 - color: 0xFFCC00 position: 192 - color: 0xFFE600 position: 224 - color: 0xFFFF00 position: 255 pages: - id: thermostat_page bg_color: color_slate_blue_gray widgets: # State - obj: id: thermostat_state x: 20 y: 20 width: 400 height: 60 align: TOP_LEFT pad_all: 0 bg_color: color_steel_blue bg_opa: 20% border_opa: TRANSP border_width: 0 shadow_opa: TRANSP radius: 10 widgets: - label: id: thermostat_state_label x: 20 align: LEFT_MID text_font: nunito_18 text_color: color_misty_blue text: " " # Preset Mode and Power Toggle - obj: id: thermostat_controls x: 535 y: 100 width: 260 height: 280 #align: TOP_LEFT pad_all: 0 bg_color: color_steel_blue bg_opa: 20% border_opa: TRANSP border_width: 0 shadow_opa: TRANSP radius: 10 widgets: - obj: id: thermostat_preset_mode_bg x: 35 y: -60 align: left_mid width: 70 height: 70 radius: 35 pad_all: 0 bg_color: color_green bg_opa: 60% shadow_opa: transp border_opa: transp border_width: 0 widgets: - label: id: thermostat_preset_mode_icon align: center text_font: mdi_icons_52 text_color: color_green text: "${pm_home_icon}" on_click: - if: condition: lambda: 'return id(thermostat_sensor_preset_mode).state == "home";' then: - homeassistant.action: action: climate.set_preset_mode data: entity_id: "${thermostat_entity}" preset_mode: away else: - homeassistant.action: action: climate.set_preset_mode data: entity_id: "${thermostat_entity}" preset_mode: home - obj: id: thermostat_power_bg x: 35 y: 60 align: left_mid width: 70 height: 70 radius: 35 pad_all: 0 bg_color: color_deep_orange bg_opa: 30% shadow_opa: transp border_opa: transp border_width: 0 widgets: - label: id: thermostat_power_icon align: center text_font: icons_38 text_color: color_deep_orange text: "${power_icon}" on_click: - homeassistant.action: action: climate.toggle data: entity_id: "${thermostat_entity}" # Temperature controls - obj: x: 5 y: -60 width: 90 height: 90 align: left_mid pad_all: 0 bg_opa: transp shadow_opa: transp border_opa: transp widgets: - obj: width: 70 height: 70 align: center clickable: true radius: 50 pad_all: 0 bg_opa: transp border_opa: transp border_width: 0 shadow_width: 8 shadow_spread: 2 shadow_color: color_black pressed: bg_color: 0x3A3A4C shadow_width: 5 on_press: then: - lambda: |- auto temp = id(thermostat_sensor_target_temp).state + 0.5; id(thermostat_sensor_target_temp).publish_state(temp); - homeassistant.service: service: climate.set_temperature data: entity_id: "${thermostat_entity}" temperature: !lambda 'return id(thermostat_sensor_target_temp).state;' - obj: width: 60 height: 60 align: center clickable: false radius: 45 pad_all: 0 bg_opa: transp border_opa: transp shadow_width: 4 shadow_color: 0xFFFFFF shadow_ofs_x: -4 shadow_ofs_y: -2 shadow_opa: 30% - obj: width: 65 height: 65 pad_all: 0 align: center clickable: false radius: 50 border_opa: transp bg_color: color_slate_blue_gray widgets: - label: y: -2 align: center text_font: nunito_48 text_color: color_misty_blue text: "+" - obj: x: 5 y: 60 width: 90 height: 90 align: left_mid pad_all: 0 bg_opa: transp shadow_opa: transp border_opa: transp widgets: - obj: width: 70 height: 70 align: center clickable: true radius: 50 pad_all: 0 bg_opa: transp border_opa: transp border_width: 0 shadow_width: 8 shadow_spread: 2 shadow_color: color_black pressed: bg_color: 0x3A3A4C shadow_width: 5 on_press: then: - lambda: |- auto temp = id(thermostat_sensor_target_temp).state - 0.5; id(thermostat_sensor_target_temp).publish_state(temp); - homeassistant.service: service: climate.set_temperature data: entity_id: "${thermostat_entity}" temperature: !lambda 'return id(thermostat_sensor_target_temp).state;' - obj: width: 60 height: 60 align: center clickable: false radius: 45 pad_all: 0 bg_opa: transp border_opa: transp shadow_width: 4 shadow_color: 0xFFFFFF shadow_ofs_x: -4 shadow_ofs_y: -2 shadow_opa: 30% - obj: width: 65 height: 65 pad_all: 0 align: center clickable: false radius: 50 border_opa: transp bg_color: color_slate_blue_gray widgets: - label: y: -2 align: CENTER text_font: nunito_48 text_color: color_misty_blue text: "-" # Name - obj: id: thermostat_name x: 100 y: 400 width: 360 height: 60 align: TOP_LEFT pad_all: 0 bg_color: color_steel_blue bg_opa: 20% border_opa: TRANSP border_width: 0 shadow_opa: TRANSP radius: 10 widgets: - label: id: thermostat_name_label x: 20 align: LEFT_MID text_font: nunito_16 text_color: color_misty_blue text: "friendly name" # main - obj: id: thermostat_bg_main width: 250 height: 480 y: 20 align: TOP_RIGHT pad_all: 0 bg_opa: TRANSP scrollable: false border_opa: TRANSP border_width: 0 shadow_opa: TRANSP radius: 10 widgets: - obj: id: thermostat_bg_placeholder radius: 240 x: 235 width: 480 height: 480 align: TOP_RIGHT bg_color: color_slate_blue_gray pad_all: 0 border_opa: TRANSP border_width: 0 shadow_opa: TRANSP - obj: id: thermostat_temp_arc_bg radius: 230 x: 230 y: 250 width: 460 height: 460 align: TOP_RIGHT bg_grad: thermostat_temp_gradient bg_grad_dir: ver pad_all: 0 border_opa: TRANSP border_width: 0 shadow_opa: TRANSP - meter: id: thermostat_meter x: 320 # was 200 y: 400 # was 250 height: 640 # was 400 width: 640 # was 400 border_width: 0 bg_color: color_slate_blue_gray align: TOP_RIGHT text_color: color_steel_blue scales: - range_from: 10 range_to: 30 angle_range: 160 rotation: 100.0 ticks: width: 1 count: 21 length: 10 color: color_white major: stride: 5 width: 5 length: 15 color: color_white label_gap: 15 indicators: - tick_style: start_value: 10 end_value: 30 color_start: color_yellow color_end: color_red width: 1 - arc: id: thermostat_arc_main align: TOP_RIGHT x: 375 # was 235 y: 400 # was 250 width: 750 # was 470 height: 750 # was 470 arc_width: 64 # was 40 min_value: 10 max_value: 30 adjustable: true adv_hittest: true rotation: 90.0 start_angle: 10 end_angle: 170 arc_opa: TRANSP indicator: arc_opa: TRANSP arc_width: 40 knob: border_color: color_misty_blue border_width: 7 bg_color: color_dark_gray bg_opa: 80% on_release: - homeassistant.service: service: climate.turn_on data: entity_id: "${thermostat_entity}" - homeassistant.service: service: climate.set_temperature data: entity_id: "${thermostat_entity}" temperature: !lambda return x; - arc: id: thermostat_arc_current_temp clickable: false align: TOP_RIGHT arc_width: 10 x: 230 y: 250 width: 450 height: 450 min_value: 10 max_value: 30 adjustable: true adv_hittest: true rotation: 90.0 start_angle: 10 end_angle: 170 arc_opa: TRANSP indicator: arc_opa: TRANSP knob: bg_color: color_dark_gray border_width: 0 bg_opa: 80% - obj: width: 140 height: 120 align: TOP_RIGHT bg_color: color_slate_blue_gray pad_all: 0 border_opa: TRANSP border_width: 0 shadow_opa: TRANSP widgets: - label: id: thermostat_target_temperature_whole x: -40 y: 240 align: TOP_RIGHT text_font: nunito_84 text_color: color_steel_blue text: " " - label: id: thermostat_target_temperature_fraction x: -10 y: 255 align: TOP_RIGHT text_font: nunito_36 text_color: color_steel_blue text: " " - label: id: thermostat_target_temperature_measurement x: -12 y: 220 align: TOP_RIGHT text_font: nunito_30 text_color: color_steel_blue text: "°C" - label: id: thermostat_state_icon x: -10 y: 150 align: TOP_RIGHT text_font: icons_48 text_color: color_steel_blue text: "${heating_icon}" - label: id: thermostat_target_temperature_icon x: -70 y: 300 align: TOP_RIGHT text_font: icons_28 text_color: color_steel_blue text: "${temperature_icon}" - label: id: thermostat_target_temperature_value x: -10 y: 300 align: TOP_RIGHT text_font: nunito_30 text_color: color_steel_blue text: " " # Return - button: id: thermostat_back x: 20 y: 400 width: 60 height: 60 align: TOP_LEFT bg_color: color_steel_blue bg_opa: 20% shadow_opa: TRANSP radius: 10 widgets: - label: id: thermostat_back_label align: CENTER text_font: icons_28 text_color: color_misty_blue text: "${exit_icon}" on_press: - lvgl.page.show: devices_page - lvgl.widget.show: menu_controls_main script: - id: thermostat_get_and_set_translated_state parameters: state_str: string then: - lambda: |- std::string state = state_str; auto it = id(climate_translations).find(state); std::string translated_state = (it != id(climate_translations).end()) ? it->second : state; lv_label_set_text(id(thermostat_state_label), translated_state.c_str()); select: - platform: lvgl widget: language_dropdown id: thermostat_select_language on_value: then: - delay: 300ms - script.execute: id: thermostat_get_and_set_translated_state state_str: !lambda 'return id(thermostat_sensor_hvac_mode).state;' esphome: on_boot: priority: -100 then: - if: condition: lambda: 'return id(thermostat_sensor_hvac_mode).has_state();' then: - script.execute: id: thermostat_get_and_set_translated_state state_str: !lambda 'return id(thermostat_sensor_hvac_mode).state;' api: on_client_connected: - if: condition: lambda: 'return id(thermostat_sensor_hvac_mode).has_state();' then: - script.execute: id: thermostat_get_and_set_translated_state state_str: !lambda 'return id(thermostat_sensor_hvac_mode).state;'