426484adf8
Adds support for Nanoleaf controllers (Light Panels / Canvas / Shapes /
Lines / Elements) via the documented HTTP REST API on port 16021.
First concrete consumer of the pair-UX scaffold from commit 2f31680 --
the abstraction is no longer speculative.
Backend:
- NanoleafClient is a single-pixel HTTP adapter: averages the strip to
one RGB triple, converts to Nanoleaf's HSB scale (H 0-360 / S 0-100 /
B 0-100), and PUTs to /api/v1/<token>/state with duration:0 so
transitions are instant for ambilight. Brightness is clamped to >=1
because Nanoleaf rejects brightness=0.
- pair_nanoleaf(host) implements the two-step handshake: POST
/api/v1/new during the 30-second pairing window the controller opens
after the user holds the power button for 5 s.
200 -> {auth_token: "..."}
403 -> raises PairingNotReady ("Hold the power button...")
other / transport error -> RuntimeError wrapping the cause
- NanoleafDeviceProvider.pair_device returns {nanoleaf_token: ...}
forwarded by POST /api/v1/devices/pair to the frontend for inclusion
in the subsequent create payload.
- mDNS discovery via _nanoleafapi._tcp (and the v1 variant); failures
yield [] rather than raising.
- Health check probes /api/v1 without a token (401/403 still proves
the host is alive).
- NanoleafConfig has nanoleaf_token + nanoleaf_min_interval_ms
(default 100 ms = ~10 Hz; HTTP overhead caps practical max ~20 Hz).
- Auth token encrypted at rest via _enc/_dec, matching Hue / BLE-Govee.
- 42 unit tests cover URL parsing, RGB->HSB conversion, pairing
handshake (200 / 403 / 500 / missing-token / transport-error),
state mutations, brightness clamp, set_power / set_brightness /
set_color, connection lifecycle, provider validate / pair /
discover / capabilities, and Device.to_config round-trip including
the encrypted-token roundtrip via to_dict + from_dict.
Frontend:
- 'nanoleaf' in DEVICE_TYPE_KEYS (next to 'govee'), HEXAGON icon
(deliberate departure from the smart-bulb lightbulb family --
Nanoleaf is panels, not bulbs, and the brand identity is hexagonal).
- isNanoleafDevice predicate + per-type field show/hide.
- Pair flow integration: when the device type is Nanoleaf, the add-
device modal retitles its submit button to "Pair Device" and
intercepts the submit. handleAddDevice awaits
runPairingFlow({deviceType: 'nanoleaf', url}), merges result.fields
({nanoleaf_token}) into the create body, then POSTs. On
PairingCancelled the user stays on the modal silently.
- Settings modal exposes the rate-limit field and a read-only
"Paired" indicator reusing the pair-modal success badge. The token
itself is never rendered to the DOM and never sent on update --
re-pairing requires delete + re-add.
- Per-type pairing instructions in en/ru/zh
(device.nanoleaf.pair.instructions) that the scaffold's i18n lookup
resolves automatically.
- Bundle: +6.4 KiB (pairing-flow.ts was tree-shaken before this
commit; now both it and the Nanoleaf branches are baked in).
The pair-UX scaffold is now proven, not speculative. Tuya and Twinkly
can follow the same shape when their phases arrive.
LedGrab - Server
High-performance FastAPI server that captures screen content and controls WLED devices for ambient lighting.
Overview
The server component provides:
- 🎯 Real-time Screen Capture - Multi-monitor support with configurable FPS
- 🎨 Advanced Processing - Border pixel extraction with color correction
- 🔧 Flexible Calibration - Map screen edges to any LED layout
- 🌐 REST API - Complete control via 25+ REST endpoints
- 💾 Persistent Storage - JSON-based device and configuration management
- 📊 Metrics & Monitoring - Real-time FPS, status, and performance data
Quick Start
Option 1: Docker (Recommended)
# Start server
docker-compose up -d
# View logs
docker-compose logs -f
# Stop server
docker-compose down
Server runs on: http://localhost:8080
Option 2: Python
# Create virtual environment
python -m venv venv
# Activate
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
# Install dependencies
pip install .
# Set PYTHONPATH
export PYTHONPATH=$(pwd)/src # Linux/Mac
set PYTHONPATH=%CD%\src # Windows
# Run server
uvicorn ledgrab.main:app --host 0.0.0.0 --port 8080
Installation
Requirements
- Python 3.11+ (for Python installation)
- Docker & Docker Compose (for Docker installation)
- WLED device on your network
See ../INSTALLATION.md for comprehensive installation guide.
Configuration
Configuration File
Edit config/default_config.yaml:
server:
host: "0.0.0.0"
port: 8080
log_level: "INFO"
processing:
default_fps: 30 # Target frames per second
max_fps: 60 # Maximum allowed FPS
border_width: 10 # Pixels to sample from edge
wled:
timeout: 5 # Connection timeout (seconds)
retry_attempts: 3 # Number of retries
storage:
devices_file: "data/devices.json"
logging:
format: "json"
file: "logs/ledgrab.log"
Environment Variables
# Server configuration
export LEDGRAB_SERVER__HOST="0.0.0.0"
export LEDGRAB_SERVER__PORT=8080
export LEDGRAB_SERVER__LOG_LEVEL="INFO"
# Processing configuration
export LEDGRAB_PROCESSING__DEFAULT_FPS=30
export LEDGRAB_PROCESSING__BORDER_WIDTH=10
# WLED configuration
export WLED_WLED__TIMEOUT=5
Usage
WLED Device Setup
Important: Configure your WLED device using the official WLED web interface before connecting it to this controller:
- Access WLED Interface: Open
http://[wled-ip]in your browser - Configure Device Settings:
- Set LED count and type
- Configure brightness, color order, and power limits
- Set up segments if needed
- Configure effects and presets
This controller only sends pixel color data - it does not manage WLED settings like brightness, effects, or segments. All WLED device configuration should be done through the official WLED interface.
API Documentation
- Web UI: http://localhost:8080 (recommended for device management)
- Swagger UI: http://localhost:8080/docs
- ReDoc: http://localhost:8080/redoc
Quick Example
# 1. Add device
curl -X POST http://localhost:8080/api/v1/devices \
-H "Content-Type: application/json" \
-d '{"name":"Living Room","url":"http://192.168.1.100","led_count":150}'
# 2. Start processing
curl -X POST http://localhost:8080/api/v1/devices/{device_id}/start
# 3. Check status
curl http://localhost:8080/api/v1/devices/{device_id}/state
Testing
# Run all tests
pytest
# Run with coverage
pytest --cov=ledgrab --cov-report=html
# Run specific test
pytest tests/test_screen_capture.py -v
Development
Project Structure
src/ledgrab/
├── main.py # FastAPI application
├── config.py # Configuration
├── api/ # API routes
├── core/ # Core functionality
│ ├── screen_capture.py
│ ├── wled_client.py
│ ├── calibration.py
│ └── processor_manager.py
├── storage/ # Data persistence
└── utils/ # Utilities
Code Quality
# Format code
black src/ tests/
# Lint code
ruff check src/ tests/
License
MIT - see ../LICENSE