CMakeLists.txt (project root -- required by ESP-IDF) sdkconfig.defaults partitions.csv README.md TODO.md main/ ├── CMakeLists.txt (the only component registration in the project) ├── idf_component.yml ├── app_config.h.example (template; copy to app_config.h) ├── app_config.h (gitignored -- credentials) ├── main.c ├── wifi/ │ ├── wifi_manager.h │ └── wifi_manager.c ├── drivers/ │ ├── i2c_bus.h (shared bus: STC8H1K28 + GT911) │ ├── i2c_bus.c │ ├── backlight/ │ │ ├── backlight.h (STC8H1K28 -- also gates GT911 reset) │ │ └── backlight.c │ ├── display/ │ │ ├── display_rgb.h │ │ └── display_rgb.c │ └── touch/ │ ├── touch_gt911.h │ └── touch_gt911.c ├── ui/ │ ├── lvgl_port.h (tick, service task, locking, pause/resume) │ ├── lvgl_port.c │ ├── ui_manager.h │ └── ui_manager.c └── api/ ├── ha_config.h (entity IDs -- tracked, not secret) ├── ha_template.h (Jinja template, built from the HA_LIGHT_LIST X-macro) ├── ha_data.h ├── ha_data.c (parsing + presentation -- SHARED with sim/) ├── ha_client.h ├── ha_client.c (esp_http_client transport -- device only) ├── ambient_weather.h └── ambient_weather.c (cloud fallback, used only when HA is unreachable) sim/ (desktop simulator -- NOT part of the IDF build) ├── CMakeLists.txt (own cmake project; idf.py never looks in here) ├── lv_conf.h (host LVGL config; font list must match sdkconfig.defaults) ├── sim_main.c (SDL window / headless PNG capture, mock data) ├── lvgl_port_sim.c (host implementation of main/ui/lvgl_port.h) ├── README.md ├── snapshots/ (captured HA responses, used as render fixtures) └── compat/ (ESP-IDF header stand-ins, so main/ui/*.c compiles as-is) ├── esp_err.h ├── esp_log.h ├── esp_check.h └── esp_compat.c tools/ ├── ha_discover.py (list Home Assistant entity IDs; reads $HA_TOKEN) └── ha_snapshot.py (capture HA responses into sim/snapshots/) Note: main/ subdirectories are plain source folders, not ESP-IDF components. IDF only auto-discovers components under a top-level components/ directory, so all sources are registered by main/CMakeLists.txt. The simulator compiles main/ui/ui_manager.c unmodified against desktop LVGL, which is why that file must stay free of hardware access. See sim/README.md.