Keep every screen's Wi-Fi awake from the shared core, so Home Assistant's messages no longer wait for the router's next beacon (0.2.88)

packages/core.yaml sets wifi: power_save_mode: none. ESPHome merges it
under the screen's own wifi: block, which keeps the network, the password
and the hotspot, and the screen's own YAML still wins on the mode. The
YAML ESP Screens writes has had the line since app 0.2.24; screens with
an older or hand-written YAML ran ESPHome's ESP32 default light, and
their messages from Home Assistant waited 76-117 ms on the bench.

esphome config on the bench screens' Device Builder YAMLs: NONE for both
without a line of their own, LIGHT when the YAML says light. The bench
Guition, flashed from its Device Builder YAML: ping 15 ms average instead
of 117 ms. tools/check.sh: 474 Python tests (a new one keeps the core's
wifi: block to this one key), 19/19 C++, editor; firmware unchanged in
size (CYD 1,678,816 B, 91.5 %; Guition 2,055,280 B), since the checkout
profiles already had the line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-19 09:06:53 +02:00
parent ff9d3af9f8
commit fcdea12c44
11 changed files with 112 additions and 13 deletions
+3 -2
View File
@@ -99,8 +99,9 @@ Guition it comes in about 1.8 s (2.8 s with 4 KB).
camera's last snapshot (it keeps one for 30 s after the last load), 4.5 s when it must ask Home
Assistant first, of which 2.4 s is the camera's own snapshot. Firmware 0.2.72 took 3.4 s and
5.8 s. Details in docs/TEST_RESULTS_0287.md.
- `power_save_mode: none` under `wifi:` (the YAML ESP Screens writes has it since app 0.2.24) keeps
the screen from waiting on its Wi-Fi now and then while an image comes in.
- Wi-Fi without power save (firmware 0.2.74 sets `power_save_mode: none` itself) makes the screen
wait on its Wi-Fi less often while an image comes in: a loop held over 50 ms in one of eleven
opens, against five of twelve with power save.
## For developers
+4 -3
View File
@@ -111,9 +111,10 @@ button. The card disappears once the screen is in the list.
## 4. Choose and edit your tiles
Tip: in your ESPHome YAML, add the line `power_save_mode: none` under `wifi:` (new
wizard YAMLs already have it); the screen is mains-powered, so this keeps it responding to
Home Assistant without Wi-Fi sleep delay.
A screen keeps its Wi-Fi awake (`power_save_mode: none`): it is mains-powered, and this way
Home Assistant's messages reach it without waiting for the router. Firmware 0.2.74 and later
do this by themselves; an older screen gets it with its next **Update**. A `power_save_mode`
under `wifi:` in the screen's own ESPHome YAML still wins.
Select your screen, tap the bar at the top of a page to give the screen its name,
and search for entities in the **Library** on the right. It has domain filters with
+1 -1
View File
@@ -7,7 +7,7 @@ already drifted apart in small ways.
| File | What it holds |
|---|---|
| `packages/core.yaml` | Everything every board shares: the LVGL tree (tiles, cards, overlays, the touch test page), the scripts, the API actions, the entities, the fonts, the globals. It contains no number that depends on the board: every such value is a `${NAME}` the board file defines. |
| `packages/core.yaml` | Everything every board shares: the LVGL tree (tiles, cards, overlays, the touch test page), the scripts, the API actions, the entities, the fonts, the globals, and Wi-Fi that never dozes (`power_save_mode: none`, firmware 0.2.74+; the network itself stays in the screen's own YAML). It contains no number that depends on the board: every such value is a `${NAME}` the board file defines. |
| `packages/boards/cyd-2432s028.yaml` | The CYD: its hardware (ILI9341 over SPI, XPT2046 resistive touch, backlight, LED), its sizes and font sizes, the code the shared lambdas take from it (the *hooks*, below), and what only a CYD has: the calibration wizard, a page swipe by LVGL's gesture, `ui_self_test`, the two scripts that drive the backlight as a light action, its climate card layout. |
| `packages/boards/guition-4848s040.yaml` | The Guition: its hardware (ST7701S RGB, GT911, PSRAM), its sizes, its hooks, and what only a Guition has: camera images (`online_image`, the alert's picture frame), the Rotation entity, the page swipe from the glass edge, the snapshot diagnostics, the climate card with its fan and swing card, the wider tile labels (`!extend` on each tile). |
| `packages/cyd.yaml`, `packages/guition.yaml` | What a screen installed from ESP Screens builds from over GitHub, unchanged in name and place: ESP Screen Manager writes every screen's YAML with `files: [packages/<board>.yaml]`. Each is a few lines: where the fonts are, the components from GitHub, and `packages: {core: !include core.yaml, board: !include boards/<board>.yaml}`. |
+11 -1
View File
@@ -219,6 +219,16 @@ icons sit off-center in the browser).
The `MDI_GLYPH_*` substitutions are retired: `TILEn_ICON` in manual
profiles must come from the set. No changed preferences or keys.
### Compatibility 0.2.88 / firmware 0.2.74
Firmware only, one setting: `packages/core.yaml` has `wifi: power_save_mode: none`, the first `wifi:` key in a package.
ESPHome merges it under the screen's own `wifi:` block, which keeps the network, the password and the fallback hotspot,
and the screen's own YAML wins on the mode as on any key (`merge_config`: the main config over every package). The
YAML ESP Screens writes has had the same line since app 0.2.24; screens with an older or hand-written YAML ran ESPHome's
ESP32 default `light` (`WIFI_PS_MIN_MODEM`). The app's Override YAML still refuses `wifi:` (`PROTECTED_OVERRIDE_KEYS`).
No protocol, storage, preference or key change. `tests/test_easy_package.py` keeps the core's `wifi:` block to that one
key and every other package without one. Details: docs/TEST_RESULTS_0288.md.
### Compatibility 0.2.87 / firmware 0.2.73
Firmware only, plus README pictures and docs/CAMERA.md. No protocol, storage, preference or key change: an older app
@@ -235,7 +245,7 @@ drives this firmware as before, and the camera messages are the same; only when
4 s refresh and the retries. `camera_spinner` turns until the first picture or a note; its ring is the new theme
role `CAMERA_TRACK` (0x393D42 in both looks). "Loading image" is gone.
- Guition `online_image` `buffer_size` 16384 (was 4096) for `camera_image` and `alert_image`: the full picture in
about 1.8 s instead of 2.8 s on the bench; without Wi-Fi power save no loop over 50 ms. 32 KB held a loop 0.1 s.
about 1.8 s instead of 2.8 s on the bench; a loop over 50 ms is rare with Wi-Fi awake. 32 KB held a loop 0.1 s.
- Alert picture: `alert_image_frame` has the alert card's radius (18) and `camera_show()` gives the alert's picture the
frame's radius. LVGL 9.5 clips an image to its own radius row by row (`radius_only` in `lv_draw_sw_img.c`) without a
layer, so `clip_corner` stays out (`tests/test_layer_free.py`). About 5 % more render time for the picture on the
+58
View File
@@ -0,0 +1,58 @@
# Test results app 0.2.88 / firmware 0.2.74 (2026-09-19)
The camera measurements of 0.2.87 showed the bench screens' Wi-Fi dozing: their Device Builder YAMLs date from before
app 0.2.24 and have no `power_save_mode`, so they ran ESPHome's default for the ESP32, `light` (`WIFI_PS_MIN_MODEM`: the
radio sleeps between the router's beacons, and what Home Assistant sends waits at the router until the next one). After
the pros and cons Max chose: "zet hem maar op none, voor iedereen vanaf nu voor altijd", with overriding still possible.
## What changed
`packages/core.yaml` has a `wifi:` block with one key, `power_save_mode: none`. ESPHome merges it under the screen's own
`wifi:` (the network, the password, the fallback hotspot stay there), and the screen's own YAML wins on any key it sets
(`merge_config` in `esphome/config_helpers.py`: the main config over every package, a later package over an earlier
one). The YAML ESP Screens writes has had the same line since app 0.2.24 and keeps it. The app's Override YAML still
refuses a `wifi:` section (`Firmware.PROTECTED_OVERRIDE_KEYS`), so the way to another mode is the screen's own YAML in
the ESPHome Device Builder.
## Checked
`esphome config` (ESPHome 2026.6.2) on copies of the bench screens' Device Builder YAMLs with the packages of this
branch:
| YAML | `power_save_mode` after the merge |
|---|---|
| Guition Wallbox, as the Device Builder has it (no line) | `NONE` |
| the same with `power_save_mode: light` under `wifi:` | `LIGHT` |
| CYD, as the Device Builder has it (no line) | `NONE` |
The Guition's build from its Device Builder YAML generates `set_power_save_mode(wifi::WIFI_POWER_SAVE_NONE)`.
## Measured on the bench Guition
Flashed over the air from its Device Builder YAML (no `power_save_mode` line) with this branch:
| | fw 0.2.73, `light` | fw 0.2.74, `none` from the package |
|---|---|---|
| Ping from the Mac (30 × 0.2 s) | 103-191 ms, 117 ms average | 5-89 ms, 15 ms average |
| Camera, cold open | 4.55 s | 4.37 s |
| Camera, warm open | 2.06 s | 2.08 s |
| Loop over 50 ms during an open | 5 of 12 opens (58-107 ms) | 1 of 5 opens (101 ms) |
With the 0.2.87 measurements of a power-save-off build (none in six opens), a loop waited over 50 ms in one of eleven
opens with the Wi-Fi awake. The camera times hardly change: the camera's own snapshot and the download dominate them.
The power the radio now spends listening was not measured; by Espressif's datasheets the receiver draws about
90-100 mA while it listens, a few tenths of a watt on a mains-powered screen.
## Automated
- `tools/check.sh`: Python 474 tests OK (new: `test_easy_package` "the Wi-Fi never dozes but stays the owner's": the
core's `wifi:` block holds only that key, no other package has one, the app's YAML still writes it), C++ 19/19,
packages, icons, editor tests, types, build and bundle PASS.
- `tools/check.sh --firmware --baseline 1678816`: CYD 1,678,816 B = 91.5 % (+0 B: the checkout profiles already set
`power_save_mode: none`), Guition 2,055,280 B = 25.3 %.
## Not tested
- The CYD on its panel: config check only. It gets the setting with its next **Update**.
- Studio 1: its YAML is in the studio's Home Assistant; it gets the setting with its next **Update** unless its own
YAML sets another mode.
+3 -3
View File
@@ -372,9 +372,9 @@ api:
# Camera images (firmware 0.2.57+): ESP Screen Manager serves each image already sized for this screen, as an uncompressed
# BMP on its own port on the LAN (screen_manager/app/camera_feed.py). ESPHome decodes a BMP piece by piece as it arrives,
# one `buffer_size` per loop, where a JPEG of the full screen held the loop for 0.6 s; touch and drawing keep their turn.
# 16 KB a loop (firmware 0.2.73+): the full picture came in 1.8 s instead of 2.8 s with 4 KB. Without Wi-Fi power save no
# loop waited over 50 ms for it (with power save now and then 0.1 s, against 65 ms with 4 KB); 32 KB saved 0.3 s more but
# held a loop 0.1 s even without power save (docs/TEST_RESULTS_0287.md). One image full screen and one in an alert's
# 16 KB a loop (firmware 0.2.73+): the full picture came in 1.8 s instead of 2.8 s with 4 KB. A loop waited over 50 ms
# for it in one of eleven opens with Wi-Fi awake (0.1 s), in five of twelve with power save; 32 KB saved 0.3 s more but
# held a loop 0.1 s more often (docs/TEST_RESULTS_0287.md, 0288). One image full screen and one in an alert's
# frame; runtime_tiles.h decides when they load and frees them when they close. Plain HTTP on the LAN, no certificates.
http_request:
timeout: 4s
+8 -1
View File
@@ -14,7 +14,7 @@
# fonts/materialdesignicons-webfont.ttf (Apache 2.0, github.com/Templarian/MaterialDesign-Webfont).
############################################################
substitutions:
SCREEN_FIRMWARE_VERSION: "0.2.73"
SCREEN_FIRMWARE_VERSION: "0.2.74"
DASHBOARD_INBOX_INTERNAL: "false"
CALIBRATION_ON_BOOT: "false" # a bench build: start on the touch test page
ROOM_NAME: "" # top left until ESP Screens sends the screen name
@@ -261,6 +261,13 @@ logger:
homeassistant.sensor: WARN
homeassistant.text_sensor: WARN
# A screen hangs on the mains, so its Wi-Fi never dozes (firmware 0.2.74+). With ESPHome's default for the ESP32,
# `light`, every message from Home Assistant waits at the router for the next beacon: 76-117 ms on the bench screens
# against about 20 ms, and an image download held a loop up to 0.1 s now and then (docs/TEST_RESULTS_0287.md). The
# network and its password stay in the screen's own YAML, and a `power_save_mode` there wins over this one.
wifi:
power_save_mode: none
# Credentials are loaded from secrets.yaml — copy secrets.yaml.example to secrets.yaml and fill in your values.
api:
+7
View File
@@ -1,3 +1,10 @@
## 0.2.88 (firmware 0.2.74)
Wi-Fi that never dozes, on every screen.
- **A screen keeps its Wi-Fi awake.** ESPHome lets an ESP32's Wi-Fi doze between the router's beacons unless the screen's YAML says otherwise. Then everything Home Assistant sends to the screen waits at the router for the next beacon: 76-117 ms on the bench screens against about 20 ms awake. That wait shows in the confirmation after a tap and in a lamp reporting its brightness. With the Wi-Fi awake, the screen also stalls less often for a moment while a camera picture comes in. The YAML ESP Screens writes has said `power_save_mode: none` since app 0.2.24, but older screens and YAMLs written by hand still dozed. The firmware now keeps the Wi-Fi awake itself; the screens hang on the mains, and it costs a few tenths of a watt. The network and its password stay in the screen's own YAML, and a `power_save_mode` there still wins.
- Needs firmware 0.2.74: press **Update** on the screen. Includes everything from 0.2.87. CYD firmware: 1,678,816 bytes, 91.5 % of the update slot, the same as 0.2.87.
## 0.2.87 (firmware 0.2.73)
A starting screen with a spinner, a camera that opens sooner with a spinner, and round corners on an alert's camera picture.
+1 -1
View File
@@ -37,7 +37,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.73'
FIRMWARE_VERSION = '0.2.74'
# 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.87"
version: "0.2.88"
slug: esp_screen_manager
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
url: https://github.com/MaxGramser/homeassistant_espscreen
+15
View File
@@ -57,6 +57,21 @@ class PackageTests(unittest.TestCase):
block = re.search(r'^' + section + r':\n.*?(?=^[a-zA-Z_]+:|\Z)', text, re.M | re.S)
self.assertFalse(block and re.search(r'^ - platform: homeassistant$', block[0], re.M), f'{path.name}: a {section} subscribes to Home Assistant')
def test_the_wifi_never_dozes_but_stays_the_owners(self):
# Firmware 0.2.74+: the core keeps Wi-Fi awake for every screen, and nothing else of Wi-Fi lives in a package;
# the network, its password and the fallback hotspot stay in the screen's own YAML, which also wins on the mode.
for path in sorted((ROOT / 'packages').rglob('*.yaml')):
text = path.read_text()
block = re.search(r'^wifi:\n(.*?)(?=^[a-zA-Z_]+:|\Z)', text, re.M | re.S)
if path == profiles.CORE:
self.assertIsNotNone(block, 'packages/core.yaml has no wifi: block')
lines = [line for line in block[1].split('\n') if line.strip() and not line.lstrip().startswith('#')]
self.assertEqual(lines, [' power_save_mode: none'])
else:
self.assertIsNone(block, f'{path.name} has a wifi: block')
# The YAML ESP Screens writes says so too (since app 0.2.24), so an older package changes nothing for those.
self.assertIn(' power_save_mode: none\n', (ROOT / 'screen_manager/app/core.py').read_text())
def test_the_manual_profile_is_gone_from_profiles_and_packages(self):
for board, name in BOARDS.items():
for entry in (name, f'packages/{board}.yaml'):