Change a screen's settings on the screen itself (0.2.52)
Holding the top bar for about a second and a half opens a settings page on the screen, with a blue line filling along the top edge while you hold. A built-in screen.settings tile and the esphome.<screen>_open_settings action open it too. The page is one constexpr table of rows in settings_screen.h: Brightness, Night, Screen (clock, back to page 1, swiping, rotation) and This screen (name, address, firmware, Home Assistant, Restart). Toggles flip on a tap, numbers and times step with -/+ (a held time walks whole hours), a choice cycles in a chip, and a row that depends on a switch above it is greyed out. The LVGL objects exist only while the page is open: +608 bytes of RAM and +10.7 KB of flash on the CYD. A change is saved, applied and reported through the existing screen_setting event, so ESP Screens shows the same value within a second. New setting auto_home (default on, 2 minutes): an open card, the settings page or a later page goes back to page 1 after that long without a touch, not only on standby. It travels beside the frozen eleven-key settings block with its own preference, so older firmware and older apps keep working. docs/SETTINGS.md is the recipe for adding a setting; the Claude skill explains the page. Firmware 0.2.44. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -176,6 +176,44 @@ 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.52 / firmware 0.2.44
|
||||
|
||||
App and firmware. Storage version and tile protocol stay 1; everything is additive, so an old screen
|
||||
with this app, and a new screen with an older app, both keep working.
|
||||
|
||||
- **Two settings, beside the frozen block.** `settings.auto_home` (bool, default `true`) and
|
||||
`settings.auto_home_seconds` (30-3600, default 120) are new keys in `SETTING_RULES`, stored in
|
||||
storage version 1. On the wire they travel as their own top-level keys of the layout message, next to
|
||||
`swipe_pages` and `rotation`; the eleven-field `settings` object is unchanged, because firmware before
|
||||
0.2.44 refuses any other size. `core.SETTINGS_BESIDE_BLOCK` is the one list of such keys and
|
||||
`layout_message` uses it. Older firmware ignores the two keys.
|
||||
- **Firmware storage.** A new preference key `0x484F4D31` holds `HomeTimeout` (two uint32: enabled,
|
||||
seconds). The Settings structure `0x53435231`, swipe `0x53575031`, rotation `0x524F5431` and the CYD
|
||||
calibration are untouched. `runtime_tiles::persist_settings()` saves all of them; ESPHome compares each
|
||||
blob with NVS before writing, so unchanged records cost no flash write.
|
||||
- **Settings page on the screen.** `components/smart_display/settings_screen.h`: one `constexpr` table of
|
||||
pages and rows, drawn into a root object created on open and deleted on close. Opened by holding the
|
||||
top bar (`attach_hold`, substitutions `SETTINGS_HOLD_X/W/DRIFT_PX`; on the Guition the strip keeps
|
||||
clear of both swipe bands), by the `screen.settings` tile, or by the API action `open_settings`
|
||||
(`page` 0-4, negative closes and goes home). A change is saved, applied (`apply_screen_settings`) and
|
||||
reported with the existing `esphome.screen_setting` event; the manager's handler accepts every key in
|
||||
`SETTING_RULES`, the new ones included. Row taps use the board's own drift limit
|
||||
(`TOUCH_MOVE_LIMIT_PX`), the hold its own.
|
||||
- **Back to page 1 by itself.** The 1 s interval runs the new `go_home` script (settings page closed,
|
||||
cards closed, page 1) when `auto_home` is on and nothing touched the screen for `auto_home_seconds`,
|
||||
while a card, the settings page or a later page is showing. Standby now always closes the settings page;
|
||||
an alert closes it too.
|
||||
- **`screen.settings` tile.** A second built-in entity next to `screen.clock`. `min_firmware` asks for
|
||||
0.2.44, so the manager does not send a layout with it to older firmware. `Tile::is_clock()` now marks
|
||||
the clock-only paths (minute redraw, second hand) that used `builtin()`; the editor mockup takes the
|
||||
icon from `tile_icons.BUILTIN_TILES`.
|
||||
- **Glyphs.** `monitor`, `information-outline` and `restart` join `tile_icons.FIXED` (183 glyphs).
|
||||
- **API.** `open_settings` is a new ESPHome action; Home Assistant lists it as
|
||||
`esphome.<screen>_open_settings` after the device reconnects with 0.2.44. The `IP address` text sensor
|
||||
gained `id: wifi_ip`; its entity is unchanged.
|
||||
- **Docs.** docs/SETTINGS.md is the recipe for adding a setting; the Claude skill gains a section on the
|
||||
page (one more YAML example), so an installed skill shows as outdated until it is installed again.
|
||||
|
||||
### Compatibility 0.2.51 / firmware 0.2.43
|
||||
|
||||
App only; no firmware change, no protocol change, no storage change. Layouts keep their shape, so an
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Settings: on the screen and in ESP Screens
|
||||
|
||||
Every screen setting lives in two places at once: the **Screen settings** block of the add-on editor, and
|
||||
the **settings page on the screen itself** (firmware 0.2.44+). This page says what that page is made of,
|
||||
and exactly what adding one more setting takes.
|
||||
|
||||
## What the user sees
|
||||
|
||||
Holding the top bar of the overview for about one and a half seconds opens the page; a blue line grows
|
||||
along the top edge while you hold, and letting go before it finishes cancels. A screen can also carry a
|
||||
`screen.settings` tile, and Home Assistant can open it with `esphome.<screen>_open_settings`.
|
||||
|
||||
The page is a menu of groups, each of which opens a page of its own:
|
||||
|
||||
| Group | Rows |
|
||||
|---|---|
|
||||
| Brightness | Brightness, Auto standby, Standby after, Standby brightness |
|
||||
| Night | Night mode, Starts, Ends, Night brightness |
|
||||
| Screen | Clock, Back to page 1, After, Also on standby, Swipe between pages, Rotation (boards that turn) |
|
||||
| This screen | Screen, Address, Firmware, Home Assistant, Restart |
|
||||
|
||||
Every change is saved on the screen, applied at once, and sent to the add-on as an
|
||||
`esphome.screen_setting` event, so the editor shows the same value within a second. The add-on remains
|
||||
the owner: its next layout message repeats every setting, and a screen that was offline catches up then.
|
||||
|
||||
## The rules the page follows
|
||||
|
||||
- **Two levels, never three.** A group page is the deepest place a setting can live.
|
||||
- **No free scrolling.** A group that does not fit gets the same `< Previous / Next >` pager as the tile
|
||||
pages. A 320x240 board shows five rows, a 480x480 board six.
|
||||
- **A row is a control, not a form.** Toggles flip on tap, numbers and times have `-` and `+`, a choice
|
||||
cycles through its options in a chip on the right.
|
||||
- **A row that depends on a switch above it is greyed out, not hidden**, so the page never jumps around.
|
||||
- **Only settings that belong to this piece of glass.** Tiles, the top bar and pages belong to the editor,
|
||||
which has a mouse and a keyboard; the screen gets what you want to change while standing in front of it.
|
||||
|
||||
## Adding a setting
|
||||
|
||||
The example below adds "Beep on touch" (`beep`), a switch.
|
||||
|
||||
### 1. Where the value lives
|
||||
|
||||
Two homes, and the choice is fixed by history:
|
||||
|
||||
- `screen_settings::Settings` in `components/smart_display/screen_settings.h` is the **frozen** block of
|
||||
eleven values that ESP Screens sends as `settings`. Its format is version 1 and changing it needs a
|
||||
preference migration *and* firmware-version gating in the add-on, because older firmware refuses a
|
||||
`settings` object that does not have exactly its own keys. Don't.
|
||||
- Anything new is a plain value in `settings_screen.h` next to `swipe_pages`, `rotation` and `auto_home`,
|
||||
with its own preference and its own key in the layout message. That is backward and forward
|
||||
compatible in both directions: old firmware ignores the key, a new screen with an old add-on keeps its
|
||||
saved value.
|
||||
|
||||
```cpp
|
||||
// settings_screen.h, next to the others
|
||||
inline int32_t swipe_pages = 0, rotation = 0, auto_home = 1, auto_home_seconds = 120, beep = 0;
|
||||
```
|
||||
|
||||
Storing it (`components/smart_display/runtime_tiles.h`): extend `HomeTimeout`-style records or add one of
|
||||
your own, load it in `load_settings()`, write it in `persist_settings()`, and accept the key in
|
||||
`receive()` next to `auto_home` — validate first, then save.
|
||||
|
||||
### 2. The row
|
||||
|
||||
One line in the table of the group it belongs to, in `settings_screen.h`:
|
||||
|
||||
```cpp
|
||||
toggle("Beep on touch", [] { return beep; },
|
||||
[](int32_t value) { beep = value ? 1 : 0; changed("beep", beep); }),
|
||||
```
|
||||
|
||||
`changed(key, value)` saves, applies and reports in one go; `key` must be the add-on's key exactly.
|
||||
The row kinds are `toggle`, `number` (fixed step, optional unit), `duration` (seconds, steps grow with
|
||||
the value), `moment` (minutes since midnight, quarters by tap and hours while held), `choice`, `info`
|
||||
and `action`. A row can carry `shown` (leave it out on boards that lack the hardware) and `enabled`
|
||||
(grey while the switch it depends on is off).
|
||||
|
||||
Watch the count: a group of more than five rows gets a pager on a 320x240 board. Six is the maximum
|
||||
that still fits a 480x480 board in one go.
|
||||
|
||||
### 3. The add-on
|
||||
|
||||
- `screen_manager/app/core.py`: one line in `SETTING_RULES` (`'beep': (False, None, None)`), which gives
|
||||
you validation, storage and the write-back from the screen for free.
|
||||
- `screen_manager/app/server.py`: add the key to `extra` in `layout_message` and send it as its own key,
|
||||
next to `auto_home`. Never let it into the `settings` block.
|
||||
- `screen_manager/app/static/app.js`: one line in `settingDefinitions` for the editor's own control.
|
||||
- Needs new firmware? `min_firmware()` in `core.py` keeps the screen from being told about something it
|
||||
cannot do.
|
||||
|
||||
### 4. Tests and proof
|
||||
|
||||
- `tests/test_settings_screen.cpp` (`clang++ -std=c++17 -Wall -Wextra -Werror -I.`) walks every page and
|
||||
every row: a new row is checked by the table test as soon as it exists.
|
||||
- `tests/test_settings_view.py` and `tests/test_layout.py` cover the add-on side.
|
||||
- Render it before believing it: `.esphome/readme-render/host_build.py <board> --compile` builds the real
|
||||
firmware for the Mac and `render_settings.py <board>` drives it over the API and saves PNGs of every
|
||||
page, including the hold gesture, the time picker and the settings tile.
|
||||
@@ -0,0 +1,70 @@
|
||||
# Test results app 0.2.52 / firmware 0.2.44 (2026-09-16)
|
||||
|
||||
The settings page on the screen itself, the `screen.settings` card, and "back to page 1 by itself".
|
||||
Firmware and app both change; the protocol grows two optional keys and the stored layouts gain two
|
||||
settings with defaults, so an old screen with the new app and a new screen with an old app both keep
|
||||
working. See CHANGELOG 0.2.52 and docs/SETTINGS.md.
|
||||
|
||||
## Automated
|
||||
|
||||
- Python (`.venv-portal`): 200 tests OK. New `tests/test_settings_page.py` (7 tests): every key the
|
||||
firmware reports back (`changed("...")` in settings_screen.h) is a key the add-on validates; the
|
||||
`settings` block the screen receives still holds exactly its eleven frozen keys while `auto_home`
|
||||
and `auto_home_seconds` ride beside it; the defaults and the refusals of the two new settings;
|
||||
`screen.settings` as a built-in tile that asks for firmware 0.2.44; and both board profiles carrying
|
||||
the page, the hold strip (clear of the Guition's swipe bands), the `open_settings` action and the
|
||||
`go_home` script.
|
||||
- C++ (`clang++ -std=c++17 -Wall -Wextra -Werror -I.`): 14/14 PASS, including new
|
||||
`tests/test_settings_screen.cpp`: the duration ladder and its symmetry, a number stopping at its
|
||||
ends, a time wrapping around midnight and walking whole hours while a key is held, every text
|
||||
("45 sec", "1 h 30", "22:00", "10:00 PM"), the rows reading and writing the real settings (including
|
||||
a lower Brightness pulling the two dim levels down), a row hidden on a board that cannot turn, the
|
||||
table itself (every menu row opens a real page, every control has a reader and a writer), how many
|
||||
rows fit per board and when the pager appears, and that one change stores, applies and reports once.
|
||||
- `generate_packages.py --check` green, `generate_icons.py --check` green (183 glyphs: three new).
|
||||
- `esphome config` green for both board profiles.
|
||||
|
||||
## Builds (ESPHome 2026.6.2)
|
||||
|
||||
Full board profiles compiled for the ESP32, not just the host:
|
||||
|
||||
| Profile | RAM | Flash |
|
||||
|---|---|---|
|
||||
| home-like-2432s028.yaml (CYD) | 23.0% (75,492 B) | 80.6% (1,479,167 B of 1,835,008) |
|
||||
| guition-4848s040.yaml | 24.0% (78,732 B) | 19.4% (1,576,143 B of 8,126,464) |
|
||||
| easy-cyd-device.yaml (package, local components) | 23.0% (75,276 B) | 76.0% (1,395,143 B) |
|
||||
| easy-guition-device.yaml (package, local components) | 24.0% (78,508 B) | 18.4% (1,494,075 B) |
|
||||
|
||||
The two Easy profiles were compiled one after the other, as RELEASING asks, from packages generated
|
||||
out of the board profiles above.
|
||||
|
||||
What the page costs, measured by building the same CYD profile at 0.2.51 (firmware 0.2.43) in a clean
|
||||
worktree: **+608 bytes of RAM** (74,884 -> 75,492) and **+10.7 KB of flash** (1,468,191 -> 1,479,167).
|
||||
The rows are a `constexpr` table in flash and the LVGL objects are built when the page opens and
|
||||
deleted when it closes, like the cards of a tile, so a closed page costs only that handful of bytes.
|
||||
|
||||
The ESP32 build also caught what the Mac could not: on xtensa `int32_t` is `long`, so a row reader
|
||||
that returns a plain `int` does not convert to `Read` at all. Every reader now says `-> int32_t`.
|
||||
|
||||
## Rendered on the real firmware (host build, SDL)
|
||||
|
||||
`.esphome/readme-render/host_build.py <board> --compile` builds the actual firmware for the Mac and
|
||||
`render_settings.py <board>` drives it over its own API. Both boards were rendered and looked at:
|
||||
|
||||
- Every page on both boards: the menu, Brightness, Night, Screen, This screen.
|
||||
- Holding the top bar: the blue line is halfway after 0.9 s of a 1.5 s hold and disappears on release.
|
||||
- The time picker worked like a finger: four taps on `+` moved 22:00 to 23:00 (quarters), then holding
|
||||
walked whole hours to 03:00. Switching the clock to 12 hour turned the same times into 3:00 AM and
|
||||
7:00 AM.
|
||||
- A switch row turning its dependent rows live and grey again (Night mode, Auto standby).
|
||||
- The `screen.settings` card on the overview, and a tap on it opening the menu.
|
||||
- "Back to page 1 by itself": with the last touch aged past the timeout, the open page closed and the
|
||||
overview came back on page 1.
|
||||
|
||||
## Not tested / for the owner
|
||||
|
||||
- Hardware. Nothing was flashed: the hold gesture on real glass (resistive CYD and capacitive Guition),
|
||||
whether 1.5 s feels right, and whether a hold that becomes a swipe cancels cleanly.
|
||||
- The write-back to a running ESP Screens (`esphome.screen_setting` event), which the firmware already
|
||||
used for the Home Assistant number entities and which this page reuses unchanged.
|
||||
- `Restart` from the page, and `esphome.<screen>_open_settings` from Home Assistant.
|
||||
Reference in New Issue
Block a user