diff --git a/README.md b/README.md index fba6433..f2c7ef2 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,58 @@ # LED Grab -Ambient lighting system that captures screen content and drives LED strips in real time. Supports WLED, Adalight, AmbileD, and DDP devices with audio-reactive effects, pattern generation, and automated profile switching. +Ambient lighting system that captures screen content and drives LED strips and smart lights in real time. Supports a wide range of devices — WLED, DDP, Adalight, smart bulbs, PC peripherals, Bluetooth strips, and more — with audio-reactive effects, pattern generation, and condition-based automation. + +**Free and open source.** LedGrab is released under the [MIT license](LICENSE) — free to use, modify, and self-host, with no accounts, telemetry, or cloud dependency. Everything runs locally on your own machine and network. ## What It Does -The server captures pixels from a screen (or Android device via ADB), extracts border colors, applies post-processing filters, and streams the result to LED strips at up to 60 fps. A built-in web dashboard provides device management, calibration, live LED preview, and real-time metrics — no external UI required. +The server captures pixels from a screen (or from a connected Android phone via ADB), extracts border colors, applies a post-processing filter pipeline, and streams the result to your LED devices at up to 60 fps. A built-in web dashboard provides device management, calibration, a visual wiring editor, live LED preview, and real-time metrics — no external UI required. -A Home Assistant integration exposes devices as entities for smart home automation. +A separate Home Assistant integration exposes devices as entities for smart-home automation. + +## Screenshots + +![LedGrab dashboard](docs/screenshots/dashboard.PNG) + +*Dashboard — live system performance, integrations, automations, and scene presets at a glance.* + +![Channels view](docs/screenshots/dashboard-targets.PNG) + +*Channels — start, stop, and monitor each source-to-device pipeline with live FPS.* + +![Live capture preview](docs/screenshots/test-preview-capture.PNG) + +*Live preview — inspect the processed capture output in real time before it reaches the LEDs.* ## Features ### Screen Capture - Multi-monitor support with per-target display selection -- 6 capture engine backends — MSS (cross-platform), DXCam, BetterCam, Windows Graphics Capture (Windows), Scrcpy (Android via ADB), Camera/Webcam (OpenCV) +- Capture engine backends: MSS (cross-platform), DXCam, BetterCam, Windows Graphics Capture (Windows only), and Camera/Webcam (OpenCV) +- Capture from a connected Android phone's screen via scrcpy (ADB) — the device is a *source*; LedGrab itself runs on your desktop - Configurable capture regions, FPS, and border width -- Capture templates for reusable configurations +- Reusable capture templates ### LED Device Support -- WLED (HTTP/UDP) with mDNS auto-discovery -- Adalight (serial) — Arduino-compatible LED controllers -- AmbileD (serial) -- DDP (Distributed Display Protocol, UDP) -- OpenRGB — PC peripherals (keyboard, mouse, RAM, fans, LED strips) -- Serial port auto-detection and baud rate configuration +LedGrab speaks many protocols, so a single setup can drive everything from a DIY strip to off-the-shelf smart bulbs: + +![Device type picker](docs/screenshots/devices.PNG) + +- **Network LED controllers** — WLED (HTTP/UDP, with mDNS auto-discovery), DDP (Pixelblaze, ESPixelStick, Falcon), Open Pixel Control (OPC), Art-Net / sACN (E1.31), ESP-NOW, and generic WebSocket streaming +- **Serial / direct hardware** — Adalight (Arduino-compatible), AmbiLED, SPI-attached strips (e.g. WS2812B), and USB HID controllers +- **Smart bulbs & panels** — Philips Hue (Entertainment API), Nanoleaf, Yeelight, WiZ, LIFX, and Govee (Wi-Fi LAN) +- **Bluetooth LE strips** — SP110E, Triones / HappyLighting, Zengge, and Govee BLE +- **PC peripherals** — OpenRGB, Razer Chroma, and SteelSeries GameSense (keyboards, mice, RAM, fans, etc.) +- **Device groups** — combine multiple devices into one logical target +- Serial port auto-detection and baud-rate configuration ### Color Processing -- Post-processing filter pipeline: brightness, gamma, saturation, color correction, auto-crop, frame interpolation, pixelation, flip +- Post-processing filter pipeline: brightness, gamma, saturation, color correction, auto-crop, frame interpolation, pixelation, flip, and more - Reusable post-processing templates -- Color strip sources: audio-reactive, pattern generator, composite layering, audio-to-color mapping +- Color strip sources: audio-reactive, pattern generator, gradients, composite layering, and audio-to-color mapping - Pattern templates with customizable effects ### Audio Integration @@ -38,17 +60,20 @@ A Home Assistant integration exposes devices as entities for smart home automati - Multichannel audio capture from any system device (input or loopback) - WASAPI engine on Windows, Sounddevice (PortAudio) engine on Linux/macOS - Per-channel mono extraction -- Audio-reactive color strip sources driven by frequency analysis +- Audio filter / processing pipeline feeding audio-reactive color sources driven by frequency analysis ### Automation -- Profile engine with condition-based switching (time of day, active window, etc.) -- Dynamic brightness value sources (schedule-based, scene-aware) -- Key Colors (KC) targets with live WebSocket color streaming +- Automations engine with condition-based rules — switch targets, scenes, or brightness by time of day, active window/process, MQTT, webhooks, or game events +- Scene presets for one-click lighting changes +- Dynamic value sources for brightness and other parameters (schedule-based, weather-based, scene-aware) +- Weather sources, clock sync, webhooks, and inbound/outbound HTTP endpoints +- Game integration adapters (e.g. League of Legends) ### Dashboard -- Web UI at `http://localhost:8080` — no installation needed on the client side +- Web UI at `http://localhost:8080` — nothing to install on the client side +- Visual node-graph editor for wiring sources → processing → targets - Progressive Web App (PWA) — installable on phones and tablets with offline caching - Responsive mobile layout with bottom tab navigation - Device management with auto-discovery wizard @@ -59,32 +84,56 @@ A Home Assistant integration exposes devices as entities for smart home automati ### Home Assistant Integration -- HACS-compatible custom component +- HACS-compatible custom component (separate repository) - Light, switch, sensor, and number entities per device -- Real-time metrics via data coordinator +- Real-time metrics via a data coordinator - WebSocket-based live LED preview in HA -## Requirements +## Platforms -- Python 3.11+ (or Docker) -- A supported LED device on the local network or connected via USB -- Windows, Linux, or macOS — all core features work cross-platform +LedGrab runs as a desktop / server application: -### Platform Notes +| Platform | Status | Notes | +| -------- | ------ | ----- | +| Windows | ✅ Supported | Installer (`.exe`) and portable ZIP; all capture/audio backends | +| Linux | ✅ Supported | Tarball and Docker image; X11 capture (Wayland in-container capture not supported) | +| macOS | ✅ Supported | Runs from source / Docker; MSS capture | +| Docker | ✅ Supported | Multi-arch container image | +| Android (TV) | ⚠️ Experimental | An on-device Android-TV build exists (APK attached to releases) but is emulator-verified only and **not officially supported** | + +> **There is no production Android app.** Android phones are only supported as a *capture source* (via scrcpy/ADB) from a desktop host. The on-device Android-TV build is experimental. + +### Feature support by OS | Feature | Windows | Linux / macOS | | ------- | ------- | ------------- | | Screen capture | DXCam, BetterCam, WGC, MSS | MSS | | Webcam capture | OpenCV (DirectShow) | OpenCV (V4L2) | | Audio capture | WASAPI, Sounddevice | Sounddevice (PulseAudio/PipeWire) | -| GPU monitoring | NVIDIA (pynvml) | NVIDIA (pynvml) | -| Android capture | Scrcpy (ADB) | Scrcpy (ADB) | +| GPU monitoring | NVIDIA (nvidia-ml-py) | NVIDIA (nvidia-ml-py) | +| Capture from Android phone | scrcpy (ADB) | scrcpy (ADB) | +| Notification capture | WinRT | dbus (Linux) | | Monitor names | Friendly names (WMI) | Generic ("Display 0") | -| Profile conditions | Process/window detection | Not yet implemented | +| Automation: window/process conditions | Supported | Partial | + +## Requirements + +- Python 3.11+ (or Docker) +- A supported LED device on the local network, connected via USB/serial, or reachable over Bluetooth +- Windows, Linux, or macOS ## Quick Start -### Docker (recommended) +### Prebuilt downloads + +Grab a ready-to-run build from the [Releases page](https://git.dolgolyov-family.by/alexei.dolgolyov/ledgrab/releases): + +- **Windows** — `LedGrab--setup.exe` (installer, no admin required) or `LedGrab--win-x64.zip` (portable) +- **Linux** — `LedGrab--linux-x64.tar.gz` +- **Docker** — see below +- **Android TV** — `.apk` (experimental, see [Platforms](#platforms)) + +### Docker (recommended for servers) ```bash git clone https://git.dolgolyov-family.by/alexei.dolgolyov/ledgrab.git @@ -115,11 +164,11 @@ export PYTHONPATH=$(pwd)/src # Linux/Mac uvicorn ledgrab.main:app --host 0.0.0.0 --port 8080 ``` -Open **http://localhost:8080** to access the dashboard. +Open to access the dashboard. -> **Important:** The default API key is `development-key-change-in-production`. Change it before exposing the server outside localhost. See [INSTALLATION.md](INSTALLATION.md) for details. +> **Network access:** By default, LedGrab allows anonymous access only from `localhost`. Any request from another machine on your LAN is rejected unless you configure an API key (`auth.api_keys`). Set a key before exposing the server on your network — see [INSTALLATION.md](INSTALLATION.md). -See [INSTALLATION.md](INSTALLATION.md) for the full installation guide, including configuration, Docker manual builds, and Home Assistant setup. +See [INSTALLATION.md](INSTALLATION.md) for the full installation guide, including configuration, Docker manual builds, and CORS setup. ## Demo Mode @@ -133,50 +182,9 @@ docker compose run -e LEDGRAB_DEMO=true server # Python LEDGRAB_DEMO=true uvicorn ledgrab.main:app --host 0.0.0.0 --port 8081 - -# Windows (installed app) -set LEDGRAB_DEMO=true -LedGrab.bat ``` -Demo mode uses port **8081**, config file `config/demo_config.yaml`, and stores data in `data/demo/` (separate from production data). It can run alongside the main server. - -## Architecture - -```text -ledgrab/ -├── server/ # Python FastAPI backend -│ ├── src/ledgrab/ -│ │ ├── main.py # Application entry point -│ │ ├── config.py # YAML + env var configuration -│ │ ├── api/ -│ │ │ ├── routes/ # REST + WebSocket endpoints -│ │ │ └── schemas/ # Pydantic request/response models -│ │ ├── core/ -│ │ │ ├── capture/ # Screen capture, calibration, pixel processing -│ │ │ ├── capture_engines/ # MSS, DXCam, BetterCam, WGC, Scrcpy, Camera backends -│ │ │ ├── devices/ # WLED, Adalight, AmbileD, DDP, OpenRGB clients -│ │ │ ├── audio/ # Audio capture engines -│ │ │ ├── filters/ # Post-processing filter pipeline -│ │ │ ├── processing/ # Stream orchestration and target processors -│ │ │ └── profiles/ # Condition-based profile automation -│ │ ├── storage/ # JSON-based persistence layer -│ │ ├── static/ # Web dashboard (vanilla JS, CSS, HTML) -│ │ │ ├── js/core/ # API client, state, i18n, modals, events -│ │ │ ├── js/features/ # Feature modules (devices, streams, targets, etc.) -│ │ │ ├── css/ # Stylesheets -│ │ │ └── locales/ # en.json, ru.json, zh.json -│ │ └── utils/ # Logging, monitor detection -│ ├── config/ # default_config.yaml -│ ├── tests/ # pytest suite -│ ├── Dockerfile -│ └── docker-compose.yml -├── docs/ -│ ├── API.md # REST API reference -│ └── CALIBRATION.md # LED calibration guide -├── INSTALLATION.md -└── LICENSE # MIT -``` +Demo mode uses port **8081**, config file `config/demo_config.yaml`, and stores data under `data/demo/` (separate from production data). It can run alongside the main server. ## Configuration @@ -187,14 +195,15 @@ server: host: "0.0.0.0" port: 8080 log_level: "INFO" + cors_origins: + - "http://localhost:8080" auth: - api_keys: - dev: "development-key-change-in-production" - -storage: - devices_file: "data/devices.json" - templates_file: "data/capture_templates.json" + # Empty (default) → loopback-only anonymous access; LAN requests are rejected. + # Add a key to enable LAN/remote access (generate one with: openssl rand -hex 32). + api_keys: {} + # api_keys: + # dev: "your-secret-key-here" logging: format: "json" @@ -202,25 +211,26 @@ logging: max_size_mb: 100 ``` -Environment variable override example: `LEDGRAB_SERVER__PORT=9090`. +- Application data is stored in a SQLite database (`data/ledgrab.db` by default). Set `LEDGRAB_DATA_DIR` to relocate the data root (database + assets). +- Environment variable override example: `LEDGRAB_SERVER__PORT=9090`. + +See [INSTALLATION.md](INSTALLATION.md) and [`server/.env.example`](server/.env.example) for the full configuration reference. ## API -The server exposes a REST API (with Swagger docs at `/docs`) covering: +The server exposes a REST API (with interactive Swagger docs at `/docs`) plus WebSocket endpoints. Resources include: - **Devices** — CRUD, discovery, validation, state, metrics -- **Capture Templates** — Screen capture configurations -- **Picture Sources** — Screen capture stream definitions -- **Picture Targets** — LED target management, start/stop processing -- **Post-Processing Templates** — Filter pipeline configurations -- **Color Strip Sources** — Audio, pattern, composite, mapped sources -- **Audio Sources** — Multichannel and mono audio device configuration -- **Pattern Templates** — Effect pattern definitions -- **Value Sources** — Dynamic brightness/value providers -- **Key Colors Targets** — KC targets with WebSocket live color stream -- **Profiles** — Condition-based automation profiles +- **Capture Templates** & **Picture Sources** — screen capture configuration and stream definitions +- **Output Targets** — LED target management, start/stop processing, live color stream +- **Post-Processing Templates** — filter pipeline configurations +- **Color Strip Sources**, **Pattern Templates**, **Gradients** — color generation +- **Audio Sources / Templates / Filters** — audio capture and reactive processing +- **Value Sources**, **Weather Sources**, **Scene Presets** — dynamic parameters and presets +- **Automations**, **Webhooks**, **HTTP Endpoints**, **Game Integration** — triggers and rules +- **MQTT** & **Home Assistant** — broker sources and HA integration -All endpoints require API key authentication via `X-API-Key` header or `?token=` query parameter. +Authentication uses a Bearer token (`Authorization: Bearer `) when API keys are configured; loopback requests are anonymous by default. WebSocket connections authenticate via a first-message handshake. See [docs/API.md](docs/API.md) for the full reference. @@ -253,16 +263,16 @@ ruff check src/ tests/ Optional extras: ```bash -pip install -e ".[perf]" # High-performance capture engines (Windows) -pip install -e ".[camera]" # Webcam capture via OpenCV +pip install -e ".[perf]" # High-performance capture engines (Windows: DXCam, BetterCam, WGC) +pip install -e ".[notifications]" # OS notification capture (WinRT / dbus) +pip install -e ".[scrcpy]" # Capture from an Android phone via scrcpy +pip install -e ".[ble]" # Bluetooth LE LED controllers (desktop only) ``` +## Contributing + +Contributions are welcome. LedGrab is MIT-licensed, so you're free to fork, modify, and self-host. Please open an issue or pull request on the [repository](https://git.dolgolyov-family.by/alexei.dolgolyov/ledgrab). + ## License -MIT — see [LICENSE](LICENSE). - -## Acknowledgments - -- [WLED](https://github.com/Aircoookie/WLED) — LED control firmware -- [FastAPI](https://fastapi.tiangolo.com/) — Python web framework -- [MSS](https://python-mss.readthedocs.io/) — Cross-platform screen capture +MIT — see [LICENSE](LICENSE). Free and open source. diff --git a/docs/API.md b/docs/API.md index 89f383d..a2e900e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1,335 +1,646 @@ -# LedGrab API Documentation +# LedGrab API Reference -Complete REST API reference for the LedGrab server. +Complete REST + WebSocket API reference for the LedGrab server. -**Base URL:** `http://localhost:8080` -**API Version:** v1 +- **Base URL:** `http://localhost:8080` +- **API version:** `v1` (all REST paths are under `/api/v1`, except `/health`) +- **Interactive docs:** Swagger UI at [`/docs`](http://localhost:8080/docs), ReDoc at [`/redoc`](http://localhost:8080/redoc), raw schema at [`/openapi.json`](http://localhost:8080/openapi.json). The interactive docs are always the authoritative, up-to-date source for request/response schemas — this file is a hand-maintained overview. + +> The application version is reported by `GET /api/v1/version`; this document is version-agnostic. --- ## Table of Contents -- [Health & Info](#health--info) -- [Device Management](#device-management) -- [Processing Control](#processing-control) -- [Settings Management](#settings-management) -- [Calibration](#calibration) -- [Metrics](#metrics) +- [Authentication](#authentication) +- [Conventions](#conventions) +- [WebSocket protocol](#websocket-protocol) +- [Worked examples](#worked-examples) +- **Endpoint reference** + - [Health & system info](#health--system-info) + - [System settings](#system-settings) + - [User preferences](#user-preferences) + - [Backup, restore & server control](#backup-restore--server-control) + - [Updates](#updates) + - [Snapshot](#snapshot) + - [Devices](#devices) + - [Capture templates, engines & filters](#capture-templates-engines--filters) + - [Picture sources](#picture-sources) + - [Post-processing templates](#post-processing-templates) + - [Output targets](#output-targets) + - [Output target control & live preview](#output-target-control--live-preview) + - [Color strip sources](#color-strip-sources) + - [Color strip processing templates](#color-strip-processing-templates) + - [Pattern templates](#pattern-templates) + - [Gradients](#gradients) + - [Audio devices](#audio-devices) + - [Audio sources](#audio-sources) + - [Audio templates & engines](#audio-templates--engines) + - [Audio processing templates](#audio-processing-templates) + - [Audio filters](#audio-filters) + - [Value sources](#value-sources) + - [Weather sources](#weather-sources) + - [Automations](#automations) + - [Scene presets](#scene-presets) + - [Sync clocks](#sync-clocks) + - [Webhooks](#webhooks) + - [HTTP endpoints](#http-endpoints) + - [Game integration](#game-integration) + - [Home Assistant](#home-assistant) + - [MQTT sources](#mqtt-sources) + - [Assets](#assets) + - [Graph wiring](#graph-wiring) + - [Web UI & PWA](#web-ui--pwa) --- -## Health & Info +## Authentication -### GET /health +LedGrab uses API-key authentication. The behavior depends on whether any keys are configured under `auth.api_keys` (see [INSTALLATION.md](../INSTALLATION.md)): -Health check endpoint. +| Situation | Loopback (`127.0.0.1` / `::1` / `localhost`) | LAN / remote | +| --------- | -------------------------------------------- | ------------ | +| **No keys configured** (default) | Allowed anonymously | **Rejected with `401`** | +| **Keys configured** | Valid Bearer token required | Valid Bearer token required | + +Pass the key as a Bearer token: + +```http +Authorization: Bearer +``` + +A few **sensitive endpoints require a real API key even from localhost** (they reject the loopback-anonymous identity): the backup download/restore endpoints, and any endpoint that reveals stored secrets (e.g. `GET /api/v1/home-assistant/sources?include_secrets=true`). Configure a key to use those. + +WebSocket endpoints authenticate with a [first-message handshake](#websocket-protocol) rather than the `Authorization` header. + +--- + +## Conventions + +- **Content type:** request and response bodies are JSON (`application/json`) unless noted (file uploads use `multipart/form-data`; some endpoints stream binary or file responses). +- **Errors:** failures return the standard FastAPI shape with an HTTP status code and a body of `{"detail": ""}`. Validation errors return `422` with a structured `detail` array. +- **IDs:** entities are addressed by string IDs (e.g. `dev_…`, `ot_…`, `css_…`) generated on creation. +- **Common create/update fields:** most configurable entities accept `name`, `description`, `tags` (string array), and UI styling fields `icon` and `icon_color`. +- **Referential integrity:** deleting an entity that is still referenced (e.g. a device used by an output target) returns `409 Conflict`. +- **Timestamps:** ISO-8601 UTC strings. + +--- + +## WebSocket protocol + +All WebSocket endpoints share the same auth handshake: + +1. The client connects. The server accepts the socket. +2. The client sends a JSON auth message as the **first** message, within ~3 seconds: `{"type": "auth", "token": ""}`. On loopback with no keys configured, `token` may be `null` or the message omitted. +3. The server replies `{"type": "auth_ok"}` on success, or `{"type": "auth_error", "reason": "..."}` then closes (close code `4401`) on failure. A cross-site `Origin` is rejected with close code `4403`. + +Browser clients must connect from an allowed `cors_origins` origin. After `auth_ok`, the stream payload depends on the endpoint (JSON event objects, JSON spectrum/metric frames, or binary RGB frames — see each endpoint's description). + +The WebSocket endpoints are listed within their resource sections below (method `WS`). + +--- + +## Worked examples + +> Example values are illustrative. + +**Health check** — `GET /health` (no auth on loopback): -**Response:** ```json { "status": "healthy", - "timestamp": "2026-02-06T12:00:00Z", - "version": "0.1.0" + "timestamp": "2026-05-29T12:00:00Z", + "version": "0.8.1", + "demo_mode": false, + "auth_required": false, + "setup_required": false, + "uptime_seconds": 3600 } ``` -### GET /api/v1/version +**Create a WLED device** — `POST /api/v1/devices`: -Get version information. - -**Response:** -```json -{ - "version": "0.1.0", - "python_version": "3.11.0", - "api_version": "v1" -} -``` - -### GET /api/v1/config/displays - -List available displays for screen capture. - -**Response:** -```json -{ - "displays": [ - { - "index": 0, - "name": "Display 1", - "width": 1920, - "height": 1080, - "is_primary": true - } - ], - "count": 1 -} -``` - ---- - -## Device Management - -### POST /api/v1/devices - -Create and attach a new WLED device. - -**Request:** ```json { "name": "Living Room TV", "url": "http://192.168.1.100", + "device_type": "wled", "led_count": 150 } ``` -**Response:** `201 Created` +Response `201 Created` returns the stored device, including its generated `id`. (For Adalight, send `device_type: "adalight"`, the serial `url` like `COM3` or `/dev/ttyUSB0`, `led_count`, and `baud_rate`. Each device type accepts its own fields — see `/docs`.) + +**Start / stop a target** — `POST /api/v1/output-targets/{target_id}/start`: + ```json -{ - "id": "device_abc123", - "name": "Living Room TV", - "url": "http://192.168.1.100", - "led_count": 150, - "enabled": true, - "status": "disconnected", - "settings": { - "display_index": 0, - "fps": 30, - "border_width": 10 - }, - "calibration": { - "layout": "clockwise", - "start_position": "bottom_left", - "segments": [...] - }, - "created_at": "2026-02-06T12:00:00Z", - "updated_at": "2026-02-06T12:00:00Z" -} +{ "status": "started", "target_id": "ot_abc123" } ``` -### GET /api/v1/devices +**Authenticated request with a configured key:** -List all attached devices. - -**Response:** -```json -{ - "devices": [...], - "count": 2 -} -``` - -### GET /api/v1/devices/{device_id} - -Get device details. - -**Response:** Same as POST response - -### PUT /api/v1/devices/{device_id} - -Update device information. - -**Request:** -```json -{ - "name": "Updated Name", - "enabled": true -} -``` - -### DELETE /api/v1/devices/{device_id} - -Delete/detach a device. - -**Response:** `204 No Content` - ---- - -## Processing Control - -### POST /api/v1/devices/{device_id}/start - -Start screen processing for a device. - -**Response:** -```json -{ - "status": "started", - "device_id": "device_abc123" -} -``` - -### POST /api/v1/devices/{device_id}/stop - -Stop screen processing. - -**Response:** -```json -{ - "status": "stopped", - "device_id": "device_abc123" -} -``` - -### GET /api/v1/devices/{device_id}/state - -Get current processing state. - -**Response:** -```json -{ - "device_id": "device_abc123", - "processing": true, - "fps_actual": 29.8, - "fps_target": 30, - "display_index": 0, - "last_update": "2026-02-06T12:00:00Z", - "errors": [] -} +```bash +curl -H "Authorization: Bearer your-api-key" \ + http://localhost:8080/api/v1/devices ``` --- -## Settings Management +## Endpoint reference -### GET /api/v1/devices/{device_id}/settings +## Health & system info -Get processing settings. +Health checks, version information, displays, system metrics, and integration status. -**Response:** -```json -{ - "display_index": 0, - "fps": 30, - "brightness": 1.0, - "smoothing": 0.3, - "interpolation_mode": "average", - "standby_interval": 1.0, - "state_check_interval": 30 -} -``` +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/health` | Service health: status, version, uptime, and whether auth/setup is required. | +| GET | `/api/v1/version` | Application version, Python version, and API version. | +| GET | `/api/v1/tags` | All tags used across every entity in the system. | +| GET | `/api/v1/config/displays` | Available displays/monitors for screen capture (optional `engine_type` query, e.g. `scrcpy`). | +| GET | `/api/v1/system/processes` | Running process names, for use in automation conditions. | +| GET | `/api/v1/system/performance` | Current CPU, RAM, and GPU utilization metrics. | +| GET | `/api/v1/system/metrics-history` | Last ~2 minutes of system and per-target metrics for dashboard charts. | +| GET | `/api/v1/system/api-keys` | API-key labels with masked values (read-only; keys live in YAML config). | +| GET | `/api/v1/system/integrations-status` | Connection status for MQTT and Home Assistant integrations. | -### PUT /api/v1/devices/{device_id}/settings +## System settings -Update processing settings. +Server configuration: MQTT broker, external URL, shutdown action, log level, ADB connection, and live log streaming. -**Request:** -```json -{ - "display_index": 1, - "fps": 60, - "brightness": 0.8 -} -``` +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/system/mqtt/settings` | Current MQTT broker settings (password masked). | +| PUT | `/api/v1/system/mqtt/settings` | Update MQTT broker settings (empty password preserves existing). | +| GET | `/api/v1/system/external-url` | Configured external base URL. | +| PUT | `/api/v1/system/external-url` | Set the external base URL for webhooks and user-visible links. | +| GET | `/api/v1/system/shutdown-action` | Configured server shutdown action (`stop_targets` or `nothing`). | +| PUT | `/api/v1/system/shutdown-action` | Set what happens to targets when the server shuts down. | +| WS | `/api/v1/system/logs/ws` | Live server log stream with a buffered backlog. | +| POST | `/api/v1/adb/connect` | Connect to a Wi-Fi ADB device by IP (auto-appends `:5555`). | +| POST | `/api/v1/adb/disconnect` | Disconnect a Wi-Fi ADB device. | +| GET | `/api/v1/system/log-level` | Current root logger level. | +| PUT | `/api/v1/system/log-level` | Change the log level at runtime without restart. | + +## User preferences + +Dashboard layout, notification settings, card display modes, and the global daylight timezone. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/preferences/dashboard-layout` | Read the saved dashboard layout (empty when unset). | +| PUT | `/api/v1/preferences/dashboard-layout` | Save the dashboard layout (opaque versioned JSON blob). | +| DELETE | `/api/v1/preferences/dashboard-layout` | Delete the saved layout; revert to default. | +| GET | `/api/v1/preferences/notifications` | Read notification preferences (server defaults when unset). | +| PUT | `/api/v1/preferences/notifications` | Persist notification preferences (channels, discovery, grace/debounce). | +| GET | `/api/v1/preferences/card-modes` | Read per-surface card-mode preferences. | +| PUT | `/api/v1/preferences/card-modes` | Save per-surface card modes (comfortable/compact/dense/row). | +| DELETE | `/api/v1/preferences/card-modes` | Delete card-mode preferences; revert to defaults. | +| GET | `/api/v1/preferences/daylight-timezone` | Read the global IANA timezone for daylight cycles. | +| PUT | `/api/v1/preferences/daylight-timezone` | Persist the daylight-cycle timezone (empty = server local). | + +## Backup, restore & server control + +Database backup/restore, server restart/shutdown, and auto-backup management. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/system/backup` | Download a full backup `.zip` (database + assets). 🔒 requires a key. | +| POST | `/api/v1/system/restore` | Upload a `.db`/`.zip` backup to restore config and trigger a restart. 🔒 requires a key. | +| POST | `/api/v1/system/restart` | Schedule a server restart and return immediately. | +| POST | `/api/v1/system/shutdown` | Gracefully shut down the server. | +| GET | `/api/v1/system/auto-backup/settings` | Auto-backup settings and status (enabled, interval, retention, last/next). | +| PUT | `/api/v1/system/auto-backup/settings` | Update auto-backup settings. | +| POST | `/api/v1/system/auto-backup/trigger` | Trigger a backup now and return its metadata. | +| GET | `/api/v1/system/backups` | List saved auto-backup files. | +| GET | `/api/v1/system/backups/{filename}` | Download a specific saved backup file. | +| DELETE | `/api/v1/system/backups/{filename}` | Delete a specific saved backup file. | + +> 🔒 = requires a real API key even from localhost (rejects loopback-anonymous access). + +## Updates + +Auto-update management: check, apply, dismiss, and configure. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/system/update/status` | Current update status (available version, install type, capability). | +| POST | `/api/v1/system/update/check` | Trigger an immediate update check. | +| POST | `/api/v1/system/update/dismiss` | Dismiss the notification for a specific version. | +| POST | `/api/v1/system/update/apply` | Download and apply the available update, then shut down. | +| GET | `/api/v1/system/update/settings` | Update settings (enabled, interval, include prereleases). | +| PUT | `/api/v1/system/update/settings` | Change auto-update settings. | + +## Snapshot + +A single aggregated poll endpoint for low-overhead clients. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/snapshot` | Full poll payload (targets, states, metrics, devices, brightness, color/value sources, scene presets, sync clocks, system) in one response. Use `?include=` to request a subset; per-section fault isolation. | + +## Devices + +LED device CRUD, pairing, discovery, health checks, brightness/power control, and the WS pixel stream. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| POST | `/api/v1/devices` | Create/attach a new LED device (validates connectivity). | +| POST | `/api/v1/devices/pair` | Run a pairing handshake before creating a device. | +| GET | `/api/v1/devices` | List all attached devices. | +| GET | `/api/v1/devices/discover` | Scan the network for devices (optional `timeout`, `device_type`). | +| GET | `/api/v1/devices/openrgb-zones` | List zones on an OpenRGB device (`url` query). | +| GET | `/api/v1/devices/batch/states` | Health/connection state for all devices at once. | +| GET | `/api/v1/devices/{device_id}` | Get a device by ID. | +| PUT | `/api/v1/devices/{device_id}` | Update device configuration. | +| DELETE | `/api/v1/devices/{device_id}` | Delete/detach a device (`409` if referenced). | +| GET | `/api/v1/devices/{device_id}/state` | Get device health/connection state. | +| POST | `/api/v1/devices/{device_id}/ping` | Force an immediate health check. | +| GET | `/api/v1/devices/{device_id}/brightness` | Get current (cached) brightness. | +| PUT | `/api/v1/devices/{device_id}/brightness` | Set brightness (`0–255`). | +| GET | `/api/v1/devices/{device_id}/power` | Get current power state. | +| PUT | `/api/v1/devices/{device_id}/power` | Turn the device on or off. | +| WS | `/api/v1/devices/{device_id}/ws` | Pixel stream for `ws` device type (`[brightness][R G B …]`). | + +## Capture templates, engines & filters + +Capture template CRUD/testing, capture engine discovery, and post-processing filter discovery. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/capture-templates` | List all capture templates. | +| POST | `/api/v1/capture-templates` | Create a capture template. | +| GET | `/api/v1/capture-templates/{template_id}` | Get a capture template by ID. | +| PUT | `/api/v1/capture-templates/{template_id}` | Update a capture template (partial). | +| DELETE | `/api/v1/capture-templates/{template_id}` | Delete a template (`409` if used by streams). | +| GET | `/api/v1/capture-engines` | List capture engines with platform availability. | +| POST | `/api/v1/capture-templates/test` | Test a capture config; returns FPS metrics + preview. | +| WS | `/api/v1/capture-templates/test/ws` | Real-time capture test with intermediate frame previews. | +| GET | `/api/v1/filters` | List post-processing filter types and option schemas. | +| GET | `/api/v1/strip-filters` | List filter types that support 1D LED-strip processing. | + +## Picture sources + +Screen captures, static images, video files, and processed streams used for color extraction. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/picture-sources` | List all picture sources. | +| POST | `/api/v1/picture-sources/validate-image` | Validate an image source and return a preview thumbnail. | +| GET | `/api/v1/picture-sources/full-image` | Serve a full-resolution image for lightbox preview (`source` query). | +| POST | `/api/v1/picture-sources` | Create a picture source (`raw`/`processed`/`static`/`video`). | +| GET | `/api/v1/picture-sources/{stream_id}` | Get a picture source by ID. | +| PUT | `/api/v1/picture-sources/{stream_id}` | Update a picture source. | +| DELETE | `/api/v1/picture-sources/{stream_id}` | Delete a picture source (`409` if referenced). | +| GET | `/api/v1/picture-sources/{stream_id}/thumbnail` | Thumbnail (first frame) for a video source. | +| POST | `/api/v1/picture-sources/{stream_id}/test` | Resolve the chain and run a capture test. | +| WS | `/api/v1/picture-sources/{stream_id}/test/ws` | Test stream with intermediate frame previews. | + +## Post-processing templates + +Reusable filter chains applied to picture sources. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/postprocessing-templates` | List all post-processing templates. | +| POST | `/api/v1/postprocessing-templates` | Create a template (name + filter list). | +| GET | `/api/v1/postprocessing-templates/{template_id}` | Get a template by ID. | +| PUT | `/api/v1/postprocessing-templates/{template_id}` | Update a template (partial). | +| DELETE | `/api/v1/postprocessing-templates/{template_id}` | Delete a template (`409` if referenced). | +| POST | `/api/v1/postprocessing-templates/{template_id}/test` | Capture from a source and apply the filters. | +| WS | `/api/v1/postprocessing-templates/{template_id}/test/ws` | Real-time test with intermediate frame previews. | + +## Output targets + +LED strips, Home Assistant light groups, and Zigbee2MQTT bulb groups. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| POST | `/api/v1/output-targets` | Create a target (`led` / `ha_light` / `z2m_light`). | +| GET | `/api/v1/output-targets` | List all output targets. | +| GET | `/api/v1/output-targets/batch/states` | Processing state for all targets at once. | +| GET | `/api/v1/output-targets/batch/metrics` | Metrics for all targets at once. | +| GET | `/api/v1/output-targets/{target_id}` | Get a single target. | +| PUT | `/api/v1/output-targets/{target_id}` | Update a target (partial, per type). | +| DELETE | `/api/v1/output-targets/{target_id}` | Delete a target (stops processing first). | + +## Output target control & live preview + +Start/stop processing, state & metrics, the calibration overlay, the global event stream, and live color/LED preview WebSockets. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| POST | `/api/v1/output-targets/bulk/start` | Start processing for multiple targets. | +| POST | `/api/v1/output-targets/bulk/stop` | Stop processing for multiple targets. | +| POST | `/api/v1/output-targets/{target_id}/start` | Start processing for one target. | +| POST | `/api/v1/output-targets/{target_id}/stop` | Stop processing for one target. | +| GET | `/api/v1/output-targets/{target_id}/state` | Current processing state (FPS, timing, device, errors). | +| GET | `/api/v1/output-targets/{target_id}/metrics` | Processing metrics (uptime, frames, error count). | +| WS | `/api/v1/events/ws` | Real-time state-change events across all targets. | +| POST | `/api/v1/output-targets/{target_id}/overlay/start` | Start the on-screen sampling/LED overlay. | +| POST | `/api/v1/output-targets/{target_id}/overlay/stop` | Stop the overlay. | +| GET | `/api/v1/output-targets/{target_id}/overlay/status` | Whether the overlay is active. | +| POST | `/api/v1/output-targets/{target_id}/ha-light/turn-off` | Turn off all HA light entities for the target. | +| WS | `/api/v1/output-targets/{target_id}/ha-light/ws` | Live HA light color preview. | +| POST | `/api/v1/output-targets/{target_id}/z2m-light/turn-off` | Publish OFF to all Zigbee2MQTT bulbs for the target. | +| WS | `/api/v1/output-targets/{target_id}/z2m-light/ws` | Live Zigbee2MQTT bulb color preview. | +| WS | `/api/v1/output-targets/{target_id}/led-preview/ws` | Live LED-strip preview (binary RGB frames). | + +## Color strip sources + +CRUD, calibration, raw color push, notifications, and preview streaming for color strip sources. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/color-strip-sources` | List all color strip sources. | +| POST | `/api/v1/color-strip-sources` | Create a color strip source (by `source_type`). | +| GET | `/api/v1/color-strip-sources/{source_id}` | Get a color strip source by ID. | +| PUT | `/api/v1/color-strip-sources/{source_id}` | Update a source; hot-reloads running streams. | +| DELETE | `/api/v1/color-strip-sources/{source_id}` | Delete a source (`409` if referenced). | +| POST | `/api/v1/color-strip-sources/{source_id}/overlay/start` | Start the screen overlay (picture-type, calibrated). | +| POST | `/api/v1/color-strip-sources/{source_id}/overlay/stop` | Stop the screen overlay. | +| GET | `/api/v1/color-strip-sources/{source_id}/overlay/status` | Whether the overlay is active. | +| POST | `/api/v1/color-strip-sources/{source_id}/colors` | Push raw LED colors to an `api_input` source. | +| POST | `/api/v1/color-strip-sources/{source_id}/notify` | Trigger a one-shot notification effect. | +| GET | `/api/v1/color-strip-sources/os-notifications/history` | Recent OS-notification capture history. | +| PUT | `/api/v1/color-strip-sources/{source_id}/calibration/test` | Light up LED edges to verify calibration. | +| POST | `/api/v1/color-strip-sources/{source_id}/key-colors/test` | Test a `key_colors` source (extract colors from rectangles). | +| WS | `/api/v1/color-strip-sources/{source_id}/key-colors/test/ws` | Real-time key-colors test preview. | +| WS | `/api/v1/color-strip-sources/preview/ws` | Transient ad-hoc source preview stream. | +| WS | `/api/v1/color-strip-sources/{source_id}/ws` | Push raw colors to an `api_input` source over WS. | +| WS | `/api/v1/color-strip-sources/{source_id}/test/ws` | Real-time source preview (binary RGB, optional JPEG). | + +## Color strip processing templates + +Reusable filter chains applied to color strips (1D LED data). + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/color-strip-processing-templates` | List all color-strip processing templates. | +| POST | `/api/v1/color-strip-processing-templates` | Create a template (name + filter list). | +| GET | `/api/v1/color-strip-processing-templates/{template_id}` | Get a template by ID. | +| PUT | `/api/v1/color-strip-processing-templates/{template_id}` | Update a template. | +| DELETE | `/api/v1/color-strip-processing-templates/{template_id}` | Delete a template (`409` if referenced). | +| WS | `/api/v1/color-strip-processing-templates/{template_id}/test/ws` | Real-time preview: apply the filter chain to an input source. | + +## Pattern templates + +Layout templates of named rectangles for LED device configuration. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/pattern-templates` | List all pattern templates. | +| POST | `/api/v1/pattern-templates` | Create a pattern template (named rectangles). | +| GET | `/api/v1/pattern-templates/{template_id}` | Get a pattern template by ID. | +| PUT | `/api/v1/pattern-templates/{template_id}` | Update a pattern template. | +| DELETE | `/api/v1/pattern-templates/{template_id}` | Delete a template (`409` if referenced by targets). | + +## Gradients + +Reusable gradient definitions (color stops). Built-in gradients are read-only but clonable. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/gradients` | List all gradients (built-in and user-created). | +| POST | `/api/v1/gradients` | Create a user-defined gradient. | +| GET | `/api/v1/gradients/{gradient_id}` | Get a gradient by ID. | +| PUT | `/api/v1/gradients/{gradient_id}` | Update a gradient (built-ins are read-only). | +| POST | `/api/v1/gradients/{gradient_id}/clone` | Clone a gradient into a customizable copy. | +| DELETE | `/api/v1/gradients/{gradient_id}` | Delete a gradient (`400` if built-in or referenced). | + +## Audio devices + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/audio-devices` | List audio input/output devices (flat list + per-engine grouping). | + +## Audio sources + +Audio capture and processing sources for audio-reactive effects. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/audio-sources` | List all audio sources (optional `source_type`). | +| POST | `/api/v1/audio-sources` | Create an audio source (`capture` or `processed`). | +| GET | `/api/v1/audio-sources/{source_id}` | Get an audio source by ID. | +| PUT | `/api/v1/audio-sources/{source_id}` | Update an audio source (partial). | +| DELETE | `/api/v1/audio-sources/{source_id}` | Delete an audio source (`409` if referenced). | +| WS | `/api/v1/audio-sources/{source_id}/test/ws` | Real-time spectrum/RMS/peak/beat analysis (~20 Hz). | + +## Audio templates & engines + +Audio capture templates and engine discovery. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/audio-templates` | List all audio capture templates. | +| POST | `/api/v1/audio-templates` | Create an audio capture template. | +| GET | `/api/v1/audio-templates/{template_id}` | Get an audio template by ID. | +| PUT | `/api/v1/audio-templates/{template_id}` | Update an audio template. | +| DELETE | `/api/v1/audio-templates/{template_id}` | Delete a template (cascades to audio sources). | +| GET | `/api/v1/audio-engines` | List audio capture engines and availability. | +| WS | `/api/v1/audio-templates/{template_id}/test/ws` | Real-time spectrum test for a template + device. | + +## Audio processing templates + +Reusable audio filter chains. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/audio-processing-templates` | List all audio processing templates. | +| POST | `/api/v1/audio-processing-templates` | Create a template (name + filter list). | +| GET | `/api/v1/audio-processing-templates/{template_id}` | Get a template by ID. | +| PUT | `/api/v1/audio-processing-templates/{template_id}` | Update a template (hot-updates running streams). | +| DELETE | `/api/v1/audio-processing-templates/{template_id}` | Delete a template (`409` if referenced). | + +## Audio filters + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/audio-filters` | List audio filter types and their option schemas. | + +## Value sources + +Dynamic data inputs (brightness and other parameters): static, animated, audio, adaptive, color, sensor, HTTP, and Home Assistant. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/value-sources` | List all value sources (optional `source_type`). | +| POST | `/api/v1/value-sources` | Create a value source (discriminated by `source_type`). | +| GET | `/api/v1/value-sources/{source_id}` | Get a value source by ID. | +| PUT | `/api/v1/value-sources/{source_id}` | Update a value source; hot-reloads running streams. | +| DELETE | `/api/v1/value-sources/{source_id}` | Delete a value source (`409` if referenced). | +| WS | `/api/v1/value-sources/{source_id}/test/ws` | Real-time value output stream (~20 Hz). | + +## Weather sources + +Weather data providers feeding weather-driven value sources. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/weather-sources` | List all weather sources. | +| POST | `/api/v1/weather-sources` | Create a weather source (provider, lat/lon, interval). | +| GET | `/api/v1/weather-sources/{source_id}` | Get a weather source by ID. | +| PUT | `/api/v1/weather-sources/{source_id}` | Update a weather source. | +| DELETE | `/api/v1/weather-sources/{source_id}` | Delete a weather source. | +| POST | `/api/v1/weather-sources/{source_id}/test` | Force-fetch current weather and return it. | + +## Automations + +Rules that trigger scene presets (time, display state, MQTT, webhooks, Home Assistant, HTTP polling, active window). + +| Method | Path | Description | +| ------ | ---- | ----------- | +| POST | `/api/v1/automations` | Create an automation (rules + scene preset + deactivation). | +| GET | `/api/v1/automations` | List automations with current activity state. | +| GET | `/api/v1/automations/{automation_id}` | Get an automation by ID (includes webhook URL if any). | +| PUT | `/api/v1/automations/{automation_id}` | Update an automation (partial); re-evaluates if enabled. | +| DELETE | `/api/v1/automations/{automation_id}` | Delete and deactivate an automation. | +| POST | `/api/v1/automations/{automation_id}/enable` | Enable and immediately evaluate rules. | +| POST | `/api/v1/automations/{automation_id}/disable` | Disable and deactivate. | + +## Scene presets + +Captured snapshots of target state that can be restored. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| POST | `/api/v1/scene-presets` | Create a preset by capturing current target state. | +| GET | `/api/v1/scene-presets` | List all scene presets. | +| GET | `/api/v1/scene-presets/{preset_id}` | Get a scene preset by ID. | +| PUT | `/api/v1/scene-presets/{preset_id}` | Update metadata and optionally change targets. | +| DELETE | `/api/v1/scene-presets/{preset_id}` | Delete a scene preset. | +| POST | `/api/v1/scene-presets/{preset_id}/recapture` | Re-capture current state into the preset. | +| POST | `/api/v1/scene-presets/{preset_id}/activate` | Activate the preset (restore captured state). | + +## Sync clocks + +Shared clocks that drive linked animations with configurable speed. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/sync-clocks` | List all synchronization clocks. | +| POST | `/api/v1/sync-clocks` | Create a sync clock. | +| GET | `/api/v1/sync-clocks/{clock_id}` | Get a sync clock by ID. | +| PUT | `/api/v1/sync-clocks/{clock_id}` | Update a clock (speed changes hot-applied). | +| DELETE | `/api/v1/sync-clocks/{clock_id}` | Delete a clock (`409` if referenced). | +| POST | `/api/v1/sync-clocks/{clock_id}/pause` | Pause the clock (freeze linked animations). | +| POST | `/api/v1/sync-clocks/{clock_id}/resume` | Resume a paused clock. | +| POST | `/api/v1/sync-clocks/{clock_id}/reset` | Reset the clock to `t=0`. | + +## Webhooks + +Inbound trigger endpoint for external services. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| POST | `/api/v1/webhooks/{token}` | Trigger an automation by secret token (`activate`/`deactivate`; rate-limited 30/min/IP). | + +## HTTP endpoints + +Outbound HTTP polling endpoints for integrations. 🔒 These require a real API key even on loopback. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/http/endpoints` | List all HTTP polling endpoints. | +| POST | `/api/v1/http/endpoints` | Create an endpoint (URL, method, auth token, headers). | +| GET | `/api/v1/http/endpoints/{endpoint_id}` | Get an endpoint by ID. | +| PUT | `/api/v1/http/endpoints/{endpoint_id}` | Update an endpoint. | +| DELETE | `/api/v1/http/endpoints/{endpoint_id}` | Delete an endpoint. | +| POST | `/api/v1/http/endpoints/test` | One-shot test fetch to validate a config before saving. | +| POST | `/api/v1/http/endpoints/{endpoint_id}/test` | Test a stored endpoint without re-entering its token. | + +## Game integration + +Game event ingestion, adapter metadata, presets, and diagnostics. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/game-integrations/presets` | List built-in effect presets. | +| GET | `/api/v1/game-integrations` | List all game integration configs. | +| POST | `/api/v1/game-integrations` | Create a game integration config. | +| GET | `/api/v1/game-integrations/{integration_id}` | Get a config by ID. | +| PUT | `/api/v1/game-integrations/{integration_id}` | Update a config. | +| DELETE | `/api/v1/game-integrations/{integration_id}` | Delete a config. | +| POST | `/api/v1/game-integrations/{integration_id}/event` | Ingest a game event (adapter-level auth; 16–64 Hz). | +| GET | `/api/v1/game-integrations/{integration_id}/status` | Runtime status (connected state, event counts). | +| GET | `/api/v1/game-integrations/{integration_id}/events` | Recent events for debugging (`limit`). | +| GET | `/api/v1/game-adapters` | List adapter types and supported events. | +| POST | `/api/v1/game-integrations/{integration_id}/apply-preset` | Apply a built-in preset (optionally replacing mappings). | +| POST | `/api/v1/game-integrations/{integration_id}/auto-setup` | Write game config files and generate an auth token. | + +## Home Assistant + +Home Assistant WebSocket sources, entity discovery, and live status. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/home-assistant/sources` | List HA sources with status and entity counts (`?include_secrets=true` 🔒). | +| POST | `/api/v1/home-assistant/sources` | Create an HA source (host, long-lived token, filters). | +| GET | `/api/v1/home-assistant/sources/{source_id}` | Get an HA source (`?include_secrets=true` 🔒). | +| PUT | `/api/v1/home-assistant/sources/{source_id}` | Update an HA source; refreshes the connection. | +| DELETE | `/api/v1/home-assistant/sources/{source_id}` | Delete an HA source and release its runtime. | +| GET | `/api/v1/home-assistant/sources/{source_id}/entities` | List available HA entities (live + cache fallback). | +| POST | `/api/v1/home-assistant/sources/{source_id}/test` | Test connection/auth and report HA version. | +| GET | `/api/v1/home-assistant/status` | Overall HA integration status per source. | + +## MQTT sources + +MQTT broker connections (sources) and status monitoring. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/mqtt/sources` | List MQTT sources with connection status. | +| POST | `/api/v1/mqtt/sources` | Create an MQTT source (broker connection). | +| GET | `/api/v1/mqtt/sources/{source_id}` | Get an MQTT source by ID. | +| PUT | `/api/v1/mqtt/sources/{source_id}` | Update a source; restarts the broker runtime. | +| DELETE | `/api/v1/mqtt/sources/{source_id}` | Delete a source and release its runtime. | +| POST | `/api/v1/mqtt/sources/{source_id}/test` | Test connection to the broker (10s timeout). | +| GET | `/api/v1/mqtt/status` | Overall MQTT integration status per source. | + +## Assets + +Media files (sounds, images, videos) used by effects and notifications. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/assets` | List assets (optional `asset_type` filter). | +| GET | `/api/v1/assets/{asset_id}` | Get asset metadata by ID. | +| POST | `/api/v1/assets` | Upload a new asset file (`multipart/form-data`). | +| PUT | `/api/v1/assets/{asset_id}` | Update asset metadata. | +| DELETE | `/api/v1/assets/{asset_id}` | Delete an asset (prebuilt assets are soft-deleted/restorable). | +| GET | `/api/v1/assets/{asset_id}/file` | Serve the asset file (download). | +| POST | `/api/v1/assets/restore-prebuilt` | Re-import any deleted prebuilt assets. | + +## Graph wiring + +The wiring-graph: schema registry, topology, dependents, validation, and subgraph duplication. + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/api/v1/graph/schema` | Registry of connectable reference fields. | +| GET | `/api/v1/graph` | Full wiring topology (nodes + edges) and validation report. | +| GET | `/api/v1/graph/dependents/{kind}/{entity_id}` | Every entity that references `(kind, entity_id)`. | +| POST | `/api/v1/graph/validate-connection` | Validate a proposed wiring edit (existence, kind, no cycle). | +| POST | `/api/v1/graph/duplicate` | Deep-clone selected value/color-strip sources with remapped wiring. | + +## Web UI & PWA + +App-level routes served by FastAPI (not under `/api/v1`). + +| Method | Path | Description | +| ------ | ---- | ----------- | +| GET | `/` | The web dashboard UI. | +| GET | `/manifest.json` | PWA manifest (root scope). | +| GET | `/sw.js` | Service worker (root scope). | +| GET | `/openapi.json` | OpenAPI schema. | +| GET | `/docs` | Swagger UI (interactive API docs). | +| GET | `/redoc` | ReDoc API reference. | --- -## Calibration +## Next steps -### GET /api/v1/devices/{device_id}/calibration - -Get calibration configuration. - -**Response:** -```json -{ - "layout": "clockwise", - "start_position": "bottom_left", - "segments": [ - { - "edge": "bottom", - "led_start": 0, - "led_count": 40, - "reverse": false - }, - { - "edge": "right", - "led_start": 40, - "led_count": 30, - "reverse": false - }, - { - "edge": "top", - "led_start": 70, - "led_count": 40, - "reverse": true - }, - { - "edge": "left", - "led_start": 110, - "led_count": 40, - "reverse": true - } - ] -} -``` - -### PUT /api/v1/devices/{device_id}/calibration - -Update calibration. - -**Request:** Same as GET response - -### POST /api/v1/devices/{device_id}/calibration/test - -Test calibration by lighting up specific edge. - -**Query Parameters:** -- `edge`: Edge to test (top, right, bottom, left) -- `color`: RGB color array (e.g., [255, 0, 0]) - ---- - -## Metrics - -### GET /api/v1/devices/{device_id}/metrics - -Get detailed processing metrics. - -**Response:** -```json -{ - "device_id": "device_abc123", - "processing": true, - "fps_actual": 29.8, - "fps_target": 30, - "uptime_seconds": 3600.5, - "frames_processed": 107415, - "errors_count": 2, - "last_error": null, - "last_update": "2026-02-06T12:00:00Z" -} -``` - ---- - -## Error Responses - -All endpoints may return error responses in this format: - -```json -{ - "error": "ErrorType", - "message": "Human-readable error message", - "detail": {...}, - "timestamp": "2026-02-06T12:00:00Z" -} -``` - -**Common HTTP Status Codes:** -- `200 OK` - Success -- `201 Created` - Resource created -- `204 No Content` - Success with no response body -- `400 Bad Request` - Invalid request -- `404 Not Found` - Resource not found -- `500 Internal Server Error` - Server error - ---- - -## Interactive Documentation - -The server provides interactive API documentation: - -- **Swagger UI:** http://localhost:8080/docs -- **ReDoc:** http://localhost:8080/redoc -- **OpenAPI JSON:** http://localhost:8080/openapi.json +- [Installation Guide](../INSTALLATION.md) +- [Calibration Guide](CALIBRATION.md) +- Interactive, always-current schemas: [`/docs`](http://localhost:8080/docs) diff --git a/docs/screenshots/dashboard-targets.PNG b/docs/screenshots/dashboard-targets.PNG new file mode 100644 index 0000000..f084bc5 Binary files /dev/null and b/docs/screenshots/dashboard-targets.PNG differ diff --git a/docs/screenshots/dashboard.PNG b/docs/screenshots/dashboard.PNG new file mode 100644 index 0000000..b9e51d2 Binary files /dev/null and b/docs/screenshots/dashboard.PNG differ diff --git a/docs/screenshots/devices.PNG b/docs/screenshots/devices.PNG new file mode 100644 index 0000000..a75a4df Binary files /dev/null and b/docs/screenshots/devices.PNG differ diff --git a/docs/screenshots/test-preview-capture.PNG b/docs/screenshots/test-preview-capture.PNG new file mode 100644 index 0000000..4053860 Binary files /dev/null and b/docs/screenshots/test-preview-capture.PNG differ diff --git a/server/CLAUDE.md b/server/CLAUDE.md index 02ccd55..746c59d 100644 --- a/server/CLAUDE.md +++ b/server/CLAUDE.md @@ -6,33 +6,35 @@ - `src/ledgrab/api/routes/` — REST API endpoints (one file per entity) - `src/ledgrab/api/schemas/` — Pydantic request/response models (one file per entity) - `src/ledgrab/core/` — Core business logic (capture, devices, audio, processing, automations) -- `src/ledgrab/storage/` — Data models (dataclasses) and JSON persistence stores +- `src/ledgrab/storage/` — Data models (dataclasses) and SQLite-backed persistence stores (`BaseSqliteStore`) - `src/ledgrab/utils/` — Utility functions (logging, monitor detection, SSRF validation, sound playback) - `src/ledgrab/static/` — Frontend files (TypeScript, CSS, locales) - `src/ledgrab/templates/` — Jinja2 HTML templates - `config/` — Configuration files (YAML) -- `data/` — Runtime data (JSON stores, persisted state) +- `data/` — Runtime data: SQLite database (`ledgrab.db`) + assets. Relocate the root with `LEDGRAB_DATA_DIR`. ## Entity & Storage Pattern -Each entity follows: dataclass model (`storage/`) + JSON store (`storage/*_store.py`) + Pydantic schemas (`api/schemas/`) + routes (`api/routes/`). +Each entity follows: dataclass model (`storage/`) + SQLite store (`storage/*_store.py`, subclassing `BaseSqliteStore`) + Pydantic schemas (`api/schemas/`) + routes (`api/routes/`). + +Stores keep an in-memory write-through cache over a per-entity SQLite table (the legacy `BaseJsonStore` still exists for reference but new stores use `BaseSqliteStore`). Schema/data shape changes go through `storage/data_migrations.py` — migrations are idempotent and tracked in a dedicated `data_migrations` audit table, so they run safely on every startup. **When renaming or restructuring stored fields, add a migration there** (see the Data Migration Policy in the root `CLAUDE.md`). ## Authentication -Server uses API key authentication via Bearer token in `Authorization` header. +API key authentication via Bearer token in the `Authorization` header (`Authorization: Bearer `). WebSocket connections authenticate with a first-message handshake (`{"type":"auth","token":""}`). See `src/ledgrab/api/auth.py` for the canonical logic. -- Config: `config/default_config.yaml` under `auth.api_keys` -- Env var: `LEDGRAB_AUTH__API_KEYS` -- When `api_keys` is empty (default), auth is disabled — all endpoints are open -- To enable auth, add key entries (e.g. `dev: "your-secret-key"`) +- Config: `config/default_config.yaml` under `auth.api_keys`; env var `LEDGRAB_AUTH__API_KEYS` +- When `api_keys` is **empty** (default): **loopback** requests (`127.0.0.1` / `::1` / `localhost`) are allowed anonymously, but **LAN / remote** requests are rejected with `401`. Auth is *not* fully open. +- When `api_keys` is **set**: a valid Bearer token is required from every client (loopback included). +- `require_authenticated()` rejects even loopback-anonymous callers on sensitive endpoints (e.g. backup download, secret reveal). ## Common Tasks ### Adding a new API endpoint -1. Create route file in `api/routes/` +1. Create route file in `api/routes/` (define an `APIRouter(prefix="/api/v1/...")`) 2. Define request/response schemas in `api/schemas/` -3. Register the router in `main.py` +3. Register the router in `api/__init__.py` (it aggregates every route module into the single `router` that `main.py` mounts) 4. Restart the server 5. Test via `/docs` (Swagger UI)