# Device Screens Management **Authored by Antigravity** **Date:** 2026-03-15 --- ## 1. What (Goal) The goal is to enable the Calendink Provider to act as a backend screen generator for dump clients (like e-ink devices) connected to the network. We need to implement a system that: - Allows devices to register themselves via their MAC address. - Provides a Frontend "Device Manager" interface where a user can upload and assign a custom LVGL XML string to each registered device. - Generates a custom PNG image on the fly when a device requests its screen, by parsing its assigned XML string using the LVGL XML runtime. - Temporarily stores this data in RAM (as per the current project architecture), deferring persistent storage (like SQLite) to a later phase. ## 2. Why (Reasoning) Dumb e-ink clients (like the TRMNL) typically cannot run complex UI frameworks or parse rich data formats like JSON to render screens themselves. They simply download and display a static image buffer. By having the ESP32-S3 Provider generate these images using LVGL's headless rendering capabilities: 1. **Centralized Configuration:** The user can design and assign screens for all their devices from a single web dashboard. 2. **Dynamic UI:** Using the new `LV_USE_XML` feature in LVGL 9.4+, the layout is completely decoupled from the C++ firmware. Users can radically change what a display looks like by simply uploading a new XML string via the web interface, without needing to recompile or flash the ESP32. 3. **Payload Efficiency:** Returning a URL `{"image_url": "/api/devices/screen.png"}` in the JSON response instead of a base64 encoded binary prevents massive memory spikes and reduces transmission time for the constrained devices. 4. **Consistency:** Storing user settings in BSS static arrays aligns with the existing non-persistent data models (like Tasks). It avoids heap fragmentation risks on the ESP32 until a proper SQLite database is integrated. ## 3. How (Implementation Details) ### Backend Storage & State Similar to the `Todo` app from `tdd/todo_list.md`, we use static arrays in the BSS segment to manage devices. The structure holds the device MAC, an `active` flag, and a statically allocated string buffer (2048 bytes) to store the uploaded LVGL XML. ```cpp struct Device { char mac[18]; bool active; char xml_layout[2048]; }; extern Device g_Devices[8]; ``` ### API Endpoints The following REST endpoints handle the device lifecycle and image generation: | Method | Endpoint | Description | |--------|----------|-------------| | `GET` | `/api/devices` | Returns a JSON array of all active devices, including whether they have a custom XML layout set. | | `POST` | `/api/devices/register` | Accepts `{"mac": "..."}`. Claims a slot in `g_Devices` if not already registered. | | `POST` | `/api/devices/layout` | Accepts `{"mac": "...", "xml": ""}`. Stores the XML string in the device's struct buffer. | | `GET` | `/api/devices/screen?mac=XX` | Returns `{"image_url": "/api/devices/screen.png?mac=XX"}` (TRMNL API pattern). | | `GET` | `/api/devices/screen.png?mac=XX` | Core rendering endpoint. Claims `g_LvglMutex`, clears the screen, parses the `xml_layout` buffer using `lv_xml_create()`, forces a refresh `lv_refr_now()`, encodes the buffer to PNG using `lodepng`, and streams the response. | ### Subsystems Config The ESP-IDF project configuration (`sdkconfig.defaults`) must be modified to enable the `CONFIG_LV_USE_XML=y` flag, which compiles the LVGL XML parser component into the firmware image. ### Frontend - **DeviceManager.svelte:** A new component accessible from the Sidebar. It fetches the device list on load. - **XML Uploading:** For each device card, a text area allows the user to paste an LVGL XML string. Clicking "Save Layout" updates the device via `POST /api/devices/layout`. - **Integration:** The `App.svelte` router will be updated to include the `'devices'` view state alongside Dashboard, Tasks, and Users.