Build the screens with ESPHome 2026.9 and keep its build memory in the app's own storage, give the CYD 94 KB more room, and stop a timer from counting one second too many (0.2.89)

The add-on ships ESPHome 2026.9.0: it builds an ESP32 with ESP-IDF itself
and keeps a compiler cache. firmware.py gives the CLI its own storage in
/data (ESPHOME_DATA_DIR /data/esphome, which the ESPHome Device Builder no
longer wipes on start; ESP-IDF and ccache in /data/idf, at most 1 GB;
compilers limited to the core count as PlatformIO ran them), removes the
unused PlatformIO once before the first ESP-IDF build, and leaves the new
folders out of backups.

Firmware 0.2.75: assertion_level SILENT in every board's esp32 block, the
core logs from INFO, and clock_parts() in runtime_model.h replaces sscanf.
timer_left() caps a running timer at its duration (the screen's clock runs
up to a second behind Home Assistant's). The packages keep min_version
2026.6.2, the top-level online_image and OTA passwords for new screens, so
an add-on or Device Builder that isn't updated yet keeps building every
screen; CI now builds both boards with the add-on's ESPHome and with
min_version. ESP-IDF's error names stay on, and build_flags stay under
platformio_options, because ESPHome 2026.9's LVGL reads only those.

tools/check.sh: 478 Python tests, 19/19 C++, editor; --firmware PASS with
ESPHome 2026.6.2 (CYD 87.7 %), 2026.8.1 (88.3 %) and 2026.9.0 (86.4 %,
was 91.5 %). Both bench screens OTA'd and watched for half an hour;
ESP Screens as a local add-on on the Yellow built both profiles
(CYD 1,037 s first build, 41 s unchanged). Details:
docs/TEST_RESULTS_0289.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Max Gramser
2026-09-19 12:35:49 +02:00
parent 51bb2c0ddc
commit 3b15e7f7a3
25 changed files with 542 additions and 41 deletions
+11
View File
@@ -1,3 +1,14 @@
## 0.2.89 (firmware 0.2.75)
ESPHome 2026.9 in ESP Screens, updates that no longer start from scratch without reason, and 94 KB more room on a CYD.
- **ESP Screens builds with ESPHome 2026.9.0** (was 2026.6.2). ESPHome now compiles an ESP32 with Espressif's own ESP-IDF instead of PlatformIO, and keeps what it compiled before in a cache. Before the first update of a screen, ESP Screens downloads ESP-IDF once (about 1.7 GB on disk) and removes PlatformIO, which only the old builds used, with the disk space it took. That first update builds each screen completely, once: on a Home Assistant Yellow 17 minutes for a CYD (about as long as before) and 23 minutes for a Guition, the download included. After it, a screen whose firmware didn't change is done in under a minute.
- **An update no longer starts from scratch because of the ESPHome Device Builder.** ESP Screens kept its memory of each build in the ESPHome folder, which the ESPHome Device Builder app empties every time it starts. The next update of every screen then rebuilt everything, 15 to 18 minutes on a Yellow. That memory now lives in ESP Screens' own storage, with ESP-IDF and the compiler's cache. Backups leave all of it out; it is downloaded or built again when needed.
- **94 KB more room on a CYD.** Its firmware is 1,585,136 bytes, 86.4 % of its update slot (was 91.5 %). That makes room for more languages and later ESPHome versions. Three changes: ESP-IDF's texts for failed internal checks are left out (49 KB; a failed check still restarts the screen), the screen logs from INFO instead of DEBUG (9 KB), and a small piece of our code no longer pulls in C's whole text scanner (9.5 KB). ESPHome 2026.9 itself saves the rest. A Guition's firmware is 110 KB smaller too.
- **Fewer log lines from the screen itself.** The screen's own log now starts at INFO: ESPHome's configuration dump and its debug lines for every state are no longer in the firmware. Nothing changes on the screen or in Home Assistant. To look closer at one screen, put `logger:` with `level: DEBUG` in its **Override YAML** and press **Update**.
- **A running timer never shows more than it lasts.** A 3-second timer could start at 0:04 on its tile. The screen counts down from Home Assistant's end time with its own clock, both in whole seconds, and its clock can be up to a second behind. The countdown now stops at the timer's duration.
- **Nothing to do in a particular order.** The firmware still builds with ESPHome 2026.6.2 or newer, so an ESP Screens that isn't updated yet, or your own ESPHome Device Builder, keeps building every screen. Update ESP Screens, then press **Update** on each screen (or let the nightly round do it): firmware 0.2.75. Includes everything from 0.2.88.
## 0.2.88 (firmware 0.2.74)
Wi-Fi that never dozes, on every screen.
+1 -1
View File
@@ -1,5 +1,5 @@
# Official ESPHome distribution: compiler, ESP-IDF, esptool and OTA CLI included.
FROM ghcr.io/esphome/esphome:2026.6.2
FROM ghcr.io/esphome/esphome:2026.9.0
ARG BUILD_VERSION
ARG BUILD_ARCH
LABEL io.hass.version="${BUILD_VERSION}" io.hass.type="app" io.hass.arch="${BUILD_ARCH}"
+3 -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.74'
FIRMWARE_VERSION = '0.2.75'
# 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.
@@ -1544,6 +1544,8 @@ def installation_yaml(data):
if not isinstance(friendly, str) or not friendly.strip() or len(friendly) > 60:
raise ValueError('Give the screen a recognizable name (60 characters max).')
quote = lambda s: json.dumps(s, ensure_ascii=False)
# An OTA password, not yet `ota: encryption:` with the api key: ESPHome before 2026.9 refuses that, and the owner's
# ESPHome Device Builder may still be older (docs/RELEASING.md, Compatibility 0.2.89).
key, ota, ap = base64.b64encode(secrets.token_bytes(32)).decode(), secrets.token_urlsafe(24), secrets.token_urlsafe(12)
return f'''# Keep this file safe: it contains the unique keys for this screen.
# Wi-Fi comes from the secrets.yaml of ESPHome Device Builder.
+37 -1
View File
@@ -47,6 +47,9 @@ class Firmware:
# flashes on a board. A build writes it next to firmware.bin: under .pioenvs/<node>/ with PlatformIO,
# under build/ with ESPHome's native ESP-IDF toolchain.
FACTORY_IMAGES = ('*/.pioenvs/*/firmware.factory.bin', '*/build/firmware.factory.bin')
# The compiler cache's limit (app 0.2.89+); past it ccache drops the oldest entries. Some seventeen full builds of
# both boards took 0.6 GB on a Mac (docs/TEST_RESULTS_0289.md).
CCACHE_SIZE = '1G'
def __init__(self, root, data):
self.root, self.data = Path(root).resolve(), Path(data)
@@ -356,9 +359,42 @@ class Firmware:
self.task=asyncio.create_task(self.run(profile,action,target))
return dict(self.job)
def build_env(self, profile):
"""The ESPHome CLI's environment: everything it downloads or builds goes in the app's own /data, which an app
update or restart keeps and a backup leaves out (config.yaml backup_exclude), none of it in the ESPHome folder.
- build/<profile>: the screen's build folder.
- esphome: ESPHome's memory of each build (storage/) and what it fetched from GitHub (app 0.2.89+). It used to
be the ESPHome folder's .esphome, which the ESPHome Device Builder app deletes whenever it starts, so every
screen's next update was built from scratch.
- idf: ESP-IDF, its tools and the compiler cache, with which ESPHome 2026.7+ builds an ESP32 instead of
PlatformIO (app 0.2.89+); by default they would go in the container's own cache, gone at every restart.
- platformio: PlatformIO, for anything ESPHome still builds with it.
As many compilers at once as the machine has cores, as PlatformIO ran them: ESP-IDF's ninja would start two
more, and each takes a few hundred MB next to Home Assistant on a small Raspberry Pi (ESPHome's own limit, which
the official ESPHome app sets too)."""
return {**os.environ, 'ESPHOME_BUILD_PATH': str(self.data / 'build' / profile.stem),
'ESPHOME_DATA_DIR': str(self.data / 'esphome'), 'ESPHOME_ESP_IDF_PREFIX': str(self.data / 'idf'),
'CCACHE_MAXSIZE': os.environ.get('CCACHE_MAXSIZE', self.CCACHE_SIZE),
'ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT': os.environ.get('ESPHOME_DEFAULT_COMPILE_PROCESS_LIMIT',
str(os.cpu_count() or 1)),
'PLATFORMIO_CORE_DIR': str(self.data / 'platformio'), 'NO_COLOR': '1'}
async def retire_platformio(self):
"""PlatformIO's ESP32 toolchains and ESP-IDF, with which ESPHome built the screens before 2026.7 (app 0.2.88 and
older), take gigabytes that no screen uses any more. They go once: before the first build with ESPHome's own
ESP-IDF, which creates /data/idf. Anything that still needs PlatformIO downloads its own part again."""
old = self.data / 'platformio'
if old.is_dir() and not (self.data / 'idf').exists():
self.logs.append('Removing PlatformIO from before ESPHome 2026.7: the screens build with ESP-IDF now.')
await asyncio.to_thread(shutil.rmtree, old, True)
async def run(self, profile, action, target):
env={**os.environ, 'PLATFORMIO_CORE_DIR':str(self.data/'platformio'), 'ESPHOME_BUILD_PATH':str(self.data/'build'/profile.stem), 'NO_COLOR':'1'}
env = self.build_env(profile)
try:
if action != 'validate':
await self.retire_platformio()
stages = ['config'] if action=='validate' else ['compile'] + (['upload'] if action=='install' else [])
for stage in stages:
cmd=['esphome']+(['--quiet'] if stage=='config' else [])+[stage,str(profile)]
+4 -2
View File
@@ -1,5 +1,5 @@
name: ESP Screen Manager
version: "0.2.88"
version: "0.2.89"
slug: esp_screen_manager
description: Choose tiles for your CYD and Guition touchscreen, without reflashing.
url: https://github.com/MaxGramser/homeassistant_espscreen
@@ -24,7 +24,9 @@ map:
- homeassistant_config:rw
uart: true
usb: true
# Reproducible compiler/download caches; retain screens.json and user configuration.
# Reproducible compiler/download caches (firmware.py build_env); retain screens.json and user configuration.
backup_exclude:
- '*/build/'
- '*/esphome/'
- '*/idf/'
- '*/platformio/'