substitutions: weather_entity: "weather.forecast_home" temperature_entity: "sensor.outside_temperature" humidity_entity: "sensor.outside_humidity" co2_entity: "sensor.carbon_dioxide" lock_icon: "\U0000e92e" air_conditioner_icon: "\U0000e93b" heating_icon: "\U0000e936" info_icon: "\U0000e904" ha_icon: "\U0000e935" # home assistant wifi_25_icon: "\U0000e931" # wifi signal from 25% to 1% wifi_50_icon: "\U0000e932" # wifi signal from 50% to 25% wifi_75_icon: "\U0000e933" # wifi signal from 75% to 50% wifi_100_icon: "\U0000e934" # wifi signal from 100% to 75% or disable humidity_icon: "\U0000e938" co2_icon: "\U0000e937" tvoc: "\U0000e93a" # air quality temperature_icon: "\U0000e939" illumination: "\U0000e92f" # lux lightbulb_icon: "\U0000e908" globals: - id: display_lock type: bool restore_value: true initial_value: "false" sensor: # WI-FI Signal - platform: wifi_signal id: wifi_signal_percent update_interval: 30s filters: - lambda: return min(max(2 * (x + 100.0), 0.0), 100.0); unit_of_measurement: "%" on_value: then: - lvgl.label.update: id: wifi_status text_color: !lambda |- if (id(wifi_signal_percent).state > 0) { return lv_color_hex(0x5CA848); } return lv_color_hex(0x5CA848); text: !lambda |- if (id(wifi_signal_percent).state > 0 && id(wifi_signal_percent).state < 26) { return "${wifi_25_icon}"; } else if (id(wifi_signal_percent).state > 25 && id(wifi_signal_percent).state < 51) { return "${wifi_50_icon}"; } else if (id(wifi_signal_percent).state > 50 && id(wifi_signal_percent).state < 76) { return "${wifi_75_icon}"; } else if (id(wifi_signal_percent).state > 75) { return "${wifi_100_icon}"; } return "${wifi_100_icon}"; # Weather temperature sensor - platform: homeassistant id: weather_temp entity_id: "${weather_entity}" attribute: temperature on_value: then: - lvgl.label.update: id: weather_temperature text: format: "%.0f°" args: [id(weather_temp).state] # Temperature Home Sensor - platform: homeassistant id: temperature_sensor entity_id: "${temperature_entity}" on_value: then: - lvgl.label.update: id: temperature_sensor_label text: format: "%.1f °C" args: [id(temperature_sensor).state] # Humidity Home Sensor - platform: homeassistant id: humidity_sensor entity_id: "${humidity_entity}" on_value: then: - lvgl.label.update: id: humidity_sensor_label text: format: "%.1f %%" args: [id(humidity_sensor).state] # CO2 Home Sensor - platform: homeassistant id: co2_sensor entity_id: "${co2_entity}" on_value: then: - lvgl.label.update: id: co2_sensor_label text: format: "%.0f PPM" args: [id(co2_sensor).state] text_sensor: # Sun horizon sensor - platform: homeassistant id: sun_state_sensor entity_id: sun.sun # Weather state sensor - platform: homeassistant id: weather_state_sensor entity_id: "${weather_entity}" on_value: then: - delay: 1s - script.execute: id: weather_get_and_set_translated_state state_str: !lambda 'return id(weather_state_sensor).state;' - script.execute: update_weather_image lvgl: pages: - id: home_page bg_color: color_slate_blue_gray widgets: # indicators - obj: id: home_bg_indicators y: 20 width: 440 height: 40 align: TOP_MID pad_all: 0 bg_color: color_steel_blue bg_opa: 20% border_opa: TRANSP border_width: 0 shadow_opa: TRANSP radius: 10 widgets: # date - label: id: display_date x: 20 align: LEFT_MID text_font: nunito_16 text_color: color_misty_blue text: "31.01" # Wi-Fi status - label: id: wifi_status align: RIGHT_MID x: -20 text: "${wifi_100_icon}" text_color: color_steel_blue text_font: icons_24 # Home Assistant status - label: id: ha_status align: RIGHT_MID x: -60 text: "${ha_icon}" text_color: color_steel_blue text_font: icons_24 # Heating status - label: id: heating_status align: RIGHT_MID x: -100 text: "${heating_icon}" text_color: color_steel_blue text_font: icons_24 # AC status - label: id: ac_status align: RIGHT_MID x: -140 y: 2 text: "${air_conditioner_icon}" text_color: color_steel_blue text_font: icons_24 # Lock status - label: id: lock_status align: RIGHT_MID x: -180 text: "${lock_icon}" text_color: color_steel_blue text_font: icons_24 # Alarm panel status # - label: # id: alarm_panel_status # align: RIGHT_MID # x: -220 # text: "${shield_arming_icon}" # text_color: color_steel_blue # text_font: mdi_icons_24 # time - obj: id: home_bg_weather_time x: 20 y: 80 width: 440 height: 220 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: display_time align: CENTER text_font: nunito_84 #nunito_64 text_color: color_misty_blue text: "23:59" # weather - obj: id: home_bg_weather x: 20 y: 340 width: 440 height: 300 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: - image: id: weather_sunny_image hidden: true y: 10 x: 10 align: TOP_LEFT src: sunny_img - image: id: weather_clear_night_image hidden: true y: 10 x: 10 align: TOP_LEFT src: clear_night_img - image: id: weather_cloudy_image hidden: true y: 20 x: 10 align: TOP_LEFT src: cloudy_img - image: id: weather_partlycloudy_sun_image hidden: true y: 10 x: 10 align: TOP_LEFT src: partlycloudy_sun_img - image: id: weather_partlycloudy_moon_image hidden: true y: 10 x: 10 align: TOP_LEFT src: partlycloudy_moon_img - image: id: weather_rainy_image hidden: true y: 10 x: 10 align: TOP_LEFT src: rainy_img - image: id: weather_pouring_image hidden: true y: 10 x: 10 align: TOP_LEFT src: pouring_img - image: id: weather_snowy_image hidden: true y: 10 x: 10 align: TOP_LEFT src: snowy_img - image: id: weather_snowy_rainy_image hidden: true y: 10 x: 10 align: TOP_LEFT src: snowy_rainy_img - image: id: weather_fog_image hidden: true y: 10 x: 10 align: TOP_LEFT src: fog_img - image: id: weather_hail_image hidden: true y: 10 x: 10 align: TOP_LEFT src: hail_img - image: id: weather_lightning_image hidden: true y: 10 x: 10 align: TOP_LEFT src: lightning_img - image: id: weather_lightning_rainy_image hidden: true y: 10 x: 10 align: TOP_LEFT src: lightning_rainy_img - image: id: weather_windy_image hidden: true y: 10 x: 25 align: TOP_LEFT src: windy_img - image: id: weather_windy_variant_image hidden: true y: 10 x: 10 align: TOP_LEFT src: windy_variant_img - label: x: -10 y: 10 align: TOP_RIGHT id: weather_temperature text_font: nunito_48 text_color: color_misty_blue text: "-25°" - label: id: weather_state_label y: -10 width: 180 height: 30 align: BOTTOM_MID text_font: nunito_16 text_color: color_misty_blue long_mode: SCROLL_CIRCULAR text: " " # sensors - obj: id: home_bg_sensors x: -20 y: 80 width: 200 height: 200 align: TOP_RIGHT pad_all: 0 bg_color: color_steel_blue bg_opa: 20% border_opa: TRANSP border_width: 0 shadow_opa: TRANSP radius: 10 widgets: # temperature - obj: y: 20 width: 160 height: 40 align: TOP_MID pad_all: 0 bg_opa: TRANSP border_opa: TRANSP border_width: 0 shadow_opa: TRANSP widgets: - label: id: temperature_sensor_icon align: LEFT_MID x: 20 text: "${temperature_icon}" text_color: color_green text_font: icons_28 - label: id: temperature_sensor_label align: LEFT_MID x: 55 text: " " text_color: color_misty_blue text_font: nunito_16 # humidity - obj: y: 80 width: 160 height: 40 align: TOP_MID pad_all: 0 bg_opa: TRANSP border_opa: TRANSP border_width: 0 shadow_opa: TRANSP widgets: - label: id: humidity_sensor_icon align: LEFT_MID x: 20 text: "${humidity_icon}" text_color: color_blue text_font: icons_28 - label: id: humidity_sensor_label align: LEFT_MID x: 55 text: " " text_color: color_misty_blue text_font: nunito_16 # co2 - obj: y: 140 width: 160 height: 40 align: TOP_MID pad_all: 0 bg_opa: TRANSP border_opa: TRANSP border_width: 0 shadow_opa: TRANSP widgets: - label: id: co2_sensor_icon align: LEFT_MID x: 20 text: "${co2_icon}" text_color: color_yellow text_font: icons_28 - label: id: co2_sensor_label align: LEFT_MID x: 55 text: " " text_color: color_misty_blue text_font: nunito_16 # display lock - obj: id: home_bg_display_lock x: 480 y: 580 width: 60 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: display_lock_btn align: CENTER text_font: icons_36 text_color: color_misty_blue text: "${lock_icon}" on_long_press: then: - if: condition: lambda: 'return id(display_lock) == false;' then: - switch.turn_on: display_lock_switch else: - switch.turn_off: display_lock_switch # display backlight off - obj: id: home_bg_display_backlight_off x: 550 y: 580 width: 60 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: display_backlight_off_btn align: CENTER text_font: icons_36 text_color: color_misty_blue text: "${lightbulb_icon}" on_click: - delay: 1s - light.turn_off: display_backlight - lvgl.pause: # display info - obj: id: home_bg_display_info x: 620 y: 580 width: 60 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: display_info_btn align: CENTER text_font: icons_36 text_color: color_misty_blue text: "${info_icon}" on_press: - lvgl.page.show: id: system_info_page animation: OUT_RIGHT time: 300ms time: - platform: sntp id: sntp_time timezone: Europe/Moscow servers: - ntp0.ntp-servers.net - ntp1.ntp-servers.net - ntp2.ntp-servers.net on_time_sync: - script.execute: time_update on_time: - minutes: '*' seconds: 0 then: - script.execute: time_update script: - id: time_update then: - lvgl.label.update: id: display_time text: !lambda |- static char time_buf[16]; auto now = id(sntp_time).now(); snprintf(time_buf, sizeof(time_buf), "%02d:%02d", now.hour, now.minute); return time_buf; - lvgl.label.update: id: display_date text: !lambda |- static char date_buf[16]; auto now = id(sntp_time).now(); snprintf(date_buf, sizeof(date_buf), "%02d.%02d", now.day_of_month, now.month); return date_buf; - id: weather_get_and_set_translated_state parameters: state_str: string then: - lambda: |- std::string state = state_str; auto it = id(weather_translations).find(state); std::string translated_state = (it != id(weather_translations).end()) ? it->second : state; lv_label_set_text(id(weather_state_label), translated_state.c_str()); - id: update_weather_image then: - lambda: |- std::string weather = id(weather_state_sensor).state; bool is_night = (id(sun_state_sensor).state == "below_horizon"); if (weather == "cloudy") { lv_obj_clear_flag(id(weather_cloudy_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "partlycloudy") { if (is_night) { lv_obj_clear_flag(id(weather_partlycloudy_moon_image), LV_OBJ_FLAG_HIDDEN); } else { lv_obj_clear_flag(id(weather_partlycloudy_sun_image), LV_OBJ_FLAG_HIDDEN); } } else if (weather == "sunny" || weather == "clear-night") { if (is_night) { lv_obj_clear_flag(id(weather_clear_night_image), LV_OBJ_FLAG_HIDDEN); } else { lv_obj_clear_flag(id(weather_sunny_image), LV_OBJ_FLAG_HIDDEN); } } else if (weather == "rainy") { lv_obj_clear_flag(id(weather_rainy_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "lightning-rainy") { lv_obj_clear_flag(id(weather_lightning_rainy_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "lightning") { lv_obj_clear_flag(id(weather_lightning_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "pouring") { lv_obj_clear_flag(id(weather_pouring_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "snowy") { lv_obj_clear_flag(id(weather_snowy_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "snowy-rainy") { lv_obj_clear_flag(id(weather_snowy_rainy_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "fog") { lv_obj_clear_flag(id(weather_fog_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "hail") { lv_obj_clear_flag(id(weather_hail_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "windy") { lv_obj_clear_flag(id(weather_windy_image), LV_OBJ_FLAG_HIDDEN); } else if (weather == "windy-variant") { lv_obj_clear_flag(id(weather_windy_variant_image), LV_OBJ_FLAG_HIDDEN); } select: - platform: lvgl widget: language_dropdown id: weather_select_language on_value: then: - lambda: |- ESP_LOGI("LANGUAGE", "Language changed, updating weather translations"); - delay: 300ms - script.execute: id: weather_get_and_set_translated_state state_str: !lambda 'return id(weather_state_sensor).state;' - lambda: |- ESP_LOGI("LANGUAGE", "Weather translations updated"); esphome: on_boot: priority: -100 then: - if: condition: lambda: 'return id(weather_state_sensor).has_state();' then: - script.execute: id: weather_get_and_set_translated_state state_str: !lambda 'return id(weather_state_sensor).state;' # - if: # condition: # lambda: 'return id(display_lock) == true;' # then: # - switch.turn_on: display_lock_switch # else: # - switch.turn_off: display_lock_switch api: on_client_connected: - if: condition: lambda: 'return (0 == client_info.find("Home Assistant "));' then: - lvgl.label.update: id: ha_status text_color: color_blue - if: condition: lambda: 'return id(weather_state_sensor).has_state();' then: - script.execute: id: weather_get_and_set_translated_state state_str: !lambda 'return id(weather_state_sensor).state;' # - if: # condition: # lambda: 'return id(display_lock) == true;' # then: # - switch.turn_on: display_lock_switch # else: # - switch.turn_off: display_lock_switch on_client_disconnected: - if: condition: lambda: 'return (0 == client_info.find("Home Assistant "));' then: - lvgl.label.update: id: ha_status text_color: color_steel_blue switch: - platform: template name: "Touchscreen block" id: display_lock_switch lambda: |- return id(display_lock); turn_on_action: - lambda: 'id(display_lock) = true;' - lvgl.button.update: id: home_page_btn clickable: false - lvgl.button.update: id: lights_group_page_btn clickable: false - lvgl.button.update: id: devices_page_btn clickable: false - lvgl.button.update: id: settings_page_btn clickable: false - lvgl.label.update: id: lock_status text_color: color_green turn_off_action: - lambda: 'id(display_lock) = false;' - lvgl.button.update: id: home_page_btn clickable: true - lvgl.button.update: id: lights_group_page_btn clickable: true - lvgl.button.update: id: devices_page_btn clickable: true - lvgl.button.update: id: settings_page_btn clickable: true - lvgl.label.update: id: lock_status text_color: color_steel_blue