Instantly share code, notes, and snippets.
Created
April 11, 2026 15:20
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save andywarburton/42f076288e7828f821cd5985cf06cb2e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| external_components: | |
| - source: github://n-serrette/esphome_sd_card | |
| components: [sd_mmc_card] | |
| substitutions: | |
| name: waveshare-s3-touch | |
| friendly_name: Waveshare S3 4.3 | |
| esphome: | |
| name: "${name}" | |
| includes: | |
| - dirent_wrap.h | |
| platformio_options: | |
| board_build.flash_mode: dio | |
| on_boot: | |
| priority: -100 | |
| then: | |
| - script.execute: load_random_background | |
| - lvgl.page.show: main_page | |
| - delay: 5s | |
| - script.execute: sync_light_states | |
| logger: | |
| level: DEBUG | |
| logs: | |
| mpu6050: WARN | |
| api: | |
| encryption: | |
| key: "XXX" | |
| ota: | |
| - platform: esphome | |
| password: "XXX" | |
| wifi: | |
| ssid: !secret wifi_ssid | |
| password: !secret wifi_password | |
| manual_ip: | |
| static_ip: 192.168.86.92 | |
| gateway: 192.168.86.1 | |
| subnet: 255.255.255.0 | |
| dns1: 192.168.86.1 | |
| web_server: | |
| esp32: | |
| board: esp32-s3-devkitc-1 | |
| variant: esp32s3 | |
| flash_size: 16MB | |
| framework: | |
| type: esp-idf | |
| advanced: | |
| include_builtin_idf_components: ["fatfs", "spiffs"] | |
| disable_vfs_support_termios: false | |
| disable_vfs_support_select: false | |
| disable_vfs_support_dir: false | |
| sdkconfig_options: | |
| CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: "y" | |
| CONFIG_ESP32S3_DATA_CACHE_64KB: "y" | |
| CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y | |
| CONFIG_SPIRAM_RODATA: y | |
| CONFIG_ESPTOOLPY_FLASHSIZE_16MB: "y" | |
| CONFIG_FATFS_LFN_STACK: "y" | |
| partitions: partitions_16mb_large.csv | |
| ch422g: | |
| - id: ch422g_hub | |
| switch: | |
| - platform: gpio | |
| id: sd_cs | |
| internal: true | |
| pin: | |
| ch422g: ch422g_hub | |
| number: 4 | |
| mode: | |
| output: true | |
| inverted: false | |
| restore_mode: ALWAYS_ON # Hold CS HIGH (deasserted) so SD card stays in SD/MMC mode | |
| - platform: gpio | |
| name: backlight | |
| id: display_backlight | |
| pin: | |
| ch422g: ch422g_hub | |
| number: 2 | |
| mode: | |
| output: true | |
| inverted: False | |
| restore_mode: ALWAYS_ON | |
| psram: | |
| mode: octal | |
| speed: 80MHz | |
| i2c: | |
| sda: 8 | |
| scl: 9 | |
| # SD card | |
| sd_mmc_card: | |
| id: sd_card | |
| clk_pin: GPIO12 | |
| cmd_pin: GPIO11 | |
| data0_pin: GPIO13 | |
| mode_1bit: true | |
| touchscreen: | |
| - platform: gt911 | |
| id: my_touchscreen | |
| interrupt_pin: 4 | |
| reset_pin: | |
| ch422g: | |
| number: 1 | |
| on_release: | |
| - script.execute: wake_screen | |
| display: | |
| - platform: rpi_dpi_rgb | |
| id: rpi_disp | |
| update_interval: never | |
| auto_clear_enabled: false | |
| color_order: RGB | |
| pclk_frequency: 16MHz | |
| dimensions: | |
| width: 800 | |
| height: 480 | |
| reset_pin: | |
| ch422g: | |
| number: 3 | |
| enable_pin: | |
| ch422g: | |
| number: 2 | |
| de_pin: | |
| number: 5 | |
| hsync_pin: | |
| number: 46 | |
| ignore_strapping_warning: true | |
| vsync_pin: | |
| number: 3 | |
| ignore_strapping_warning: true | |
| pclk_pin: 7 | |
| 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] | |
| number: | |
| - platform: template | |
| name: "Temperature Offset" | |
| id: temperature_offset_ui | |
| unit_of_measurement: "°C" | |
| min_value: -20 | |
| max_value: 20 | |
| step: 0.1 | |
| mode: box | |
| update_interval: never | |
| optimistic: true | |
| restore_value: true | |
| initial_value: 0 | |
| icon: "mdi:thermometer" | |
| entity_category: config | |
| on_value: | |
| - lambda: 'id(mpu6050_sensor).update();' | |
| - platform: template | |
| name: LVGL Screen timeout | |
| optimistic: true | |
| id: display_timeout | |
| unit_of_measurement: "s" | |
| initial_value: 60 | |
| restore_value: true | |
| min_value: 60 | |
| max_value: 600 | |
| step: 5 | |
| mode: box | |
| globals: | |
| - id: bg_buffer | |
| type: uint8_t* | |
| restore_value: no | |
| initial_value: 'nullptr' | |
| - id: clock_manually_shown | |
| type: bool | |
| restore_value: no | |
| initial_value: 'false' | |
| script: | |
| # Loads a random raw RGB565 binary (800×480×2 bytes) from /images on the SD card. | |
| # Create images with: ffmpeg -i input.jpg -vf scale=800:480 -f rawvideo -pix_fmt rgb565le output.bin | |
| - id: load_random_background | |
| then: | |
| - lambda: |- | |
| static lv_img_dsc_t img_dsc = {}; | |
| // Allocate permanent PSRAM buffer once (800×480 RGB565 = 768 KB) | |
| if (id(bg_buffer) == nullptr) { | |
| id(bg_buffer) = (uint8_t*) heap_caps_malloc(800 * 480 * 2, MALLOC_CAP_SPIRAM); | |
| if (id(bg_buffer) == nullptr) { | |
| ESP_LOGE("bg", "PSRAM allocation failed"); | |
| return; | |
| } | |
| } | |
| // List files in /sdcard/images | |
| DIR* dir = opendir("/sdcard/images"); | |
| if (!dir) { | |
| ESP_LOGW("bg", "Cannot open /sdcard/images — create this folder on the SD card"); | |
| return; | |
| } | |
| std::vector<std::string> files; | |
| struct dirent* ent; | |
| while ((ent = readdir(dir)) != nullptr) { | |
| if (ent->d_type == DT_REG) { | |
| files.push_back(std::string("/sdcard/images/") + ent->d_name); | |
| } | |
| } | |
| closedir(dir); | |
| if (files.empty()) { | |
| ESP_LOGW("bg", "No files found in /sdcard/images"); | |
| return; | |
| } | |
| // Pick a random file | |
| std::string path = files[esp_random() % files.size()]; | |
| ESP_LOGI("bg", "Loading background: %s", path.c_str()); | |
| // Read directly into PSRAM buffer — avoids large heap allocation | |
| FILE* f = fopen(path.c_str(), "rb"); | |
| if (!f) { | |
| ESP_LOGE("bg", "Cannot open %s", path.c_str()); | |
| return; | |
| } | |
| size_t n = fread(id(bg_buffer), 1, 800 * 480 * 2, f); | |
| fclose(f); | |
| if (n != 800 * 480 * 2) { | |
| ESP_LOGE("bg", "Read %d bytes, expected %d — wrong image size?", (int)n, 800*480*2); | |
| return; | |
| } | |
| // Swap bytes to match display colour order (RGB565 big-endian) | |
| uint16_t* pixels = (uint16_t*) id(bg_buffer); | |
| for (int i = 0; i < 800 * 480; i++) { | |
| pixels[i] = (pixels[i] >> 8) | (pixels[i] << 8); | |
| } | |
| // Configure LVGL image descriptor pointing at our PSRAM buffer | |
| img_dsc.header.cf = LV_IMG_CF_TRUE_COLOR; | |
| img_dsc.header.always_zero = 0; | |
| img_dsc.header.w = 800; | |
| img_dsc.header.h = 480; | |
| img_dsc.data_size = 800 * 480 * 2; | |
| img_dsc.data = id(bg_buffer); | |
| // Apply as background of the LVGL page | |
| lv_obj_set_style_bg_img_src(id(main_page)->obj, &img_dsc, LV_PART_MAIN); | |
| lv_obj_set_style_bg_img_opa(id(main_page)->obj, LV_OPA_COVER, LV_PART_MAIN); | |
| lv_obj_invalidate(id(main_page)->obj); | |
| # Syncs all light button colours to current HA state. | |
| - id: sync_light_states | |
| then: | |
| - lambda: |- | |
| auto update = [](lv_obj_t* btn, lv_obj_t* lbl, bool on) { | |
| lv_obj_set_style_bg_color(btn, on ? lv_color_hex(0xFFAA00) : lv_color_hex(0xFFFFFF), LV_PART_MAIN); | |
| lv_label_set_text(lbl, on ? "ON" : "OFF"); | |
| }; | |
| update(id(hall_btn), id(hall_state_lbl), id(light_hall_state).state); | |
| update(id(kitchen_btn), id(kitchen_state_lbl), id(light_kitchen_state).state); | |
| update(id(living_room_btn), id(living_room_state_lbl), id(light_living_room_state).state); | |
| update(id(extension_btn), id(extension_state_lbl), id(light_extension_state).state); | |
| update(id(toilet_btn), id(toilet_state_lbl), id(light_toilet_state).state); | |
| update(id(dining_room_btn), id(dining_room_state_lbl), id(light_dining_room_state).state); | |
| # Turns off the screen after 10 min of screensaver. | |
| - id: screen_off_timer | |
| mode: restart | |
| then: | |
| - delay: 10min | |
| - logger.log: "Screen off after screensaver timeout" | |
| - switch.turn_off: display_backlight | |
| - lvgl.pause: | |
| # Wakes the screen: loads a fresh background then resumes LVGL. | |
| # mode: single prevents simultaneous triggers from multiple accel axes. | |
| - id: wake_screen | |
| mode: single | |
| then: | |
| - if: | |
| condition: lvgl.is_paused | |
| then: | |
| - script.stop: screen_off_timer | |
| - script.execute: load_random_background | |
| - lvgl.resume: | |
| - lvgl.widget.redraw: | |
| - switch.turn_on: display_backlight | |
| - globals.set: | |
| id: clock_manually_shown | |
| value: 'false' | |
| - lvgl.widget.show: main_content | |
| - lvgl.widget.hide: big_time_label | |
| - script.execute: sync_light_states | |
| else: | |
| - if: | |
| condition: | |
| lambda: "return id(clock_manually_shown);" | |
| then: | |
| - script.stop: screen_off_timer | |
| - script.execute: load_random_background | |
| - globals.set: | |
| id: clock_manually_shown | |
| value: 'false' | |
| - lvgl.widget.show: main_content | |
| - lvgl.widget.hide: big_time_label | |
| - script.execute: sync_light_states | |
| time: | |
| - platform: homeassistant | |
| id: ha_time | |
| on_time_sync: | |
| then: | |
| - lvgl.label.update: | |
| id: time_label | |
| text: !lambda |- | |
| char buf[8]; | |
| auto t = id(ha_time).now(); | |
| snprintf(buf, sizeof(buf), "%02d:%02d", t.hour, t.minute); | |
| return std::string(buf); | |
| - lvgl.label.update: | |
| id: big_time_label | |
| text: !lambda |- | |
| char buf[8]; | |
| auto t = id(ha_time).now(); | |
| snprintf(buf, sizeof(buf), "%02d:%02d", t.hour, t.minute); | |
| return std::string(buf); | |
| on_time: | |
| - seconds: 0 | |
| then: | |
| - lvgl.label.update: | |
| id: time_label | |
| text: !lambda |- | |
| char buf[8]; | |
| auto t = id(ha_time).now(); | |
| snprintf(buf, sizeof(buf), "%02d:%02d", t.hour, t.minute); | |
| return std::string(buf); | |
| - lvgl.label.update: | |
| id: big_time_label | |
| text: !lambda |- | |
| char buf[8]; | |
| auto t = id(ha_time).now(); | |
| snprintf(buf, sizeof(buf), "%02d:%02d", t.hour, t.minute); | |
| return std::string(buf); | |
| interval: | |
| - interval: 1min | |
| then: | |
| - if: | |
| condition: | |
| lambda: "return id(clock_manually_shown);" | |
| then: | |
| - script.execute: load_random_background | |
| font: | |
| - file: "gfonts://Noto+Sans" | |
| id: roboto_24 | |
| size: 24 | |
| glyphs: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.!: °%" | |
| - file: "gfonts://Noto+Sans@700" | |
| id: roboto_bold_24 | |
| size: 24 | |
| glyphs: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.!: °%" | |
| - file: "gfonts://Noto+Sans" | |
| id: roboto_18 | |
| size: 18 | |
| glyphs: "OFN" | |
| - file: "gfonts://Noto+Sans" | |
| id: noto_96 | |
| size: 96 | |
| glyphs: "0123456789:" | |
| - file: "gfonts://Noto+Sans@700" | |
| id: noto_240 | |
| size: 240 | |
| glyphs: "0123456789:-" | |
| - file: "gfonts://Noto+Sans@700" | |
| id: noto_300 | |
| size: 300 | |
| glyphs: "0123456789:" | |
| sensor: | |
| - platform: mpu6050 | |
| id: mpu6050_sensor | |
| address: 0x68 | |
| update_interval: 200ms | |
| temperature: | |
| name: "Temperature" | |
| id: mpu_temperature | |
| unit_of_measurement: "°C" | |
| accuracy_decimals: 1 | |
| filters: | |
| - throttle_average: 60s | |
| - lambda: "return x + id(temperature_offset_ui).state;" | |
| accel_x: | |
| name: "Accel X" | |
| internal: true | |
| filters: | |
| - delta: 0.5 | |
| on_value: | |
| then: | |
| - script.execute: wake_screen | |
| accel_y: | |
| name: "Accel Y" | |
| internal: true | |
| filters: | |
| - delta: 0.5 | |
| on_value: | |
| then: | |
| - script.execute: wake_screen | |
| accel_z: | |
| name: "Accel Z" | |
| internal: true | |
| filters: | |
| - delta: 0.5 | |
| on_value: | |
| then: | |
| - script.execute: wake_screen | |
| - platform: homeassistant | |
| id: ha_temperature | |
| entity_id: sensor.average_home_temperature | |
| on_value: | |
| - lvgl.label.update: | |
| id: temp_label | |
| text: !lambda |- | |
| char buf[20]; | |
| snprintf(buf, sizeof(buf), "%.1f °C", x); | |
| return std::string(buf); | |
| - platform: homeassistant | |
| id: ha_humidity | |
| entity_id: sensor.average_home_humidity | |
| on_value: | |
| - lvgl.label.update: | |
| id: humidity_label | |
| text: !lambda |- | |
| char buf[20]; | |
| snprintf(buf, sizeof(buf), "%.0f%%", x); | |
| return std::string(buf); | |
| binary_sensor: | |
| - platform: homeassistant | |
| id: light_hall_state | |
| entity_id: light.nabu_casa_skyconnect_v1_0_hall | |
| on_state: | |
| - if: | |
| condition: | |
| lambda: "return x;" | |
| then: | |
| - lvgl.widget.update: | |
| id: hall_btn | |
| bg_color: 0xFFAA00 | |
| - lvgl.label.update: | |
| id: hall_state_lbl | |
| text: "ON" | |
| else: | |
| - lvgl.widget.update: | |
| id: hall_btn | |
| bg_color: 0xFFFFFF | |
| - lvgl.label.update: | |
| id: hall_state_lbl | |
| text: "OFF" | |
| - platform: homeassistant | |
| id: light_kitchen_state | |
| entity_id: light.nabu_casa_skyconnect_v1_0_kitchen | |
| on_state: | |
| - if: | |
| condition: | |
| lambda: "return x;" | |
| then: | |
| - lvgl.widget.update: | |
| id: kitchen_btn | |
| bg_color: 0xFFAA00 | |
| - lvgl.label.update: | |
| id: kitchen_state_lbl | |
| text: "ON" | |
| else: | |
| - lvgl.widget.update: | |
| id: kitchen_btn | |
| bg_color: 0xFFFFFF | |
| - lvgl.label.update: | |
| id: kitchen_state_lbl | |
| text: "OFF" | |
| - platform: homeassistant | |
| id: light_living_room_state | |
| entity_id: light.living_room_lights | |
| on_state: | |
| - if: | |
| condition: | |
| lambda: "return x;" | |
| then: | |
| - lvgl.widget.update: | |
| id: living_room_btn | |
| bg_color: 0xFFAA00 | |
| - lvgl.label.update: | |
| id: living_room_state_lbl | |
| text: "ON" | |
| else: | |
| - lvgl.widget.update: | |
| id: living_room_btn | |
| bg_color: 0xFFFFFF | |
| - lvgl.label.update: | |
| id: living_room_state_lbl | |
| text: "OFF" | |
| - platform: homeassistant | |
| id: light_extension_state | |
| entity_id: light.nabu_casa_skyconnect_v1_0_extension | |
| on_state: | |
| - if: | |
| condition: | |
| lambda: "return x;" | |
| then: | |
| - lvgl.widget.update: | |
| id: extension_btn | |
| bg_color: 0xFFAA00 | |
| - lvgl.label.update: | |
| id: extension_state_lbl | |
| text: "ON" | |
| else: | |
| - lvgl.widget.update: | |
| id: extension_btn | |
| bg_color: 0xFFFFFF | |
| - lvgl.label.update: | |
| id: extension_state_lbl | |
| text: "OFF" | |
| - platform: homeassistant | |
| id: light_toilet_state | |
| entity_id: light.toilet_bulb_light | |
| on_state: | |
| - if: | |
| condition: | |
| lambda: "return x;" | |
| then: | |
| - lvgl.widget.update: | |
| id: toilet_btn | |
| bg_color: 0xFFAA00 | |
| - lvgl.label.update: | |
| id: toilet_state_lbl | |
| text: "ON" | |
| else: | |
| - lvgl.widget.update: | |
| id: toilet_btn | |
| bg_color: 0xFFFFFF | |
| - lvgl.label.update: | |
| id: toilet_state_lbl | |
| text: "OFF" | |
| - platform: homeassistant | |
| id: light_dining_room_state | |
| entity_id: light.dining_room_bulb_light | |
| on_state: | |
| - if: | |
| condition: | |
| lambda: "return x;" | |
| then: | |
| - lvgl.widget.update: | |
| id: dining_room_btn | |
| bg_color: 0xFFAA00 | |
| - lvgl.label.update: | |
| id: dining_room_state_lbl | |
| text: "ON" | |
| else: | |
| - lvgl.widget.update: | |
| id: dining_room_btn | |
| bg_color: 0xFFFFFF | |
| - lvgl.label.update: | |
| id: dining_room_state_lbl | |
| text: "OFF" | |
| lvgl: | |
| on_idle: | |
| timeout: 2min | |
| then: | |
| - logger.log: "Screensaver activated" | |
| - globals.set: | |
| id: clock_manually_shown | |
| value: 'true' | |
| - lvgl.widget.hide: main_content | |
| - lvgl.widget.show: big_time_label | |
| - script.execute: screen_off_timer | |
| pages: | |
| - id: main_page | |
| bg_color: 0x000000 | |
| widgets: | |
| # ── Main content (hidden in clock/screensaver mode) ────────────────── | |
| - obj: | |
| id: main_content | |
| x: 0 | |
| y: 0 | |
| width: 800 | |
| height: 480 | |
| bg_opa: 0% | |
| border_width: 0 | |
| pad_all: 0 | |
| scrollable: false | |
| widgets: | |
| # ── Header bar: temp + humidity (left) | time + charge (right) | |
| - obj: | |
| x: 0 | |
| y: 0 | |
| width: 800 | |
| height: 60 | |
| bg_color: 0x111111 | |
| bg_opa: 82% | |
| border_width: 0 | |
| radius: 0 | |
| pad_all: 0 | |
| widgets: | |
| - label: | |
| id: temp_label | |
| x: 20 | |
| y: 14 | |
| text: "-- °C" | |
| text_font: roboto_24 | |
| text_color: 0xFFFFFF | |
| - label: | |
| id: humidity_label | |
| x: 185 | |
| y: 14 | |
| text: "--%" | |
| text_font: roboto_24 | |
| text_color: 0xFFFFFF | |
| - label: | |
| id: time_label | |
| align: RIGHT_MID | |
| x: -25 | |
| y: 0 | |
| text: "--:--" | |
| text_font: roboto_24 | |
| text_color: 0xFFFFFF | |
| # ── Blind column (left) ───────────────────────────────────────── | |
| - button: | |
| id: blind_up_btn | |
| x: 20 | |
| y: 95 | |
| width: 180 | |
| height: 100 | |
| bg_color: 0x000000 | |
| bg_opa: 40% | |
| border_color: 0x000000 | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: cover.open_cover | |
| data: | |
| entity_id: cover.blind_button_basher_living_room_blinds | |
| widgets: | |
| - label: | |
| text: "UP" | |
| align: CENTER | |
| text_font: roboto_bold_24 | |
| text_color: 0xFFFFFF | |
| - button: | |
| id: blind_stop_btn | |
| x: 20 | |
| y: 215 | |
| width: 180 | |
| height: 100 | |
| bg_color: 0x000000 | |
| bg_opa: 40% | |
| border_color: 0x000000 | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: cover.stop_cover | |
| data: | |
| entity_id: cover.blind_button_basher_living_room_blinds | |
| widgets: | |
| - label: | |
| text: "STOP" | |
| align: CENTER | |
| text_font: roboto_bold_24 | |
| text_color: 0xFFFFFF | |
| - button: | |
| id: blind_down_btn | |
| x: 20 | |
| y: 335 | |
| width: 180 | |
| height: 100 | |
| bg_color: 0x000000 | |
| bg_opa: 40% | |
| border_color: 0x000000 | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: cover.close_cover | |
| data: | |
| entity_id: cover.blind_button_basher_living_room_blinds | |
| widgets: | |
| - label: | |
| text: "DOWN" | |
| align: CENTER | |
| text_font: roboto_bold_24 | |
| text_color: 0xFFFFFF | |
| # ── Light buttons: two columns (right) ────────────────────────── | |
| - button: | |
| id: hall_btn | |
| x: 275 | |
| y: 95 | |
| width: 245 | |
| height: 105 | |
| bg_color: 0xFFFFFF | |
| bg_opa: 30% | |
| border_color: 0xFFFFFF | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: light.toggle | |
| data: | |
| entity_id: light.nabu_casa_skyconnect_v1_0_hall | |
| widgets: | |
| - label: | |
| text: "HALL" | |
| x: 12 | |
| y: 12 | |
| text_font: roboto_bold_24 | |
| text_color: 0x000000 | |
| - label: | |
| id: hall_state_lbl | |
| text: "OFF" | |
| x: 12 | |
| y: 40 | |
| text_font: roboto_18 | |
| text_color: 0x000000 | |
| - button: | |
| id: kitchen_btn | |
| x: 535 | |
| y: 95 | |
| width: 245 | |
| height: 105 | |
| bg_color: 0xFFFFFF | |
| bg_opa: 30% | |
| border_color: 0xFFFFFF | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: light.toggle | |
| data: | |
| entity_id: light.nabu_casa_skyconnect_v1_0_kitchen | |
| widgets: | |
| - label: | |
| text: "KITCHEN" | |
| x: 12 | |
| y: 12 | |
| text_font: roboto_bold_24 | |
| text_color: 0x000000 | |
| - label: | |
| id: kitchen_state_lbl | |
| text: "OFF" | |
| x: 12 | |
| y: 40 | |
| text_font: roboto_18 | |
| text_color: 0x000000 | |
| - button: | |
| id: living_room_btn | |
| x: 275 | |
| y: 215 | |
| width: 245 | |
| height: 105 | |
| bg_color: 0xFFFFFF | |
| bg_opa: 30% | |
| border_color: 0xFFFFFF | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: light.toggle | |
| data: | |
| entity_id: light.living_room_lights | |
| widgets: | |
| - label: | |
| text: "LIVING ROOM" | |
| x: 12 | |
| y: 12 | |
| text_font: roboto_bold_24 | |
| text_color: 0x000000 | |
| - label: | |
| id: living_room_state_lbl | |
| text: "OFF" | |
| x: 12 | |
| y: 40 | |
| text_font: roboto_18 | |
| text_color: 0x000000 | |
| - button: | |
| id: extension_btn | |
| x: 535 | |
| y: 215 | |
| width: 245 | |
| height: 105 | |
| bg_color: 0xFFFFFF | |
| bg_opa: 30% | |
| border_color: 0xFFFFFF | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: light.toggle | |
| data: | |
| entity_id: light.nabu_casa_skyconnect_v1_0_extension | |
| widgets: | |
| - label: | |
| text: "EXTENSION" | |
| x: 12 | |
| y: 12 | |
| text_font: roboto_bold_24 | |
| text_color: 0x000000 | |
| - label: | |
| id: extension_state_lbl | |
| text: "OFF" | |
| x: 12 | |
| y: 40 | |
| text_font: roboto_18 | |
| text_color: 0x000000 | |
| - button: | |
| id: toilet_btn | |
| x: 275 | |
| y: 335 | |
| width: 245 | |
| height: 105 | |
| bg_color: 0xFFFFFF | |
| bg_opa: 30% | |
| border_color: 0xFFFFFF | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: light.toggle | |
| data: | |
| entity_id: light.toilet_bulb_light | |
| widgets: | |
| - label: | |
| text: "TOILET" | |
| x: 12 | |
| y: 12 | |
| text_font: roboto_bold_24 | |
| text_color: 0x000000 | |
| - label: | |
| id: toilet_state_lbl | |
| text: "OFF" | |
| x: 12 | |
| y: 40 | |
| text_font: roboto_18 | |
| text_color: 0x000000 | |
| - button: | |
| id: dining_room_btn | |
| x: 535 | |
| y: 335 | |
| width: 245 | |
| height: 105 | |
| bg_color: 0xFFFFFF | |
| bg_opa: 30% | |
| border_color: 0xFFFFFF | |
| border_width: 2 | |
| radius: 15 | |
| shadow_color: 0x000000 | |
| shadow_width: 8 | |
| shadow_ofs_x: 2 | |
| shadow_ofs_y: 4 | |
| shadow_opa: 60% | |
| on_click: | |
| - homeassistant.service: | |
| service: light.toggle | |
| data: | |
| entity_id: light.dining_room_bulb_light | |
| widgets: | |
| - label: | |
| text: "DINING ROOM" | |
| x: 12 | |
| y: 12 | |
| text_font: roboto_bold_24 | |
| text_color: 0x000000 | |
| - label: | |
| id: dining_room_state_lbl | |
| text: "OFF" | |
| x: 12 | |
| y: 40 | |
| text_font: roboto_18 | |
| text_color: 0x000000 | |
| # ── Big clock (screensaver/clock mode) ────────────────────────────── | |
| - label: | |
| id: big_time_label | |
| text: "--:--" | |
| align: BOTTOM_RIGHT | |
| x: 30 | |
| y: 90 | |
| text_font: noto_300 | |
| text_color: 0xFFFFFF | |
| text_opa: 80% | |
| hidden: true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment