From 5d6651d68b8ebddd31c8930fc91f2d08e14e7288 Mon Sep 17 00:00:00 2001 From: Max Gramser Date: Sun, 13 Sep 2026 15:45:17 +0200 Subject: [PATCH] Watch the feed by the declared keepalive and HA by the API link (0.2.26) Since 0.2.20 the manager repeats a layout every 120 s, while the firmware treated anything older than 95 s (sized for the original 25 s keepalive) as stale: on a quiet screen every card read "Niet beschikbaar" and the header "HA niet verbonden" for 25-45 s of each two-minute round, on both boards. "HA niet verbonden" now follows ESPHome's own api_is_connected(); the feed watchdog uses the cadence the layout message declares ("keepalive", seconds, 120 s when absent) and tolerates one missed round plus loop slack before showing "ESP Screens niet actief". The tick redraws once when either flips. Firmware 0.2.22 for CYD and Guition. Co-Authored-By: Claude Fable 5.1 --- components/smart_display/runtime_tiles.h | 21 +++++++++++++++++++-- docs/EASY_SETUP.md | 6 ++++-- docs/RELEASING.md | 14 ++++++++++++++ guition-4848s040.yaml | 2 +- home-like-2432s028.yaml | 2 +- packages/cyd.yaml | 2 +- packages/guition.yaml | 2 +- screen_manager/CHANGELOG.md | 4 ++++ screen_manager/app/core.py | 2 +- screen_manager/app/server.py | 5 ++++- screen_manager/config.yaml | 2 +- tests/test_extended.py | 2 ++ 12 files changed, 53 insertions(+), 11 deletions(-) diff --git a/components/smart_display/runtime_tiles.h b/components/smart_display/runtime_tiles.h index f266055..5538088 100644 --- a/components/smart_display/runtime_tiles.h +++ b/components/smart_display/runtime_tiles.h @@ -10,6 +10,7 @@ #include "esphome/components/json/json_util.h" #include "esphome/components/api/api_server.h" #include "esphome/core/hal.h" +#include "esphome/core/util.h" #include "esphome/core/time.h" #include "lvgl.h" #include @@ -42,6 +43,9 @@ inline std::string timer_text(const Tile &t); inline std::string last_run_text(uint32_t epoch); inline int active_index = -1; inline uint32_t last_received = 0; +// Seconds between the manager's full repeats; every layout message declares it (app +// 0.2.26+). Older managers get the 120 s that app 0.2.20 introduced. +inline uint32_t keepalive_seconds = 120; inline std::function layout_changed, refresh, dismiss, settings_changed; inline esphome::ESPPreferenceObject settings_preference; inline void load_settings() { @@ -98,7 +102,13 @@ inline bool has_icon_glyph(uint32_t codepoint) { for (auto &w : widgets) if (w.icon_font) { lv_font_glyph_dsc_t dsc; return lv_font_get_glyph_dsc(w.icon_font, &dsc, codepoint, 0); } return false; } -inline bool fresh() { return model.ready() && esphome::millis() - last_received < 95000; } +// Home Assistant's API link as ESPHome itself tracks it: gone the moment the socket drops, +// back the moment HA reconnects, no guessing from message age. +inline bool ha_connected() { return esphome::api_is_connected(); } +// The manager repeats the whole layout every keepalive; one missed round plus its 20 s +// loop slack and the sending itself are tolerated before the feed counts as gone. +inline bool feed_alive() { return esphome::millis() - last_received < keepalive_seconds * 2000 + 60000; } +inline bool fresh() { return model.ready() && ha_connected() && feed_alive(); } inline float number(JsonVariant value, float fallback = NAN) { if (!value.is() && !value.is()) return fallback; float n = value.as(); @@ -141,6 +151,8 @@ inline std::string receive(const std::string &payload) { if(!root["swipe_pages"].isNull() && !root["swipe_pages"].is())return false; if(!root["rotation"].isNull() && (!root["rotation"].is() || root["rotation"].as()>270 || root["rotation"].as()%90!=0))return false; + if(!root["keepalive"].isNull() && (!root["keepalive"].is() || + root["keepalive"].as()<5 || root["keepalive"].as()>3600))return false; inbox = string(root["inbox"], 160); bool changed = false; if (!model.set_layout(entities, string(root["title"], 96), changed)) return false; @@ -158,6 +170,7 @@ inline std::string receive(const std::string &payload) { } if(rotation_changed && settings_changed)settings_changed(); if (changed) { active_index = -1; for (auto &w : widgets) w.cached_active = -1; if (dismiss) dismiss(); } + if (root["keepalive"].is()) keepalive_seconds = root["keepalive"].as(); last_received = esphome::millis(); if (layout_changed) layout_changed(); if (refresh) refresh(); @@ -1255,7 +1268,7 @@ inline void set_busy(Widgets &w,bool busy,bool large){ inline void render(lv_obj_t *room) { if (!enabled) return; room_label=room; - label(room, !model.configured ? "Kies tegels in HA" : !model.ready() ? "Tegels laden..." : !fresh() ? "HA niet verbonden" : model.title); + label(room, !model.configured ? "Kies tegels in HA" : !model.ready() ? "Tegels laden..." : !ha_connected() ? "HA niet verbonden" : !feed_alive() ? "ESP Screens niet actief" : model.title); for (size_t slot = 0; slot < 6; ++slot) { auto &w=widgets[slot]; if(!w.tile || w.index>=model.count)continue; @@ -1549,6 +1562,7 @@ inline void show_page(int &page, lv_obj_t *previous, lv_obj_t *next, lv_obj_t *n } inline uint32_t last_live_second=0; +inline bool was_fresh=false; inline void tick() { if(detail_root && !lv_obj_has_flag(detail_root,LV_OBJ_FLAG_HIDDEN) && detail_index