diff --git a/esphome/esp-bedside-panel.yaml b/esphome/esp-bedside-panel.yaml index 1e3453e..0c77a69 100644 --- a/esphome/esp-bedside-panel.yaml +++ b/esphome/esp-bedside-panel.yaml @@ -6,6 +6,7 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml #:########################################################################################:# # VERSIONS: +# V1.34 2026-07-19 Added three ambient-light ranges and periodic backlight rechecking # V1.33 2026-07-19 Raised the default night lux threshold after TEMT6000 testing # V1.32 2026-07-19 Added persistent ambient-light thresholds, brightness levels and idle delays # V1.31 2026-07-19 Added TEMT6000 ambient-light voltage and illuminance sensors @@ -118,10 +119,12 @@ # - Sleep: 2 hrs starts a local 120-minute countdown and turns the heat pump off. # - Pressing the active Sleep button again cancels the countdown. # - TEMT6000 voltage and approximate lux are exposed for calibration. -# - Touch immediately restores a day/night brightness selected from ambient lux. +# - Ambient light is divided into Dark, Normal and Bright ranges. +# - Touch immediately restores the active brightness for the current range. # - The current page dims after a configurable idle delay. # - A second configurable delay changes to the large Clock page. -# - Calibration, threshold, brightness and delay values persist across reboot. +# - Clock/idle brightness is rechecked periodically as ambient light changes. +# - Calibration, thresholds, brightness and delay values persist across reboot. #:########################################################################################:# # OFFLINE NOTES: # - LVGL, the clock, touch and backlight operate locally. @@ -140,12 +143,12 @@ substitutions: # Device Naming device_name: "esp-bedside-panel" friendly_name: "ESP Bedside Panel" - description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Adaptive touch/idle brightness with a calibrated 20 lx default night threshold. (Layout V1.1)" + description_comment: "Guition JC1060P470C_I_W_Y ESP32-P4 bedside control panel. Three-range adaptive touch and clock brightness with periodic ambient rechecking. (Layout V1.1)" device_area: "Bedroom" # Project Naming project_name: "Guition.JC1060P470C_I_W_Y" - project_version: "v1.33" + project_version: "v1.34" # Passwords & Secrets api_key: !secret esp-api_key @@ -176,15 +179,31 @@ substitutions: # These initialise the persistent ESPHome number entities on first install or # after flash preferences are erased. Once adjusted through Home Assistant, # restored values take precedence after reboot. + # + # Observed TEMT6000 values before installation: + # Very dark: approximately 6-12 lx + # Bright light: approximately 642-643 lx ambient_lux_multiplier_default: "200" - display_night_lux_threshold_default: "20" - display_day_active_brightness_default: "80" - display_night_active_brightness_default: "25" - display_day_dim_brightness_default: "15" - display_night_dim_brightness_default: "4" + + display_dark_lux_threshold_default: "20" + display_bright_lux_threshold_default: "250" + + display_dark_touch_brightness_default: "25" + display_normal_touch_brightness_default: "80" + display_bright_touch_brightness_default: "95" + + display_dark_clock_brightness_default: "4" + display_normal_clock_brightness_default: "15" + display_bright_clock_brightness_default: "60" + display_dim_delay_default: "30" display_clock_delay_default: "60" + # The ADC samples every second and publishes a filtered reading about every + # three seconds. This controls how often the current screen brightness is + # re-evaluated against the latest lux value. + adaptive_backlight_recheck_interval: "10s" + # Security Camera Settings # Change this only if homeassistant.local is not reachable from the panel. home_assistant_internal_url: "http://homeassistant.local:8123" @@ -735,13 +754,13 @@ number: mode: box - platform: template - id: display_night_lux_threshold - name: "${friendly_name} Night Lux Threshold" + id: display_dark_lux_threshold + name: "${friendly_name} Dark Lux Threshold" icon: mdi:weather-night entity_category: config optimistic: true restore_value: true - initial_value: "${display_night_lux_threshold_default}" + initial_value: "${display_dark_lux_threshold_default}" unit_of_measurement: "lx" min_value: 0 max_value: 200 @@ -749,13 +768,41 @@ number: mode: box - platform: template - id: display_day_active_brightness - name: "${friendly_name} Day Touch Brightness" - icon: mdi:brightness-7 + id: display_bright_lux_threshold + name: "${friendly_name} Bright Lux Threshold" + icon: mdi:white-balance-sunny entity_category: config optimistic: true restore_value: true - initial_value: "${display_day_active_brightness_default}" + initial_value: "${display_bright_lux_threshold_default}" + unit_of_measurement: "lx" + min_value: 25 + max_value: 1000 + step: 5 + mode: box + + - platform: template + id: display_dark_touch_brightness + name: "${friendly_name} Dark Touch Brightness" + icon: mdi:brightness-3 + entity_category: config + optimistic: true + restore_value: true + initial_value: "${display_dark_touch_brightness_default}" + unit_of_measurement: "%" + min_value: 2 + max_value: 60 + step: 1 + mode: slider + + - platform: template + id: display_normal_touch_brightness + name: "${friendly_name} Normal Touch Brightness" + icon: mdi:brightness-6 + entity_category: config + optimistic: true + restore_value: true + initial_value: "${display_normal_touch_brightness_default}" unit_of_measurement: "%" min_value: 5 max_value: 100 @@ -763,27 +810,41 @@ number: mode: slider - platform: template - id: display_night_active_brightness - name: "${friendly_name} Night Touch Brightness" - icon: mdi:brightness-4 + id: display_bright_touch_brightness + name: "${friendly_name} Bright Touch Brightness" + icon: mdi:brightness-7 entity_category: config optimistic: true restore_value: true - initial_value: "${display_night_active_brightness_default}" + initial_value: "${display_bright_touch_brightness_default}" unit_of_measurement: "%" - min_value: 2 - max_value: 80 + min_value: 10 + max_value: 100 step: 1 mode: slider - platform: template - id: display_day_dim_brightness - name: "${friendly_name} Day Dim Brightness" - icon: mdi:brightness-5 + id: display_dark_clock_brightness + name: "${friendly_name} Dark Clock Brightness" + icon: mdi:clock-outline entity_category: config optimistic: true restore_value: true - initial_value: "${display_day_dim_brightness_default}" + initial_value: "${display_dark_clock_brightness_default}" + unit_of_measurement: "%" + min_value: 1 + max_value: 30 + step: 1 + mode: slider + + - platform: template + id: display_normal_clock_brightness + name: "${friendly_name} Normal Clock Brightness" + icon: mdi:clock-outline + entity_category: config + optimistic: true + restore_value: true + initial_value: "${display_normal_clock_brightness_default}" unit_of_measurement: "%" min_value: 1 max_value: 60 @@ -791,16 +852,16 @@ number: mode: slider - platform: template - id: display_night_dim_brightness - name: "${friendly_name} Night Dim Brightness" - icon: mdi:brightness-3 + id: display_bright_clock_brightness + name: "${friendly_name} Bright Clock Brightness" + icon: mdi:clock-outline entity_category: config optimistic: true restore_value: true - initial_value: "${display_night_dim_brightness_default}" + initial_value: "${display_bright_clock_brightness_default}" unit_of_measurement: "%" - min_value: 1 - max_value: 40 + min_value: 5 + max_value: 100 step: 1 mode: slider @@ -856,8 +917,11 @@ sensor: # # lux = voltage x configurable multiplier # - # Calibration, threshold, brightness and delay values are persistent ESPHome + # Calibration, thresholds, brightness and delay values are persistent ESPHome # number entities and can be adjusted from Home Assistant. + # + # GPIO20 is sampled every second. The median filter publishes every third + # sample, and the template lux value updates every three seconds. - platform: adc id: ambient_light_voltage @@ -1446,6 +1510,12 @@ globals: restore_value: true initial_value: "0" + # True while the screen is using idle/clock brightness. + - id: display_is_dimmed + type: bool + restore_value: false + initial_value: "false" + # True only while the Security page is visible. - id: security_page_active type: bool @@ -1468,25 +1538,55 @@ script: # ADAPTIVE DISPLAY BACKLIGHT #:######################################################################################:# # - # Brightness is selected only at touch/idle transitions. It is not changed - # continuously as lux moves, preventing feedback from the display backlight. + # Three ambient ranges are used: + # Dark: lux <= Dark Lux Threshold + # Normal: between the two thresholds + # Bright: lux >= Bright Lux Threshold + # + # The active and clock/idle brightness levels are independently adjustable + # and restored after reboot. - id: apply_active_backlight mode: restart then: + - lambda: |- + id(display_is_dimmed) = false; + - light.turn_on: id: display_backlight brightness: !lambda |- const float lux = id(ambient_light_illuminance).state; - const float threshold = id(display_night_lux_threshold).state; + + float dark_threshold = + id(display_dark_lux_threshold).state; + float bright_threshold = + id(display_bright_lux_threshold).state; + + if (isnan(dark_threshold)) { + dark_threshold = 20.0f; + } + + if ( + isnan(bright_threshold) || + bright_threshold <= dark_threshold + ) { + bright_threshold = 250.0f; + } + float brightness_percent; - if (isnan(lux) || lux <= threshold) { + if (isnan(lux) || lux <= dark_threshold) { brightness_percent = - id(display_night_active_brightness).state; + id(display_dark_touch_brightness).state; + id(ambient_light_band_text).publish_state("Dark"); + } else if (lux >= bright_threshold) { + brightness_percent = + id(display_bright_touch_brightness).state; + id(ambient_light_band_text).publish_state("Bright"); } else { brightness_percent = - id(display_day_active_brightness).state; + id(display_normal_touch_brightness).state; + id(ambient_light_band_text).publish_state("Normal"); } if (isnan(brightness_percent)) { @@ -1502,19 +1602,44 @@ script: - id: apply_dim_backlight mode: restart then: + - lambda: |- + id(display_is_dimmed) = true; + - light.turn_on: id: display_backlight brightness: !lambda |- const float lux = id(ambient_light_illuminance).state; - const float threshold = id(display_night_lux_threshold).state; + + float dark_threshold = + id(display_dark_lux_threshold).state; + float bright_threshold = + id(display_bright_lux_threshold).state; + + if (isnan(dark_threshold)) { + dark_threshold = 20.0f; + } + + if ( + isnan(bright_threshold) || + bright_threshold <= dark_threshold + ) { + bright_threshold = 250.0f; + } + float brightness_percent; - if (isnan(lux) || lux <= threshold) { + if (isnan(lux) || lux <= dark_threshold) { brightness_percent = - id(display_night_dim_brightness).state; + id(display_dark_clock_brightness).state; + id(ambient_light_band_text).publish_state("Dark"); + } else if (lux >= bright_threshold) { + brightness_percent = + id(display_bright_clock_brightness).state; + id(ambient_light_band_text).publish_state("Bright"); } else { brightness_percent = - id(display_day_dim_brightness).state; + id(display_normal_clock_brightness).state; + id(ambient_light_band_text).publish_state("Normal"); } if (isnan(brightness_percent)) { @@ -1525,7 +1650,7 @@ script: std::max(1.0f, std::min(100.0f, brightness_percent)); return brightness_percent / 100.0f; - transition_length: 1000ms + transition_length: 700ms #:######################################################################################:# # BOTTOM NAVIGATION LIVE TIME @@ -3761,6 +3886,27 @@ script: # Update minute-resolution clock values locally #:########################################################################################:# interval: + # Re-evaluate the screen against the latest filtered lux reading. This allows + # the clock to brighten during the day and dim again as the room gets darker. + - interval: "${adaptive_backlight_recheck_interval}" + then: + - if: + condition: + lambda: |- + return id(lvgl_ready); + + then: + - if: + condition: + lambda: |- + return id(display_is_dimmed); + + then: + - script.execute: apply_dim_backlight + + else: + - script.execute: apply_active_backlight + - interval: 10s then: - if: @@ -3894,6 +4040,16 @@ switch: # https://esphome.io/components/text_sensor/ #:########################################################################################:# text_sensor: + #:######################################################################################:# + # AMBIENT LIGHT DIAGNOSTICS + #:######################################################################################:# + - platform: template + id: ambient_light_band_text + name: "${friendly_name} Ambient Light Band" + icon: mdi:theme-light-dark + entity_category: diagnostic + update_interval: never + #:######################################################################################:# # BEDROOM / CLOCK STATUS TEXT #:######################################################################################:#