Tidy the standby code around Wake and Sleep (0.2.54)

One rule ends a Sleep: a tap, Wake or an alert. The Auto standby switch
cleared sleep_requested when Home Assistant turned standby off, while
ESP Screens and the settings page on the screen did not; now only
wake_display clears the flag, and apply_screen_settings still wakes a
screen the standby time dimmed.

dim_display no longer checks anything itself (the interval checks Auto
standby, the Sleep button sets the flag). It dims, then runs go_home with
"also on standby" on, otherwise closes the settings page and every card
through close_cards. Before, it copied half of go_home and hid the four
YAML overlays itself, so a runtime detail card stayed open under a dimmed
screen. close_cards is now the one runtime_tiles::dismiss() call: the
fallback and the widget.hide actions after its return ran for nothing.

The backlight light becomes internal. Home Assistant could change it, but
the minute tick put the level back, and switching it off darkened the
screen without standby. The brightness numbers and Wake/Sleep replace it.
A dead clock-label branch from the manual profile leaves
apply_screen_settings.

Checked with 209 Python and 14 C++ tests, host builds of both boards over
the native API, and ESP32 builds of both Easy profiles. Firmware 0.2.46.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-16 10:50:43 +02:00
parent da20782d3d
commit f3af5c2125
12 changed files with 207 additions and 182 deletions
+2 -1
View File
@@ -220,7 +220,8 @@ with the `button.press` action:
time starts counting again. On a screen that is already on, only the count starts again.
- **`button.<screen>_sleep`** puts the screen in standby right away, the same as when the standby
time runs out, and also works with **Auto standby** off. The screen stays in standby until someone
taps it, **Wake** is pressed or an alert comes in. An alert that is showing closes (reported as `remote`).
taps it, **Wake** is pressed or an alert comes in; switching Auto standby off doesn't end it. An alert
that is showing closes (reported as `remote`).
Neither button saves anything on the screen, so an automation may press them as often as it likes,
on every motion too. That is the difference with the **Auto standby** switch, which is a setting.
+22
View File
@@ -176,6 +176,28 @@ icons sit off-center in the browser); run `generate_packages.py` afterward.
The `MDI_GLYPH_*` substitutions are retired: `TILEn_ICON` in manual
profiles must come from the set. No changed preferences or keys.
### Compatibility 0.2.54 / firmware 0.2.46
Board profiles; the app raises `FIRMWARE_VERSION` and adjusts the Auto standby row of the Claude skill.
Storage, tile protocol, preferences and keys are unchanged.
- The Auto standby switch no longer clears `sleep_requested`; `wake_display` is the only place that does.
The one rule lives in `apply_screen_settings`: a dimmed screen wakes when standby is off unless
`sleep_requested` is set, whichever path turned standby off (the switch, ESP Screens, the settings page).
- `dim_display` has no condition any more; its callers decide (the 1 s interval checks `standby_enabled`,
the Sleep button sets the flag). It sets `display_dimmed`, runs `apply_screen_settings`, then `go_home`
with `home_on_standby` on, otherwise `settings_screen::close()` and `close_cards`. Runtime detail cards
now close on standby in both cases, as the light, colour and climate overlays already did.
- `close_cards` is `runtime_tiles::dismiss()`, which on_boot sets on both boards (hide the detail card,
clear `active_entity`, hide the four overlays). The fallback after it and the four `lvgl.widget.hide`
actions ran for nothing: a `return` in a lambda does not skip the actions that follow it.
- The `back_light` light is `internal: true`, so Home Assistant loses `light.<screen>_display_backlight`
(CYD: `light.<screen>_power_display_backlight`); it stays in the registry as unavailable until deleted.
Nothing in the app, the tools or the tests used it; the firmware keeps the light under the same id.
- `apply_screen_settings` loses the `!runtime_tiles::enabled` clock-label branch: on_boot sets `enabled`
before anything can run the script. The `runtime_tiles::enabled` checks before preference saves stay,
because `load_settings()` makes that preference right after `enabled` is set.
### Compatibility 0.2.53 / firmware 0.2.45
Firmware and board profiles; the app raises `FIRMWARE_VERSION` and extends the Claude skill. Storage,
+51
View File
@@ -0,0 +1,51 @@
# Test results app 0.2.54 / firmware 0.2.46 (2026-09-16)
The standby code tidied after a review of Wake and Sleep: one rule for ending a Sleep, standby through
`go_home` / `close_cards`, `close_cards` as one call, the backlight light internal, and a dead
manual-profile branch removed. Storage, tile protocol, preferences and keys are unchanged. See
CHANGELOG 0.2.54 and the compatibility note in docs/RELEASING.md.
## Automated
- Python (`.venv-portal`): 209 tests OK. `tests/test_wake_sleep.py` now has 9 tests, run against both
board profiles and both generated packages. New: `dim_display` sets `display_dimmed`, runs
`apply_screen_settings` and then `go_home` or `settings_screen::close()` + `close_cards`, with no
overlay hiding of its own; it has exactly two callers, and the standby interval only calls it with
Auto standby on. `close_cards` is the single `runtime_tiles::dismiss()` call, and that callback
hides the detail card, clears `active_entity` and hides the four overlays. `sleep_requested` is
cleared in exactly one place (`wake_display`) and the Auto standby switch no longer mentions it.
The `back_light` light is `internal: true`.
- C++ (`clang++ -std=c++17 -Wall -Wextra -Werror -I.`): 14/14 PASS (no C++ changed).
- `generate_packages.py --check` green.
- Home Assistant (read-only `search/related`): no automation, script or scene uses
`light.studio_1_display_backlight`, the only backlight light in Max's Home Assistant.
## Builds (ESPHome 2026.6.2)
Both Easy Setup profiles, compiled one after the other from packages generated out of the board profiles:
| Profile | RAM | Flash | vs 0.2.53 |
|---|---|---|---|
| easy-cyd-device.yaml (package, local components) | 23.0% (75,468 B) | 76.1% (1,395,683 B) | -144 B RAM, -744 B flash |
| easy-guition-device.yaml (package, local components) | 24.0% (78,692 B) | 18.4% (1,495,307 B) | -152 B RAM, -764 B flash |
## Behaviour on the real profile logic (host build)
Same route as 0.2.53: the generated packages built for the Mac with ESPHome's host platform and driven
over the native API, now with the probe also reporting the settings page, the climate overlay and
`active_entity`, plus test-only actions that open a card and flip "also on standby". Result: 35/35 checks passed on the Guition package and 35/35 on the CYD package (the CYD still lists its RGB `LED` light, as intended).
Checked on both boards, besides everything from 0.2.53:
1. The backlight light is not among the entities Home Assistant gets.
2. With "also on standby" on and off: Sleep closes an open settings page and an open card (overlay
hidden, `active_entity` empty); the standby time closes an open card too.
3. The standby time dims with Auto standby on; switching Auto standby off wakes that screen.
4. Sleep, then switching Auto standby from on to off: the screen stays asleep at the standby level;
a tap on the overlay wakes it.
## Not tested / for the owner
- Hardware. Nothing was flashed: the bench boards (192.168.146.134 / .136) were unreachable, and Studio 1
was busy with another session's touch log over USB. On real glass after the update: Sleep with a card
open, and whether Home Assistant shows the old backlight light as unavailable (delete it there).
+20 -42
View File
@@ -2,7 +2,7 @@
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.45"
SCREEN_FIRMWARE_VERSION: "0.2.46"
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
TOUCH_MOVE_LIMIT_PX: "0"
@@ -1042,8 +1042,9 @@ switch:
- platform: restart
name: "Restart"
# Auto standby, the same setting as the checkbox in ESP Screens (firmware 0.2.41+), so Home Assistant
# automations can keep a screen on. Off wakes the screen and keeps it on; on counts the standby time
# from that moment. Nothing runs at boot: the switch follows the saved setting.
# automations can keep a screen on. Off wakes a screen the standby time dimmed and keeps it on (a Sleep
# holds until a tap, Wake or an alert); on counts the standby time from that moment. Nothing runs at
# boot: the switch follows the saved setting.
- platform: template
id: setting_auto_standby
name: "Auto standby"
@@ -1065,8 +1066,6 @@ switch:
auto &s = screen_settings::current;
if (!s.standby_enabled) return;
s.standby_enabled = false;
// Switching it off wakes the screen, also out of Sleep; a switch that was already off leaves Sleep alone.
id(sleep_requested) = false;
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
runtime_tiles::setting_event("standby_enabled", 0);
id(apply_screen_settings).execute();
@@ -1095,10 +1094,13 @@ output:
frequency: 20000Hz
light:
# The firmware owns the backlight: brightness, standby, night and alerts set it, and the minute tick
# restores it. Home Assistant uses the brightness numbers and the Wake and Sleep buttons (0.2.46+).
- platform: monochromatic
output: gpio_backlight_pwm
name: "Display Backlight"
id: back_light
internal: true
restore_mode: ALWAYS_ON
default_transition_length: 150ms
@@ -3561,11 +3563,6 @@ script:
id(set_backlight).execute(level, id(display_dimmed) ? 1500 : 300);
if (id(display_dimmed)) { lv_obj_clear_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN); lv_obj_move_foreground(id(dim_wake_overlay)); }
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
// Runtime mode draws the clock in the top bar; the manual profile keeps its own label.
if (!runtime_tiles::enabled) {
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
}
// The four setting entities only publish a changed value (HA keeps identical states anyway).
if (id(setting_brightness).state != settings.brightness) id(setting_brightness).publish_state(settings.brightness);
if (id(setting_standby_brightness).state != settings.standby_brightness) id(setting_standby_brightness).publish_state(settings.standby_brightness);
@@ -3573,17 +3570,11 @@ script:
if (id(setting_standby_seconds).state != settings.standby_seconds) id(setting_standby_seconds).publish_state(settings.standby_seconds);
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
# Every card closes through runtime_tiles::dismiss (set in on_boot): the runtime detail card, the light,
# colour and climate overlays, and active_entity, which the swipe guard reads.
- id: close_cards
then:
- lambda: |-
// Runtime cards and the legacy overlays close through one path; the swipe guard
// reads active_entity, so a hidden card must never leave it behind.
if (runtime_tiles::dismiss) { runtime_tiles::dismiss(); return; }
id(active_entity).clear();
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- lambda: 'if (runtime_tiles::dismiss) runtime_tiles::dismiss();'
# Everything back to the overview: the settings page, every card and the first page of tiles.
# Used by standby, by the "Back to page 1" timeout and by the Home Assistant action.
@@ -3722,35 +3713,22 @@ script:
- delay: !lambda "return ms + 40;"
- lambda: backlight_fade::sync(*id(gpio_backlight_pwm), *id(back_light), level / 100.0f);
# Standby: the standby time ran out (the 1 s interval checks Auto standby) or Sleep was pressed.
# The settings page and every card close; with "also on standby" the screen goes back to page 1 too.
- id: dim_display
mode: restart
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
- if:
condition:
# Sleep from Home Assistant dims with Auto standby off too.
lambda: 'return screen_settings::current.standby_enabled || id(sleep_requested);'
lambda: 'return screen_settings::current.home_on_standby;'
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
// A settings page behind a dark screen helps nobody; it always closes.
settings_screen::close();
if (screen_settings::current.home_on_standby) {
// Runtime detail cards live above the tile grid, so paging alone cannot close them.
runtime_tiles::hide_detail();
id(active_entity).clear();
id(tile_page) = 0;
id(show_tile_page).execute();
}
# Back to the home screen on standby. Deliberately via ESPHome's
# actions and NOT with lv_obj_add_flag in a lambda: this script
# runs from an interval, so outside LVGL's own handling.
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- script.execute: go_home
else:
- lambda: 'settings_screen::close();'
- script.execute: close_cards
# A -/+ tap on the climate card: the number follows at once, the call waits for the last tap.
# Holding a key steps three times a second.
+20 -42
View File
@@ -17,7 +17,7 @@
############################################################
substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.45"
SCREEN_FIRMWARE_VERSION: "0.2.46"
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
TOUCH_MOVE_LIMIT_PX: "56"
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
@@ -1175,8 +1175,9 @@ switch:
- platform: restart
name: "Restart"
# Auto standby, the same setting as the checkbox in ESP Screens (firmware 0.2.41+), so Home Assistant
# automations can keep a screen on. Off wakes the screen and keeps it on; on counts the standby time
# from that moment. Nothing runs at boot: the switch follows the saved setting.
# automations can keep a screen on. Off wakes a screen the standby time dimmed and keeps it on (a Sleep
# holds until a tap, Wake or an alert); on counts the standby time from that moment. Nothing runs at
# boot: the switch follows the saved setting.
- platform: template
id: setting_auto_standby
name: "Auto standby"
@@ -1198,8 +1199,6 @@ switch:
auto &s = screen_settings::current;
if (!s.standby_enabled) return;
s.standby_enabled = false;
// Switching it off wakes the screen, also out of Sleep; a switch that was already off leaves Sleep alone.
id(sleep_requested) = false;
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
runtime_tiles::setting_event("standby_enabled", 0);
id(apply_screen_settings).execute();
@@ -1245,10 +1244,13 @@ output:
inverted: true
light:
# The firmware owns the backlight: brightness, standby, night and alerts set it, and the minute tick
# restores it. Home Assistant uses the brightness numbers and the Wake and Sleep buttons (0.2.46+).
- platform: monochromatic
output: gpio_backlight_pwm
name: "Power Display Backlight"
id: back_light
internal: true
restore_mode: ALWAYS_ON
- platform: rgb
@@ -3606,11 +3608,6 @@ script:
id(back_light).turn_on().set_transition_length(300).set_brightness(level / 100.0f).perform();
if (id(display_dimmed)) { lv_obj_clear_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN); lv_obj_move_foreground(id(dim_wake_overlay)); }
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
// Runtime mode draws the clock in the top bar; the manual profile keeps its own label.
if (!runtime_tiles::enabled) {
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
}
// The four setting entities only publish a changed value (HA keeps identical states anyway).
if (id(setting_brightness).state != settings.brightness) id(setting_brightness).publish_state(settings.brightness);
if (id(setting_standby_brightness).state != settings.standby_brightness) id(setting_standby_brightness).publish_state(settings.standby_brightness);
@@ -3618,17 +3615,11 @@ script:
if (id(setting_standby_seconds).state != settings.standby_seconds) id(setting_standby_seconds).publish_state(settings.standby_seconds);
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
# Every card closes through runtime_tiles::dismiss (set in on_boot): the runtime detail card, the light,
# colour and climate overlays, and active_entity, which the swipe guard reads.
- id: close_cards
then:
- lambda: |-
// Runtime cards and the legacy overlays close through one path; the swipe guard
// reads active_entity, so a hidden card must never leave it behind.
if (runtime_tiles::dismiss) { runtime_tiles::dismiss(); return; }
id(active_entity).clear();
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- lambda: 'if (runtime_tiles::dismiss) runtime_tiles::dismiss();'
# Everything back to the overview: the settings page, every card and the first page of tiles.
# Used by standby, by the "Back to page 1" timeout and by the Home Assistant action.
@@ -3763,35 +3754,22 @@ script:
- lvgl.widget.hide: dim_wake_overlay
- script.execute: close_cards
# Standby: the standby time ran out (the 1 s interval checks Auto standby) or Sleep was pressed.
# The settings page and every card close; with "also on standby" the screen goes back to page 1 too.
- id: dim_display
mode: restart
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
- if:
condition:
# Sleep from Home Assistant dims with Auto standby off too.
lambda: 'return screen_settings::current.standby_enabled || id(sleep_requested);'
lambda: 'return screen_settings::current.home_on_standby;'
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
// A settings page behind a dark screen helps nobody; it always closes.
settings_screen::close();
if (screen_settings::current.home_on_standby) {
// Runtime detail cards live above the tile grid, so paging alone cannot close them.
runtime_tiles::hide_detail();
id(active_entity).clear();
id(tile_page) = 0;
id(show_tile_page).execute();
}
# Back to the home screen on standby. Deliberately via ESPHome's
# actions and NOT with lv_obj_add_flag in a lambda: this script
# runs from an interval, so outside LVGL's own handling.
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- script.execute: go_home
else:
- lambda: 'settings_screen::close();'
- script.execute: close_cards
# A -/+ tap on the climate card: the number follows at once, the call waits for the last tap.
# Holding a key steps three times a second.
+20 -42
View File
@@ -18,7 +18,7 @@
############################################################
substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.45"
SCREEN_FIRMWARE_VERSION: "0.2.46"
# A tap survives a finger that drifts up to this far (320 px over 58 mm: 1 cm); further is a swipe.
TOUCH_MOVE_LIMIT_PX: "56"
# Shortest contact that counts as a tap; a resistive panel bounces, a capacitive one does not.
@@ -1164,8 +1164,9 @@ switch:
- platform: restart
name: "Restart"
# Auto standby, the same setting as the checkbox in ESP Screens (firmware 0.2.41+), so Home Assistant
# automations can keep a screen on. Off wakes the screen and keeps it on; on counts the standby time
# from that moment. Nothing runs at boot: the switch follows the saved setting.
# automations can keep a screen on. Off wakes a screen the standby time dimmed and keeps it on (a Sleep
# holds until a tap, Wake or an alert); on counts the standby time from that moment. Nothing runs at
# boot: the switch follows the saved setting.
- platform: template
id: setting_auto_standby
name: "Auto standby"
@@ -1187,8 +1188,6 @@ switch:
auto &s = screen_settings::current;
if (!s.standby_enabled) return;
s.standby_enabled = false;
// Switching it off wakes the screen, also out of Sleep; a switch that was already off leaves Sleep alone.
id(sleep_requested) = false;
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
runtime_tiles::setting_event("standby_enabled", 0);
id(apply_screen_settings).execute();
@@ -1234,10 +1233,13 @@ output:
inverted: true
light:
# The firmware owns the backlight: brightness, standby, night and alerts set it, and the minute tick
# restores it. Home Assistant uses the brightness numbers and the Wake and Sleep buttons (0.2.46+).
- platform: monochromatic
output: gpio_backlight_pwm
name: "Power Display Backlight"
id: back_light
internal: true
restore_mode: ALWAYS_ON
- platform: rgb
@@ -3595,11 +3597,6 @@ script:
id(back_light).turn_on().set_transition_length(300).set_brightness(level / 100.0f).perform();
if (id(display_dimmed)) { lv_obj_clear_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN); lv_obj_move_foreground(id(dim_wake_overlay)); }
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
// Runtime mode draws the clock in the top bar; the manual profile keeps its own label.
if (!runtime_tiles::enabled) {
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
}
// The four setting entities only publish a changed value (HA keeps identical states anyway).
if (id(setting_brightness).state != settings.brightness) id(setting_brightness).publish_state(settings.brightness);
if (id(setting_standby_brightness).state != settings.standby_brightness) id(setting_standby_brightness).publish_state(settings.standby_brightness);
@@ -3607,17 +3604,11 @@ script:
if (id(setting_standby_seconds).state != settings.standby_seconds) id(setting_standby_seconds).publish_state(settings.standby_seconds);
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
# Every card closes through runtime_tiles::dismiss (set in on_boot): the runtime detail card, the light,
# colour and climate overlays, and active_entity, which the swipe guard reads.
- id: close_cards
then:
- lambda: |-
// Runtime cards and the legacy overlays close through one path; the swipe guard
// reads active_entity, so a hidden card must never leave it behind.
if (runtime_tiles::dismiss) { runtime_tiles::dismiss(); return; }
id(active_entity).clear();
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- lambda: 'if (runtime_tiles::dismiss) runtime_tiles::dismiss();'
# Everything back to the overview: the settings page, every card and the first page of tiles.
# Used by standby, by the "Back to page 1" timeout and by the Home Assistant action.
@@ -3752,35 +3743,22 @@ script:
- lvgl.widget.hide: dim_wake_overlay
- script.execute: close_cards
# Standby: the standby time ran out (the 1 s interval checks Auto standby) or Sleep was pressed.
# The settings page and every card close; with "also on standby" the screen goes back to page 1 too.
- id: dim_display
mode: restart
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
- if:
condition:
# Sleep from Home Assistant dims with Auto standby off too.
lambda: 'return screen_settings::current.standby_enabled || id(sleep_requested);'
lambda: 'return screen_settings::current.home_on_standby;'
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
// A settings page behind a dark screen helps nobody; it always closes.
settings_screen::close();
if (screen_settings::current.home_on_standby) {
// Runtime detail cards live above the tile grid, so paging alone cannot close them.
runtime_tiles::hide_detail();
id(active_entity).clear();
id(tile_page) = 0;
id(show_tile_page).execute();
}
# Back to the home screen on standby. Deliberately via ESPHome's
# actions and NOT with lv_obj_add_flag in a lambda: this script
# runs from an interval, so outside LVGL's own handling.
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- script.execute: go_home
else:
- lambda: 'settings_screen::close();'
- script.execute: close_cards
# A -/+ tap on the climate card: the number follows at once, the call waits for the last tap.
# Holding a key steps three times a second.
+20 -42
View File
@@ -3,7 +3,7 @@
# Source profile for packages/guition.yaml (tools/generate_packages.py); see docs/GUITION.md.
# A screen gets its tiles from the ESP Screen Manager add-on while it runs. The CYD is its own profile.
substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.45"
SCREEN_FIRMWARE_VERSION: "0.2.46"
# 0: no drift limit; LVGL decides, releasing inside the tile is a tap, leaving it is not
# (like iOS). The CYD keeps a limit because its resistive panel jumps.
TOUCH_MOVE_LIMIT_PX: "0"
@@ -1031,8 +1031,9 @@ switch:
- platform: restart
name: "Restart"
# Auto standby, the same setting as the checkbox in ESP Screens (firmware 0.2.41+), so Home Assistant
# automations can keep a screen on. Off wakes the screen and keeps it on; on counts the standby time
# from that moment. Nothing runs at boot: the switch follows the saved setting.
# automations can keep a screen on. Off wakes a screen the standby time dimmed and keeps it on (a Sleep
# holds until a tap, Wake or an alert); on counts the standby time from that moment. Nothing runs at
# boot: the switch follows the saved setting.
- platform: template
id: setting_auto_standby
name: "Auto standby"
@@ -1054,8 +1055,6 @@ switch:
auto &s = screen_settings::current;
if (!s.standby_enabled) return;
s.standby_enabled = false;
// Switching it off wakes the screen, also out of Sleep; a switch that was already off leaves Sleep alone.
id(sleep_requested) = false;
if (runtime_tiles::enabled) runtime_tiles::settings_preference.save(&s);
runtime_tiles::setting_event("standby_enabled", 0);
id(apply_screen_settings).execute();
@@ -1084,10 +1083,13 @@ output:
frequency: 20000Hz
light:
# The firmware owns the backlight: brightness, standby, night and alerts set it, and the minute tick
# restores it. Home Assistant uses the brightness numbers and the Wake and Sleep buttons (0.2.46+).
- platform: monochromatic
output: gpio_backlight_pwm
name: "Display Backlight"
id: back_light
internal: true
restore_mode: ALWAYS_ON
default_transition_length: 150ms
@@ -3550,11 +3552,6 @@ script:
id(set_backlight).execute(level, id(display_dimmed) ? 1500 : 300);
if (id(display_dimmed)) { lv_obj_clear_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN); lv_obj_move_foreground(id(dim_wake_overlay)); }
else lv_obj_add_flag(id(dim_wake_overlay), LV_OBJ_FLAG_HIDDEN);
// Runtime mode draws the clock in the top bar; the manual profile keeps its own label.
if (!runtime_tiles::enabled) {
if (settings.show_clock) lv_obj_clear_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
else lv_obj_add_flag(id(lbl_time), LV_OBJ_FLAG_HIDDEN);
}
// The four setting entities only publish a changed value (HA keeps identical states anyway).
if (id(setting_brightness).state != settings.brightness) id(setting_brightness).publish_state(settings.brightness);
if (id(setting_standby_brightness).state != settings.standby_brightness) id(setting_standby_brightness).publish_state(settings.standby_brightness);
@@ -3562,17 +3559,11 @@ script:
if (id(setting_standby_seconds).state != settings.standby_seconds) id(setting_standby_seconds).publish_state(settings.standby_seconds);
if (!id(ui_refresh).is_running()) id(ui_refresh).execute();
# Every card closes through runtime_tiles::dismiss (set in on_boot): the runtime detail card, the light,
# colour and climate overlays, and active_entity, which the swipe guard reads.
- id: close_cards
then:
- lambda: |-
// Runtime cards and the legacy overlays close through one path; the swipe guard
// reads active_entity, so a hidden card must never leave it behind.
if (runtime_tiles::dismiss) { runtime_tiles::dismiss(); return; }
id(active_entity).clear();
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- lambda: 'if (runtime_tiles::dismiss) runtime_tiles::dismiss();'
# Everything back to the overview: the settings page, every card and the first page of tiles.
# Used by standby, by the "Back to page 1" timeout and by the Home Assistant action.
@@ -3711,35 +3702,22 @@ script:
- delay: !lambda "return ms + 40;"
- lambda: backlight_fade::sync(*id(gpio_backlight_pwm), *id(back_light), level / 100.0f);
# Standby: the standby time ran out (the 1 s interval checks Auto standby) or Sleep was pressed.
# The settings page and every card close; with "also on standby" the screen goes back to page 1 too.
- id: dim_display
mode: restart
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
- if:
condition:
# Sleep from Home Assistant dims with Auto standby off too.
lambda: 'return screen_settings::current.standby_enabled || id(sleep_requested);'
lambda: 'return screen_settings::current.home_on_standby;'
then:
- lambda: |-
id(display_dimmed) = true;
id(apply_screen_settings).execute();
// A settings page behind a dark screen helps nobody; it always closes.
settings_screen::close();
if (screen_settings::current.home_on_standby) {
// Runtime detail cards live above the tile grid, so paging alone cannot close them.
runtime_tiles::hide_detail();
id(active_entity).clear();
id(tile_page) = 0;
id(show_tile_page).execute();
}
# Back to the home screen on standby. Deliberately via ESPHome's
# actions and NOT with lv_obj_add_flag in a lambda: this script
# runs from an interval, so outside LVGL's own handling.
- lvgl.widget.hide: brightness_overlay
- lvgl.widget.hide: color_detail_overlay
- lvgl.widget.hide: climate_detail_overlay
- lvgl.widget.hide: climate_mode_overlay
- script.execute: go_home
else:
- lambda: 'settings_screen::close();'
- script.execute: close_cards
# A -/+ tap on the climate card: the number follows at once, the call waits for the last tap.
# Holding a key steps three times a second.
+10
View File
@@ -1,3 +1,13 @@
## 0.2.54 (firmware 0.2.46)
The standby code, tidied after a review of Wake and Sleep.
- **One rule for Sleep.** A screen put to sleep with Sleep stays asleep until someone taps it, Wake is pressed or an alert comes in. Switching Auto standby off from Home Assistant used to end it, while the same switch in ESP Screens or on the screen did not; now none of them do. Switching Auto standby off still wakes a screen that its standby time dimmed.
- **Standby closes every card.** The settings page and any open card close when the screen goes into standby, and with "also on standby" on it goes back to page 1 as well. Until now a weather or media card stayed open under a dimmed screen while a light or climate card closed.
- **The backlight light is gone from Home Assistant.** `Display Backlight` (on the CYD `Power Display Backlight`) fought the screen's own brightness: the screen put its level back within a minute, and switching the light off made the screen dark without standby, so taps landed on tiles you couldn't see. Use the brightness numbers and the Wake and Sleep buttons. If Home Assistant still lists the old light as unavailable after the update, you can delete it there.
- Under the hood: standby goes through the same scripts as "back to page 1", every card closes through one call, and a leftover of the old manual profile is gone.
- Needs firmware 0.2.46: press **Update** on the screen.
## 0.2.53 (firmware 0.2.45)
Wake a screen or put it to sleep from Home Assistant.
+1 -1
View File
@@ -258,7 +258,7 @@ Every screen has these entities in Home Assistant, on its ESPHome device. `<scre
|---|---|
| `button.<screen>_wake` | Wakes the screen the way a tap does and counts the standby time from that moment; a screen that is already on only restarts that count. Firmware {WAKE_SLEEP_MIN_FIRMWARE} or newer. |
| `button.<screen>_sleep` | Puts the screen in standby right away, also with Auto standby off, and closes an alert that is showing. It stays in standby until someone taps it, Wake is pressed or an alert arrives. Firmware {WAKE_SLEEP_MIN_FIRMWARE} or newer. |
| `switch.<screen>_auto_standby` | On: the screen dims after the standby time without a touch. Off: the screen wakes up and stays on. Firmware {AUTO_STANDBY_MIN_FIRMWARE} or newer. |
| `switch.<screen>_auto_standby` | On: the screen dims after the standby time without a touch. Off: the screen wakes up and stays on, except after Sleep, which holds until Wake, a tap or an alert. Firmware {AUTO_STANDBY_MIN_FIRMWARE} or newer. |
| `number.<screen>_standby_after` | Seconds without a touch before standby, 60 to 86400. |
| `number.<screen>_normal_brightness` | Brightness while in use, 5 to 100 %. |
| `number.<screen>_standby_brightness` | Brightness in standby, 0 to 100 %, at most the normal brightness. |
+1 -1
View File
@@ -17,7 +17,7 @@ WEEKDAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su']
REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
REFS = {'cyd': 'main', 'guition': 'main'}
# Firmware shipped with this app release; screens below it get an update offer.
FIRMWARE_VERSION = '0.2.45'
FIRMWARE_VERSION = '0.2.46'
# The Auto standby switch a screen offers Home Assistant automations.
AUTO_STANDBY_MIN_FIRMWARE = '0.2.41'
# The settings page the screen opens itself, and the screen.settings tile that opens it.
+1 -1
View File
@@ -1,5 +1,5 @@
name: ESP Screen Manager
version: "0.2.53"
version: "0.2.54"
slug: esp_screen_manager
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
url: https://github.com/MaxGramser/homeassistant_espscreen
+39 -10
View File
@@ -1,7 +1,8 @@
"""Wake and Sleep buttons for Home Assistant automations (app 0.2.53, firmware 0.2.45).
"""Wake and Sleep buttons for Home Assistant automations (app 0.2.53, firmware 0.2.45) and the standby
code around them (tidied in app 0.2.54, firmware 0.2.46).
Both board profiles have to wire them the same way: Wake is a tap, Sleep is the standby time running out
right away, and a Sleep holds with Auto standby off until a tap, Wake or an alert ends it.
right away, and a Sleep holds until a tap, Wake or an alert ends it, whatever turns Auto standby off.
"""
import re
import sys
@@ -69,13 +70,42 @@ class Profiles(unittest.TestCase):
steps = re.findall(r'script\.execute:(?:\s+id:)?\s+(\w+)|(id\(sleep_requested\) = true;)', sleep.split('else:', 1)[1])
self.assertEqual([step[0] or 'flag' for step in steps], ['flag', 'alert_dismiss', 'dim_display'], name)
self.assertIn('reason: "remote"', sleep, f'{name}: HA hears the alert was closed from Home Assistant')
dim = script(text, 'dim_display')
self.assertIn("lambda: 'return screen_settings::current.standby_enabled || id(sleep_requested);'", dim, name)
apply = script(text, 'apply_screen_settings')
self.assertIn('if (id(display_dimmed) && !settings.standby_enabled && !id(sleep_requested)) {', apply,
f'{name}: the minute tick must not undo a Sleep')
self.assertEqual(len(re.findall(r'id\(sleep_requested\) = true;', text)), 1, f'{name}: only the button starts a Sleep')
def test_standby_has_one_way_in(self):
for name, text in self.profiles.items():
# dim_display decides nothing itself: the interval asks for Auto standby, the Sleep button sets the flag.
dim = script(text, 'dim_display')
self.assertRegex(dim, r"then:\s+- lambda: \|-\s+id\(display_dimmed\) = true;\s+id\(apply_screen_settings\)\.execute\(\);\s+- if:", name)
self.assertRegex(dim, r"return screen_settings::current\.home_on_standby;'\s+then:\s+- script\.execute: go_home\s+else:\s+"
r"- lambda: 'settings_screen::close\(\);'\s+- script\.execute: close_cards", name)
self.assertNotIn('lvgl.widget.hide', dim, f'{name}: cards close through close_cards')
callers = re.findall(r'script\.execute: dim_display', text)
self.assertEqual(len(callers), 2, f'{name}: the standby interval and the Sleep button')
interval = re.search(r'if \(!screen_settings::current\.standby_enabled\) return false;\s+'
r'if \(id\(display_dimmed\) \|\| id\(touch_down\) \|\| id\(calibration_active\) \|\| id\(alert_active\)\) return false;\s+'
r'return \(millis\(\) - id\(last_touch_ms\)\) > [^\n]+\n\s+then:\s+- script\.execute: dim_display', text)
self.assertTrue(interval, f'{name}: the standby time only dims with Auto standby on')
def test_every_card_closes_through_dismiss(self):
for name, text in self.profiles.items():
body = [line.strip() for line in script(text, 'close_cards').split('then:', 1)[1].splitlines()
if line.strip() and not line.strip().startswith('#')]
self.assertEqual(body, ["- lambda: 'if (runtime_tiles::dismiss) runtime_tiles::dismiss();'"], name)
dismiss = re.search(r'runtime_tiles::dismiss = \[\]\(\) \{(.*?)\};', text, re.S)[1]
for step in ('runtime_tiles::hide_detail();', 'id(active_entity).clear();', 'id(brightness_overlay)',
'id(color_detail_overlay)', 'id(climate_detail_overlay)', 'id(climate_mode_overlay)'):
self.assertIn(step, dismiss, name)
def test_the_backlight_belongs_to_the_firmware(self):
for name, text in self.profiles.items():
light = item(section(text, 'light'), 'id', 'back_light')
self.assertIn('platform: monochromatic', light, name)
self.assertIn('internal: true', light, f'{name}: Home Assistant would fight standby over it')
def test_the_flag_starts_false_and_is_never_saved(self):
for name, text in self.profiles.items():
flag = item(section(text, 'globals'), 'id', 'sleep_requested')
@@ -83,13 +113,12 @@ class Profiles(unittest.TestCase):
self.assertIn('restore_value: no', flag, name)
self.assertIn("initial_value: 'false'", flag, name)
def test_switching_auto_standby_off_still_wakes_the_screen(self):
def test_only_a_tap_wake_or_an_alert_end_a_sleep(self):
for name, text in self.profiles.items():
switch = item(section(text, 'switch'), 'name', 'Auto standby')
off = switch.split('turn_off_action:', 1)[1]
# Only a real change wakes: a switch that was already off returns before the flag is touched.
self.assertRegex(off, r'if \(!s\.standby_enabled\) return;\s+s\.standby_enabled = false;\s+(//[^\n]*\n\s+)?'
r'id\(sleep_requested\) = false;', name)
# wake_display is the one place that clears the flag; Auto standby, from any side, leaves it alone.
self.assertEqual(len(re.findall(r'id\(sleep_requested\) = false;', text)), 1, name)
self.assertIn('id(sleep_requested) = false;', script(text, 'wake_display'), name)
self.assertNotIn('sleep_requested', item(section(text, 'switch'), 'name', 'Auto standby'), name)
if __name__ == '__main__':