Use native Guition panel configuration and add light card styling

This commit is contained in:
Max Gramser
2026-09-12 14:41:47 +02:00
parent 917b3d6d09
commit 6cddac8ec6
10 changed files with 679 additions and 415 deletions
+2 -2
View File
@@ -100,7 +100,7 @@ inline lv_obj_t *plain(lv_obj_t *parent, int x, int y, int w, int h) {
lv_obj_set_pos(obj, x, y); lv_obj_set_size(obj, w, h);
return obj;
}
inline void setup(lv_obj_t *parent, const lv_font_t *font, int width, int height) {
inline void setup(lv_obj_t *parent, const lv_font_t *font, int width, int height, bool light_theme = false) {
if (ready) return;
ready = true;
bool large = width >= 480;
@@ -114,7 +114,7 @@ inline void setup(lv_obj_t *parent, const lv_font_t *font, int width, int height
auto &row = rows[i]; row.index = i;
row.box = plain(parent, margin, top + i * spacing, w, spacing - 2);
lv_obj_set_style_text_font(row.box, font, 0);
lv_obj_set_style_text_color(row.box, lv_color_hex(0xEEEEF2), 0);
lv_obj_set_style_text_color(row.box, lv_color_hex(light_theme ? 0x1B1B1B : 0xEEEEF2), 0);
auto *label = lv_label_create(row.box); lv_label_set_text(label, names[i]);
lv_obj_set_pos(label, 2, 0);
row.value = lv_label_create(row.box); lv_obj_align(row.value, LV_ALIGN_TOP_RIGHT, -2, 0);
+9 -5
View File
@@ -13,6 +13,7 @@
namespace runtime_tiles {
inline bool enabled = false;
inline bool light_theme = false;
inline Model model;
inline int active_index = -1;
inline uint32_t last_received = 0;
@@ -210,11 +211,14 @@ inline void render(lv_obj_t *room) {
if (w.cached_active == static_cast<int>(on)) continue;
w.cached_active = on;
uint32_t accent = t.domain() == "light" ? 0xE4B23C : t.domain() == "climate" ? 0xF18750 : 0x548BD4;
lv_obj_set_style_bg_color(w.tile, lv_color_hex(on ? 0xF5F1E8 : 0x202A38), 0);
lv_obj_set_style_bg_color(w.circle, lv_color_hex(on ? accent : 0x303A48), 0);
lv_obj_set_style_text_color(w.icon, lv_color_hex(on ? 0xFFFFFF : accent), 0);
lv_obj_set_style_text_color(w.title, lv_color_hex(on ? 0x172232 : 0xF3F5F7), 0);
lv_obj_set_style_text_color(w.value, lv_color_hex(on ? 0x46525E : 0xAFBAC8), 0);
lv_obj_set_style_bg_color(w.tile, lv_color_hex(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_bg_color(w.circle, lv_color_hex(light_theme ? (on ? 0xD5EEFC : 0xF0F0F0) : (on ? accent : 0x263B50)), 0);
lv_obj_set_style_text_color(w.icon, lv_color_hex(light_theme ? (on ? 0x009FE3 : 0x9E9E9E) : (on ? 0x172232 : 0xF3F5F7)), 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);
}
}
inline std::string vacuum_option(unsigned index) {
+43
View File
@@ -0,0 +1,43 @@
"""Observe a screen over its encrypted API; does not wake it or operate HA devices."""
import argparse
import asyncio
from pathlib import Path
import time
import yaml
from aioesphomeapi import APIClient, LogLevel
async def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--host', required=True)
parser.add_argument('--name', required=True)
parser.add_argument('--seconds', type=int, default=720)
parser.add_argument('--secrets', type=Path, default=Path(__file__).resolve().parents[1] / 'secrets.yaml')
args = parser.parse_args()
if not 1 <= args.seconds <= 86400:
parser.error('--seconds must be between 1 and 86400')
key = yaml.safe_load(args.secrets.read_text())['api_encryption_key']
client = APIClient(args.host, 6053, noise_psk=key, expected_name=args.name,
client_info='Read-only display observation')
await client.connect(login=True)
try:
info = await client.device_info()
print(f'Connected: {info.name}, firmware {info.project_version}, {info.compilation_time}', flush=True)
_, actions = await client.list_entities_services()
state_action = next(action for action in actions if action.name == 'ui_state')
def log(message):
line = message.message.decode(errors='replace') if isinstance(message.message, bytes) else message.message
if '[ui_diag' in line or '[health' in line:
print(line, flush=True)
client.subscribe_logs(log, log_level=LogLevel.LOG_LEVEL_DEBUG, dump_config=False)
end = time.monotonic() + args.seconds
while time.monotonic() < end:
await client.execute_service(state_action, {})
await asyncio.sleep(min(30, max(0, end - time.monotonic())))
# Confirms a live API response, not just a local connection flag.
await client.device_info()
print('Observation completed. Physical display quality still requires visual inspection.', flush=True)
finally:
await client.disconnect()
if __name__ == '__main__':
asyncio.run(main())
+6 -4
View File
@@ -6,8 +6,10 @@ ST7701S RGB-display en **GT911 capacitieve touch**, ESP32-S3, 16 MB flash en
altijd de elektronica. Dit profiel configureert geen relais.
Hardwarebron: [ESPHome Guition-bordbeschrijving](https://devices.esphome.io/devices/guition-esp32-s3-4848s040/)
en het ingebouwde [MIPI RGB-model](https://esphome.io/components/display/mipi_rgb/).
De daadwerkelijk geïnstalleerde ESPHome 2026.6.2 bevat dit model.
en de ingebouwde [ST7701S-driver](https://esphome.io/components/display/st7701s/).
De paneelconfiguratie volgt de originele, fysiek schoon bevonden fabrikantdemo;
zie [de vergelijking](GUITION_FACTORY_REFERENCE.md). Er is geen lokale RGB-driver
of periodiek herstel-script nodig. De GT911 gebruikt ongemirrorde coördinaten.
## Bestanden
@@ -25,8 +27,8 @@ geen `calibration.yaml` of XPT2046-correctie over.
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 donkerblauwe
achtergrond, gedempte uitgeschakelde tegels en lichte actieve tegels maken
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.
De bestaande tegelacties, climatebediening en vacuumkaart zijn behouden.
+93
View File
@@ -0,0 +1,93 @@
# Guition 4848S040: onafhankelijke fabrikanttest
Deze test onderscheidt onze ESPHome/UI van de oorspronkelijke paneelaansturing.
Een passende demo is gevonden in het fabrikantpakket dat SpotPear aanbiedt:
- [Verkoper met downloadlink](https://spotpear.com/ESP32-S3-4-inch-LCD-Touchscreen-Display-SHT20-Temperature-Humidity-480x480-RS485-Relay-GC9503-ST7701-FT6336U-GT911-86-TVbox/forum-answer/357.html)
- [Origineel archief](https://cdn.static.spotpear.com/uploads/picture/learn/ESP32/ESP32-S3-4inch/4.0inch_ESP32-4848S040.zip)
- [Community-uitleg voor bouwen met LVGL 8/9](https://github.com/paulhamsh/LVGL_GUITION)
In het archief:
- `1-Demo/Demo_Arduino/1_2_4.0_LvglWidgets/4.0_LvglWidgets/`: broncode.
- `1-Demo/Demo_Arduino/Libraries/Arduino_GFX-master/`: meegeleverde paneeldriver.
- `8-Burn operation/Burn files/4.0_LvglWidgets.bin`: samengevoegde firmware,
652640 bytes, flashadres 0. Bootloader op 0, partitietabel op 0x8000,
applicatie op 0x10000.
- SHA256 van die demo: `9662c193fc52407bba2ed1462c582d40148f7d8eedfc806db4e8fff7685abba1`.
Gebruik uitsluitend de Widgets-demo voor de displaytest. De andere meegeleverde
voorbeelden zijn relay- en muziektoepassingen. De Widgets-bron gebruikt alleen
paneel, GT911 en achtergrondverlichting en heeft geen HA-/wifi-configuratie.
## Opvallende referentie-instellingen
De oorspronkelijke bron gebruikt ArduinoGFX, `st7701_type1_init_operations`,
16 MHz pixelklok, RGB-pinnen R=11/12/13/14/0, G=8/20/3/46/9/10,
B=4/5/6/7/15. Horizontaal front/pulse/back=10/8/50; verticaal=10/8/20.
De paneelmodus is 0x3A=0x60, RGB666 op een 16-bits bedrade bus, met 0xCD=0x00.
Rotatie 0 schrijft C7=0 in registerbank 0x10 en MADCTL=0 in bank 0.
De achtergrondverlichting staat via GPIO38 continu aan, zonder PWM.
De gamma- en spanningswaarden lijken op ESPHome's ST7701S-basis, maar de volgorde,
porches en rotatiecommando's verschillen. Dat is aanleiding voor vergelijking,
geen bewijs dat een specifieke wijziging strepen oplost.
## Back-up en terugkeer
Verifieer eerst de chip/MAC en de echte USB-poort. Een volledige flashback-up is
het veiligst. Voor deze specifieke demo worden alleen sectoren binnen de eerste
1 MiB overschreven. Een exacte back-up van dat bereik is voldoende om die
overschrijving terug te draaien als de demo geen andere flashgebieden beschrijft.
Bewaar daarnaast de eigen firmware/YAML. Flashback-ups bevatten persoonlijke
sleutels: alleen lokaal opslaan, nooit in Git.
Gebruik geen erase-all. Herstel na de test de geback-upte sectoren en verifieer de
ESPHome-identiteit, versie, voorkeuren en HA-koppeling. Leg het fysieke resultaat
apart vast: een succesvolle upload zegt niets over het zichtbare beeld.
## Uitgevoerde test op 12 september 2026
- Fabrikantarchief opgehaald en de Widgets-bron plus meegeleverde driver gelezen.
- De eerste 1 MiB (alle door deze demo overschreven sectoren) via USB geback-upt;
921600/460800 baud gaven communicatieproblemen, 115200 baud werkte.
- Originele Widgets-binary ongewijzigd op adres 0 geschreven; hashverificatie geslaagd.
- USB-bootlog meldt `LVGL Widgets Demo` en `Setup done`, vervolgens GT911-polling.
Ook meldt de oorspronkelijke demo GPIO-fouten bij het opzetten; daarom blijft
fysieke bevestiging nodig. De eigenaar bevestigt daarna: de fabrikantdemo is schoon, zonder strepen.
Daarmee is goede beeldweergave op dit fysieke paneel aangetoond. Dit sluit
niet iedere hardware-/voedingsfactor uit, maar maakt onze aansturing de
eerste onderzoekslijn.
## Native ESPHome-kandidaat 0.1.3
De standaard `st7701s`-component krijgt dezelfde 16 MHz-klok en horizontale
10/8/50 en verticale 10/8/20 timing als de fabrikantdemo. SPI gebruikt MODE0;
kleurvolgorde RGB, geen inversie of hardwarematige spiegeling. Na de ingebouwde
initialisatie worden de 16-bits busconfiguratie CD=00 en de standaard registerbank
expliciet gekozen, gevolgd door 3A=60 en de wachttijd na sleep-out. De GT911 blijft
in dezelfde ongemirrorde oriëntatie. De ESPHome RGB-driver wordt niet gekopieerd
of gepatcht; de eerdere extra SDK-cache-/restart-instellingen zijn verwijderd.
De eigenaar meldt dat deze kandidaat schoner is, maar nog enkele vaste verticale
banden toont. Dit is dus geen afgeronde oplossing. De fabrikantdemo blijft de
schone referentie. Verschillen zijn onder meer ESP-IDF/LVGL-versies, framebuffer-
en bouncebuffergebruik, wifi/API, PWM en de extra initialisatiestappen van de
standaarddriver (inclusief software-reset). Native betekent hier de ingebouwde
ESPHome-driver, niet byte-voor-byte dezelfde uitvoering als de fabrikantdemo.
Daarna is alleen de Guition-interface licht vormgegeven: achtergrond F7F7F7,
witte kaarten, rand DDDDDD, donkere tekst en blauwe accenten. Ook de detailkaarten
gebruiken lichte bedieningselementen. Geometrie, paginering, touch, HA-indeling en
opgeslagen instellingen blijven behouden. Deze stijlwijziging is geen strepenfix.
### Lichte interface: softwarecontrole
De Python-suite (33 tests), zeven C++-regressietests en packagegeneratorcontrole
slagen. De eerste lichte firmware is via OTA geïnstalleerd; tien paginacontroles
plus vijftig overlaywisselingen slagen zonder HA-acties. De schermopname toont de
nieuwe lichte kaarten met de bestaande HA-indeling. Lange namen kregen daarna
vaste regelhoogtes met afkapping, zodat titel en status niet overlappen.
Een interne schermopname bewijst geen storingsvrije fysieke paneeluitvoer.
+256 -198
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -777,7 +777,7 @@ esphome:
min_version: 2026.6.2
project:
name: maxgramser.esp_screen
version: "0.1.2"
version: "0.1.3"
includes:
- cyd_ui.h
- light_controls.h
@@ -1498,7 +1498,7 @@ text_sensor:
- platform: template
name: "Schermfirmware"
entity_category: diagnostic
lambda: 'return {"0.1.2"};'
lambda: 'return {"0.1.3"};'
update_interval: 60s
- platform: template
id: smartdisplay_action
+2 -2
View File
@@ -781,7 +781,7 @@ esphome:
min_version: 2026.6.2
project:
name: maxgramser.esp_screen
version: "0.1.2"
version: "0.1.3"
includes:
- <esphome/components/smart_display/cyd_ui.h>
- <esphome/components/smart_display/light_controls.h>
@@ -1451,7 +1451,7 @@ text_sensor:
- platform: template
name: "Schermfirmware"
entity_category: diagnostic
lambda: 'return {"0.1.2"};'
lambda: 'return {"0.1.3"};'
update_interval: 60s
- platform: template
id: smartdisplay_action
+256 -198
View File
File diff suppressed because it is too large Load Diff
+10 -4
View File
@@ -13,14 +13,20 @@ VALUES=dict(re.findall(r'^ (\w+): "([^"]*)"',SOURCE,re.M))
class GuitionTests(unittest.TestCase):
def test_hardware_is_s3_rgb_with_capacitive_touch(self):
self.assertIn('model: GUITION-4848S040',SOURCE)
self.assertIn('platform: st7701s',SOURCE)
self.assertNotIn('platform: mipi_rgb',SOURCE)
self.assertIn('platform: gt911',SOURCE)
self.assertIn('mode: octal',SOURCE)
self.assertNotIn('platform: xpt2046',SOURCE)
self.assertNotIn('set_raw_correction',SOURCE)
self.assertEqual(VALUES['TOUCH_MIRROR_X'],'true')
self.assertEqual(VALUES['TOUCH_MIRROR_Y'],'true')
self.assertEqual(VALUES['TOUCH_MIRROR_X'],'false')
self.assertEqual(VALUES['TOUCH_MIRROR_Y'],'false')
self.assertIn('pclk_frequency: 16MHz',SOURCE)
self.assertIn('hsync_back_porch: 50',SOURCE)
self.assertIn('vsync_back_porch: 20',SOURCE)
self.assertIn('spi_mode: MODE0',SOURCE)
self.assertIn('[0x3A, 0x60]',SOURCE)
self.assertNotIn('CONFIG_LCD_RGB_RESTART_IN_VSYNC',SOURCE)
self.assertIn('execute_from_psram: true',SOURCE)
self.assertNotIn('id: output_red',SOURCE)
self.assertEqual(VALUES['DISPLAY_W'],'480')
@@ -46,7 +52,7 @@ class GuitionTests(unittest.TestCase):
def test_all_tiles_guard_events_and_clip_long_titles(self):
for i in range(1,11):
self.assertEqual(SOURCE.count(f'cyd::touch_guard.accept(millis(), {i})'),2)
self.assertRegex(SOURCE,rf'id: t{i}_title\n\s+width: 130\n\s+long_mode: DOT')
self.assertRegex(SOURCE,rf'id: t{i}_title\n\s+height: 24\n\s+width: 130\n\s+long_mode: DOT')
self.assertEqual(VALUES['AUTO_DIM_TIMEOUT'],'600')
def test_gt911_verification_accepts_pixels_and_rejects_wrong_orientation(self):