--- a/esp-bedside-panel.yaml +++ b/esp-bedside-panel.yaml @@ -5,6 +5,7 @@ # https://home.fox.co.nz/gitea/zorruno/zorruno-homeassistant/src/branch/master/esphome/esp-bedside-panel.yaml #:########################################################################################:# # VERSIONS: +# V1.56 2026-09-04 Added latched blackout after 10 seconds below 1 lx with immediate exit # V1.55 2026-08-16 Show only Clock time and date in Blackout ambient-light mode # V1.54 2026-08-16 Dimmed Clock time and date palette in Blackout ambient-light mode @@ -248,7 +249,6 @@ # Bright light: approximately 642-643 lx ambient_lux_multiplier_default: "200" - - display_blackout_lux_threshold_default: "2" display_dark_lux_threshold_default: "20" display_bright_lux_threshold_default: "250" @@ -921,21 +921,6 @@ step: 5 mode: box - - platform: template - id: display_blackout_lux_threshold - name: "${friendly_name} Blackout Lux Threshold" - icon: mdi:brightness-1 - entity_category: config - optimistic: true - restore_value: true - initial_value: "${display_blackout_lux_threshold_default}" - unit_of_measurement: "lx" - min_value: 0 - max_value: 20 - step: 0.5 - mode: box - - platform: template id: display_dark_lux_threshold @@ -1228,6 +1213,45 @@ update_interval: 3s lambda: |- const float voltage = id(ambient_light_voltage).state; + + if (isnan(voltage)) { + return NAN; + } + + float multiplier = id(ambient_lux_multiplier).state; + + if (isnan(multiplier) || multiplier <= 0.0f) { + multiplier = 200.0f; + } + + return voltage * multiplier; + on_value: + then: + - if: + condition: + lambda: |- + return !isnan(x) && x >= 1.0f; + then: + - script.stop: latch_display_blackout + - lambda: |- + id(display_blackout_active) = false; + - if: + condition: + lambda: |- + return id(lvgl_ready) && id(display_is_dimmed); + then: + - script.execute: apply_dim_backlight + else: + - if: + condition: + lambda: |- + return id(lvgl_ready); + then: + - script.execute: apply_active_backlight + else: + - if: + condition: + lambda: |- + return !isnan(x) && x < 1.0f && !id(display_blackout_active); + then: + - script.execute: latch_display_blackout - float multiplier = id(ambient_lux_multiplier).state; - - if (isnan(voltage)) { - return NAN; - } - - if (isnan(multiplier) || multiplier <= 0.0f) { - multiplier = 200.0f; - } - - return voltage * multiplier; - platform: homeassistant @@ -1918,6 +1942,12 @@ restore_value: false initial_value: "false" + # True after ambient light remains below 1 lx for 10 continuous seconds. + - id: display_blackout_active + type: bool + restore_value: false + initial_value: "false" + # True only while the Security page is visible. @@ -3133,31 +3163,52 @@ - # Four ambient ranges are used: - # Blackout: lux < Blackout Lux Threshold - # Dark: Blackout threshold to Dark Lux Threshold - # Normal: between Dark and Bright thresholds - # Bright: lux >= Bright Lux Threshold + # Blackout latches after ambient light remains below 1 lx for 10 seconds and + # exits immediately when ambient light reaches 1 lx. The remaining ranges are: + # Dark: lux <= Dark Lux Threshold + # Normal: between Dark and Bright thresholds + # Bright: lux >= Bright Lux Threshold # # The active and clock/idle brightness levels are independently adjustable # and restored after reboot. + - id: latch_display_blackout + mode: single + then: + - delay: 10s + - if: + condition: + lambda: |- + const float lux = id(ambient_light_illuminance).state; + return !isnan(lux) && lux < 1.0f; + then: + - lambda: |- + id(display_blackout_active) = true; + - if: + condition: + lambda: |- + return id(lvgl_ready) && id(display_is_dimmed); + then: + - script.execute: apply_dim_backlight + else: + - if: + condition: + lambda: |- + return id(lvgl_ready); + then: + - script.execute: apply_active_backlight + # Keep the idle Clock unobtrusive at the minimum usable backlight level. - id: apply_clock_blackout_palette @@ -3146,15 +3197,7 @@ - if: condition: lambda: |- - const float lux = id(ambient_light_illuminance).state; - float blackout_threshold = - id(display_blackout_lux_threshold).state; - - if (isnan(blackout_threshold)) { - blackout_threshold = 2.0f; - } - - return !isnan(lux) && lux < blackout_threshold; + return id(display_blackout_active); @@ -3212,22 +3255,12 @@ brightness: !lambda |- const float lux = id(ambient_light_illuminance).state; - float blackout_threshold = - id(display_blackout_lux_threshold).state; float dark_threshold = id(display_dark_lux_threshold).state; float bright_threshold = id(display_bright_lux_threshold).state; - if (isnan(blackout_threshold)) { - blackout_threshold = 2.0f; - } - if ( isnan(dark_threshold) || - dark_threshold <= blackout_threshold + dark_threshold <= 0.0f ) { dark_threshold = 20.0f; } @@ -3239,7 +3272,7 @@ float brightness_percent; - if (!isnan(lux) && lux < blackout_threshold) { + if (id(display_blackout_active)) { brightness_percent = id(display_blackout_brightness).state; @@ -3280,22 +3313,12 @@ brightness: !lambda |- const float lux = id(ambient_light_illuminance).state; - float blackout_threshold = - id(display_blackout_lux_threshold).state; float dark_threshold = id(display_dark_lux_threshold).state; float bright_threshold = id(display_bright_lux_threshold).state; - if (isnan(blackout_threshold)) { - blackout_threshold = 2.0f; - } - if ( isnan(dark_threshold) || - dark_threshold <= blackout_threshold + dark_threshold <= 0.0f ) { dark_threshold = 20.0f; } @@ -3307,7 +3330,7 @@ float brightness_percent; - if (!isnan(lux) && lux < blackout_threshold) { + if (id(display_blackout_active)) { brightness_percent = id(display_blackout_brightness).state;