alexei.dolgolyov e5a2af9821 feat(ui): dashboard polish, richer perf strip, transport-bar controls
Dashboard perf strip:
- Unified rack-module shell with hairline-divided cells (mockup parity)
  replacing 3 separate perf cards. Cells auto-wrap to 2 rows of 4 on
  widescreen; responsive breakpoints at 1100 / 760 / 480 px.
- Active Patches cell (first) shows running/total channel count plus up
  to 4 live FPS readouts with channel-colored stripes; bottom-right
  radial glow anchors the "live channel bank" corner.
- Total FPS cell — aggregate throughput across running targets, mono
  "fps" unit suffix, session-peak-scaled sparkline with a 60 FPS floor.
- Devices cell — online/total count + per-device dot strip (green when
  online with signal-glow, coral when offline, tooltip with name +
  latency), fed from /devices/batch/states (added to the dashboard
  batch poll).
- Value font uses clamp(1.8rem, 2.8vw, 2.8rem) + white-space: nowrap so
  long readouts (RAM "18.9/31.8 GB", GPU "50% · 37°C") scale down
  instead of wrapping.
- Sparklines anchor to the cell bottom via margin-top: auto so baselines
  align across cells regardless of subtitle presence.
- App-load tag ("APP 3.1%") moved to a pinned top-right position per
  card, accent-colored pill; replaces the subdued inline badge.
- Perf mode toggle (System / App / Both) triggers an immediate poll so
  positioning updates without waiting for the next tick.
- Chart.js removed from perf-charts — inline SVG sparklines with
  drop-shadow filter for the "lit instrument" feel. Chart.js still used
  for per-target FPS charts via chart-utils (now owns the registration).
- Fixed history seed bug: app_ram is MB in the server history payload,
  not percent — convert to percent using sample's ram_total before
  pushing into _appHistory.ram. Skip seeding app_gpu_mem since the
  history schema has no gpu_memory_total.
- Temperature card reveals with an explanatory hint when the backend
  reports cpu_temp_hint_key (e.g. Windows without LibreHardwareMonitor)
  instead of silently hiding; .perf-chart-card-hint neutralizes the big
  display font so the message reads as plain body copy.

Transport bar:
- LED brand mark — 28 px, double-layer signal glow (0 22px + 0 8px),
  brandPulse animation. Brand-stack wraps the title + version so
  "LED GRAB" sits above "V0.3.0" on a single line each.
- Transport status chip — bigger (9/18 padding), mono uppercase,
  inner+outer signal glow when .is-armed.
- Transport meta cells — Uptime (JS-local session ticker), CPU (app
  CPU share), Mem (app RAM, G/M format) as stacked KEY/VALUE mono
  readouts with hairline separators.
- New interactive Poll cell cycles through 1/2/5/10s presets on click;
  replaces the range slider that used to live in the Dashboard toolbar
  (it controlled the whole app, not just the Dashboard).
- Header icon buttons — hairline-bordered 30 px squares with channel-
  glow on hover, replacing the pill container.
- Perf poll moved to global bootstrap so transport CPU / Mem stay live
  across all tabs (was paused when leaving the Dashboard).
- Connection pip (#server-status) hidden; the brand mark itself turns
  coral when offline via :has() selector on .header-title.

Dashboard cards:
- renderDashboardTarget now emits full rack-module markup with CH badge,
  name, meta, LED cluster, 3-cell metric grid (FPS / Uptime / Errors),
  and patch-label + stop button. Running cards get the signal-flow
  strip at the bottom. data-fps-text / data-uptime-text / data-errors-
  text hooks preserved so _updateRunningMetrics updates in place.
- LED count surfaced in the target card meta line (e.g. "LED · WLED ·
  144 LED · GRADIENT") when the linked device reports led_count > 0.
- Integrations (HA + MQTT) picked up .mod-head markup — compact module
  layout with online/offline patch indicator. Integration card stripe
  uses the default signal color (not cyan or amber).
- Scene presets, sync clocks, automations gain the same compact module
  treatment. Automations/scenes dropped into a dashboard-autostart-grid
  so they share the visual language.
- Perf mode toggle, stream sub-tabs, cs-count / tree-count /
  tab-badge / dashboard-section-count badges all use the mono
  rectangular style with tabular-nums.

Command palette:
- Flat background (no gradient), channel-accent rule across the top,
  mono placeholder / group headers / footer, active result gets a
  channel-green left stripe.

Modals:
- Popover + backdrop get a stronger radial dim + 6 px blur.
- Per-modal-ID channel lanes (target→green, source→cyan, audio→magenta,
  automation/scene→violet, settings→amber, confirm→coral) via --modal-ch
  override.
- Modal header picks up a vertical channel stripe + hairline divider;
  footer gets hairline top + subtle wash.

Components:
- Inputs use hairline borders + tabular-nums mono for number fields;
  focus state has channel-green ring + soft glow.
- Buttons switch to mono-uppercase with signal-glow on primary,
  coral-glow on danger, hairline border on secondary.
- Card background flattened — removed gradient wash in favor of solid
  --lux-bg-1 for both dark (#0e1014) and light (#f6f8fb).
- Page background: pure black for dark, pure white for light.

Color-picker:
- Always detaches to <body> with fixed positioning when its swatch sits
  inside an overflow: hidden / auto / clip ancestor (perf strip, modal
  bodies, tree-dd panels). Prevents the popover getting clipped.

Settings modal:
- Remembers the last-opened tab via localStorage key
  settings_active_tab; falls back to 'general' if the tab id no longer
  exists. Explicit overrides (donation → about, update badge →
  updates) still work because callers invoke switchSettingsTab after
  openSettingsModal.

Microcopy:
- Sidebar / transport localization for en/ru/zh:
  sidebar.workspaces · transport.meta.{uptime,cpu,mem,poll,poll_hint}
  · transport.status.{ready,armed} · dashboard.perf.{active_patches,
  total_fps,devices}

Backend (coordinated with frontend):
- /system/performance now returns cpu_temp_hint_key when no live CPU
  temperature is available, so the Temperature card can render an
  actionable explainer instead of being hidden. Frontend respects the
  key via t() lookup.

Section headers:
- Underline switched from dashed to solid; channel-green accent rule
  (40 px) on the left remains.

Build / tests:
- ruff clean on touched Python files.
- tsc --noEmit clean.
- Python metrics-provider tests: 18 passed.
- CSS bundle ~214 KB.
2026-04-24 20:28:44 +03:00
2026-04-22 19:48:37 +03:00

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.

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.

A Home Assistant integration exposes devices as entities for smart home automation.

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)
  • Configurable capture regions, FPS, and border width
  • Capture templates for reusable configurations

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

Color Processing

  • Post-processing filter pipeline: brightness, gamma, saturation, color correction, auto-crop, frame interpolation, pixelation, flip
  • Reusable post-processing templates
  • Color strip sources: audio-reactive, pattern generator, composite layering, audio-to-color mapping
  • Pattern templates with customizable effects

Audio Integration

  • 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

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

Dashboard

  • Web UI at http://localhost:8080 — no installation needed on the client side
  • 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
  • Visual calibration editor with overlay preview
  • Live LED strip preview via WebSocket
  • Real-time FPS, latency, and uptime charts
  • Localized in English, Russian, and Chinese

Home Assistant Integration

  • HACS-compatible custom component
  • Light, switch, sensor, and number entities per device
  • Real-time metrics via data coordinator
  • WebSocket-based live LED preview in HA

Requirements

  • 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

Platform Notes

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)
Monitor names Friendly names (WMI) Generic ("Display 0")
Profile conditions Process/window detection Not yet implemented

Quick Start

git clone https://git.dolgolyov-family.by/alexei.dolgolyov/ledgrab.git
cd ledgrab/server
docker compose up -d

Manual

Requires Python 3.11+ and Node.js 18+.

git clone https://git.dolgolyov-family.by/alexei.dolgolyov/ledgrab.git
cd ledgrab/server

# Build the frontend bundle
npm ci && npm run build

# Create a virtual environment and install
python -m venv venv
source venv/bin/activate        # Linux/Mac
# venv\Scripts\activate         # Windows
pip install .

# Start the server
export PYTHONPATH=$(pwd)/src    # Linux/Mac
# set PYTHONPATH=%CD%\src       # Windows
uvicorn ledgrab.main:app --host 0.0.0.0 --port 8080

Open http://localhost:8080 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 for details.

See INSTALLATION.md for the full installation guide, including configuration, Docker manual builds, and Home Assistant setup.

Demo Mode

Demo mode runs the server with virtual devices, sample data, and isolated storage — useful for exploring the UI without real hardware.

Set the LEDGRAB_DEMO environment variable to true, 1, or yes:

# Docker
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

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

Configuration

Edit server/config/default_config.yaml or use environment variables with the LEDGRAB_ prefix:

server:
  host: "0.0.0.0"
  port: 8080
  log_level: "INFO"

auth:
  api_keys:
    dev: "development-key-change-in-production"

storage:
  devices_file: "data/devices.json"
  templates_file: "data/capture_templates.json"

logging:
  format: "json"
  file: "logs/ledgrab.log"
  max_size_mb: 100

Environment variable override example: LEDGRAB_SERVER__PORT=9090.

API

The server exposes a REST API (with Swagger docs at /docs) covering:

  • 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

All endpoints require API key authentication via X-API-Key header or ?token= query parameter.

See docs/API.md for the full reference.

Calibration

The calibration system maps screen border pixels to physical LED positions. Configure layout direction, start position, and per-edge segments through the web dashboard or API.

See docs/CALIBRATION.md for a step-by-step guide.

Home Assistant

For Home Assistant integration, see the separate ledgrab-haos-integration repository.

Development

cd server

# Install with dev dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format and lint
black src/ tests/
ruff check src/ tests/

Optional extras:

pip install -e ".[perf]"     # High-performance capture engines (Windows)
pip install -e ".[camera]"   # Webcam capture via OpenCV

License

MIT — see LICENSE.

Acknowledgments

  • WLED — LED control firmware
  • FastAPI — Python web framework
  • MSS — Cross-platform screen capture
S
Description
Ambient lighting system that captures screen content and drives LED strips in real time. Supports WLED, Adalight, AmbileD, DDP, and OpenRGB devices with audio-reactive effects, pattern generation, CSS-driven color strips, and automated profile switching. Built-in web dashboard included.
Readme MIT 52 MiB
2026-06-23 14:48:51 +03:00
Languages
Python 52.7%
TypeScript 30.2%
HTML 7.1%
CSS 6.9%
Kotlin 2%
Other 1%