Fix what a full test round of today's releases found: a ticking single clock, a switch that works in standby, no taps from a swipe (0.2.78)

A test and code-analysis round of 0.2.72 to 0.2.76 (suites at every tag, sanitizers, ESP32 and GitHub builds, host
harnesses on both boards, the editor in a browser, best practice checked against ESPHome, LVGL, Home Assistant and
Vue) found these, fixed here with Max's choices for the product questions:

- Firmware 0.2.65: the second hand of a single analog clock moves again; on a page that is one full on/off tile the
  waking push also switches it; on the Guition a flick is never a tap and a finger that slides off a tile doesn't tap
  it; Previous/Next react to a firm press; the same Go to page tile may sit on several pages; a layout the screen has
  no memory for is refused instead of crashing; 64-bit redraw mask for 48 tiles; Montserrat 14 no longer built.
- App: Save refuses a layout too long for one message or too big for eight pages; an offline screen keeps its
  firmware version from the device registry; HA refusals reach the editor; 128 KB requests; changelog out of the
  live stream; cached hashed assets; tini as init and a clean stop; changelog parsing without asterisks or crashes.
- Editor: clicking the open screen keeps unsaved edits, edits during a save stay unsaved, a Page row and phone
  dragging to other pages, repeated page tiles, limits from the server, HA's error messages, a type-checked build.
- tools/check.sh + GitHub Actions + a CYD flash budget; docs match 48 tiles and the new editor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-18 17:25:23 +02:00
parent c09e4e333f
commit 1a6308aba7
80 changed files with 2623 additions and 320 deletions
+23 -21
View File
@@ -38,9 +38,9 @@ Use the GitHub version for updates; a local test add-on is a separate app.
1. Connect the screen with a **USB data cable** to the machine running Home
Assistant. With multiple boards: connect them one at a time for the first
installation, or check which port belongs to this screen.
2. Open **Settings** at the top right of ESP Screens and click **New screen**. Choose CYD or Guition and give the screen a name,
for example `Kitchen`. The device name (`kitchen`) follows from that; use
**customize** to choose a different one.
2. Click **New screen** in the sidebar of ESP Screens, under your screens. Choose CYD
or Guition and give the screen a name, for example `Kitchen`. The device name
(`kitchen`) follows from that; use **customize** to choose a different one.
3. Wi-Fi: if `wifi_ssid` and `wifi_password` are already in the ESPHome `secrets.yaml`,
the screen uses them automatically. If they're missing, or the file doesn't
exist yet, the window asks for them once and ESP Screens only adds the
@@ -93,7 +93,7 @@ USB, see [CALIBRATING.md](CALIBRATING.md).
## 3. Pair the screen with Home Assistant
This happens in Home Assistant itself, outside ESP Screens. As long as a profile
hasn't been added to Home Assistant yet, it appears on the left under **My screens**
hasn't been added to Home Assistant yet, it appears in the sidebar under **Screens**
as a *not yet in Home Assistant* card, with an **Open Devices & services**
button and **Copy API key**; the done screen of **New screen** has the same
button. The card disappears once the screen is in the list.
@@ -115,8 +115,10 @@ Tip: in your ESPHome YAML, add the line `power_save_mode: none` under `wifi:` (n
wizard YAMLs already have it); the screen is mains-powered, so this keeps it responding to
Home Assistant without Wi-Fi sleep delay.
Select your screen, fill in the title, and search for entities. The picker has
domain filters with colored icons. You can add up to twenty tiles.
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
colored icons. You can add up to 48 tiles, one for every slot
(firmware 0.2.62+; see below for older firmware).
The screen preview shows their placement: two columns, six slots per page,
up to eight pages. Every tile has a fixed slot that only changes if
you drag it; empty slots stay empty, wherever you leave them. Drag a tile
@@ -125,27 +127,27 @@ swap (the other tile takes the freed-up slot, or otherwise the nearest free
slot); everything else stays put. While dragging, the preview already shows where
everything will land; an empty page then stands ready below the last page. **Add
page** creates an empty page that's kept; an empty page gets
**Remove page**. Click an empty slot to place the next tile from the picker
**Remove page**. Click an empty slot to place the next tile from the library
there. Use the arrow keys to move a focused tile.
Click a tile for a custom name and **Configure control & display**:
click behavior, a mini-slider, a large value, a graph (sensors), a
weather forecast (weather), or the **Double-width** setting. A double-width tile for
a climate, switch, light, fan, vacuum, cover, media player,
number, select, timer, scene, script, or button gets **direct
Click a tile and its settings open in a drawer on the right, with the preview
still in view: a custom name, click behavior, a mini-slider, a large value, a graph
(sensors), a weather forecast (weather), or the size: **Double-width** or **Full
page** (firmware 0.2.62+). A double-width tile for a climate, switch, light, fan,
vacuum, cover, media player, number, select, timer, scene, script, or button gets **direct
control** on the right, like the rows in Home Assistant (for example temperature − / +,
open/stop/close, volume with mute, a toggle); under **Direct control
on the tile**, choose which set, or **None** (firmware 0.2.19+). **Open control** on
a weather tile shows the weather card with the coming hours and days (rain included);
on a climate tile, the card with an on/off button and the mode, fan, and
swing settings. The built-in **Clock**
sits at the top of the picker; find sun, timers, and people via the filters. Under
sits at the top of the library; find sun, timers, and people via the filters. Under
**Pastel background**, choose a custom color with dark text; **Default** restores
the normal look, and **None** drops the card, so the content sits the same size
directly on the screen background (firmware 0.2.16+). This requires firmware 0.2.10+.
Adding without a chosen slot fills the first free slot. Fixed slots and empty
slots work on the screen from firmware 0.2.26 on; older firmware shifts the
tiles up to the first free slot, and the editor notes that below the preview.
The preview shows the layout, not live sensor values.
The preview shows the values Home Assistant reports right now (app 0.2.73+).
Click **Save & send** to send your changes.
- Light, switch, input_boolean, and fan: tap to turn on/off.
@@ -164,11 +166,11 @@ Click **Save & send** to send your changes.
shows 1, 6, or 24 hours.
- Select/input_select: open the picker menu.
From firmware 0.2.7, twenty tiles fit across up to four pages. Older
firmware keeps the limit of ten until you update. Under **General settings
→ Swipe between pages**, you can enable swiping. On the Guition (firmware
0.2.24+), you then swipe inward from the left or right edge, like the
back-swipe gesture on a phone; slow or fast, and a swipe starting in the
From firmware 0.2.62, 48 tiles fit across up to eight pages. Firmware 0.2.7 to
0.2.61 keeps the limit of twenty (four pages) and older firmware ten, until you
update. In the **Screen settings** tab, **Swipe between pages** turns on swiping.
On the Guition (firmware 0.2.24+), you then swipe inward from the left or right
edge, like the back-swipe gesture on a phone; slow or fast, and a swipe starting in the
middle of the screen does nothing, so tapping and dragging tiles never
accidentally changes pages. On the CYD, it stays a quick swipe across the screen. Sliders
only control their value; detail menus and standby don't change pages.
@@ -263,8 +265,8 @@ HA mechanisms used: [Ingress](https://developers.home-assistant.io/docs/apps/pre
Update ESP Screen Manager to 0.1.2 and install the new firmware once
via your **existing** ESPHome device → Install → Wirelessly. Keep your own YAML
with Wi-Fi and keys. Then open the screen in ESP Screen Manager and expand
**Screen settings**. After adjusting, click **Save & send**.
with Wi-Fi and keys. Then open the screen in ESP Screen Manager and its
**Screen settings** tab. A change there applies at once; there is nothing to save.
After this, changes to these settings don't need a new firmware flash.
| Setting | Options | Default |
+11 -10
View File
@@ -27,10 +27,10 @@ carry over `calibration.yaml` or the XPT2046 correction.
### Mounting and screen rotation (runtime 0.2.9+)
Install Guition firmware 0.2.9 and ESP Screen Manager 0.2.9. Open the screen
in the management page, go to **Screen settings**, and choose 0°, 90°, 180°,
or 270° (clockwise) under **Rotate screen**. Click save. After that, a
firmware flash is no longer needed to change the angle. The setting is kept
in the app data and on the screen after a restart.
in the management page, open the **Screen settings** tab, and choose 0°, 90°,
180°, or 270° (clockwise) under **Rotation**. It applies at once; there is
nothing to save. After that, a firmware flash is no longer needed to change the
angle. The setting is kept in the app data and on the screen after a restart.
This uses native ESPHome/LVGL rotation for both display and touch, with no changes
to the panel initialization or GT911 mirroring. After mounting, physically check the
@@ -39,18 +39,19 @@ only visible for the Guition once its new firmware has been discovered by HA.
The CYD stays on its existing orientation with its own calibration.
Six tiles of **218 × 108 pixels** per page, 12px spacing, and a
separate navigation strip. In ESP Screen Manager, twenty tiles fit across up to
four pages; navigation disappears at six or fewer. The current interface has a
light gray background, white cards, and colored domain icons. From 0.2.10, you
can choose a pastel background with dark text per tile. Standby starts
after ten minutes without touch by default and is adjustable in the management page.
separate navigation strip. In ESP Screen Manager, up to 48 tiles fit across up to
eight pages (firmware 0.2.62+; twenty over four pages before); navigation disappears
at six or fewer. The current interface has a light gray background, white cards,
and colored domain icons. From 0.2.10, you can choose a pastel background with
dark text per tile. Standby starts after ten minutes without touch by default and
is adjustable in the management page.
The backlight dims through ESPHome's own light transition (1.5 s to standby, 80 ms to
wake). Firmware 0.2.13-0.2.56 used the LEDC hardware fader (`backlight_fade.h`) instead;
0.2.57 went back to ESPHome's transition, because that fader reached into ESPHome's LEDC
output, which ESPHome 2026.8 no longer allows.
The existing tile actions, climate control, and vacuum card are preserved.
Every card type works in all twenty positions.
Every card type works in all 48 positions.
## New installation
+78 -9
View File
@@ -23,16 +23,42 @@
The editor is the Vue app in `web/`: after a change under `web/src`, run
`cd web && npm ci && npm test && npm run check && npm run build` and commit
`screen_manager/app/static` with it (that folder is the build output; never edit it by hand).
2. Run all Python tests with aiohttp installed, all `tests/*.cpp`, the
generator with `--check`, and compile both Easy Setup profiles. Do that
sequentially: profiles with the same `DEVICE_NAME` share one build folder, and a
parallel build can make an upload pick the wrong `firmware.bin`. Check that no
secrets are in Git.
2. Run `tools/check.sh` (with `PYTHON=.venv-portal/bin/python` on a development machine). It runs all
Python tests with aiohttp, PyYAML, Pillow and fontTools installed, every `tests/*.cpp` with
`clang++ -std=c++17 -Wall -Wextra -Werror -I.`, both generators with `--check`, and the editor's
`npm ci`, `npm test`, `npm run check` and `npm run build`, and fails when that fresh build differs from the
`screen_manager/app/static` in Git (committed or staged). For a firmware change, `tools/check.sh --firmware`
compiles both board profiles with placeholder secrets from a temporary folder (never the real `secrets.yaml`) and
applies the flash budget below; `--all` does both. CI (`.github/workflows/ci.yml`) runs the same script on every
push and pull request to main. Compile sequentially: profiles with the same `DEVICE_NAME` share one build folder,
and a parallel build can make an upload pick the wrong `firmware.bin` (the check builds are called `check-cyd` and
`check-guition` and build under `.esphome/check`, apart from the bench profiles). Check that no secrets are in Git.
**Flash budget of the CYD** (Max's rule, app 0.2.78). The CYD has 4 MB of flash and two update slots of
1,835,008 bytes; the Guition's 16 MB leave it far from any limit. Measure the CYD on the build users get: the YAML
`core.installation_yaml()` writes has the Wi-Fi fallback access point (`wifi: ap:`) and `captive_portal:`, about
84 KB more than a profile without them. The board profiles carry both, so `tools/check.sh --firmware` measures
that shape; it reads the slot from the build's `partitions.csv` (`app0`, `ota_0`) and the image from
`firmware.ota.bin`, and `--baseline <bytes>` prints the delta against the last release. The check-profile tables
in earlier test results left both out and read about 84 KB low (0.2.72: 1,453,647 bytes, 79.2 %, where the
user-shaped build was 1,538,032 bytes, 83.8 %). Build with the add-on's pinned ESPHome (`screen_manager/Dockerfile`)
and, when the ESPHome Device Builder ships a newer ESPHome, with that one too (`ESPHOME=<its esphome command>`),
because users build their updates there.
| CYD image, share of 1,835,008 bytes | Rule |
|---|---|
| up to 90 % | normal |
| 90-93 % | tight: every release states its flash delta; a delta over 8 KB needs a matching saving or Max's OK |
| 93-95 % | only fixes ship |
| over 95 % | never: that keeps about 90 KB for ESPHome upgrades and users' own overrides |
3. Test app start, saving, restarting/updating with existing layouts,
reconnecting to HA, and an ESP restart. Test a new card on real
hardware. A good build doesn't replace physical touch acceptance.
4. Bump the app version and firmware project version; write the CHANGELOG and concrete
test results. Only publish compatible changes directly to main.
test results. Only publish compatible changes directly to main. `tests/test_release_lint.py`
(part of `tools/check.sh`) holds `config.yaml`'s version, the first CHANGELOG heading and its
firmware against `FIRMWARE_VERSION`, keeps the CHANGELOG headings unique and newest first, and checks
that every `fonts/...` file the packages fetch from GitHub is in the tree.
5. Commit and push main (the only release branch). Create an immutable tag
`screens-vX.Y.Z` from the same commit. Test the remote YAML in an empty folder:
all components/fonts must be fetchable via GitHub.
@@ -52,7 +78,7 @@ For backend tests on a development machine:
```sh
python3 -m venv .venv-portal
.venv-portal/bin/pip install aiohttp PyYAML
.venv-portal/bin/pip install aiohttp PyYAML Pillow fonttools
.venv-portal/bin/python -m unittest discover -s tests
```
@@ -189,6 +215,34 @@ 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.78 / firmware 0.2.65
Repeated navigation tiles: the same `screen.page_<n>` may sit on several pages of one screen. Firmware 0.2.65 accepts
repeats of `screen.page_*` only (`Model::set_layout`, `page_entity()`); every Home Assistant entity still appears once.
The app sends a repeat only to 0.2.65+ (`PAGE_TILE_REPEAT_MIN_FIRMWARE` in `min_firmware`); an older screen gets
"Install screen firmware 0.2.65 or newer first". Tile events from Claude in HA pick a copy by `slot`/`page` (move:
`from_slot`/`from_page`); without one they act on the first copy in slot order, and the answer names its page and slot.
- Firmware: `dirty_tiles` is 64 bits (`tile_bit`, `mark_tile`, `static_assert(MAX_TILES <= 64)`). `set_layout` refuses a
layout whose tile list does not fit the largest free block (`tile_room`, ESP32 only) and reports it through
`Model::refusal`; `TileAllocator` logs and aborts on a null block instead of writing through it. The second hand
moves on `analog` and `calendar` dials while part 18 is an `lv_line` (`lv_obj_check_type`). Guition: an
`LV_EVENT_GESTURE` handler on `home_page` consumes the contact (no page flip; LVGL needs speed and 50 px), and
`runtime_tiles::event` drops a tap or hold released outside the tile (`lv_obj_hit_test`); LVGL's `PRESS_LOCK` stays so
a finger that slides on to a slider never drags it. `dim_wake_overlay` calls `runtime_tiles::wake_tap()` after
`wake_display`: on a page that is one full tile whose tap is `<domain>.toggle`, the waking push also taps it. Page
bars (tile pages and the settings page) react to `on_click`/`LV_EVENT_CLICKED`. `lvgl: default_font` is a Roboto font
of each board, so ESPHome no longer builds Montserrat 14 (CYD -13.9 KB).
- App: `Manager.save` encodes the real layout message and refuses one over 4096 bytes; `core.packed_slots` refuses tiles
that don't fit eight pages. `discover_screens` adds `firmware_known`: the "Screen firmware" sensor when it holds a
strict X.Y.Z, else the device registry's `sw_version` in the form `X.Y.Z (ESPHome …)`. Every screen in the inventory
carries `firmware_known`, `tile_limit`, `full_page` and `page_tiles_repeat`; the changelog moved from the update
summary (and the live stream) to the full inventory's top-level `changelog`. HA refusals answer 400 and an unreachable
HA 503, both JSON `{"error"}`. `client_max_size` 128 KB. Hashed `/assets/` files get `private, max-age=31536000,
immutable`; the page and `/api` keep `no-store`. `config.yaml` drops `init: false` (tini is PID 1 again) and the server
uses aiohttp's `handle_signals`.
- Editor: `npm run build` type-checks first (`vue-tsc --noEmit`), `devEngines` pins Node. Storage unchanged.
### Compatibility 0.2.74 / firmware 0.2.62
Forty-eight tiles per screen, a tile over the whole page (`size: full`) and navigation tiles (`screen.page_1` to
@@ -196,6 +250,11 @@ Forty-eight tiles per screen, a tile over the whole page (`size: full`) and navi
gets "Install screen firmware 0.2.62 or newer first". Older apps keep working with this firmware: they never send
`full` or a page tile, and the firmware still packs `wide` as before.
Downgrade: an app older than 0.2.74 stops at startup on a stored layout with more than 20 tiles or a page tile
(`validate_layout` raises "Choose at most 20 tiles." or "This entity isn't supported." even with `stored=True`);
`screens.json` stays untouched. Before going back, trim the layout to 20 normal tiles, or restore the app backup (the
Supervisor restores the version and its data together).
- Firmware: `MAX_TILES` is `MAX_SLOTS` (48). `Model::tiles` is a `std::vector` sized to the layout (a `TileAllocator`
over ESPHome's `RAMAllocator`, so a Guition keeps the list in PSRAM; the CYD's falls back to its heap); nothing may
index `tiles` beyond `count`. A `Tile` has `full` next to `wide` (a full tile is also wide) and `cells()`; `pack()`
@@ -227,6 +286,9 @@ storage or editor change, and an older app drives this firmware as before.
the Guition), a contact that moved, a contact that already counted, and a tap within 150 ms of the last accepted
tap on the same button (bounce). `show_page` already drops a fill still under way when the next page is asked for,
so nothing else changed. `tests/test_cyd_ui.cpp` covers the page buttons.
- On the CYD a lift-off bounce is stopped first by the XPT2046 filter (`components/xpt2046/touchscreen/touch_filter.h`:
three matching samples start a contact, two empty samples release it, one empty sample is ignored); the 40 ms
minimum and the 150 ms same-button gap of `accept_repeat` are backup nets behind it (noted 2026-09-18).
- The settings page's own pager (`settings_screen.h`) never used the guard and is unchanged. Tile taps, cards, keys,
chips and sliders keep `accept()` and its window.
@@ -1138,8 +1200,15 @@ rotation mapping, `update()`, `end()`); the indev callback is gone. Otherwise un
### Compatibility 0.2.33 / firmware 0.2.28
Firmware only. `TouchGuard::configure(0, ...)` disables the movement limit
(`moved_` is then never set; LVGL's press-lost decides whether a tap goes through). The
Guition profile sets `TOUCH_MOVE_LIMIT_PX` to 0, the CYD keeps 56. Otherwise unchanged.
(`moved_` is then never set). The Guition profile sets `TOUCH_MOVE_LIMIT_PX` to 0, the
CYD keeps 56. Otherwise unchanged.
Correction (2026-09-18): this note said LVGL's press-lost then decides whether a tap goes
through. It doesn't: a tile keeps LVGL's `LV_OBJ_FLAG_PRESS_LOCK` (on by default), so a
finger that slides off never sends `LV_EVENT_PRESS_LOST`, and on the Guition a release
outside the tile still counted as a tap. From firmware 0.2.65 a tile without a movement limit
checks it itself (`runtime_tiles::event`): a tap or hold whose finger lets go outside the tile
does nothing. PRESS_LOCK stays on.
### Compatibility 0.2.32 / firmware 0.2.27
+11 -5
View File
@@ -1,15 +1,20 @@
# Testresultaten app 0.2.33 / firmware 0.2.28 (2026-09-14)
Alleen firmware: op de Guition beslist LVGL of een tik doorgaat (loslaten binnen de tegel
telt), de verplaatsingsgrens van de tap-guard staat daar uit. Max koos dit na een live
geweigerde druk van 73 px op Studio 1.
Alleen firmware: op de Guition zou LVGL beslissen of een tik doorgaat (loslaten binnen de
tegel telt; zie de correctie hieronder), de verplaatsingsgrens van de tap-guard staat daar
uit. Max koos dit na een live geweigerde druk van 73 px op Studio 1.
## Wat veranderde
- `TouchGuard::configure(move_limit_px, ...)` met 0 betekent geen grens: `moved_` wordt nooit
gezet, `accept()` en `accept_repeat()` kijken alleen nog naar duur, dubbel gebruik en
dender. LVGL's press-lost (vinger verlaat het object) bepaalt of de klik doorgaat.
dender. LVGL's press-lost (vinger verlaat het object) zou bepalen of de klik doorgaat.
- Guition-profiel `TOUCH_MOVE_LIMIT_PX: "0"`; CYD blijft 56 (resistief paneel springt).
- Correctie (2026-09-18): LVGL beslist dat niet. Een tegel houdt LVGL's `LV_OBJ_FLAG_PRESS_LOCK`
(standaard aan), dus een vinger die van de tegel schuift stuurt nooit `LV_EVENT_PRESS_LOST`, en
loslaten buiten de tegel telde op de Guition nog steeds als tik. Vanaf firmware 0.2.65 controleert
de tegel dat zelf (`runtime_tiles::event`): een tik of vasthouden waarbij de vinger buiten de tegel
loslaat, doet niets. PRESS_LOCK blijft aan.
## Geautomatiseerd
@@ -24,6 +29,7 @@ geweigerde druk van 73 px op Studio 1.
## Nog te controleren op Studio 1
- Een stevige, schuivende druk op een tegel voert de actie uit zolang je binnen de tegel
loslaat; over de rand van de tegel loslaten doet niets (LVGL press-lost).
loslaat; over de rand van de tegel loslaten doet niets (zo bedoeld; waar vanaf firmware 0.2.65,
zie de correctie hierboven).
- De randveeg (0.2.27) na een update zonder eerst een kaart te sluiten, en de eerste veeg
na een paginawissel; het log meldt `randveeg niet gevuurd: …` als hij te kort was.
+5 -2
View File
@@ -72,5 +72,8 @@ the sizes differ from the bench profiles by the length of those strings; the cod
- Real hardware: neither bench screen ran this firmware before the release. Max's own screens get it through Update;
the tap that started this is the one to try there, on the Guition and on the CYD.
- A resistive panel's lift-off bounce on the CYD is covered by the 40 ms minimum contact and the 150 ms gap, the same
net the -/+ keys have had since 0.2.25 (physically tested then), not by a new measurement.
- A resistive panel's lift-off bounce on the CYD, not measured again. Correction (2026-09-18, touch analysis): the
first net for that bounce is the XPT2046 filter (`components/xpt2046/touchscreen/touch_filter.h`), which starts a
contact only after three matching samples, ignores one empty sample and releases after two, so a bounce never
reaches LVGL as a new tap. `accept_repeat`'s 40 ms minimum contact and 150 ms same-button gap are backup nets
behind it, the same the -/+ keys have had since 0.2.25 (physically tested then).
+5 -2
View File
@@ -24,7 +24,8 @@ CHANGELOG 0.2.74 and the compatibility note in docs/RELEASING.md. Built on 0.2.7
- Python (`.venv-portal`): 396 tests OK, including the new `tests/test_full_page_tiles.py` (sizes and footprints,
in-order packing, free slots, validation of full and page tiles, the firmware they need, the layout sensor, the wire
form of built-in tiles, tile events for Claude in Home Assistant, the 48 limit).
form of built-in tiles, tile events for Claude in Home Assistant, the 48 limit). Correction: the committed 0.2.74
runs 409 tests; 396 was counted on the branch before the rebase onto the Vue editor.
- C++ (`c++ -std=c++17 -Wall -Wextra -pedantic`): 17/17 PASS. `tests/test_runtime_model.cpp` now covers 48 tiles, a
list sized to the layout, `screen.page_<n>` validation and page targets, and full tiles in `pack()` and `place()`
(a page of their own, snapping to the page start, the page count).
@@ -77,7 +78,9 @@ the harness follows each board's demo layout). What the checks cover:
Both bench boards were OTA-flashed with firmware 0.2.62 from the Mac (`esphome upload easy-<board>-device.yaml
--device <ip>`), the Guition first, and report 0.2.62 in Home Assistant. Heap right after boot (HA sensors): Guition
89 KB free / 60 KB minimum / 45 KB largest block (0.2.59: 76 / 57 / 33), CYD 116 KB / 107 KB / 59 KB (0.2.59: 106 /
96 / 51).
96 / 51). Correction: the after-boot figures are not the steady state. With 48 populated tiles the CYD has about
97 KB free, a 49 KB largest block and a 78.6 KB minimum; a 13-tile steady state is about 98 KB free / 49 KB largest
block, so compare against those, not against 116 / 59.
- `hw_push.py <scene> [cyd]` pushes one full-page scene straight to a board's `screen_message` action and, on the
Guition, snapshots the LVGL render through `ui_snapshot`: the big button (lit amber, in Max's dark look a brown
+5 -2
View File
@@ -33,8 +33,11 @@ Measured by compiling the same profile twice at the same commit (0.2.72, `9f5694
| 4 bpp (this release) | 1 632 736 bytes | 89.0 % |
| difference | +94 704 bytes | +5.2 points |
On 0.2.74 (`bb25353`, which added the 40 px full-page font and the 48-tile code) the release build is 1 655 568 bytes,
90.2 % of the slot, static RAM 21.7 % (71 188 of 327 680 bytes); about 180 KB stay free for later rounds.
Correction (after the release): that table was measured at 0.2.72, before 0.2.74's 40 px full-page icon font, which
also went to 4 bpp. The released CYD build of this commit (`261fa82`) is 1 655 584 bytes, 90.2 % of the 1 835 008-byte
slot: +107 744 bytes against 0.2.74 (1 547 840 bytes, 84.4 %), matching ESPHome's own glyph count (+107 756 bytes).
So the release costs 108 KB, not 95 KB, and the slot is 90 % full, not 89 %. Static RAM 21.7 % (71 188 of 327 680
bytes); about 180 KB stay free for later rounds.
The glyph bitmaps alone grow by 108 KB (309 icons at three sizes plus the big font, counted with freetype); the
linker's alignment takes a little of that back. Fonts live in flash, so RAM does not move (24.1 % static in both
+59
View File
@@ -0,0 +1,59 @@
# Test results app 0.2.78 / firmware 0.2.65 (2026-09-18)
Max: "ga eens je ultra best doen om de commits van vandaag lokaal 100% te testen", then "gedegen code analyse en check op
internet of dit best practise is", and after the report "can you address them?". This release fixes what that test round
of 0.2.72 to 0.2.76 found, with Max's choices for the product questions: a swipe is never a tap, the waking push also
switches a full-page switch, and the same page tile may sit on several pages. See CHANGELOG 0.2.78 and the compatibility
note in docs/RELEASING.md.
## The test round of 0.2.72 to 0.2.76 (before the fixes)
- Python suite at every release point: 386 (2cb3479, 9f5694b), 398 (1ed42dc), 409 (bb25353, 261fa82), 0 skips, no
order dependence over 43 runs (reverse, shuffled, `-X dev`). Only the older test_camera.Feed timing flake failed.
- C++: 17/17 at every tag with the project's flags, ASan + UBSan, libc++ hardening, g++ 16 with `_GLIBCXX_DEBUG`, plus
594 new runtime_model checks, 20,022 comparisons with the add-on's packing and 200,000 fuzz rounds, all clean.
- Editor: Vitest 46/46, vue-tsc clean, and the committed bundle rebuilt byte for byte at 1ed42dc, bb25353 and 261fa82.
- ESP32 builds of both boards, also fetched from GitHub the way users get them; the add-on image built and started.
- Host harnesses: full page 37/37 per board, page buttons 12/12 per board (the 0.2.71 rule fails 3/12 and 4/12, as it
should), wake/sleep, settings, cover and history on the Guition without regressions; 4 bpp icons measured (2 grey
levels became 15 to 16).
- Found and fixed here: the frozen second hand of a single analog clock (since 0.2.74), unsaved edits lost when clicking
the open screen, dragging to page 2+ on a phone, Identify and Try it answering 500, a 48-tile layout too long for one
message saving anyway, packing past slot 47, an offline screen counted as very old firmware, the 32-bit redraw mask,
a crash instead of a refusal when memory runs out, a firm press on Previous/Next doing nothing, python as PID 1
(every stop took 10 s), the 16 KB request limit, asterisks in What's new, docs that still said twenty tiles.
## Automated (this release)
- `tools/check.sh` (new; the same script runs in GitHub Actions), on this release rebased onto 0.2.77 (the media card):
Python 454 tests OK, C++ 18/18 with `-Wall -Wextra -Werror`, packages and icons current, Vitest green, vue-tsc clean, build, and the committed editor bundle
equal to a fresh build.
- `tools/check.sh --firmware` with ESPHome 2026.6.2, on copies of both board profiles with the fallback access point and
captive portal users get:
| Board | firmware.bin | of the app slot | vs 0.2.75 |
|---|---|---|---|
| CYD | 1,652,384 B | 90.0 % of 1,835,008 B (183 KB free) | -3,200 B (Montserrat 14 no longer built: -13,936 B; the 0.2.77 media card adds the rest) |
| Guition | 2,026,608 B | 24.9 % of 8,126,464 B | |
The CYD sits at the edge of the new budget's "tight" band (90-93 %): the next release states its flash delta.
## Firmware on the Mac (host builds, virtual finger)
- Full-page harness (`fullpage_host.py`, 37 checks: taps, holds, drags, keys, page jumps, 48 tiles, six forecast/clock
flips), on the fixes before the rebase onto 0.2.77: Guition 37/37, CYD 37/37. The CYD's first run gave 36/37: the
harness repeats a tap when the host loop stretches the contact past 350 ms, and the firmware had already counted the
first one (two light.toggle calls 0.7 s apart); the rerun without that stall passed 37/37.
## Not tested
- On real hardware: nothing of this release has run on a bench screen yet. To check on the glass: a single analog clock
ticks; a flick across a full-page light on the Guition does nothing while a tap and a hold still work; one push on a
dimmed screen with a full-page light switches it; a firm press on Next turns the page.
- Unit tests cover the repeated page tiles (tests/test_runtime_model.cpp and the Python suite), the memory refusal (a
stand-in for the free block size on the host), a consumed contact after a flick and the 300 ms page-bar case. The
gesture handler, the slide-off check and the waking push run inside LVGL and have code review only, no host harness
run with a finger.
- Build machine note: Homebrew's Python 3.14.7 cannot load pyexpat on macOS 26.2 (system libexpat lacks
`XML_SetAllocTrackerActivationThreshold`), which breaks PlatformIO's penv; the builds here ran with ESPHome 2026.6.2 in a
Python 3.11 venv.