psram: mode: octal speed: 80MHz i2c: id: i2c_main sda: 8 scl: 9 frequency: 100kHz # CH422G I/O expander (Waveshare uses it for LCD reset/backlight/touch reset) ch422g: - id: ch422g_hub # --- Backlight control (CH422G IO2 is common for Waveshare LCD BL) --- switch: - platform: gpio name: "LCD Backlight Raw" id: lcd_backlight_raw restore_mode: ALWAYS_ON pin: ch422g: ch422g_hub number: 2 mode: output: true # A nicer HA-exposed control (so you can also automate it from HA) light: - platform: binary name: "HA Remote Backlight" output: lcd_backlight_out id: ha_remote_backlight output: - platform: template id: lcd_backlight_out type: binary write_action: - if: condition: lambda: return state; then: - switch.turn_on: lcd_backlight_raw else: - switch.turn_off: lcd_backlight_raw # --- Inactivity tracking (dim/off + wake on touch) --- globals: - id: last_activity_ms type: uint32_t restore_value: no initial_value: '0' - id: last_battery_level_pct type: float restore_value: no initial_value: '-1.0' - id: battery_charging_inferred type: bool restore_value: no initial_value: 'false' interval: - interval: 1s then: - lambda: |- if (id(last_activity_ms) == 0) id(last_activity_ms) = millis(); # Turn off backlight ONCE at 2 minutes idle. - if: condition: lambda: |- const uint32_t idle_s = (millis() - id(last_activity_ms)) / 1000; return idle_s == 120; then: - light.turn_off: ha_remote_backlight # --- Display --- display: - platform: mipi_rgb model: ESP32-S3-TOUCH-LCD-7-800X480 id: main_display update_interval: never auto_clear_enabled: false reset_pin: ch422g: ch422g_hub number: 3 mode: output: true # --- Touch --- touchscreen: platform: gt911 id: touch_panel update_interval: 120ms reset_pin: ch422g: ch422g_hub number: 1 mode: output: true on_touch: then: - lambda: |- id(last_activity_ms) = millis(); - light.turn_on: ha_remote_backlight # --- MDI Icon Font --- font: - file: "https://raw.githubusercontent.com/Templarian/MaterialDesign-Webfont/master/fonts/materialdesignicons-webfont.ttf" id: mdi_icons size: 24 bpp: 4 glyphs: # Tile icons - "\U000F0335" # mdi:lightbulb - "\U000F0425" # mdi:power - "\U000F0426" # mdi:power-plug - "\U000F07E9" # mdi:power-socket-us - "\U000F1A26" # mdi:toggle-switch-variant-off # Battery status icons - "\U000F0079" # mdi:battery - "\U000F12A1" # mdi:battery-low - "\U000F12A2" # mdi:battery-medium - "\U000F12A3" # mdi:battery-high - "\U000F12A4" # mdi:battery-charging-low - "\U000F12A5" # mdi:battery-charging-medium - "\U000F12A6" # mdi:battery-charging-high - "\U000F0091" # mdi:battery-unknown - "\U000F10CD" # mdi:battery-alert-variant-outline # Navigation bar icons - "\U000F04B9" # mdi:sofa - "\U000F06B5" # mdi:lamp - "\U000F04DE" # mdi:stove - "\U000F12BD" # mdi:stairs-up - "\U000F1239" # mdi:desk - "\U000F06D9" # mdi:garage - "\U000F0531" # mdi:tree # --- LVGL UI --- # Note: On ESP32-S3-Touch-LCD-7, GPIO14 is used by the RGB display bus, # so it cannot be reused as ADC for battery telemetry in this display mode. # --- Battery fuel gauge (MAX17043) --- sensor: - platform: max17043 id: max17043_battery i2c_id: i2c_main update_interval: 120s battery_voltage: name: "Remote Battery Voltage" id: remote_battery_voltage entity_category: diagnostic device_class: voltage state_class: measurement accuracy_decimals: 3 on_value: then: - lambda: |- ESP_LOGI("battery", "Voltage: %.3f V", x); battery_level: name: "Remote Battery Level" id: remote_battery_level entity_category: diagnostic device_class: battery state_class: measurement accuracy_decimals: 0 on_value: then: - lambda: |- const float pct = x; ESP_LOGI("battery", "Level: %.0f%%", pct); const bool valid = pct >= 0.0f && pct <= 100.0f; // Infer charging from SOC trend when no dedicated charge-detect pin exists. if (valid) { if (id(last_battery_level_pct) >= 0.0f) { if (pct > id(last_battery_level_pct) + 0.2f) { id(battery_charging_inferred) = true; } else if (pct < id(last_battery_level_pct) - 0.2f) { id(battery_charging_inferred) = false; } } id(last_battery_level_pct) = pct; } const char *icon = "\U000F0091"; // mdi:battery-unknown if (!valid) { icon = "\U000F0091"; // mdi:battery-unknown } else if (pct >= 99.0f) { icon = "\U000F0079"; // mdi:battery (full) } else if (id(battery_charging_inferred)) { if (pct < 40.0f) { icon = "\U000F12A4"; // mdi:battery-charging-low } else if (pct < 80.0f) { icon = "\U000F12A5"; // mdi:battery-charging-medium } else { icon = "\U000F12A6"; // mdi:battery-charging-high } } else { if (pct < 15.0f) { icon = "\U000F10CD"; // mdi:battery-alert-variant-outline } else if (pct < 40.0f) { icon = "\U000F12A1"; // mdi:battery-low } else if (pct < 80.0f) { icon = "\U000F12A2"; // mdi:battery-medium } else { icon = "\U000F12A3"; // mdi:battery-high } } lv_label_set_text(id(nav_battery_icon_home), icon); lv_label_set_text(id(nav_battery_icon_living_room), icon); lv_label_set_text(id(nav_battery_icon_kitchen), icon); lv_label_set_text(id(nav_battery_icon_upstairs), icon); lv_label_set_text(id(nav_battery_icon_office), icon); lv_label_set_text(id(nav_battery_icon_garage), icon); lv_label_set_text(id(nav_battery_icon_outside), icon);