esphome/thermostat.yml
2026-05-18 10:16:31 -05:00

1474 lines
49 KiB
YAML

substitutions:
color_bg: '0x282A36'
color_bg_alt: '0x34353e'
color_white: '0xE2E4E5'
color_temp_current: '0x57c7ff'
color_dark_blue: '0x57c7ff'
color_light_blue: '0x9aedfe'
color_green: '0x5AF78E'
color_red: '0xff5c57'
color_darker_white: '0xA5A5A9'
esphome:
name: thermostat
esp32:
board: esp32s3box
variant: esp32s3
flash_size: 8MB
framework:
type: esp-idf
version: recommended
sdkconfig_options:
CONFIG_IDF_TARGET: "esp32s3"
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240: y
CONFIG_ESPTOOLPY_FLASHMODE_QIO: y
CONFIG_ESPTOOLPY_FLASHFREQ_80M: y
CONFIG_ESPTOOLPY_FLASHSIZE_8MB: y
CONFIG_IDF_EXPERIMENTAL_FEATURES: y
CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y
CONFIG_SPIRAM: y
CONFIG_SPIRAM_MODE_OCT: y
CONFIG_SPIRAM_RODATA: y
CONFIG_SPIRAM_SPEED_80M: y
CONFIG_FREERTOS_HZ: "1000"
CONFIG_ESP32S3_DATA_CACHE_LINE_64B: y
COMPILER_OPTIMIZATION_PERF: y
psram:
mode: octal
speed: 80MHz
# Enable logging
logger:
level: DEBUG
# baud_rate: 0
ch422g:
# address: 0x24
# Enable Home Assistant API
api:
reboot_timeout: 30min
batch_delay: 300ms
# password: ""
ota:
- platform: esphome
password: ""
wifi:
ssid: "Cochrun"
password: "GreasyChicken784"
power_save_mode: none
reboot_timeout: 20s
manual_ip:
static_ip: 192.168.1.123
gateway: 192.168.1.1
subnet: 255.255.255.0
# ssid: "TFC"
# password: "Disciple77"
ap:
ssid: "Thermostat Fallback Hotspot"
password: "RUfAhbEfZmOU"
captive_portal:
web_server:
port: 80
version: 3
http_request:
bluetooth_proxy:
i2c:
- id: bus_a
sda: GPIO08
scl: GPIO09
scan: True
frequency: 400kHz
button:
- platform: restart
name: "Restart Device"
- platform: safe_mode
name: "Restart in Safe Mode"
time:
- platform: homeassistant
id: hass_time
- platform: sntp
timezone: America/Chicago
id: sntp_time
on_time:
- seconds: /5 # Update every second
then:
- lvgl.label.update:
id: time_label
text: !lambda |-
auto time = id(sntp_time).now();
return time.strftime("%a %b %e, %I:%M %p");
- minutes: /2 # Update every second
then:
- lambda: |-
auto therm = id(climape);
if (therm->mode == CLIMATE_MODE_HEAT && therm->action != CLIMATE_ACTION_HEATING) {
therm->refresh();
};
if (therm->mode == CLIMATE_MODE_COOL && therm->action != CLIMATE_ACTION_COOLING) {
therm->refresh();
};
- script.execute:
id: refresh_hvac_state
- hours: 1,2,3,4
minutes: 5
seconds: 0
then:
- switch.turn_on: switch_antiburn
- hours: 1,2,3,4
minutes: 35
seconds: 0
then:
- switch.turn_off: switch_antiburn
# - script.execute: update_date_time_label
script:
- id: refresh_hvac_state
then:
# - http_request.get:
# url: http://192.168.1.124/switch/heat
# capture_response: true
# request_headers:
# Content-Type: application/json
# on_response:
# then:
# - lambda: |-
# json::parse_json(body, [](JsonObject root) -> bool {
# if (root["value"]) {
# ESP_LOGD("main", "Heat is: %d", root["value"]);
# return root["value"];
# } else {
# ESP_LOGD("main", "Heat problem");
# return false;
# }
# });
sensor:
- platform: bme280_i2c
update_interval: 20s
# iir_filter: 16x
temperature:
name: "Temperature"
id: temperature
filters:
- offset: !lambda return id(temp_offset).state;
# on_value:
# then:
# - lvgl.label.update:
# id: temp_label
# text: !lambda |-
# auto temp = x * (9.0/5.0) + 32.0;
# return str_truncate(to_string(temp), 4);
# - lvgl.indicator.update:
# id: current_temp_needle
# value: !lambda |-
# return x * 10.0;
humidity:
name: "Humidity"
id: humidity
filters:
- round: 1
on_value:
then:
- lvgl.label.update:
id: humidity_label
text: !lambda |-
auto hum = " " + str_truncate(to_string(x), 4) + "%";
return hum;
pressure:
name: "Pressure"
id: pressure
address: 0x76
- platform: internal_temperature
name: "Chip Temperature"
- platform: template
name: "Altitude"
lambda: |-
const float STANDARD_SEA_LEVEL_PRESSURE = 1013.25; //in hPa, see note
return ((id(temperature).state + 273.15) / 0.0065) *
(powf((STANDARD_SEA_LEVEL_PRESSURE / id(pressure).state), 0.190234) - 1); // in meter
update_interval: 15s
icon: 'mdi:signal'
unit_of_measurement: 'm'
- platform: absolute_humidity
name: "Absolute Humidity"
temperature: temperature
humidity: humidity
- platform: template
name: "Dew Point"
lambda: |-
return (243.5*(log(id(humidity).state/100)+((17.67*id(temperature).state)/
(243.5+id(temperature).state)))/(17.67-log(id(humidity).state/100)-
((17.67*id(temperature).state)/(243.5+id(temperature).state))));
unit_of_measurement: °C
icon: 'mdi:thermometer-alert'
- platform: homeassistant
id: living_room_temp
entity_id: sensor.living_room_sensor_living_room_temperature
# filters:
# - lambda: return (x - 32.0) * (5.0/9.0);
- platform: homeassistant
id: bedroom_temp
entity_id: sensor.bedroom_temperature_offset
# filters:
# - lambda: return (x - 32.0) * (5.0/9.0);
- platform: homeassistant
id: basement_temp
entity_id: sensor.basement_sensor_basement_room_temperature
# filters:
# - lambda: return (x - 32.0) * (5.0/9.0);
- platform: homeassistant
id: house_temp
entity_id: sensor.house_temperature
filters:
- lambda: return (x - 32.0) * (5.0/9.0);
- platform: template
id: used_temp
lambda: |-
if (id(house_temp).has_state()) {
return id(house_temp).state;
} else {
return id(temperature).state;
};
on_value:
then:
- lvgl.label.update:
id: temp_label
text: !lambda |-
auto temp = x * (9.0/5.0) + 32.1;
return str_truncate(to_string(temp), 4);
- lvgl.indicator.update:
id: current_temp_needle
value: !lambda |-
return x * 10.0;
mapping:
- id: weather_mapping
from: string
to: string
entries:
clear-day: "󰖙"
light-rain: "󰖗"
medium-rain: "󰖗"
heavy-rain: "󰖗"
rain: "󰖗"
cloudy: "󰖐"
windy: ""
partly-cloudy-night: "󰼱"
partly-cloudy-day: "󰖕"
clear-night: "󰖔"
text_sensor:
- platform: homeassistant
id: weather
entity_id: sensor.pirateweather_icon
# on_value:
# then:
# - lvgl.label.update:
# id: out_weather_label
# text: !lambda |-
# return id(weather_mapping)[x];
- platform: homeassistant
id: weather_summary
entity_id: sensor.pirateweather_minutely_summary
on_value:
then:
- lvgl.label.update:
id: out_weather_sum_label
text: !lambda |-
auto summary = id(weather).get_state();
auto icon = id(weather_mapping)[summary];
return icon + " " + x;
- platform: homeassistant
id: weather_temp
entity_id: sensor.pirateweather_temperature
on_value:
then:
- lvgl.label.update:
id: out_temp_label
text: !lambda |-
auto temp = " " + x + "°";
return temp;
- platform: homeassistant
id: weather_hum
entity_id: weather.pirateweather
attribute: humidity
# on_value:
# then:
# - lvgl.label.update:
# id: out_hum_label
# text: !lambda |-
# auto hum = " " + x + "%";
# return hum;
- platform: homeassistant
id: weather_dew
entity_id: weather.pirateweather
attribute: dew_point
# on_value:
# then:
# - lvgl.label.update:
# id: out_dew_label
# text: !lambda |-
# auto temp = "󰏈 " + x + "°";
# return temp;
- platform: homeassistant
id: high_temp
entity_id: number.today_s_high
on_value:
then:
- lvgl.label.update:
id: high_out_label
text: !lambda |-
auto temp = "󰸃 " + x + "°";
return temp;
- platform: homeassistant
id: low_temp
entity_id: number.tonight_s_low
on_value:
then:
- lvgl.label.update:
id: low_out_label
text: !lambda |-
auto temp = "󰸂 " + x + "°";
return temp;
# - platform: template
# id: weather_temp
# entity_id: weather.pirateweather
# attribute: temperature
switch:
- platform: homeassistant
id: heater
entity_id: switch.hvac_heat
- platform: homeassistant
id: ac
entity_id: switch.hvac_ac
- platform: homeassistant
id: fan
entity_id: switch.hvac_fan
# These are for controling the hvac from the thermostat directly w/o
# needing to have home assistant running or the server running
- platform: template
name: hvac_heat_state
id: switch_heat_state
internal: true
optimistic: true
on_turn_on:
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_on
on_turn_off:
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_off
- platform: template
name: hvac_ac_state
id: switch_ac_state
internal: true
optimistic: true
on_turn_on:
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_on
on_turn_off:
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_off
- platform: template
name: hvac_fan_state
id: switch_fan_state
internal: true
optimistic: true
on_turn_on:
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_on
on_turn_off:
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_off
## This will turn all of the hvac off with one switch
- platform: template
name: HVAC Master Control
id: switch_master
optimistic: true
on_turn_on:
- climate.control:
id: climape
preset: Home
mode: HEAT_COOL
on_turn_off:
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_off
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_off
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_off
- climate.control:
id: climape
preset: Away
mode: "OFF"
- platform: template
name: Antiburn
id: switch_antiburn
icon: mdi:television-shimmer
optimistic: true
entity_category: "config"
turn_on_action:
- logger.log: "Starting Antiburn"
- if:
condition: lvgl.is_paused
then:
- lvgl.resume:
- lvgl.widget.redraw:
- lvgl.pause:
show_snow: true
turn_off_action:
- logger.log: "Stopping Antiburn"
- if:
condition: lvgl.is_paused
then:
- lvgl.resume:
- lvgl.widget.redraw:
# - platform: gpio
# name: backlight
# id: display_backlight
# pin:
# ch422g:
# number: 2
# allow_other_uses: true
# mode:
# output: true
# inverted: False
# restore_mode: ALWAYS_ON
climate:
- platform: thermostat
id: climape
name: "Thermostat"
sensor: used_temp
humidity_sensor: humidity
startup_delay: true
min_cooling_off_time: 60s
min_cooling_run_time: 60s
min_heating_off_time: 60s
min_heating_run_time: 60s
min_fanning_off_time: 60s
min_fanning_run_time: 60s
min_idle_time: 60s
cool_deadband: 0.9 °F
cool_overrun: 0.0 °F
heat_deadband: 0.1 °F
heat_overrun: 0.9 °F
visual:
temperature_step:
target_temperature: 0.5 °F
current_temperature: 0.1 °F
cool_action:
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_off
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_on
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_on
heat_action:
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_off
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_on
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_on
idle_action:
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_off
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_off
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_off
fan_only_action:
- http_request.post:
url: http://192.168.1.124/switch/ac/turn_off
- http_request.post:
url: http://192.168.1.124/switch/heat/turn_off
- http_request.post:
url: http://192.168.1.124/switch/fan/turn_on
- lambda: !lambda ESP_LOGD("fan", "DA FAN");
default_preset: Home
preset:
- name: Home
mode: heat_cool
default_target_temperature_low: 68 °F
default_target_temperature_high: 77 °F
- name: Summer
mode: cool
default_target_temperature_low: 60 °F
default_target_temperature_high: 77 °F
- name: Winter
mode: heat
default_target_temperature_low: 68 °F
default_target_temperature_high: 85 °F
- name: Night Winter
mode: heat
default_target_temperature_low: 64 °F
default_target_temperature_high: 85 °F
- name: Away
mode: heat_cool
default_target_temperature_low: 60 °F
default_target_temperature_high: 85 °F
on_state:
then:
# - lvgl.indicator.update:
# id: hvac_temp_ticks
# start_value: !lambda |-
# return x.target_temperature_high * 10.0;
# end_value: !lambda |-
# return x.target_temperature_low * 10.0;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_HEAT;"
then:
- lvgl.arc.update:
id: hvac_temp_knob
value: !lambda |-
return x.target_temperature_low * 10.0;
else:
- lvgl.arc.update:
id: hvac_temp_knob
value: !lambda |-
return x.target_temperature_high * 10.0;
- if:
condition:
lambda: "return x.mode != CLIMATE_MODE_OFF;"
then:
- lvgl.label.update:
id: action_label
text:
format: "%s"
args: [ 'climate_action_to_string(x.action)' ]
else:
- lvgl.label.update:
id: set_temp_label
text: "CLIMATE_OFF"
- if:
condition:
lambda: "return x.mode == CLIMATE_MODE_COOL;"
then:
- lvgl.widget.update:
id: cool_button
state:
checked: true
- lvgl.widget.update:
id: heat_button
state:
checked: false
- lvgl.widget.update:
id: fan_button
state:
checked: false
- lvgl.widget.update:
id: off_button
state:
checked: false
# - lvgl.arc.update:
# id: current_temp_arc
# value: !lambda |-
# return x.target_temperature_high * (9.0/5.0) + 32.0;
- lvgl.label.update:
id: set_temp_label
text:
format: "%.1f°"
args: [ 'x.target_temperature_high * (9.0/5.0) + 32.0' ]
- if:
condition:
lambda: "return x.mode == CLIMATE_MODE_HEAT;"
then:
- lvgl.widget.update:
id: cool_button
state:
checked: false
- lvgl.widget.update:
id: heat_button
state:
checked: true
- lvgl.widget.update:
id: fan_button
state:
checked: false
- lvgl.widget.update:
id: off_button
state:
checked: false
# - lvgl.arc.update:
# id: current_temp_arc
# value: !lambda |-
# return x.target_temperature_low * (9.0/5.0) + 32.0;
- lvgl.label.update:
id: set_temp_label
text:
format: "%.1f°"
args: [ 'x.target_temperature_low * (9.0/5.0) + 32.0' ]
- if:
condition:
lambda: "return x.mode == CLIMATE_MODE_FAN_ONLY;"
then:
- lvgl.widget.update:
id: cool_button
state:
checked: false
- lvgl.widget.update:
id: heat_button
state:
checked: false
- lvgl.widget.update:
id: fan_button
state:
checked: true
- lvgl.widget.update:
id: off_button
state:
checked: false
# - lvgl.arc.update:
# id: current_temp_arc
# value: !lambda |-
# return x.target_temperature_high * (9.0/5.0) + 32.0;
- lvgl.label.update:
id: set_temp_label
text:
format: "%.1f°"
args: [ 'x.target_temperature_high * (9.0/5.0) + 32.0' ]
- if:
condition:
lambda: "return x.mode == CLIMATE_MODE_OFF;"
then:
- lvgl.widget.update:
id: cool_button
state:
checked: false
- lvgl.widget.update:
id: heat_button
state:
checked: false
- lvgl.widget.update:
id: fan_button
state:
checked: false
- lvgl.widget.update:
id: off_button
state:
checked: true
# output:
# - platform: gpio
# id: backlight
# pin:
# ch422g:
# number: 2
# allow_other_uses: true
# Example minimal configuration entry
display:
- platform: rpi_dpi_rgb
id: my_display
auto_clear_enabled: false
update_interval: never
color_order: RGB
pclk_frequency: 16MHz
dimensions:
width: 800
height: 480
reset_pin:
ch422g:
number: 3
enable_pin:
ch422g:
# allow_other_uses: true
number: 2
de_pin:
number: 5
hsync_pin:
number: 46
ignore_strapping_warning: true
vsync_pin:
number: 3
ignore_strapping_warning: true
pclk_pin:
number: 7
# allow_other_uses: true
hsync_back_porch: 30
hsync_front_porch: 210
hsync_pulse_width: 30
vsync_back_porch: 4
vsync_front_porch: 4
vsync_pulse_width: 4
data_pins:
red: [1, 2, 42, 41, 40]
blue: [14, 38, 18, 17, 10]
green: [39, 0, 45, 48, 47, 21]
font:
- file:
type: gfonts
family: Quicksand
weight: 500
id: h2
size: 40
extras:
- file: "fonts/vm.ttf"
glyphs: [
"󰖩", # nf:wifi
"", # nf:up
"", # nf:down
"󱖞", # nf:off
"", # nf:humidity
"", # nf:temp
"󰏈", # nf:dew
"󰖙", # nf:clear
"󰖗", # nf:rain
"󰖘", # nf:snow
"󰖕", # nf:partlycloudy
"󰖓", # nf:stormy
"󰖐", # nf:cloudy
"󰖔", # nf:night
"", # nf:breezy
"󰼱", # nf:cloudynight
"󰸃", # nf:therm-chev-up
"󰸂", # nf:therm-chev-down
]
- file:
type: gfonts
family: Quicksand
weight: 400
id: h3
size: 30
extras:
- file: "fonts/vm.ttf"
glyphs: [
"󰖩", # nf:wifi
"", # nf:up
"", # nf:down
"󱖞", # nf:off
"", # nf:humidity
"", # nf:temp
"󰏈", # nf:dew
"󰖙", # nf:clear
"󰖗", # nf:rain
"󰖘", # nf:snow
"󰖕", # nf:partlycloudy
"󰖓", # nf:stormy
"󰖐", # nf:cloudy
"󰖔", # nf:night
"", # nf:breezy
"󰼱", # nf:cloudynight
"󰸃", # nf:therm-chev-up
"󰸂", # nf:therm-chev-down
]
- file:
type: gfonts
family: Quicksand
weight: 400
id: h4
size: 20
extras:
- file: "fonts/vm.ttf"
glyphs: [
"󰖩", # nf:wifi
"", # nf:up
"", # nf:down
"󱖞", # nf:off
"", # nf:humidity
"", # nf:temp
"󰏈", # nf:dew
"󰖙", # nf:clear
"󰖗", # nf:rain
"󰖘", # nf:snow
"󰖕", # nf:partlycloudy
"󰖓", # nf:stormy
"󰖐", # nf:cloudy
"󰖔", # nf:night
"", # nf:breezy
"󰼱", # nf:cloudynight
"󰸃", # nf:therm-chev-up
"󰸂", # nf:therm-chev-down
]
- file:
type: gfonts
family: Quicksand
weight: 400
id: body_font
size: 18
- file:
type: gfonts
family: Quicksand
weight: 700
id: h1
size: 120
- file: "fonts/vm.ttf"
id: md_icons
size: 30
glyphs: [
"󰖩", # nf:wifi
"", # nf:up
"", # nf:down
"󱖞", # nf:off
]
- file: "fonts/vm.ttf"
id: sm_icons
size: 20
glyphs: [
"󰖩", # nf:wifi
]
- file: "fonts/materialdesignicons-webfont.ttf"
id: icons
size: 30
glyphs: [
"\U000F050F", # mdi:thermometer
"\U000F058E", # mdi:water-percent
"\U000F0425", # mdi:power
"\U000F0238", # mdi:fire
"\U000F1A45", # mdi:heat-wave
"\U000F0717", # mdi:snowflake
"\U000F1A79", # mdi:sun-snowflake-variant
"\U000F0210", # mdi:fan
"\U000F171D", # mdi:fan-auto
"\U000F1472", # mdi:fan-speed-1
"\U000F1473", # mdi:fan-speed-2
"\U000F1474", # mdi:fan-speed-3
"\U000F032A", # mdi:leaf
"\U000F04B9", # mdi:sofa
"\U000F14DE", # mdi:rocket-launch
"\U000F0D80", # mdi:home-floor-1
"\U000F0D81", # mdi:home-floor-2
"\U000F004D", # mdi:arrow-left
"\U000F09DF", # mdi:circle-small
"\U000F0E03", # mdi:thermometer-chevron-up
"\U000F0E02", # mdi:thermometer-chevron-down
"\U000F0594", # mdi:weather-night (clear)
"\U000F0590", # mdi:weather-cloudy (cloudy)
"\U000F0898", # mdi:weather-hurricane (cyclone, tropical_cyclone)
"\U000F0F30", # mdi:weather-hazy (dust, dusty, haze, hazy)
"\U000F0591", # mdi:weather-fog (fog)
"\U000F12CB", # mdi:snowflake-melt (frost)
"\U000F0596", # mdi:weather-pouring (heavy_shower, heavy_showers, rain)
"\U000F0F33", # mdi:weather-partly-rainy (light_rain)
# mdi:weather-light-showers (light_shower, light_showers)
"\U000F0599", # mdi:weather-sunny (mostly_sunny, sunny)
"\U000F0595", # mdi:weather-partly-cloudy (partly_cloudy)
"\U000F0597", # mdi:weather-rainy (shower, showers)
"\U000F0598", # mdi:weather-snowy (snow)
"\U000F0593", # mdi:weather-lightning
"\U000F067E", # mdi:weather-lightning-rainy (storm, storms)
"\U000F059D", # mdi:weather-windy (wind, windy)
"\U000F0592", # mdi:weather-hail
]
touchscreen:
- platform: gt911
id: my_touchscreen
interrupt_pin: 4
reset_pin:
ch422g:
number: 1
on_touch:
- lambda: |-
ESP_LOGI("cal", "x=%d, y=%d, x_raw=%d, y_raw=%0d",
touch.x,
touch.y,
touch.x_raw,
touch.y_raw
);
# on_release:
# then:
# - if:
# condition: lvgl.is_paused
# then:
# - lvgl.resume:
# - lvgl.widget.redraw:
# - switch.turn_on: display_backlight
color:
- id: bg
hex: 282A36
# output:
# - platform: ledc
# pin:
# number: 7
# allow_other_uses: true
# frequency: 1220
# id: backlight
# light:
# - platform: monochromatic
# output: backlight
# name: Display Backlight
# id: back_light
# restore_mode: ALWAYS_ON
number:
- platform: template
name: LVGL Screen timeout
optimistic: true
id: display_timeout
unit_of_measurement: "s"
initial_value: 45
restore_value: true
min_value: 10
max_value: 180
step: 5
mode: box
- platform: template
name: Temperature Offset
optimistic: true
icon: "mdi:thermometer-chevron-down"
id: temp_offset
unit_of_measurement: "°F"
initial_value: -3.1
restore_value: true
min_value: -7.0
max_value: 7.0
step: 0.1
mode: box
lvgl:
# buffer_size: 25%
default_font: h2
theme:
slider:
bg_color: 0x57c7ff
knob:
bg_color: 0x57c7ff
# on_idle:
# timeout: !lambda "return (id(display_timeout).state * 1000);"
# then:
# - logger.log: "LVGL is idle"
# - switch.turn_off: display_backlight
# - lvgl.pause:
pages:
- id: main_page
bg_color: 0x282A36
widgets:
- obj:
align: center
bg_color: 0x282A36
border_width: 0
height: 480
width: 800
scrollable: false
pad_all: 0
margin_left: 0
margin_top: 0
margin_bottom: 0
margin_right: 0
widgets:
- obj:
bg_color: ${color_bg}
width: 100%
border_width: 0
pad_top: 0
margin_top: 0
widgets:
- label:
id: time_label
text: ""
text_color: ${color_darker_white}
text_font: h3
align: top_left
- label:
id: wifi_label
text: "󰖩"
text_color: ${color_darker_white}
align: top_right
text_font: sm_icons
- obj:
bg_opa: 0
height: 480
width: 100%
border_width: 0
widgets:
# Arc Outer Ring only for visual effect
- arc:
id: arc_ring_outer
height: 370
width: 370
y: -20
align: CENTER
arc_color: ${color_light_blue}
arc_opa: COVER
arc_width: 5
knob:
bg_color: ${color_white}
# Meter ticks and needle
- meter:
id: meter_ticks
hidden: false
height: 360
width: 360
y: -20
align: CENTER
bg_opa: TRANSP
border_width: 0
scales:
range_from: 80 # 16°C
range_to: 340 # 34°C
# Set temperature ticks background
ticks:
count: 60
length: 18
width: 2
color: ${color_white}
indicators:
- line:
id: current_temp_needle
color: ${color_red}
width: 8
length: 35
value: 340 # 34°C Should be the same as the current temperature label
# Set temperature ticks
# - tick_style:
# id: hvac_temp_ticks
# start_value: 80
# end_value: 340 # 24°C Should be the same as the set temperature label
# color_start: ${color_green}
# color_end: ${color_green}
# width: 2
# Curremt temperature needle
# Meter inner Ring only for visual effect
- obj:
id: meter_ring_inner
height: 300
width: 300
y: -20
radius: 150
align: CENTER
border_width: 12
border_color: ${color_bg}
bg_color: ${color_bg_alt}
# Set temperature knob, should change with the set temperature ticks
- arc:
id: hvac_temp_knob
hidden: false
align: CENTER
arc_opa: TRANSP
adjustable: true
value: 240 # 24°C (24x10) Should be the same as the set temperature needle
min_value: 80
max_value: 340
width: 335
height: 335
y: -20
arc_width: 16
change_rate: 550
indicator:
arc_opa: TRANSP
arc_width: 16
knob:
bg_color: ${color_dark_blue}
border_width: 4
border_color: ${color_white}
on_change:
then:
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_COOL;"
then:
- climate.control:
id: climape
target_temperature_high: !lambda |-
return x / 10;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_FAN_ONLY;"
then:
- climate.control:
id: climape
target_temperature_high: !lambda |-
return x / 10;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_HEAT;"
then:
- climate.control:
id: climape
target_temperature_low: !lambda |-
return x / 10;
- obj:
align: BOTTOM_MID
width: 100%
bg_color: 0x282A36
border_width: 0
bg_opa: 0%
pad_top: 20
pad_bottom: 0
layout:
type: flex
pad_row: 4
pad_column: 4
flex_align_main: CENTER
flex_align_cross: END
flex_align_track: END
widgets:
- button:
id: heat_button
height: 80
width: 80
bg_color: 0x34353e
border_width: 2
border_color: 0xE2E4E5
radius: 30
checkable: true
checked:
bg_color: 0xff5c57
on_click:
then:
- lvgl.widget.update:
id: off_button
state:
checked: false
- lvgl.widget.update:
id: fan_button
state:
checked: false
- lvgl.widget.update:
id: cool_button
state:
checked: false
- climate.control:
id: climape
mode: HEAT
widgets:
- label:
text: "\U000F0238"
align: center
text_font: icons
text_color: 0xE2E4E5
- button:
id: cool_button
height: 80
width: 80
bg_color: 0x34353e
border_width: 2
border_color: 0xE2E4E5
radius: 30
checkable: true
checked:
bg_color: ${color_dark_blue}
on_click:
then:
- lvgl.widget.update:
id: off_button
state:
checked: false
- lvgl.widget.update:
id: fan_button
state:
checked: false
- lvgl.widget.update:
id: heat_button
state:
checked: false
- climate.control:
id: climape
mode: COOL
widgets:
- label:
id: cool_butt_text
text: "\U000F0717"
align: center
text_font: icons
text_color: 0xE2E4E5
checked:
text_color: ${color_bg}
- button:
id: fan_button
height: 80
width: 80
bg_color: 0x34353e
border_width: 2
border_color: 0xE2E4E5
radius: 30
checkable: true
checked:
bg_color: 0x5AF78E
on_click:
then:
- lvgl.widget.update:
id: off_button
state:
checked: false
- lvgl.widget.update:
id: cool_button
state:
checked: false
- lvgl.widget.update:
id: heat_button
state:
checked: false
- climate.control:
id: climape
mode: FAN_ONLY
widgets:
- label:
text: "\U000F0210"
align: center
text_font: icons
text_color: 0xE2E4E5
- button:
id: off_button
height: 80
width: 80
bg_color: 0x34353E
border_width: 2
border_color: 0xE2E4E5
radius: 30
checkable: true
checked:
bg_color: 0xFF5C57
on_click:
then:
- lvgl.widget.update:
id: fan_button
state:
checked: false
- lvgl.widget.update:
id: cool_button
state:
checked: false
- lvgl.widget.update:
id: heat_button
state:
checked: false
- climate.control:
id: climape
mode: 'OFF'
widgets:
- label:
text: "󱖞"
text_font: md_icons
align: center
x: -5
text_color: 0xE2E4E5
- obj:
height: 60%
y: 80
width: 150
x: 600
bg_opa: 0
border_width: 0
layout:
type: flex
pad_row: 40
pad_column: 4
flex_flow: COLUMN
flex_align_main: CENTER
flex_align_cross: CENTER
flex_align_track: CENTER
widgets:
- button:
id: temp_up_button
height: 80
width: 80
bg_color: ${color_bg_alt}
border_width: 2
border_color: 0xE2E4E5
radius: 30
pressed:
bg_color: ${color_green}
on_click:
then:
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_COOL;"
then:
- climate.control:
id: climape
target_temperature_high: !lambda |-
return ((id(climape).target_temperature_high * 1.8 + 32.0 + 1.0) - 32.0) / 1.8;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_FAN_ONLY;"
then:
- climate.control:
id: climape
target_temperature_high: !lambda |-
return ((id(climape).target_temperature_high * 1.8 + 32.0 + 1.0) - 32.0) / 1.8;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_HEAT;"
then:
- climate.control:
id: climape
target_temperature_low: !lambda |-
return ((id(climape).target_temperature_low * 1.8 + 32.0 + 1.0) - 32.0) / 1.8;
widgets:
- label:
text: ""
align: center
text_font: md_icons
text_color: 0xE2E4E5
- button:
id: temp_down_button
height: 80
width: 80
bg_color: ${color_bg_alt}
border_width: 2
border_color: 0xE2E4E5
radius: 30
pressed:
bg_color: ${color_green}
on_click:
then:
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_COOL;"
then:
- climate.control:
id: climape
target_temperature_high: !lambda |-
return ((id(climape).target_temperature_high * 1.8 + 32.0 - 1.0) - 32.0) / 1.8;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_FAN_ONLY;"
then:
- climate.control:
id: climape
target_temperature_high: !lambda |-
return ((id(climape).target_temperature_high * 1.8 + 32.0 - 1.0) - 32.0) / 1.8;
- if:
condition:
lambda: "return id(climape).mode == CLIMATE_MODE_HEAT;"
then:
- climate.control:
id: climape
target_temperature_low: !lambda |-
return ((id(climape).target_temperature_low * 1.8 + 32.0 - 1.0) - 32.0) / 1.8;
widgets:
- label:
text: ""
align: center
text_font: md_icons
text_color: 0xE2E4E5
- obj:
height: 100%
y: 0
width: 170
x: 0
bg_opa: 0
border_width: 0
scrollable: false
pad_top: 30
layout:
type: flex
pad_row: 15
pad_column: 2
flex_flow: COLUMN
flex_align_main: START
flex_align_cross: START
flex_align_track: CENTER
widgets:
- label:
id: out_temp_label
text_font: h2
text: ""
text_color: ${color_white}
align: LEFT_MID
- label:
id: high_out_label
text_font: h3
text: "󰸃 : 50°"
text_color: ${color_white}
align: LEFT_MID
- label:
id: low_out_label
text_font: h3
text: "󰸂 : 50°"
text_color: ${color_white}
align: LEFT_MID
- label:
id: humidity_label
text_font: h3
text: " : 50%"
text_color: ${color_white}
align: LEFT_MID
# - label:
# id: out_weather_label
# text_font: h3
# text: ""
# text_color: ${color_white}
# pad_top: 20
# align: LEFT_MID
# long_mode: WRAP
- label:
id: out_weather_sum_label
text_font: h4
text: ""
width: 150
pad_top: 20
text_color: ${color_white}
align: LEFT_MID
long_mode: WRAP
# - label:
# id: out_hum_label
# text_font: h3
# text: " : 50%"
# text_color: ${color_white}
# align: LEFT_MID
# - label:
# id: out_dew_label
# text_font: h3
# text: "."
# text_color: ${color_white}
# align: LEFT_MID
- label:
id: action_label
text_font: h2
text: "Loading..."
align: center
# x: 145
y: -90
text_color: 0xA5A5A9
- label:
id: temp_label
text_font: h1
text: "00"
align: center
y: -10
text_color: 0xE2E4E5
- label:
id: set_temp_label
text_font: h2
text: "00"
align: center
y: 70
text_color: 0xA5A5A9