diff --git a/README.md b/README.md
index 0203173..a0c0ace 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,132 @@
# Home Assistant ESP Screens
-**Nieuw scherm installeren? Begin met [Easy Setup](docs/EASY_SETUP.md).**
-Installeer ESP Screen Manager in Home Assistant, download de YAML voor CYD of
-Guition, bouw en flash vanuit dezelfde app via USB op je Raspberry en kies je tegels in de beheerpagina.
-Zoeken op entiteit/apparaat/ruimte, slepen, opslaan — zonder opnieuw flashen.
+Een Home Assistant-bedieningsscherm dat je zelf indeelt. **ESP Screen Manager**
+beheert je tegels én bouwt en installeert de ESPHome-firmware vanuit Home Assistant.
+Geen blueprint, MQTT of long-lived token nodig voor normaal gebruik.
-Updates van de app behouden je indelingen. Nieuwe firmware installeer je via
-ESPHome OTA met behoud van je eigen naam, wifi en sleutels.
-Zie [de nieuwe kaarten, inspector en firmwarepagina](docs/WHATS_NEW_020.md) en
-[updates publiceren](docs/RELEASING.md).
+**Nieuw scherm? Volg de [complete installatiehandleiding](docs/EASY_SETUP.md).**
+Voor elke nieuwe gebruiker en elk nieuw apparaat maak je een eigen profiel.
-De handleiding hieronder blijft beschikbaar voor handmatige installatie via een
-computer. Voor de handmatige Guition-route: [Guition](docs/GUITION.md).
+## Ondersteunde schermen
+
+| Scherm | Resolutie | Display / touch |
+| --- | --- | --- |
+| CYD ESP32-2432S028 | 320 × 240 | ILI9341 / resistieve XPT2046 |
+| Guition ESP32-S3-4848S040, 4 inch | 480 × 480 | ST7701S RGB / capacitieve GT911 |
+
+Gebruik deze exacte bordvarianten: gelijkende productnamen kunnen andere
+controllers of aansluitingen hebben. Wallbox-relais worden niet aangestuurd.
+De firmware en ingebouwde CLI zijn getest met **ESPHome 2026.6.2**.
+
+## Wat je kunt instellen
+
+- **Tot twintig tegels**, verdeeld over maximaal vier vaste pagina's met zes
+ tegels. Zoek op entiteit, apparaat of ruimte en sleep om te ordenen.
+- **Instellingen per tegel:** eigen naam, klikgedrag, een kleine slider waar
+ ondersteund, of een grote waarde voor bijvoorbeeld temperatuur en verbruik.
+- **Pastel achtergronden per tegel:** kies rood voor een alles-uit-script,
+ groen voor alles-aan, of een andere kleur. Titel en status blijven donker en
+ leesbaar. De kleur verschijnt ook in het schermvoorbeeld; **Standaard** herstelt
+ de normale kleuren. Vereist firmware 0.2.10 of nieuwer.
+- **Lampbediening:** helderheid, regenboogkleur en wittemperatuur volgens de
+ mogelijkheden van de lamp. Open de detailbediening met een lange aanraking.
+- **Meer kaarten:** climate, vacuum, fan, cover, media player, sensoren,
+ select/input_select, number/input_number, schakelaars, scènes, scripts en
+ buttons. Een sensor kan een historiekaart voor 1, 6 of 24 uur openen.
+- **Scherminstellingen:** standby-tijd, normale en gedimde helderheid,
+ nachturen, klok, terug naar de hoofdpagina en optioneel swipen tussen pagina's.
+- **Guition-rotatie:** 0°, 90°, 180° of 270°, direct vanuit de beheerpagina.
+ De native LVGL-rotatie draait beeld en touch samen. De CYD behoudt zijn vaste
+ oriëntatie en eigen kalibratie.
+- **Inspector:** controleer entiteiten, status en configuratie in ESP Screens.
+ Actiefeedback laat zien dat een opdracht onderweg is.
+
+Functies hangen af van de mogelijkheden die Home Assistant voor een entiteit
+meldt. De app moet blijven draaien om de schermen van actuele gegevens te voorzien.
+
+## Installeren vanuit Home Assistant
+
+Voor Home Assistant OS met Apps/Add-ons op **aarch64 of amd64**:
+
+1. Open de appwinkel en voeg deze repository toe:
+ `https://github.com/MaxGramser/homeassistant_espscreen`.
+2. Installeer **ESP Screen Manager**, start de app en open **ESP Screens**.
+ ESPHome Device Builder is optioneel: de ESPHome-CLI zit al in deze app.
+3. Kies **Nieuw scherm**, selecteer CYD of Guition en geef een unieke naam.
+ Gebruik **Bewaar profiel in ESP Screens**. Bestaande `wifi_ssid` en
+ `wifi_password` in ESPHome `secrets.yaml` worden hergebruikt; bij een verse
+ installatie vraagt de wizard de wifi eenmalig. API- en OTA-sleutels worden
+ uniek aangemaakt en in je eigen apparaat-YAML bewaard.
+4. Sluit het scherm met een USB-datakabel aan op de **Home Assistant-machine**.
+ Open **Firmware & USB**, kies het eigen profiel en de juiste USB-poort en
+ start **Bouwen & installeren**. Een eerste build kan meerdere minuten duren.
+5. **CYD:** doorloop de kalibratie op het scherm. **Guition:** gebruikt GT911
+ zonder resistieve kalibratie. Koppel daarna het ontdekte ESPHome-apparaat in
+ **Instellingen → Apparaten & diensten**. Gebruik bij een sleutelvraag de
+ `api.encryption.key` uit je eigen YAML. Geef het apparaat toestemming om
+ Home Assistant-acties uit te voeren.
+6. Selecteer het scherm in ESP Screens, kies je tegels en klik
+ **Opslaan & naar scherm**. Test vervolgens de fysieke bediening.
+
+Je kunt later vanuit **Firmware & USB → Wifi / OTA** nieuwe firmware installeren.
+Gebruik voor een bestaand scherm altijd het bestaande profiel; opnieuw een
+installatieprofiel aanmaken genereert nieuwe sleutels.
+
+## Tegels en kleuren aanpassen
+
+Open **Tegels instellen**, klik een tegel in het schermvoorbeeld en open
+**Bediening & weergave instellen**. Kies bij **Pastel achtergrond** een kleur,
+zoals rood of groen. Pas eventueel de naam, klikactie, mini-slider of grote
+waarde aan. Klik **Opslaan & naar scherm** om de wijzigingen toe te passen.
+Dit vereist na de eerste ondersteunende firmware-update geen nieuwe flash.
+
+Een kleur is een vaste keuze voor die tegel: hij blijft dus bijvoorbeeld rood
+wanneer je het alles-uit-script gebruikt. De entiteitsstatus en actiefeedback
+blijven afzonderlijk zichtbaar.
+
+## Updates en behoud van je instellingen
+
+| Wijziging | Actie |
+| --- | --- |
+| Tegels, namen, kleuren, volgorde of scherminstellingen | Opslaan in ESP Screens; geen firmwareflash |
+| Nieuwe versie van de beheerpagina | ESP Screen Manager updaten in de HA-appwinkel |
+| Nieuwe functie op het fysieke scherm | Bestaand profiel bijwerken via Firmware & USB → Wifi / OTA |
+
+De eigen YAML en wifi/API/OTA-instellingen blijven in de ESPHome-configmap.
+Tegelindelingen en opties staan in de permanente appdata. CYD-kalibratie en
+schermvoorkeuren blijven op het apparaat opgeslagen. Updates vervangen deze
+gebruikersgegevens niet. Maak wel normale Home Assistant-back-ups en bewaar je
+apparaatprofielen; een app verwijderen of flashgeheugen wissen is geen update.
+
+Zie [releasegeschiedenis](screen_manager/CHANGELOG.md) en
+[releases en protocolcompatibiliteit](docs/RELEASING.md).
+
+## Handleidingen en hulp bij installatie
+
+- [Complete installatie vanuit ESP Screens](docs/EASY_SETUP.md)
+- [Guition-hardware, montage en rotatie](docs/GUITION.md)
+- [CYD-kalibratie en USB-diagnose](docs/CALIBREREN.md)
+- [Fysieke acceptatietest](docs/ACCEPTATIE.md)
+- [Instructies voor developers en LLM's](AGENTS.md)
+
+Geef een developer of LLM een schone kopie van deze repository en bijvoorbeeld:
+
+> Lees AGENTS.md, README.md en docs/EASY_SETUP.md. Help me dit CYD- of
+> Guition-scherm via USB op mijn Home Assistant te installeren. Identificeer
+> het bord en gebruik mijn bestaande profiel als dat er al is. Begeleid
+> kalibratie, HA-koppeling, tegelkeuze en fysieke tests. Houd sleutels lokaal
+> en geef aan welke controles werkelijk zijn uitgevoerd.
+
+Een build bewijst niet dat fysieke touch of het paneelbeeld goed is. De eigenaar
+moet het beeld controleren en de gevraagde tikken uitvoeren.
+
+
+Oudere handmatige CYD-installatie via een computer
+
+Onderstaande route gebruikt het oudere handmatige profiel met maximaal tien
+tegels. De positiebeperkingen hiervan gelden niet voor de twintig runtime-tegels
+van ESP Screen Manager. Gebruik voor nieuwe installaties bij voorkeur de route
+hierboven; de handmatige instructies blijven beschikbaar voor onderhoud.
# CYD Home Assistant-bedieningsscherm
@@ -234,3 +349,5 @@ Project-, ESPHome-driver- en fontlicenties staan respectievelijk in `LICENSE`,
`components/xpt2046/LICENSE` en `fonts/`. Historische borddiagnose staat in
[CYD_STABILITY.md](CYD_STABILITY.md); volg voor een **nieuwe** installatie de
handleidingen hierboven.
+
+
diff --git a/components/smart_display/runtime_model.h b/components/smart_display/runtime_model.h
index 92a8977..aa251f4 100644
--- a/components/smart_display/runtime_model.h
+++ b/components/smart_display/runtime_model.h
@@ -36,7 +36,7 @@ struct Tile {
std::array history{};
bool has_history = false;
float battery = NAN, volume = NAN;
- uint32_t supported = 0;
+ uint32_t supported = 0, background = 0;
std::string revision, pending_revision;
uint32_t pending_since = 0;
bool pending = false, confirmed = false, local_feedback = false;
diff --git a/components/smart_display/runtime_tiles.h b/components/smart_display/runtime_tiles.h
index 6cffdf7..2f3d7c2 100644
--- a/components/smart_display/runtime_tiles.h
+++ b/components/smart_display/runtime_tiles.h
@@ -1,5 +1,6 @@
#pragma once
#include "runtime_model.h"
+#include "tile_palette.h"
#include "screen_settings.h"
#include "esphome/core/preferences.h"
#include "cyd_ui.h"
@@ -136,6 +137,7 @@ inline std::string receive(const std::string &payload) {
std::string revision = string(root["state"]); serializeJson(a, revision);
tile.observe(revision);
auto options = root["o"];
+ tile.background = tile_palette::color(string(options["background"],16));
tile.tap = string(options["tap"]); if (tile.tap.empty()) tile.tap="auto";
tile.display = string(options["display"]); if (tile.display.empty()) tile.display="standard";
tile.inline_control = string(options["inline"]); if (tile.inline_control.empty()) tile.inline_control="none";
@@ -531,19 +533,20 @@ inline void render(lv_obj_t *room) {
auto color=lv_color_hex(accent);
if(t.domain()=="light" && on && t.has_hs_color)
color=lv_color_hsv_to_rgb(t.hue%360,t.saturation,100);
- auto circle_color=available?lv_color_mix(color,lv_color_hex(light_theme?0xFFFFFF:0x263B50),light_theme?38:65):lv_color_hex(light_theme?0xF0F0F0:0x263B50);
+ bool dark_text=light_theme || t.background!=0;
+ auto circle_color=available?lv_color_mix(color,lv_color_hex(dark_text?0xFFFFFF:0x263B50),dark_text?38:65):lv_color_hex(dark_text?0xF0F0F0:0x263B50);
// Darken the foreground slightly: very pale bulbs still need a visible icon.
- auto icon_color=available?lv_color_mix(color,lv_color_hex(light_theme?0x333333:0xFFFFFF),light_theme?205:185):lv_color_hex(0x9E9E9E);
+ auto icon_color=available?lv_color_mix(color,lv_color_hex(dark_text?0x333333:0xFFFFFF),dark_text?205:185):lv_color_hex(0x9E9E9E);
lv_obj_set_style_bg_color(w.slider,color,LV_PART_INDICATOR);
- lv_obj_set_style_bg_color(w.slider,lv_color_mix(color,lv_color_hex(light_theme?0xFFFFFF:0x263B50),30),LV_PART_MAIN);
- lv_obj_set_style_bg_color(w.tile, lv_color_hex(light_theme ? 0xFFFFFF : (on ? 0xF5F1E8 : 0x526C85)), 0);
+ lv_obj_set_style_bg_color(w.slider,lv_color_mix(color,lv_color_hex(dark_text?0xFFFFFF:0x263B50),30),LV_PART_MAIN);
+ lv_obj_set_style_bg_color(w.tile, lv_color_hex(t.background ? t.background : (light_theme ? 0xFFFFFF : (on ? 0xF5F1E8 : 0x526C85))), 0);
lv_obj_set_style_border_width(w.tile, 1, 0);
lv_obj_set_style_border_opa(w.tile, LV_OPA_COVER, 0);
- lv_obj_set_style_border_color(w.tile, lv_color_hex(light_theme ? 0xDDDDDD : (on ? 0xF5F1E8 : 0x9CB3C8)), 0);
+ lv_obj_set_style_border_color(w.tile, t.background ? lv_color_mix(lv_color_hex(t.background),lv_color_hex(0x000000),220) : lv_color_hex(light_theme ? 0xDDDDDD : (on ? 0xF5F1E8 : 0x9CB3C8)), 0);
lv_obj_set_style_bg_color(w.circle,circle_color,0);
lv_obj_set_style_text_color(w.icon,icon_color,0);
- lv_obj_set_style_text_color(w.title, lv_color_hex(light_theme ? 0x1B1B1B : (on ? 0x172232 : 0xF3F5F7)), 0);
- lv_obj_set_style_text_color(w.value, lv_color_hex(light_theme ? 0x616161 : (on ? 0x46525E : 0xF0F4F8)), 0);
+ lv_obj_set_style_text_color(w.title, lv_color_hex(dark_text ? 0x1B1B1B : (on ? 0x172232 : 0xF3F5F7)), 0);
+ lv_obj_set_style_text_color(w.value, lv_color_hex(t.background ? 0x46525E : (light_theme ? 0x616161 : (on ? 0x46525E : 0xF0F4F8))), 0);
}
}
@@ -563,6 +566,12 @@ inline bool check_tile_geometry() {
fits=fits && value.y2
+#include
+
+namespace tile_palette {
+// Match the editor palette; all backgrounds are chosen for dark text.
+inline uint32_t color(const std::string &name) {
+ if(name=="red")return 0xFADADD;
+ if(name=="orange")return 0xFFE1C6;
+ if(name=="yellow")return 0xFFF0C2;
+ if(name=="green")return 0xD9EEDC;
+ if(name=="mint")return 0xD5F0EA;
+ if(name=="blue")return 0xD9EAFB;
+ if(name=="purple")return 0xE9DDF5;
+ if(name=="pink")return 0xF7DDEC;
+ if(name=="gray")return 0xE5E7EB;
+ return 0; // Automatic/default, including unknown future palette names.
+}
+}
diff --git a/docs/EASY_SETUP.md b/docs/EASY_SETUP.md
index 3fa6b77..e6baa2f 100644
--- a/docs/EASY_SETUP.md
+++ b/docs/EASY_SETUP.md
@@ -1,12 +1,6 @@
-# Installeren vanuit ESP Screens (vanaf 0.2.0)
+# Installeren en beheren vanuit ESP Screens
-De makkelijkste route is nu één app: **ESP Screen Manager**. Deze kan zelf
-ESPHome-profielen bewaren, bouwen en via USB of OTA installeren. Zie de
-[complete stappen voor deze route](WHATS_NEW_020.md#firmware-zonder-een-tweede-beheerpagina).
-De instructies hieronder blijven bruikbaar als je ESPHome Device Builder wilt
-blijven gebruiken. Beide routes behouden dezelfde eigen YAML en secrets.
-
-# Een nieuw scherm, van USB naar dagelijks gebruik
+Een nieuw scherm, van USB naar dagelijks gebruik
Met **ESP Screen Manager** kies je je tegels in Home Assistant. Je zoekt op
naam, apparaat, ruimte of entity-ID, zet ze in de gewenste volgorde en klikt
@@ -14,7 +8,8 @@ op **Opslaan & naar scherm**. Daarna blijft de status automatisch actueel.
Je hoeft voor andere tegels **niet opnieuw te flashen**.
Dit werkt met Home Assistant OS op een 64-bits Raspberry Pi of een amd64-machine,
-ESPHome Device Builder en een van deze exacte schermvarianten:
+en een van deze exacte schermvarianten. De ESPHome-CLI zit in ESP Screen Manager;
+ESPHome Device Builder is optioneel:
| Keuze | Hardware |
| --- | --- |
@@ -25,63 +20,55 @@ Andere schermen met ongeveer dezelfde naam kunnen andere pinnen hebben. Gebruik
het bordprofiel dat bij de hardware hoort. Gebruik een USB-kabel die data ondersteunt.
De wallbox-relais worden niet gebruikt.
-## 1. Installeer de twee apps
+## 1. Installeer ESP Screen Manager
1. Open **Instellingen → Apps → Installeer een app** (in oudere HA-versies:
**Instellingen → Add-ons → Add-onwinkel**).
-2. Installeer **ESPHome Device Builder**, start deze en zet hem in de zijbalk.
- Gebruik ESPHome **2026.6.2 of nieuwer**. De release is met 2026.6.2 getest.
-3. Open in de appwinkel het menu rechtsboven → **Repositories**. Voeg toe:
+2. Open het menu rechtsboven → **Repositories** en voeg toe:
`https://github.com/MaxGramser/homeassistant_espscreen`.
-4. Zoek **ESP Screen Manager**, installeer en start deze. Zet **Starten bij
- opstarten** en **Tonen in zijbalk** aan. Open de webinterface.
+3. Installeer en start **ESP Screen Manager**. Zet **Starten bij opstarten**
+ en **Tonen in zijbalk** aan. Open de webinterface **ESP Screens**.
-De beheerpagina draait binnen Home Assistant en gebruikt je HA-aanmelding.
-Je hoeft geen extra gebruiker, MQTT, blueprint of long-lived token in te stellen.
-Installeer voor normaal gebruik de GitHub-versie: een handmatig gekopieerde lokale
-test-add-on ontvangt geen updates uit GitHub.
+Deze app bevat de geteste ESPHome 2026.6.2-CLI en draait binnen je HA-aanmelding.
+Een tweede ESPHome-beheerpagina, MQTT, blueprint of long-lived token is niet nodig.
+Gebruik de GitHub-versie voor updates; een lokale test-add-on is een andere app.
-## 2. Download je eigen installatie-YAML
+## 2. Maak en bewaar je eigen apparaatprofiel
-Klik in ESP Screens op **Nieuw scherm**. Kies CYD of Guition, een herkenbare naam
-en een unieke apparaatnaam zoals `scherm-keuken`. Klik **Maak installatie-YAML**. Gebruik **Kopieer YAML** om de tekst meteen in
-ESPHome te plakken, of **Download bestand**. Op een HTTP-verbinding kan Chrome
-downloads blokkeren; de kopieeroptie blijft bruikbaar.
+Klik **Nieuw scherm**. Kies CYD of Guition, een herkenbare naam en een unieke
+apparaatnaam, bijvoorbeeld `scherm-keuken`. Klik **Bewaar profiel in ESP Screens**.
-Bij **Bewaar profiel in ESP Screens** controleert de wizard automatisch de bestaande
-ESPHome `secrets.yaml`. Zijn `wifi_ssid` en `wifi_password` aanwezig, dan hoef je
-geen wifi in te vullen. Alleen bij een eerste installatie zonder secretsbestand
-vraagt hij die gegevens één keer. Bestaande secrets worden niet overschreven.
+De wizard controleert de bestaande ESPHome `secrets.yaml`. Met `wifi_ssid` en
+`wifi_password` aanwezig hoef je geen wifi in te vullen. Alleen als er nog geen
+secretsbestand is, vraagt de wizard je 2,4GHz-wifi eenmalig. Een bestaand bestand
+met ontbrekende of ongeldige wifi-sleutels moet je eerst herstellen; het wordt
+niet stilzwijgend overschreven.
-Dit bestand bevat unieke API- en OTA-sleutels. Bewaar het. Maak voor ieder nieuw
-scherm een nieuw bestand. Download voor een bestaand scherm niet telkens een nieuw
-bestand: daarmee zou je nieuwe sleutels genereren.
+Je apparaat-YAML bevat unieke API- en OTA-sleutels. Bewaar dit profiel en gebruik
+het opnieuw bij updates. Voor een bestaand scherm telkens een nieuw profiel
+maken genereert nieuwe sleutels en is niet de updateroute.
-Open **ESPHome Device Builder → Secrets** en vul eenmaal je 2,4GHz-wifi in:
-
-```yaml
-wifi_ssid: "Jouw wifi"
-wifi_password: "Jouw wifiwachtwoord"
-```
-
-Andere bestaande secrets mogen blijven staan. Gebruik geen gedeelde API-sleutel
-voor alle nieuwe schermen; de gedownloade YAML bevat al een eigen sleutel.
+Wil je ESPHome Device Builder gebruiken, kies dan **Maak installatie-YAML** en
+kopieer/download die YAML naar een eigen apparaat daar. Beide routes gebruiken
+dezelfde firmwarepakketten. Wifi blijft in ESPHome `secrets.yaml`; API- en
+OTA-sleutels staan in het eigen apparaatprofiel.
## 3. Eerste flash via USB op de Raspberry
-1. Klik in ESPHome op **New device**, gebruik dezelfde unieke naam en sla de
- directe installatie over. Kies zo nodig ESP32 als tijdelijke wizardkeuze.
-2. Klik **Edit** bij het nieuwe apparaat. Vervang de **hele inhoud** door je
- gedownloade YAML en sla op. Het pakket levert zelf de juiste hardwareconfiguratie.
-3. Sluit het scherm met USB aan op de Raspberry waarop Home Assistant draait.
-4. Klik **Install → Plug into the computer running ESPHome Device Builder**.
- Kies de USB-seriële poort van dit scherm. Bij meerdere schermen: sluit ze voor
- de eerste installatie één voor één aan.
-5. Wacht op een geslaagde build én upload. De eerste build kan op een Raspberry
- lang duren. Laat deze afronden. Het scherm maakt daarna verbinding met wifi.
+1. Sluit het scherm met een **USB-datakabel** aan op de machine waarop Home
+ Assistant draait. Bij meerdere borden: identificeer eerst de juiste poort,
+ of sluit ze voor de eerste installatie één voor één aan.
+2. Open **Firmware & USB** in ESP Screens en kies het zojuist bewaarde profiel.
+3. Kies bij **Installeren naar** de USB-poort van dit scherm.
+4. Klik **Bouwen & installeren** en wacht op een geslaagde build én upload.
+ De eerste build kan op een Raspberry meerdere minuten duren.
+5. Het scherm herstart en verbindt met wifi. Latere firmware-updates kunnen via
+ **Wifi / OTA** in dezelfde pagina, met het bestaande profiel.
-Een browseroptie **Plug into this computer** bedoelt de computer waarop je browser
-draait, niet de Raspberry. Kies die alleen wanneer het scherm aan die computer zit.
+Een USB-kabel aan je laptop is niet zichtbaar als USB-poort van de Raspberry.
+Gebruik voor die route de ESPHome-CLI op de laptop met je eigen profiel, of de
+browserinstallatie van ESPHome Device Builder. De stappen hierboven gebruiken
+uitsluitend ESP Screens op de HA-machine.
**CYD:** bij de eerste start verschijnt de kalibratie. Tik het zichtbare kruisje
drie keer rustig aan, houd elke tik kort vast en volg steeds het volgende kruisje.
@@ -112,7 +99,9 @@ domeinfilters met gekleurde iconen. Je kunt maximaal twintig tegels toevoegen.
Het schermvoorbeeld toont hun plaats: twee kolommen, zes tegels per pagina.
Sleep een tegel naar een andere tegel om te ordenen. Klik op een tegel voor
een eigen naam, de pijltjes voor volgorde en **Bediening & weergave instellen**:
-klikgedrag, een mini-slider of een grote waarde waar ondersteund. Een lege
+klikgedrag, een mini-slider of een grote waarde waar ondersteund. Kies bij
+**Pastel achtergrond** een eigen kleur met donkere tekst; **Standaard** herstelt
+de normale weergave. Hiervoor is firmware 0.2.10+ nodig. Een lege
tegel brengt je naar de zoekbalk; toevoegen vult de eerstvolgende vrije positie.
Het voorbeeld toont de indeling, geen live sensorwaarden.
Klik **Opslaan & naar scherm** om je wijzigingen door te sturen.
@@ -144,7 +133,7 @@ zodra het scherm terugkomt. De app moet blijven draaien voor actuele tegeldata.
| --- | --- | --- |
| Andere entiteiten, namen of volgorde | Opslaan in ESP Screens | Wifi, sleutels, kalibratie |
| Nieuwe beheerpagina/appversie | Appwinkel → ESP Screen Manager → Update | Alle indelingen in `/data/screens.json` |
-| Nieuwe schermfunctie/kaart | ESPHome → bestaand apparaat → Install → Wirelessly | Eigen YAML, sleutels en CYD-kalibratie; app stuurt tegels opnieuw |
+| Nieuwe schermfunctie/kaart | Firmware & USB → bestaand profiel → Wifi / OTA | Eigen YAML, sleutels en CYD-kalibratie; app stuurt tegels opnieuw |
De eigen YAML verwijst naar de firmwarepakketten in `main`. Bij een nieuwe build
haalt ESPHome de nieuwste gepubliceerde pakket- en componentcode op. Je vervangt
@@ -152,7 +141,7 @@ je eigen YAML dus niet door een nieuw downloadbestand. Wifi, naam en sleutels
staan buiten het gedeelde pakket en blijven gelijk.
Maak vóór updates een Home Assistant-back-up inclusief ESP Screen Manager en
-ESPHome Device Builder. **Verwijderen/herinstalleren** van een app is niet hetzelfde
+de eigen ESPHome-configuraties. **Verwijderen/herinstalleren** van een app is niet hetzelfde
als updaten; daarmee kun je de gegevensmap wissen. Houd de apparaatnaam en de
entity-ID van **Tegelinstellingen** gelijk, zodat de bestaande indeling gekoppeld blijft.
@@ -207,7 +196,9 @@ Hierna vragen wijzigingen aan deze instellingen geen nieuwe firmwareflash.
| Helderheid nacht | 0–100%, maximaal normale helderheid | 10% |
| Klok tonen | Aan/uit | Aan |
| Tijdnotatie | 24 of 12 uur, zonder AM/PM | 24 uur |
-| Terug naar pagina 1 | Bij standby; anders huidige pagina onthouden | Uit |
+| Terug naar pagina 1 | Sluit ook detailmenu’s bij standby | Uit |
+| Vegen tussen pagina’s | Native horizontale swipe, firmware 0.2.7+ | Uit |
+| Guition draaien | 0°, 90°, 180°, 270°, firmware 0.2.9+ | 0° |
Nachturen gebruiken de tijdzone van het ESPHome-apparaat en de tijd uit HA.
Zonder geldige tijd gebruikt het scherm de gewone standbyhelderheid; gelijke
@@ -226,5 +217,6 @@ Bij oudere firmware toont de beheerpagina dat eerst een update nodig is.
De tegels blijven bruikbaar. Handmatige YAML-profielen blijven hun substitutions
gebruiken; voor beheer via deze app gebruik je het Easy Setup-pakket.
-De gemelde Guition-paneelstrepen worden apart onderzocht. Deze instellingenupdate
-wijzigt geen displaytimings of driver en is geen bewezen oplossing voor die strepen.
+De huidige Guition gebruikt de native ST7701S-configuratie; zie
+[de hardwarevergelijking](GUITION_FACTORY_REFERENCE.md). Instellingen en
+tegelkleuren veranderen geen paneeltimings. Controleer beeld en touch fysiek.
diff --git a/docs/GUITION.md b/docs/GUITION.md
index a7673a1..ead615e 100644
--- a/docs/GUITION.md
+++ b/docs/GUITION.md
@@ -39,15 +39,16 @@ alleen zichtbaar voor de Guition nadat zijn nieuwe firmware door HA is ontdekt.
De CYD blijft op zijn bestaande oriëntatie met zijn eigen kalibratie.
Zes tegels van **218 × 108 pixels** per pagina, 12px tussenruimte en een
-aparte navigatiestrook. Vanaf zeven tegels verschijnt pagina twee; tot zes
-verdwijnt de navigatie. Grotere letters, iconen, dimmers en kleurkeuzes; de
-climatekaart heeft een grotere temperatuurweergave. Een egaal blauwgrijze
-achtergrond, duidelijk omlijnde blauwe uitgeschakelde tegels en lichte actieve tegels maken
-de status herkenbaar. Standby begint na tien minuten zonder aanraking.
+aparte navigatiestrook. In ESP Screen Manager passen twintig tegels op maximaal
+vier pagina’s; tot zes verdwijnt de navigatie. De huidige interface heeft een
+lichtgrijze achtergrond, witte kaarten en gekleurde domeiniconen. Vanaf 0.2.10 kun
+je per tegel een pastel achtergrond kiezen met donkere tekst. Standby begint
+standaard na tien minuten zonder aanraking en is instelbaar in de beheerpagina.
De bestaande tegelacties, climatebediening en vacuumkaart zijn behouden.
-De vacuumkaart blijft gekoppeld aan tegel 6. Posities 8/10 zijn eenvoudige
-actietegels; zie [TEGELS.md](TEGELS.md) voor de ondersteunde bindingen.
+Alle kaarttypen zijn beschikbaar op alle twintig runtime-posities. Alleen in
+het oude handmatige profiel blijft de vacuumkaart op tegel 6 en hebben posities
+8/10 beperkingen; zie [TEGELS.md](TEGELS.md).
## Nieuwe installatie
diff --git a/docs/RELEASING.md b/docs/RELEASING.md
index ea9b49f..84502d4 100644
--- a/docs/RELEASING.md
+++ b/docs/RELEASING.md
@@ -111,3 +111,13 @@ De firmware bewaart de hoek als afzonderlijke uint32 op key `0x524F5431`; de
bestaande Settings-structuur en CYD-kalibratie blijven ongewijzigd. Alleen de
Guition activeert de rotatiecallback. De diagnostische entity `Guition schermtype`
maakt deze capability herkenbaar, ook als het scherm offline of hernoemd is.
+
+### Compatibiliteit 0.2.10
+
+`tiles[].options.background` is een optionele paletnaam binnen opslagversie 1.
+Op de draad staat dit in het bestaande `o`-object. Oudere firmware negeert het
+veld; nieuwe firmware valt bij onbekende namen terug op de normale kleuren.
+De app accepteert uitsluitend het lichte palet uit `TILE_BACKGROUNDS`. Een oude
+editor die `background` weglaat behoudt de opgeslagen kleur voor dezelfde entity;
+expliciet `auto` herstelt de standaard. Geen gewijzigde voorkeurstructuur of
+sleutels, geen nieuwe firmwareflash nodig voor latere kleurkeuzes.
diff --git a/docs/TEST_RESULTS_0210.md b/docs/TEST_RESULTS_0210.md
new file mode 100644
index 0000000..8ea2235
--- /dev/null
+++ b/docs/TEST_RESULTS_0210.md
@@ -0,0 +1,24 @@
+# 0.2.10 — pastel tegelkleuren en actuele documentatie
+
+De editor levert negen lichte kleuren plus Standaard. Dezelfde paletwaarden
+worden in firmware gebruikt. De gekozen achtergrond blijft vast bij een
+statuswisseling; iconen, status en actiefeedback blijven de entiteit volgen.
+Titels zijn bij een eigen kleur donker op zowel Guition als CYD.
+
+## Uitgevoerd
+
+- 51 Python-tests geslaagd, inclusief overeenkomst tussen editor- en
+ firmwarepalet, minimaal 7:1 titelcontrast en 4,5:1 statuscontrast, validatie
+ van kleuren en behoud bij appherstart/opslaan vanuit een oude editor.
+- Alle negen C++-testprogramma's geslaagd; JavaScript-syntax gecontroleerd.
+- Beide runtimefirmwares gebouwd met ESPHome 2026.6.2 en via OTA op de vooraf
+ geïdentificeerde Guition en CYD gezet. Eigen sleutels en kalibratie behouden.
+- Ook beide handmatige firmwareprofielen bouwen succesvol.
+- Op beide schermen met hun bestaande indeling: tien paginacontroles,
+ vijftig overlaycycli en de slidertest geslaagd.
+- De renderdiagnose controleert bij gekleurde tegels ook de werkelijk
+ toegepaste LVGL-achtergrond en donkere titelkleur.
+
+De README en Easy Setup beschrijven nu de één-app-route met de ingebouwde
+ESPHome-CLI. De oude handmatige CYD-route blijft ingeklapt beschikbaar en wordt
+expliciet onderscheiden van de runtime-indeling met twintig tegels.
diff --git a/guition-4848s040.yaml b/guition-4848s040.yaml
index 276f59f..e86454b 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.9"
+ SCREEN_FIRMWARE_VERSION: "0.2.10"
DYNAMIC_TILES: "false"
DASHBOARD_INBOX_INTERNAL: "true"
DEVICE_NAME: "guition-wallbox"
diff --git a/home-like-2432s028.yaml b/home-like-2432s028.yaml
index 845f24e..29b31ba 100644
--- a/home-like-2432s028.yaml
+++ b/home-like-2432s028.yaml
@@ -58,7 +58,7 @@
############################################################
substitutions:
- SCREEN_FIRMWARE_VERSION: "0.2.9"
+ SCREEN_FIRMWARE_VERSION: "0.2.10"
DYNAMIC_TILES: "false"
DASHBOARD_INBOX_INTERNAL: "true"
DEVICE_NAME: "cyd-new"
diff --git a/packages/cyd.yaml b/packages/cyd.yaml
index 15ce60c..04c3b42 100644
--- a/packages/cyd.yaml
+++ b/packages/cyd.yaml
@@ -59,7 +59,7 @@
############################################################
substitutions:
- SCREEN_FIRMWARE_VERSION: "0.2.9"
+ SCREEN_FIRMWARE_VERSION: "0.2.10"
DYNAMIC_TILES: "true"
DASHBOARD_INBOX_INTERNAL: "false"
DEVICE_NAME: "cyd-new"
diff --git a/packages/guition.yaml b/packages/guition.yaml
index 0503497..37702eb 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.9"
+ SCREEN_FIRMWARE_VERSION: "0.2.10"
DYNAMIC_TILES: "true"
DASHBOARD_INBOX_INTERNAL: "false"
DEVICE_NAME: "guition-new"
diff --git a/screen_manager/CHANGELOG.md b/screen_manager/CHANGELOG.md
index c4a03db..383f45e 100644
--- a/screen_manager/CHANGELOG.md
+++ b/screen_manager/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.2.10
+
+- Kies per tegel een pastel achtergrond: rood, oranje, geel, groen, mint, blauw, paars, roze of grijs. Standaard herstelt de normale kleuren. De keuze is zichtbaar in het schermvoorbeeld en werkt met donkere tekst op Guition én CYD.
+- Tegelkleuren blijven behouden bij appupdates en bij opslaan vanuit een oudere beheerpagina. Installeer firmware 0.2.10 op het scherm om de kleuren weer te geven.
+- README en Easy Setup beschrijven de huidige één-app-installatie, twintig tegels, inspector, rotatie en updates via de ingebouwde ESPHome-CLI.
+
## 0.2.9
- Guition: kies 0°, 90°, 180° of 270° onder Scherminstellingen. Na opslaan draait de interface direct mee, inclusief touch. De hoek blijft bewaard na herstart; een nieuwe flash per hoek is niet nodig.
diff --git a/screen_manager/app/core.py b/screen_manager/app/core.py
index 1b743c4..ad8a522 100644
--- a/screen_manager/app/core.py
+++ b/screen_manager/app/core.py
@@ -10,6 +10,20 @@ REPO = 'https://github.com/MaxGramser/homeassistant_espscreen'
REFS = {'cyd': 'main', 'guition': 'main'}
ATTRS = frozenset('brightness percentage current_position current_temperature temperature current_humidity min_temp max_temp target_temp_step supported_color_modes hvac_modes 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 media_title options min max step temperature_unit supported_features'.split())
+
+TILE_BACKGROUNDS = {
+ 'auto': {'label': 'Standaard', 'color': None},
+ 'red': {'label': 'Rood', 'color': '#FADADD'},
+ 'orange': {'label': 'Oranje', 'color': '#FFE1C6'},
+ 'yellow': {'label': 'Geel', 'color': '#FFF0C2'},
+ 'green': {'label': 'Groen', 'color': '#D9EEDC'},
+ 'mint': {'label': 'Mint', 'color': '#D5F0EA'},
+ 'blue': {'label': 'Blauw', 'color': '#D9EAFB'},
+ 'purple': {'label': 'Paars', 'color': '#E9DDF5'},
+ 'pink': {'label': 'Roze', 'color': '#F7DDEC'},
+ 'gray': {'label': 'Grijs', 'color': '#E5E7EB'},
+}
+
# Additive schema 1 extension. An absent object retains old firmware/YAML defaults.
SETTING_RULES = {
'standby_enabled': (True, None, None),
@@ -72,8 +86,10 @@ def validate_layout(data):
item = {'entity': tile['entity'], 'name': name.strip()}
if 'options' in tile:
options = tile['options']
- if not isinstance(options, dict) or set(options) - {'tap', 'display', 'inline', 'history_hours'}:
+ if not isinstance(options, dict) or set(options) - {'tap', 'display', 'inline', 'history_hours', 'background'}:
raise ValueError('Onbekende tegelinstellingen.')
+ if 'background' in options and (not isinstance(options['background'],str) or options['background'] not in TILE_BACKGROUNDS):
+ raise ValueError('Kies een pastel achtergrondkleur uit het palet.')
choices = {'tap': ('auto', 'detail', 'toggle', 'none'), 'display': ('standard', 'watch'), 'inline': ('none', 'slider')}
domain = tile['entity'].split('.')[0]
for key, allowed in choices.items():
diff --git a/screen_manager/app/server.py b/screen_manager/app/server.py
index db30be1..19b489c 100644
--- a/screen_manager/app/server.py
+++ b/screen_manager/app/server.py
@@ -12,7 +12,7 @@ import math
from firmware import Firmware
from aiohttp import ClientSession, ClientTimeout, WSMsgType, web
-from core import discover, installation_yaml, packets, state_message, validate_layout, validate_settings
+from core import TILE_BACKGROUNDS, discover, installation_yaml, packets, state_message, validate_layout, validate_settings
LOG = logging.getLogger('screen_manager')
@@ -191,6 +191,9 @@ class Manager:
raise ValueError('Rotatie vereist een Guition met firmware 0.2.9 of nieuwer.')
old_tiles = {t['entity']:t for t in self.layouts.get(inbox,{}).get('tiles',[])}
for tile in layout['tiles']:
+ old_options=old_tiles.get(tile['entity'],{}).get('options',{})
+ if 'options' in tile and 'background' not in tile['options'] and 'background' in old_options:
+ tile['options']['background']=old_options['background']
if 'options' not in tile and 'options' in old_tiles.get(tile['entity'],{}):
tile['options'] = old_tiles[tile['entity']]['options'].copy()
known = {e['id'] for e in self.inventory()[1]}
@@ -320,7 +323,7 @@ def create_app(manager, development=False):
for screen in screens:
screen['layout'] = manager.layouts.get(screen['id'], {'title': 'Thuis', 'tiles': []})
screen['delivery'] = manager.status.get(screen['id'], 'Kies je eerste tegels')
- return web.json_response({'csrf': csrf, 'connected': manager.ha.online, 'screens': screens, 'entities': entities})
+ return web.json_response({'csrf': csrf, 'connected': manager.ha.online, 'screens': screens, 'entities': entities, 'backgrounds': TILE_BACKGROUNDS})
async def save(request):
manager.save(request.match_info['inbox'], await request.json())
return web.json_response({'saved': True})
diff --git a/screen_manager/app/static/app.js b/screen_manager/app/static/app.js
index 7768cff..361d986 100644
--- a/screen_manager/app/static/app.js
+++ b/screen_manager/app/static/app.js
@@ -249,6 +249,8 @@ function renderPreview() {
const card = node("button", undefined, "preview-tile");
if (tile) {
const name = tile.name || inventory.entities.find(e => e.id === tile.entity)?.name || tile.entity;
+ const background=inventory.backgrounds?.[tile.options?.background]?.color;
+ if(background)card.style.backgroundColor=background;
card.append(domainBadge(tile.entity), node("strong", name));
card.draggable = true;
card.ondragstart = (e) => { dragIndex = slot; e.dataTransfer.setData("text/plain", String(slot)); };
@@ -358,6 +360,27 @@ function renderTiles() {
if (!e.target.closest("input, select, button, summary, .tile-options"))
options.open = !options.open;
};
+ const palette = node("fieldset", undefined, "tile-palette");
+ palette.append(node("legend", "Pastel achtergrond"));
+ const swatches = node("div", undefined, "palette-swatches");
+ for(const [key, choice] of Object.entries(inventory.backgrounds || {})) {
+ const button=node("button", undefined, "palette-choice");
+ button.type="button";
+ button.setAttribute("aria-label", `Achtergrond: ${choice.label}`);
+ button.setAttribute("aria-pressed", String((tile.options?.background || "auto") === key));
+ const sample=node("span", undefined, "palette-sample");
+ if(choice.color)sample.style.backgroundColor=choice.color;
+ else sample.classList.add("palette-auto");
+ button.append(sample,node("span",choice.label));
+ button.onclick=()=>{
+ tile.options={...tile.options,background:key};
+ for(const other of swatches.children)other.setAttribute("aria-pressed",String(other===button));
+ markDirty();renderPreview();
+ };
+ swatches.append(button);
+ }
+ palette.append(swatches,node("small", "Donkere tekst blijft leesbaar. Kies Standaard voor de normale kleuren. Vereist schermfirmware 0.2.10+."));
+ options.append(palette);
const controls = {};
const domain = tile.entity.split(".")[0];
const fields = [
@@ -803,7 +826,7 @@ async function inspect(entity) {
card.append(
node(
"small",
- `Kleine slider: ${options.inline === "slider" ? "ja" : "nee"} · Weergave: ${options.display === "watch" ? "grote waarde" : "standaard"}`,
+ `Kleine slider: ${options.inline === "slider" ? "ja" : "nee"} · Weergave: ${options.display === "watch" ? "grote waarde" : "standaard"} · Achtergrond: ${inventory.backgrounds?.[options.background || "auto"]?.label || "Standaard"}`,
),
);
$("#inspection-summary").append(card);
diff --git a/screen_manager/app/static/style.css b/screen_manager/app/static/style.css
index 0333b93..0c9f6be 100644
--- a/screen_manager/app/static/style.css
+++ b/screen_manager/app/static/style.css
@@ -720,3 +720,13 @@ pre {
#tiles > .selected-tile { background: #f8fafc; border-color: #aac4df; }
.picker { position: sticky; top: 16px; }
@media (max-width: 800px) { .picker { position: static; } }
+
+.tile-palette { margin: 12px 0; padding: 12px; border: 1px solid #d8dfe5; border-radius: 12px; }
+.tile-palette legend { font-weight: 650; padding: 0 5px; }
+.palette-swatches { display: grid; grid-template-columns: repeat(auto-fit, minmax(66px, 1fr)); gap: 8px; margin-bottom: 10px; }
+.palette-choice { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 6px; color: #1b1b1b; background: white; border: 2px solid transparent; border-radius: 10px; font-size: 12px; }
+.palette-choice[aria-pressed="true"] { border-color: #1674ae; background: #edf6ff; }
+.palette-choice:focus-visible { outline: 3px solid #1674ae; outline-offset: 2px; }
+.palette-sample { width: 36px; height: 28px; border-radius: 7px; border: 1px solid #0002; }
+.palette-auto { background: linear-gradient(135deg, #fff 50%, #e5e7eb 50%); }
+.tile-palette small { color: #52616e; }
diff --git a/screen_manager/config.yaml b/screen_manager/config.yaml
index e1063e8..97e1829 100644
--- a/screen_manager/config.yaml
+++ b/screen_manager/config.yaml
@@ -1,5 +1,5 @@
name: ESP Screen Manager
-version: "0.2.9"
+version: "0.2.10"
slug: esp_screen_manager
description: Kies tegels voor je CYD en Guition touchscreen, zonder opnieuw flashen.
url: https://github.com/MaxGramser/homeassistant_espscreen
diff --git a/tests/test_tile_palette.cpp b/tests/test_tile_palette.cpp
new file mode 100644
index 0000000..10e18d2
--- /dev/null
+++ b/tests/test_tile_palette.cpp
@@ -0,0 +1,9 @@
+#include "components/smart_display/tile_palette.h"
+#include
+int main() {
+ assert(tile_palette::color("auto")==0);
+ assert(tile_palette::color("")==0);
+ assert(tile_palette::color("unknown-future-choice")==0);
+ assert(tile_palette::color("red")!=0);
+ assert(tile_palette::color("green")!=tile_palette::color("red"));
+}
diff --git a/tests/test_tile_palette.py b/tests/test_tile_palette.py
new file mode 100644
index 0000000..d47a00a
--- /dev/null
+++ b/tests/test_tile_palette.py
@@ -0,0 +1,39 @@
+import re
+import tempfile
+import unittest
+from pathlib import Path
+import test_portal
+from core import TILE_BACKGROUNDS, validate_layout
+
+def luminance(color):
+ rgb=[int(color[i:i+2],16)/255 for i in (1,3,5)]
+ linear=[v/12.92 if v<=0.04045 else ((v+0.055)/1.055)**2.4 for v in rgb]
+ return sum(v*w for v,w in zip(linear,(.2126,.7152,.0722)))
+
+class PaletteTests(unittest.IsolatedAsyncioTestCase):
+ def test_palette_matches_firmware_and_has_readable_text(self):
+ header=(Path(__file__).resolve().parents[1]/'components/smart_display/tile_palette.h').read_text()
+ native=dict(re.findall(r'name=="(\w+)"\)return 0x([A-F0-9]+)',header))
+ expected={k:v['color'][1:] for k,v in TILE_BACKGROUNDS.items() if v['color']}
+ self.assertEqual(native,expected)
+ for color in expected.values():
+ for foreground,minimum in (('#1B1B1B',7),('#46525E',4.5)):
+ self.assertGreaterEqual((luminance('#'+color)+.05)/(luminance(foreground)+.05),minimum)
+
+ async def test_old_editor_preserves_color_and_explicit_auto_clears(self):
+ with tempfile.TemporaryDirectory() as tmp:
+ m=test_portal.ManagerTests().setup_manager(Path(tmp)/'screens.json')
+ layout={'title':'Thuis','tiles':[{'entity':'light.a','options':{'background':'red','inline':'slider'}}]}
+ m.save('text.screen',layout)
+ m.save('text.screen',{'title':'Nieuw','tiles':[{'entity':'light.a','options':{'inline':'none'}}]})
+ fresh=test_portal.ManagerTests().setup_manager(m.path)
+ tile=fresh.layouts['text.screen']['tiles'][0]
+ self.assertEqual(tile['options'],{'background':'red','inline':'none'})
+ await fresh.sync_one('text.screen',fresh.layouts['text.screen'])
+ self.assertEqual(fresh.ha.messages[1][1]['o']['background'],'red')
+ fresh.save('text.screen',{'title':'Nieuw','tiles':[{'entity':'light.a','options':{'background':'auto'}}]})
+ self.assertEqual(fresh.layouts['text.screen']['tiles'][0]['options']['background'],'auto')
+
+ def test_unapproved_colors_rejected(self):
+ for value in ('#000000','url(test)','unknown',None,{},42):
+ with self.assertRaises(ValueError):validate_layout({'title':'Thuis','tiles':[{'entity':'light.a','options':{'background':value}}]})