From 5a05f52dbc64dadf5f224e7ad28225625235e669 Mon Sep 17 00:00:00 2001 From: Max Gramser Date: Sun, 13 Sep 2026 15:11:40 +0200 Subject: [PATCH] Lighter tile model, mini-slider handle, CYD light cards, wifi without modem sleep (0.2.24) App 0.2.24 / firmware 0.2.20. - Forecast and hourly data live in vectors that only weather tiles fill: Tile 2016 -> 1216 bytes, CYD free heap 20-26 KB -> 34-37 KB in the self-test. - The bottom mini slider shows the same white handle as the control sliders. - CYD light/fan/cover card uses the Guition light palette (last dark-theme remnant). - power_save_mode: none in the wizard YAML and board profiles; docs explain adding it to existing YAMLs. Rendering review in docs/TEST_RESULTS_0224.md. Co-Authored-By: Claude Fable 5.1 --- a.out | Bin 0 -> 33432 bytes components/smart_display/runtime_model.h | 8 +++--- components/smart_display/runtime_tiles.h | 32 +++++++++++++---------- docs/EASY_SETUP.md | 4 +++ docs/RELEASING.md | 7 +++++ docs/TEST_RESULTS_0224.md | 29 ++++++++++++++++++++ guition-4848s040.yaml | 4 ++- home-like-2432s028.yaml | 6 +++-- packages/cyd.yaml | 4 +-- packages/guition.yaml | 2 +- screen_manager/CHANGELOG.md | 6 +++++ screen_manager/app/core.py | 3 ++- screen_manager/config.yaml | 2 +- 13 files changed, 80 insertions(+), 27 deletions(-) create mode 100755 a.out create mode 100644 docs/TEST_RESULTS_0224.md diff --git a/a.out b/a.out new file mode 100755 index 0000000000000000000000000000000000000000..6b7a56a01b58905b049addef8e2c45eda8edd2b5 GIT binary patch literal 33432 zcmeI5U1(fY5XWclCUs*IO{<8lO0PmBHA_-!P@xnyw$TJj6WSFQKYF^^+)Wmf-LiWZ zleXF$v3^j5q#9WIQbeQ#Y$>G=eXtTKip2+$U|Z>f3VxQ-LNO>Q#yE5C-R#}1+K0Xf z{SVANGjrz5&iS2_yq>SFUizz+C_pR*>vGoaDx$q)vPHClwS!fu?zX4epKIUJCDC%x zi_1gTcv9yrPE;z|9*dSw>-CZH7>x89o<-!J~!5?3mp zemRxyRjG6`qy2Jztqe!@8i&7jPDr!wO)vGwM5+Yo<~dA(C6+2O80D2=jNx|H+B~9h*QxQ`yvHzlTyQ9 zrUNP&ch>~zn%Rx?8~ydSqtpTex`VAj$0*uYT;)PfmBPc=lYw$+a9aS>4}U$(Mhn zwxw61->M$suil^bb?=VePsKfw-^}_umnwpv)Zsw*^J-W}w{rR*> z->k*nIce)~&b9`dpYWD&HqnEu($AWE{&Z#<12h*p`tD3)HIE_oYiO>qj_n}Z%V>^kj?&Z}W8C8X0gZ;`HvJ!dQQvFuE*UD1OU_6zkI(|Q!6GSBa_JDpl zT=#RA_TBfji|=pqC}~>65HeibI6|hOpG@~c|2YU+kwCmTleg*ehxQK-^o_syWpYO>`^)exBQH#)&-9xI zFW&#ki@zUO_RZlvA03NjKEIHhtU5P6{gTlYt6tcBeD}`1=TGkIcsqRPM(4jfbMcKU z#aZ3bnx(zOzXbW4d+MlvxC-W X>c@AE?O9ki@yGbbN4EVOcVYbv3khUJ literal 0 HcmV?d00001 diff --git a/components/smart_display/runtime_model.h b/components/smart_display/runtime_model.h index 2b7e8e6..0733fe3 100644 --- a/components/smart_display/runtime_model.h +++ b/components/smart_display/runtime_model.h @@ -52,11 +52,9 @@ struct Tile { unsigned option_count = 0, history_hours = 24; std::array history{}; bool has_history = false; - std::array forecast; - unsigned forecast_count = 0; - // Weather card details (0.2.19+): the next hours, wind and feels-like temperature. - std::array hours; - unsigned hour_count = 0; + // Weather only: up to five days and eight hours; empty vectors cost nothing on the other tiles. + std::vector forecast; + std::vector hours; float wind = NAN, feels = NAN; std::string wind_unit; // When a scene, script or button last ran (unix time), pre-computed by the manager. diff --git a/components/smart_display/runtime_tiles.h b/components/smart_display/runtime_tiles.h index 850f280..e19b931 100644 --- a/components/smart_display/runtime_tiles.h +++ b/components/smart_display/runtime_tiles.h @@ -195,17 +195,17 @@ inline std::string receive(const std::string &payload) { bool repack = was_wide != tile.wide; // Pre-computed extras: the manager converts time zones and fetches forecasts. auto extra = root["x"]; - tile.forecast_count = 0; + tile.forecast.clear(); if (extra["days"].is()) for (JsonVariant day : extra["days"].as()) { - if (tile.forecast_count == tile.forecast.size()) break; - auto &f = tile.forecast[tile.forecast_count++]; + if (tile.forecast.size() == 5) break; + tile.forecast.emplace_back(); auto &f = tile.forecast.back(); f.day = string(day["d"], 3); f.condition = string(day["c"], 20); f.high = number(day["h"]); f.low = number(day["l"]); f.rain = number(day["p"]); f.mm = number(day["r"]); } - tile.hour_count = 0; + tile.hours.clear(); if (extra["hours"].is()) for (JsonVariant hour : extra["hours"].as()) { - if (tile.hour_count == tile.hours.size()) break; - auto &h = tile.hours[tile.hour_count++]; + if (tile.hours.size() == 8) break; + tile.hours.emplace_back(); auto &h = tile.hours.back(); h.time = string(hour["t"], 5); h.condition = string(hour["c"], 20); h.temp = number(hour["h"]); h.rain = number(hour["p"]); h.mm = number(hour["r"]); } tile.last_run = extra["last"].is() ? extra["last"].as() : 0; @@ -439,7 +439,7 @@ inline void render_weather_detail(const Tile &t,bool large,int width,int height, int text_h=lv_font_get_line_height(detail_font),small_h=lv_font_get_line_height(small),mini_h=lv_font_get_line_height(mini),tiny_h=lv_font_get_line_height(tiny); int icon_h=lv_font_get_line_height(icon_font),big_h=lv_font_get_line_height(big),hero=std::max(icon_h,big_h); int card_pad=large?14:7,inner=width-2*pad-2*card_pad; - unsigned columns=std::min(t.hour_count,6); + unsigned columns=std::min(t.hours.size(),6); int hours_h=columns?small_h+mini_h+text_h+small_h+(large?16:6):0; int card_a_h=card_pad+hero+(columns?(large?14:8)+hours_h:0)+card_pad; int y=large?62:38; @@ -470,15 +470,15 @@ inline void render_weather_detail(const Tile &t,bool large,int width,int height, } y+=card_a_h+(large?12:6); // Coming days: a heading and a card with one row per day. - if(!t.forecast_count){detail_text(detail_root,"Geen dagvoorspelling van Home Assistant",pad,y,width-2*pad,small,LV_TEXT_ALIGN_LEFT,muted);return;} + if(!t.forecast.size()){detail_text(detail_root,"Geen dagvoorspelling van Home Assistant",pad,y,width-2*pad,small,LV_TEXT_ALIGN_LEFT,muted);return;} if(large){detail_text(detail_root,"Komende dagen",pad+4,y,width-2*pad,detail_font,LV_TEXT_ALIGN_LEFT,muted);y+=text_h+8;} int card_b_h=height-y-(large?10:4); auto *days=detail_card(pad,y,width-2*pad,card_b_h); - int row_pad=large?8:4,row=(card_b_h-2*row_pad)/(int)t.forecast_count; + int row_pad=large?8:4,row=(card_b_h-2*row_pad)/(int)t.forecast.size(); int day_w=large?46:26,icon_x=card_pad+day_w,cond_x=icon_x+mini_h+(large?12:5); int high_w=large?52:30,low_w=large?46:28,rain_w=large?120:60,drop_w=tiny_h+(large?4:2); int temps_x=width-2*pad-card_pad-high_w-low_w,rain_x=temps_x-(large?14:6)-rain_w; - for(unsigned i=0;i80?28:10);lv_obj_align(w.slider,LV_ALIGN_BOTTOM_MID,0,0);lv_slider_set_range(w.slider,0,1000); - lv_obj_set_style_bg_color(w.slider,lv_color_hex(0x111111),LV_PART_KNOB);lv_obj_set_style_pad_hor(w.slider,lv_obj_get_height(tile)>80?-10:0,LV_PART_KNOB);lv_obj_set_style_pad_ver(w.slider,lv_obj_get_height(tile)>80?-5:1,LV_PART_KNOB);lv_obj_set_style_radius(w.slider,14,LV_PART_MAIN);lv_obj_set_style_radius(w.slider,14,LV_PART_INDICATOR);lv_obj_set_style_radius(w.slider,3,LV_PART_KNOB);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFCE5B4),LV_PART_MAIN);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFB900),LV_PART_INDICATOR); - lv_obj_set_style_opa(w.slider,LV_OPA_TRANSP,LV_PART_KNOB); + // A short white bar inside the fill as handle, like the control sliders (invisible before 0.2.20). + int strip=lv_obj_get_height(tile)>80?28:10; + lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFFFFF),LV_PART_KNOB);lv_obj_set_style_bg_opa(w.slider,LV_OPA_COVER,LV_PART_KNOB); + lv_obj_set_style_pad_hor(w.slider,-(strip/2-(strip>20?3:2)),LV_PART_KNOB);lv_obj_set_style_pad_ver(w.slider,-(strip/4),LV_PART_KNOB); + lv_obj_set_style_border_width(w.slider,0,LV_PART_KNOB);lv_obj_set_style_shadow_width(w.slider,0,LV_PART_KNOB); + lv_obj_set_style_radius(w.slider,14,LV_PART_MAIN);lv_obj_set_style_radius(w.slider,14,LV_PART_INDICATOR);lv_obj_set_style_radius(w.slider,2,LV_PART_KNOB);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFCE5B4),LV_PART_MAIN);lv_obj_set_style_bg_color(w.slider,lv_color_hex(0xFFB900),LV_PART_INDICATOR); lv_obj_add_flag(w.slider,LV_OBJ_FLAG_HIDDEN); lv_obj_remove_flag(w.slider,LV_OBJ_FLAG_GESTURE_BUBBLE); lv_obj_add_event_cb(w.slider,slider_event,LV_EVENT_ALL,(void*)(uintptr_t)index); @@ -953,7 +957,7 @@ inline void render_forecast(Widgets &w,const Tile &t,bool large,int width,int he part_label(w,2,w.value_font,0,y+std::max(icon_h,temp_h)+2,left-4,LV_TEXT_ALIGN_LEFT,weather_text(t.state)); int column=(width-left)/5,day_h=lv_font_get_line_height(title_font),icon_col=lv_font_get_line_height(day_icon); for(unsigned k=0;k<5;++k){ - int x=left+k*column;bool has=k80; set_busy(w,pending && !t.builtin(),large_tile); // Cards that replace the name/status layout entirely. - bool clock=t.builtin(), forecast=d=="weather" && t.display=="forecast" && w.wide && t.forecast_count>0 && fresh() && t.available(); + bool clock=t.builtin(), forecast=d=="weather" && t.display=="forecast" && w.wide && t.forecast.size()>0 && fresh() && t.available(); bool sunpath=d=="sun" && t.display=="sunpath" && w.wide && !t.sunrise.empty() && !t.sunset.empty() && fresh() && t.available(); bool graph=d=="sensor" && t.display=="graph" && t.has_history && !clock; bool custom=clock||forecast||sunpath; diff --git a/docs/EASY_SETUP.md b/docs/EASY_SETUP.md index 08c9c1b..2fc022b 100644 --- a/docs/EASY_SETUP.md +++ b/docs/EASY_SETUP.md @@ -94,6 +94,10 @@ je later opnieuw meten. Voor diagnose en de oudere handmatige installatie: ## 5. Kies en wijzig je tegels +Tip: zet in je ESPHome-YAML onder `wifi:` de regel `power_save_mode: none` (nieuwe +wizard-YAML's hebben hem al); het scherm hangt aan het lichtnet en reageert dan zonder +wifi-slaapvertraging op Home Assistant. + Selecteer je scherm, vul de titel in en zoek entiteiten. De kiezer heeft domeinfilters met gekleurde iconen. Je kunt maximaal twintig tegels toevoegen. Het schermvoorbeeld toont hun plaats: twee kolommen, zes tegels per pagina. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index f3a28a2..e3c2a48 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -177,6 +177,13 @@ staan iconen in de browser uit het midden); draai daarna `generate_packages.py`. De substituties `MDI_GLYPH_*` zijn vervallen: `TILEn_ICON` in handmatige profielen moet uit de set komen. Geen gewijzigde preferences of sleutels. +### Compatibiliteit 0.2.24 / firmware 0.2.20 + +Alleen intern: `Tile::forecast` en `Tile::hours` zijn vectoren (voorheen vaste arrays in +elke tegel); protocol en opslag ongewijzigd. De wizard-YAML krijgt `power_save_mode: none` +onder `wifi:`; de gedeelde pakketten bevatten geen wifi-blok, dus bestaande schermen +veranderen pas als de gebruiker de regel zelf toevoegt. + ### Compatibiliteit 0.2.23 / firmware 0.2.19 Opslagversie en tegelprotocol blijven 1. `tiles[].options.controls` is additief: diff --git a/docs/TEST_RESULTS_0224.md b/docs/TEST_RESULTS_0224.md new file mode 100644 index 0000000..feda47a --- /dev/null +++ b/docs/TEST_RESULTS_0224.md @@ -0,0 +1,29 @@ +# Testresultaten app 0.2.24 / firmware 0.2.20 (2026-09-13) + +Vervolg op 0.2.23: doorlichting van de rendering, minder geheugen per tegel, zichtbare +greep op de mini-slider, CYD-kaarten in het lichte palet, wifi zonder modem-slaap. + +## Doorlichting (wat is gemeten, wat is veranderd, wat bewust niet) + +- LVGL 9.5 via ESPHome 2026.6.2 tekent al partieel (`LV_DEF_REFR_PERIOD 16`, kernloop 16 ms, + alleen ongeldig gemaakte gebieden gaan naar het paneel; `auto_clear_enabled: false`). + `lv_obj_set_x/y/width/height` slaan een ongewijzigde waarde over, maar elke + `lv_obj_set_style_*` maakt het object ongeldig. Daarom (0.2.23) alleen nog zetten bij een + echte wijziging en geen herrender meer elke 250 ms tijdens *bezig*. +- `Tile` was 2016 bytes × 20 = 40 KB statisch; de dag- en uurvoorspelling zitten nu in + vectoren die alleen weertegels vullen: 1216 bytes × 20 = 24 KB. CYD vrije heap tijdens de + selftest met de 18-tegels demo: 20–26 KB (0.2.19) → 34–37 KB (0.2.20). Guition 6,33 → 6,35 MB. +- Niet veranderd, bewust: de paginawissel tekent twee keer (skelet, dan inhoud; gewenste + directe feedback); GT911 op 20 ms; `buffer_size` 25% (PSRAM) en 12% (CYD). +- `power_save_mode: none` in wizard-YAML en bordprofielen (hangt aan het lichtnet). + +## Geautomatiseerd + +- Python: 82 tests OK. C++: 11 tests PASS. `generate_icons.py --check`, `generate_packages.py --check` groen. +- Beide profielen compileren; OTA naar beide borden; `device_info` meldt 0.2.20. + +## Op de borden + +- Guition en CYD `ui_self_test` met `send_layout.py --controls` (18 tegels): 10/10 PASS, + geen geometrie-, paneel- of paletfouten. +- Nog fysiek te bekijken door Max: greep op de mini-slider, CYD-lichtkaart in het lichte palet. diff --git a/guition-4848s040.yaml b/guition-4848s040.yaml index 892fc8b..3b3e1ab 100644 --- a/guition-4848s040.yaml +++ b/guition-4848s040.yaml @@ -1,7 +1,7 @@ # Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911. # Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile. substitutions: - SCREEN_FIRMWARE_VERSION: "0.2.19" + SCREEN_FIRMWARE_VERSION: "0.2.20" DYNAMIC_TILES: "false" DASHBOARD_INBOX_INTERNAL: "true" DEVICE_NAME: "guition-wallbox" @@ -834,6 +834,8 @@ ota: wifi: ssid: !secret wifi_ssid password: !secret wifi_password + # Mains-powered panel: no modem sleep, so HA updates and OTA arrive without beacon latency. + power_save_mode: none ap: ssid: "Smartdisplay Fallback Hotspot" password: !secret ap_password diff --git a/home-like-2432s028.yaml b/home-like-2432s028.yaml index 3cef6b2..9765829 100644 --- a/home-like-2432s028.yaml +++ b/home-like-2432s028.yaml @@ -58,7 +58,7 @@ ############################################################ substitutions: - SCREEN_FIRMWARE_VERSION: "0.2.19" + SCREEN_FIRMWARE_VERSION: "0.2.20" DYNAMIC_TILES: "false" DASHBOARD_INBOX_INTERNAL: "true" DEVICE_NAME: "cyd-new" @@ -861,7 +861,7 @@ esphome: id(active_climate_swing_modes) = t.swing_modes; id(active_climate_swing_mode) = t.swing_mode; id(open_value_overlay).execute(t.name, t.entity, t.domain(), "auto", t.active(), 0, t.brightness, t.percentage, t.position, runtime_tiles::icon_for(t), - 0x565C66, 0xE4B23C, 0xFFFFFF, "Uit", "Aan", t.modes, + 0xD5EEFC, 0x00A6ED, 0x1B1B1B, "Uit", "Aan", t.modes, t.current, t.target, t.humidity, t.state, t.hvac_modes, std::to_string(t.minimum), std::to_string(t.maximum), std::to_string(t.step)); }; @@ -1029,6 +1029,8 @@ ota: wifi: ssid: !secret wifi_ssid password: !secret wifi_password + # Mains-powered panel: no modem sleep, so HA updates and OTA arrive without beacon latency. + power_save_mode: none ap: ssid: "Smartdisplay Fallback Hotspot" password: !secret ap_password diff --git a/packages/cyd.yaml b/packages/cyd.yaml index 994370e..28b5734 100644 --- a/packages/cyd.yaml +++ b/packages/cyd.yaml @@ -59,7 +59,7 @@ ############################################################ substitutions: - SCREEN_FIRMWARE_VERSION: "0.2.19" + SCREEN_FIRMWARE_VERSION: "0.2.20" DYNAMIC_TILES: "true" DASHBOARD_INBOX_INTERNAL: "false" DEVICE_NAME: "cyd-new" @@ -865,7 +865,7 @@ esphome: id(active_climate_swing_modes) = t.swing_modes; id(active_climate_swing_mode) = t.swing_mode; id(open_value_overlay).execute(t.name, t.entity, t.domain(), "auto", t.active(), 0, t.brightness, t.percentage, t.position, runtime_tiles::icon_for(t), - 0x565C66, 0xE4B23C, 0xFFFFFF, "Uit", "Aan", t.modes, + 0xD5EEFC, 0x00A6ED, 0x1B1B1B, "Uit", "Aan", t.modes, t.current, t.target, t.humidity, t.state, t.hvac_modes, std::to_string(t.minimum), std::to_string(t.maximum), std::to_string(t.step)); }; diff --git a/packages/guition.yaml b/packages/guition.yaml index 1ae8120..68c4daa 100644 --- a/packages/guition.yaml +++ b/packages/guition.yaml @@ -2,7 +2,7 @@ # Guition ESP32-S3-4848S040 / 4-inch EU wallbox, 480x480 IPS + GT911. # Use guition-device.yaml; see docs/GUITION.md. CYD hardware is a separate profile. substitutions: - SCREEN_FIRMWARE_VERSION: "0.2.19" + SCREEN_FIRMWARE_VERSION: "0.2.20" DYNAMIC_TILES: "true" DASHBOARD_INBOX_INTERNAL: "false" DEVICE_NAME: "guition-new" diff --git a/screen_manager/CHANGELOG.md b/screen_manager/CHANGELOG.md index 4b4ea78..b2924cc 100644 --- a/screen_manager/CHANGELOG.md +++ b/screen_manager/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.2.24 (firmware 0.2.20) + +- **Lichter en zuiniger**: de dag- en uurvoorspelling zitten alleen nog in het geheugen van weertegels (was 832 bytes in elk van de twintig tegels, ruim 16 KB op de CYD zonder PSRAM). De mini-slider onderin een tegel (**Kleine slider**) heeft nu dezelfde zichtbare witte greep als de nieuwe schuiven. De licht-, ventilator- en zonweringkaart op de CYD gebruiken het lichte palet van de Guition (de laatste rest van het donkere thema). +- **Wifi zonder modem-slaap** (`power_save_mode: none`) in de installatie-YAML van de wizard en de bordprofielen: statusupdates, tikacties en OTA wachten niet meer op een wifi-beacon. Bestaande schermen: voeg de regel zelf toe onder `wifi:` in je ESPHome-YAML. +- Doorlichting van de rendering: LVGL 9.5 tekent partieel en slaat ongewijzigde posities en maten al over; de winst zat in de stijlzetters (0.2.23) en in niet meer herrenderen tijdens *bezig*. Een paginawissel tekent bewust twee keer (skelet, dan inhoud). Verder geen structurele last gevonden; zie docs/TEST_RESULTS_0224.md. + ## 0.2.23 (firmware 0.2.19) - **Directe bediening op dubbelbrede tegels**, zoals de entiteitsrijen in Home Assistant: rechts op de kaart staan knoppen of een schuif, links blijven icoon, naam en status. Per domein kies je in het tegelpaneel onder **Directe bediening op de tegel** welke set de tegel toont: diff --git a/screen_manager/app/core.py b/screen_manager/app/core.py index da6c540..5a052ba 100644 --- a/screen_manager/app/core.py +++ b/screen_manager/app/core.py @@ -16,7 +16,7 @@ WEEKDAYS = ['ma', 'di', 'wo', 'do', 'vr', 'za', 'zo'] 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.19' +FIRMWARE_VERSION = '0.2.20' ATTRS = frozenset('brightness percentage current_position current_temperature temperature current_humidity min_temp max_temp target_temp_step supported_color_modes hvac_modes hvac_action hs_color color_temp_kelvin min_color_temp_kelvin max_color_temp_kelvin fan_speed_list unit_of_measurement battery_level fan_speed volume_level is_volume_muted media_title options min max step temperature_unit supported_features device_class next_rising next_setting finishes_at duration remaining humidity wind_speed wind_speed_unit apparent_temperature fan_modes swing_modes fan_mode swing_mode'.split()) # Attributes whose boolean value the screen needs; every other bool stays behind. BOOL_ATTRS = frozenset(['is_volume_muted']) @@ -417,6 +417,7 @@ ota: wifi: ssid: !secret wifi_ssid password: !secret wifi_password + power_save_mode: none ap: ssid: {quote(name + ' Setup')} password: {quote(ap)} diff --git a/screen_manager/config.yaml b/screen_manager/config.yaml index cd3c5e3..1d147ed 100644 --- a/screen_manager/config.yaml +++ b/screen_manager/config.yaml @@ -1,5 +1,5 @@ name: ESP Screen Manager -version: "0.2.23" +version: "0.2.24" slug: esp_screen_manager description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen. url: https://github.com/MaxGramser/homeassistant_espscreen