Files
Max Gramser c09e4e333f Add the Studio 1 hardware results to the 0.2.77 test report
Docs only, no version change: the add-on update and the OTA to firmware 0.2.64,
the media card with a real Sonos cover, and an alert with a camera over it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 17:23:38 +02:00

113 lines
9.7 KiB
Markdown

# Test results app 0.2.77 / firmware 0.2.64 (2026-09-18)
Max asked for a media card "like Spotify or Apple Music now playing" instead of the plain card with three text keys
and a thin volume slider, with the album cover through the camera-image route, working as the card behind a media
tile, as a tile over the whole page, and in the light and the dark look. Then: the CYD gets the new card without the
picture ("te risky"), a player does not always have a picture, and renders first so he could approve the look.
See CHANGELOG 0.2.77. Built on 0.2.76 (`bdb1f12`).
## What changed
- `components/smart_display/media_card.h` (new): the card's geometry and words, free of LVGL. A tall area (the
Guition's card) stacks cover, title, artist · album, the bar row (elapsed | bar | total on one line), the keys
(previous 52, play or pause 64 on the accent, next) and the volume row. A wide area (the CYD's card, a tile over the
whole page) puts the cover at the left with the column beside it; what does not fit goes in order: the artist line,
the times, then the cover shrinks. `tests/test_media_card.cpp` checks every shape for overlaps and order.
- `runtime_tiles.h`: `render_media_detail` (the card), `render_media_full` (the tile over the whole page, parts in
`w.extra`), `media_action` shared by both (20 play/pause, 21 previous, 22 next, 23 mute, 24 turn on), the card's
key handler pulled out of `detail_button` into `detail_command`, `media_progress` moved by `tick()` once a second,
and the cover: `cover_want`/`cover_ready`/`cover_tick`/`cover_offer` around one `camera_view::Feed` that loads a
link once (`once`, `loaded`), owned by the card or by a tile slot and dropped when the owner goes; a camera full
screen takes the shared image buffer and the cover comes back after. The camera answer parser accepts `t: cover`.
The palette pass leaves the media tile's parts alone (it painted them the card's grey). A player's state falls
back to the screen's own word ("Playing") when Home Assistant sent none.
- One picture at a time (asked by Max after approving the renders: "two images at once, is that too much?"). An
alert already closes every card (`wake_display` runs `close_cards`), so the card's cover is dropped with it. A
media tile over the whole page stays under the alert: `alert_image_due()` holds its cover while the alert's
picture is announced, loading or due for a retry, and an alert picture that arrives while a cover is on its way
(or while a dropped cover's download is still being closed) waits for it (`alert_retry_at`, started by
`camera_tick`). Memory was never the limit: the pictures live in PSRAM (cover ~70 KB, alert 172 KB); the rule
keeps the main loop, where touch waits, to one download.
- `runtime_model.h`: `Extra` carries artist, album, picture mark, duration, position and its moment.
- `camera_view.h`: `open(entity, once)`, `loaded`; an empty answer to a one-time feed stops the asking.
- `theme.h`: `theme::of(lv_color_t)` (the 24-bit value of a drawn colour, for the cover's corner colour).
- `tile_controls.h`: `feature::MEDIA_TURN_ON`.
- App: `core.media_extras` (`x: artist, album, dur, pos, at, pic`), `COVER_MIN_FIRMWARE = 0.2.64`;
`camera_feed.cover_supported/can_show_cover/cover_request/encode_cover`, `CameraFeed.cover()` (fetched when the
picture address changed), `Link.cover`; `server.HomeAssistant.media_picture/media_image`, `Manager.cover_message`,
`answer_camera` serves covers to a Guition on 0.2.64+ for players on its layout.
- Both profiles: firmware 0.2.64. Docs: CHANGELOG, README rows, docs/CAMERA.md "The album cover on the media card".
## Checks
- `tests/test_media_card.cpp` (geometry, progress, clock text, subtitle), `tests/test_camera_view.cpp` (one-time
feed), all 18 C++ suites: pass.
- `tests/test_media_card.py` (14): extras, firmware wiring, cover encoding (corners, square crop, transparency),
fetch-on-change and links, the app's answer through `Manager.answer_camera` (Guition 0.2.64 gets a link, a stream
an empty one, a stranger nothing, older firmware and a CYD nothing). Whole Python suite: 409 + 14, pass.
- Host harness (`.esphome/readme-render/media/media_host.py`, real ESPHome API, virtual finger, the app's own
`answer_camera` behind `/dev/cover`): **Guition 64/64, CYD 42/42.** Covered: the card of a playing player (title,
artist · album, elapsed from the reported position and running on, keys enabled, slider at 32 %, bar track and
fill), the cover asked for with size and page colour and loaded once over the placeholder, pause → `media_play_pause`
with the keys faded during the wait and back after, next, mute (`is_volume_muted: true`), the slider drag →
`volume_set` ≈ 0.8, closing drops the cover; a stream (no times, no cover request); a paused muted TV with few
features (play key, muted key, "Muted", next and mute faded, previous not, a still bar, its cover); an off player
("Off", no times); dark mode (dark keys, the cover asked again with the dark page colour); a tile over the whole
page (track beside the cover, keys, slider, "48 %", times, cover at the tile's size and colour, the bar running,
the tile's pause key acting on the study speaker without opening the card, the tile's slider → `volume_set` ≈ 0.2,
a new picture in Home Assistant → new mark → the tile asks again and loads the new cover, holding the tile opens the
card which takes the cover at its own size, closing gives it back to the tile); a stream over the page (no times,
no cover, the study cover dropped); an idle player ("Not playing"); a wide media tile with a volume control keeps
its panel; no errors in the firmware log. The CYD asks for no cover anywhere.
Alerts with a camera (Guition): an alert closes the open media card and drops its cover, and its picture loads
alone; under a full-page media tile whose cover is not loaded yet, the cover waits until the alert's picture is
shown and loads after it; a cover on its way (a link that answers after 1.5 s) finishes first and the alert's
picture ("waits for the cover") follows. The firmware log shows the downloads one after the other in all three.
- Renders (`render_media.py`, both boards, light and dark): card of a playing player, a stream, a paused TV, an off
player, tiles over the page (study, radio, idle). Sent to Max for approval.
- ESP32 builds of both Easy Setup profiles: see the sizes below.
- Hardware: Studio 1 (Guition, this network's Home Assistant) — see below.
## Sizes
Both Easy Setup profiles compile with ESPHome 2026.6.2, no warnings (see the pitfalls for this Mac's Python).
| Build | Flash | RAM |
|---|---|---|
| Guition (easy-guition-device) | 1,971,975 B, 24.3 % of 8 MB | 76,852 B, 23.5 % |
| CYD (easy-cyd-device) | 1,582,183 B, 86.2 % of the 1.835 MB update slot | 71,276 B, 21.8 % |
## Hardware (Studio 1, Guition on the studio's Home Assistant, 2026-09-18 17:20)
- Released as `079ffcf`, tag `screens-v0.2.77`. The add-on on the studio's Home Assistant went 0.2.75 → 0.2.77 from the
store; Studio 1 got firmware 0.2.64 over OTA, built from GitHub main (so the published package and its fonts and
components resolve; both packages also pass `esphome config` from an empty folder). It came back on the same address,
Home Assistant reports firmware 0.2.64 and the tile settings read *Synced*.
- Two Sonos tiles were added through the `esp_screens_add_tile` event: `media_player.office_rukbunker` as a full-page
tile on page 4 and `media_player.keuken_bar` as a single tile on page 5 (the Sonos group plays in the studio).
- The card of the Keuken Bar tile (opened with `preview_runtime_card`): the app answered one cover request (the screen
asked a second time after 10 s, its normal retry, because the first event arrived while the app reconnected), the
real Spotify cover (69 KB BMP) loaded in one download and showed with the title, the artist, the running bar, the
keys and the muted volume row.
- Media card open with its cover, then `esp_screens_show_alert` with `camera.amcrest_camera`: the card closed, the
alert's picture (182 KB) loaded alone and showed 2.2 s after the event; the alert timed out by itself.
- Heap after these tests: 96 KB free, 55 KB largest block, lowest free 91 KB (0.2.63 before the update: 86 / 32 / 61 KB).
- Not tested on hardware: the full-page tile (a page change needs a finger) and the keys and slider on a real player
(they play music in the studio): Max checks those himself.
## Pitfalls
- `render_time` on a host build freezes the clock, and the progress bar with it: the harness shifts the demo's
report moments to now instead.
- LVGL's `LV_LABEL_LONG_DOT` writes the dots into the label's text: a probe sees "Miles Davis · Kin...".
- `tick()` re-enables every object in `detail_actions` after a wait: a key the player lacks must stay out of it.
- The tile palette pass paints every custom part; a mode that paints its own parts must be skipped there.
- Homebrew's python@3.14.7 on this Mac has a broken `pyexpat` (it loads macOS's `/usr/lib/libexpat.1.dylib`, which
lacks `_XML_SetAllocTrackerActivationThreshold`), so `platform.mac_ver()` returns '' and pip's truststore and `uv`
refuse the interpreter. The pioarduino platform then cannot rebuild `~/.platformio/penv` ("Failed to install Python
dependencies into penv"). Setting `DYLD_LIBRARY_PATH=/opt/homebrew/opt/expat/lib` is not enough, because
pioarduino starts `uv` without it. What works: ESPHome 2026.6.2 in a venv on PlatformIO's own bundled Python 3.11
(`~/.platformio/python3`), where `mac_ver()` is fine. Every session must build with that same Python: pioarduino
recreates the penv whenever the Python version changes, so a build on another Python breaks the next one elsewhere.
`brew reinstall python@3.14` (or expat) is the real fix. Never run two ESPHome builds at once.