# 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": "<lvgl 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.

### XML Runtime Integration
The user provided documentation for the `LV_USE_XML` runtime feature. We must:
1. Call `lv_xml_register_component_from_data("current_device", dev->xml_layout)` to register the XML payload.
2. Check if the XML string contains `<screen>`. If it does, LVGL expects us to instantiate it as a full screen using `lv_obj_t * root = lv_xml_create_screen("current_device");`.
3. If it does not contain `<screen>`, it's just a regular component/widget, so we create it *on* the active screen using `lv_obj_t * root = lv_xml_create(scr, "current_device", NULL);`.
4. Fallback to a string label if the XML is empty or parsing fails.

### 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`.
- **Debug Features:** A collapsed section (e.g. `<details>`) in the UI will contain a button to "Register Debug Device" that triggers a POST to `/api/devices/register` with a random or hardcoded MAC (e.g., `00:11:22:33:44:55`).
- **Integration:** The `App.svelte` router will be updated to include the `'devices'` view state alongside Dashboard, Tasks, and Users.
