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
+1 -11
View File
@@ -2144,13 +2144,6 @@ inline const char *icon_for(const Tile &tile) {
if (d == "camera" || d == "image") return "\U000F07AE";
return "\U000F0425";
}
// HA duration strings ("0:05:00") to seconds; 0 when unusable.
inline uint32_t duration_seconds(const std::string &text) {
unsigned h = 0, m = 0, s = 0;
if (sscanf(text.c_str(), "%u:%u:%u", &h, &m, &s) == 3) return h * 3600 + m * 60 + s;
if (sscanf(text.c_str(), "%u:%u", &m, &s) == 2) return m * 60 + s;
return 0;
}
inline std::string countdown(uint32_t seconds) {
char b[16];
if (seconds >= 3600) snprintf(b, sizeof(b), "%u:%02u:%02u", seconds / 3600, seconds / 60 % 60, seconds % 60);
@@ -2171,7 +2164,7 @@ inline std::string last_run_text(uint32_t epoch) {
}
inline std::string timer_text(const Tile &t) {
const Extra &x = t.extra();
if (t.state == "active") { uint32_t now = now_epoch(); return countdown(x.timer_end > now && now ? x.timer_end - now : 0); }
if (t.state == "active") return countdown(timer_left(x.timer_end, now_epoch(), x.duration));
if (t.state == "paused") return "Paused " + countdown(duration_seconds(x.remaining));
return x.duration.empty() ? "Off" : countdown(duration_seconds(x.duration));
}
@@ -2617,9 +2610,6 @@ inline void render_graph(Widgets &w,const Tile &t,bool large,int x,int y,int wid
}
// Sun path: horizon, an arc from sunrise to sunset and the sun at the current
// position (or below the horizon at night). Wide cards only.
inline int minutes_of(const std::string &clock) {
unsigned h=0,m=0;return sscanf(clock.c_str(),"%u:%u",&h,&m)==2 && h<24 && m<60 ? int(h*60+m) : -1;
}
inline void render_sunpath(Widgets &w,const Tile &t,bool large,int width,int height) {
begin_extra(w,"sunpath",width,height);
const lv_font_t *title_font=lv_obj_get_style_text_font(w.title,LV_PART_MAIN);