- Add enumerate_devices_by_engine() returning per-engine device lists
without cross-engine dedup so frontend can filter correctly
- API /audio-devices now includes by_engine dict alongside flat list
- Frontend caches per-engine data, filters device dropdown by selected
template's engine_type, refreshes on template change
- Reorder getting-started tutorial: add API docs and accent color steps
- Fix tutorial trigger button focus outline persisting on step 2
- Use accent color variable for tutorial pulse ring animation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Show CPU and GPU model names as overlays on performance chart cards
- Add cpu_name field to performance API with cross-platform detection
- Extract reusable color-picker popover module (9 presets + custom picker)
- Per-chart color customization for CPU/RAM/GPU performance charts
- Redesign header: compact toolbar container with icon-only buttons
- Compact language dropdown (EN/RU/ZH), icon-only login/logout
- Use accent color for FPS charts, range slider accent, dashboard icons
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add protocol field (ddp/http) to storage, API schemas, routes, processor
- WledTargetProcessor passes protocol to create_led_client(use_ddp=...)
- Target editor: protocol dropdown + keepalive in collapsible Specific Settings
- FPS, brightness threshold, adaptive FPS moved to main form area
- Hide Specific Settings section for serial devices (protocol is WLED-only)
- Card badge: show DDP/HTTP for WLED devices, Serial for serial devices
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DDP uses fire-and-forget UDP, so when a WiFi device becomes overwhelmed
by sustained traffic, sends appear successful while the device is
actually unreachable. This adds:
- HTTP liveness probe (GET /json/info, 2s timeout) every 10s during
streaming, exposed as device_streaming_reachable in target state
- Adaptive FPS (opt-in): exponential backoff when device is unreachable,
gradual recovery when it stabilizes — finds sustainable send rate
- Honest health checks: removed the lie that forced device_online=true
during streaming; now runs actual health checks regardless
- Target editor toggle, FPS display shows effective rate when throttled,
health dot reflects streaming reachability, red highlight when
unreachable
- Auto-backup scheduling support in settings modal
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: GET /api/v1/system/backup bundles all 11 store JSON files into a
single downloadable backup with metadata envelope. POST /api/v1/system/restore
validates and writes stores atomically, then schedules a delayed server restart
via detached restart.ps1 subprocess.
Frontend: Settings modal (gear button in header) with Download Backup and
Restore from Backup buttons. Restore shows confirm dialog, uploads via
multipart FormData, then displays fullscreen restart overlay that polls
/health until the server comes back and reloads the page.
Locales: en, ru, zh translations for all settings.* keys.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stability: Add outer try/except/finally with _running=False cleanup to all 6
processing loop methods (live, color_strip, effect, audio, composite, mapped).
Add exponential backoff on consecutive capture errors in live_stream. Move
audio stream.stop() outside lock scope.
Performance: Replace per-pixel Python loop with np.array().tobytes() in
ddp_client. Vectorize pixelate filter with cv2.resize down+up. Vectorize
gradient rendering with np.searchsorted.
Frontend: Add lockBody/unlockBody re-entrancy counter. Add {once:true} to
fetchWithAuth abort listener. Null ws.onclose before ws.close() in LED preview.
Backend: Remove auth token prefix from log messages. Add atomic_write_json
helper (tempfile + os.replace) and update all 10 stores. Add name uniqueness
checks to all update methods. Fix DELETE status codes to 204 in audio_sources
and value_sources. Fix get_source() silent bug in color_strip_sources.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace blocking REST-based capture tests with WebSocket endpoints that
stream intermediate frame thumbnails at ~100ms intervals, giving real-time
visual feedback during capture. Preview resolution adapts dynamically to
the client viewport size and device pixel ratio.
- New shared helper (_test_helpers.py) with engine_factory pattern to
avoid MSS thread-affinity issues
- WS endpoints for stream, capture template, and PP template tests
- Enhanced overlay spinner with live preview image and stats
- Frontend _runTestViaWS shared helper replaces three REST test runners
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add real-time value source test: WebSocket endpoint streams get_value() at
~20Hz, frontend renders scrolling time-series chart with min/max/current stats
- Add auto-gain for audio value sources: rolling peak normalization with slow
decay, sensitivity range increased to 0.1-20.0
- Always show brightness overlay on LED preview when brightness source is set
- Refactor ValueStreamManager to shared ref-counted streams (value streams
produce scalars, not LED-count-dependent, so sharing is correct)
- Simplify acquire/release API: remove consumer_id parameter since streams
are no longer consumer-dependent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New per-target property: when effective output brightness
(max pixel value × device/source brightness) falls below
the threshold, LEDs turn off completely. Useful for cutting
dim flicker in audio-reactive and ambient setups.
Threshold slider (0–254) in target editor, badge on card,
hot-swap to running processors, persisted in storage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add WebSocket endpoints for live audio spectrum streaming at ~20Hz
- Audio source test: resolves device/channel, shares stream via ref-counting
- Audio template test: includes device picker dropdown for selecting input
- Canvas-based 64-band spectrum visualizer with falling peaks and beat flash
- Channel-aware: mono sources show left/right/mixed spectrum correctly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces an engine+template abstraction for audio capture, mirroring the
existing screen capture engine pattern. This enables multiple audio backends
(WASAPI for Windows, sounddevice for cross-platform) with per-source
engine configuration via reusable templates.
Backend:
- AudioCaptureEngine ABC with WasapiEngine and SounddeviceEngine implementations
- AudioEngineRegistry for engine discovery and factory creation
- AudioAnalyzer class decouples FFT/RMS/beat analysis from engine-specific capture
- ManagedAudioStream wraps engine stream + analyzer in background thread
- AudioCaptureTemplate model and AudioTemplateStore with JSON CRUD
- AudioCaptureManager keyed by (engine_type, device_index, is_loopback)
- Auto-migration: default template created on startup, assigned to existing sources
- Full REST API: CRUD for audio templates + engine listing with availability flags
- audio_template_id added to MultichannelAudioSource model and API schemas
Frontend:
- Audio template cards in Streams > Audio tab with engine badge and config details
- Audio template editor modal with engine selector and dynamic config fields
- Audio template dropdown in multichannel audio source editor
- Template name crosslink badge on multichannel audio source cards
- Confirm modal z-index fix (always stacks above editor modals)
- i18n keys for EN and RU
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add auto_start boolean field to PictureTarget model (persisted per-target)
- Wire auto_start through API schemas, routes, and store
- Auto-start targets on server boot in main.py lifespan
- Add star toggle button on target cards (next to delete button)
- Add auto-start section on dashboard between performance and profiles
- Remove auto-start section from profiles tab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
filter_template references were silently ignored in PP template test,
picture source test, and KC target test routes — they created a no-op
FilterTemplateFilter instead of expanding into the referenced template's
filters. Centralized expansion logic into PostprocessingTemplateStore.
resolve_filter_instances() and use it in all test routes + live stream
manager.
Also fixed empty template_id when adding filter_template filters: the
select dropdown showed the first template visually but onchange never
fired, saving "" instead. Now initializes with first choice's value and
auto-corrects stale/empty values at render time.
Other fixes: ScreenCapture dimensions now use actual image shape after
filter processing; brightness source label emoji updates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instrument AudioCaptureStream with read/FFT timing and
AudioColorStripStream with render timing. Display audio-specific
timing segments (read/fft/render/send) in the target card
breakdown bar when an audio source is active.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Virtual device with configurable LED count, RGB/RGBW mode, and simulated
send latency. Includes full provider/client implementation, API schema
support, and frontend add/settings modal integration.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Stream real-time LED colors from running WLED targets to the browser via
binary WebSocket (RGB bytes, throttled to ~15 fps). Toggle button on
target card opens a compact canvas strip that renders each frame using
ImageData. Cached last frame is re-rendered after card reconciliation to
prevent flicker during auto-refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New source_type "api_input" allows external clients to push raw LED
color arrays ([R,G,B] per LED) via REST POST or WebSocket. Includes
configurable fallback color and timeout for automatic revert when no
data is received. Stream auto-sizes LED count from the target device.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single "adaptive" type with adaptive_mode sub-selector by two
distinct source types in the dropdown. Removes the adaptive_mode field
entirely — the source_type itself carries the mode. Clearer UX with
"Adaptive (Time of Day)" and "Adaptive (Scene)" as separate options.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New "adaptive" value source type that automatically adjusts brightness
based on external conditions. Two sub-modes: time-of-day (schedule-based
interpolation with midnight wrap) and scene brightness (frame luminance
analysis via numpy BT.601 subsampling with EMA smoothing).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Background task samples system (CPU/RAM/GPU) and per-target (FPS/timing) metrics
every 1s into a 120-sample ring buffer (~2 min). New API endpoint
GET /system/metrics-history returns the buffer. Dashboard charts now seed from
server history on load instead of sessionStorage, surviving page refreshes.
Also removes emoji from brightness source labels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extend value source brightness modulation to Key Colors targets (matching LED target support).
Also fix stream subtab CSS selector collision that broke target subtab selection, and use 🔢 emoji
for value source UI elements.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduces a new Value Source entity that produces a scalar float (0.0-1.0)
for dynamic brightness modulation. Three subtypes: Static (constant),
Animated (sine/triangle/square/sawtooth waveform), and Audio-reactive
(RMS/peak/beat from mono audio source). Value sources can be optionally
attached to LED targets to control brightness each frame.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Segments are redundant now that the "mapped" CSS type handles spatial
multiplexing internally. Each target now references one color_strip_source_id
instead of an array of segments with start/end/reverse ranges.
Backward compat: existing targets with old segments format are migrated
on load by extracting the first segment's CSS source ID.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Audio sources moved to separate tab with dedicated CRUD API, store, and editor modal
- New "mapped" color strip source type: assigns different CSS sources to distinct LED sub-ranges (zones)
- Mapped stream runtime with per-zone sub-streams, auto-sizing, hot-update support
- Target editor auto-collapses segments UI when mapped CSS is selected
- Delete protection for CSS sources referenced by mapped zones
- Compact header/footer layout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New scrcpy/ADB capture engine that captures Android device screens over
ADB using screencap polling. Supports USB and WiFi ADB connections with
device auto-discovery. Engine-aware display picker shows Android devices
when scrcpy engine is selected, with inline ADB connect form for WiFi
devices.
Key changes:
- New scrcpy_engine.py using adb screencap polling (~1-2 FPS over WiFi)
- Engine-aware GET /config/displays?engine_type= API
- ADB connect/disconnect API endpoints (POST /adb/connect, /adb/disconnect)
- Display picker supports engine-specific device lists
- Stream/test modals pass engine type to display picker
- Test template handler changed to sync def to prevent event loop blocking
- Restart script merges registry PATH for newly-installed tools
- All engines (including unavailable) shown in engine list with status flag
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audio capture now produces per-channel FFT spectrum and RMS alongside
the existing mono mix. Each audio color strip source can select which
channel to visualize via a new "Channel" dropdown. This enables stereo
setups with separate left/right segments on the same LED strip.
Also shows the device LED count under the device selector in the target
editor for quick reference.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each target now has a segments list where each segment maps a color strip
source to a pixel range (start/end) on the device with optional reverse.
This enables composing multiple visualizations on a single LED strip.
Old targets auto-migrate from the single source format on load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add new "audio" color strip source type with three visualization modes
(spectrum analyzer, beat pulse, VU meter) supporting WASAPI loopback and
microphone input via PyAudioWPatch. Includes shared audio capture with
ref counting, real-time FFT spectrum analysis, and beat detection.
Improve all referential integrity 409 error messages across delete
endpoints to include specific names of referencing entities instead of
generic "one or more" messages.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Composite sources stack multiple existing color strip sources as layers
with configurable blend modes (Normal, Add, Multiply, Screen) and per-layer
opacity. Includes full CRUD, hot-reload, delete protection for referenced
layers, and pre-allocated integer blend math at 30 FPS.
Also eliminates per-frame numpy allocations in color_strip_stream,
effect_stream, and wled_target_processor (buffer pre-allocation).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LED skip: set first N and last M LEDs to black on a target. Color sources
(static, gradient, effect, color cycle) render across only the active
(non-skipped) LEDs. Processor pads with blacks before sending to device.
Rename standby_interval → keepalive_interval across all Python, API
schemas, and JS. from_dict falls back to old key for existing configs.
Remove legacy migration functions (_migrate_devices_to_targets,
_migrate_targets_to_color_strips) and legacy fields from target model.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Filter Template meta-filter: reference existing PP templates inside others
for composable, DRY filter chains. Filters are recursively expanded at
pipeline build time with cycle detection. New `select` option type with
dynamic choices populated by the API.
Keepalive optimization: serial devices (Adalight, AmbiLED) don't need
keepalive — they hold last frame indefinitely. Check `standby_required`
capability at processor start, skip keepalive sends for serial targets,
and hide keepalive metrics in the UI. Rename "Standby Interval" to
"Keep Alive Interval" throughout the frontend.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New features:
- Procedural effect source type with fire, meteor, plasma, noise, and aurora algorithms
using palette LUT system and 1D value noise generator
- 12 predefined gradient presets (rainbow, sunset, ocean, forest, fire, lava, aurora,
ice, warm, cool, neon, pastel) selectable from a dropdown in the gradient editor
- Auto-crop filter: min aspect ratio parameter to prevent false-positive cropping
in dark scenes on ultrawide displays
Optimization:
- Static/gradient sources without animation: stream thread sleeps 0.25s instead of
frame_time; processor repolls at frame_time instead of 5ms (~40x fewer iterations)
- Inverted isinstance checks in routes to test for PictureColorStripSource only
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: add batch endpoints for target states, metrics, and device
health to replace O(N) individual API calls per poll cycle.
Frontend: use batch endpoints in dashboard/targets/profiles tabs,
fix Chart.js instance leaks, debounce server event reloads, add
i18n active-tab guards, clean up ResizeObserver on pattern editor
close, cache uptime timer DOM refs, increase KC auto-refresh to 2s.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace animation Enable checkbox with None option in effect selector;
show effect description tooltip; disable speed slider when None selected
- Allow target FPS range 1-90 (was 10-90) across UI and backend validation
- Scope serial COM connections to target lifetime (no idle caching);
use temporary connections for power-off/test mode
- Fix serial black frame on stop: flush after write, delay after task
cancel to prevent race with in-flight thread pool write
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove static/idle color from entire stack (storage, API, processing, UI, CSS, locales)
- Simplify device power button to turn-off only (send black frame, no toggle)
- Send black frame on serial port close (AdalightClient.close)
- Fix settings modal serial port dropdown showing WLED devices due to stale deviceType
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FPS is a consumption property (how fast to send to a device), not a
production property. Two targets sharing the same source may need
different FPS. This moves the fps field from PictureColorStripSource
to WledPictureTarget across the full stack.
The capture stream now auto-adjusts its rate to max(all connected
target FPS values) via ColorStripStreamManager tracking per-consumer
FPS. UI updates: FPS slider in target editor, FPS badge on target
cards, LED count repositioned in CSS editor, consistent speed icons.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The GET /devices/{id}/brightness endpoint was making an HTTP request to
the ESP32 over WiFi on every frontend poll (~3s), causing 150ms async
event loop jitter that froze the LED processing loop. Cache brightness
server-side after first fetch/set, add frontend dedup guard, reduce
get_device_info() frequency, and add processing loop timing diagnostics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change /api/v1/system/performance from async def to def so FastAPI
runs the blocking psutil + NVML GPU queries in a thread pool instead
of freezing the event loop (polled every 2s by dashboard)
- Batch profile engine's 3 separate run_in_executor detection calls
into a single _detect_all_sync() call, reducing event loop wake-ups
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- color_cycle is now a top-level source type (alongside picture/static/gradient)
with a configurable color list and cycle_speed; defaults to full rainbow spectrum
- ColorCycleColorStripSource + ColorCycleColorStripStream: smooth 30 fps interpolation
between user-defined colors, one full cycle every 20s at speed=1.0
- Removed color_cycle animation sub-type from StaticColorStripStream
- Color cycle editor: compact horizontal swatch layout, proper module-scope fix
(colorCycleAdd/Remove now exposed on window, DOM-synced before mutations)
- Animation enabled + Frame interpolation checkboxes use toggle-switch style
- Removed Potential FPS metric from targets and KC targets metric grids
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a new 'static' source type that fills all device LEDs with a
single constant RGB color — no screen capture or processing required.
- StaticColorStripSource storage model (color + led_count=0 auto-size)
- StaticColorStripStream: no background thread, configure() sizes to device
LED count at processor start; hot-updates preserve runtime size
- ColorStripStreamManager dispatches static sources (no LiveStream needed)
- WledTargetProcessor calls stream.configure(device_led_count) on start
- API schemas/routes: source_type Literal["picture","static"]; color field;
overlay/calibration-test endpoints return 400 for static
- Frontend: type selector modal, color picker, type-aware card rendering
(🎨 icon + color swatch), LED count field hidden for static type
- Locale keys: color_strip.type, color_strip.static_color (en + ru)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Target overlay works without active processing: route pre-loads calibration
and display info from the CSS store, passes to processor as fallback
- Fix server crash on repeated overlay: replace per-window tk.Tk() with single
persistent hidden root; each overlay is a Toplevel child dispatched via
root.after() — eliminates Tcl interpreter crashes on Windows
- Fix edge test colors not lighting up: always call set_test_mode regardless
of processing state (was guarded by 'not proc.is_running'); pass calibration
so _send_test_pixels knows which LEDs map to which edges
- Fix device reset on overlay stop: keep idle serial client cached after
clearing test mode; start_processing() already closes it before connecting
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add explicit led_count to PictureColorStripSource (0 = auto from calibration)
- Stream pads with black or truncates to match led_count exactly
- Calibration dialog: show led_count input above visual editor in CSS mode
- Calibration dialog: pre-populate led_count with effective count (cal sum) when stored value is 0
- Calibration dialog: sync preview label live as led_count input changes
- CSS editor: group brightness/saturation/gamma into collapsible "Color Corrections" section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extracts color processing and calibration out of WledPictureTarget into a
new PictureColorStripSource entity, enabling multiple LED targets to share
one capture/processing pipeline.
New entities & processing:
- storage/color_strip_source.py: ColorStripSource + PictureColorStripSource models
- storage/color_strip_store.py: JSON-backed CRUD store (prefix css_)
- core/processing/color_strip_stream.py: ColorStripStream ABC + PictureColorStripStream (runs border-extract → map → smooth → brightness/sat/gamma in background thread)
- core/processing/color_strip_stream_manager.py: ref-counted shared stream manager
Modified storage/processing:
- WledPictureTarget simplified to device_id + color_strip_source_id + standby_interval + state_check_interval
- Device model: calibration field removed
- WledTargetProcessor: acquires ColorStripStream from manager instead of running its own pipeline
- ProcessorManager: wires ColorStripStreamManager into TargetContext
API layer:
- New routes: GET/POST/PUT/DELETE /api/v1/color-strip-sources, PUT calibration/test
- Removed calibration endpoints from /devices
- Updated /picture-targets CRUD for new target structure
Frontend:
- New color-strips.js module with CSS editor modal and card rendering
- Calibration modal extended with CSS mode (css-id hidden field + device picker)
- targets.js: Color Strip Sources section added to LED tab; target editor/card updated
- app.js: imports and window globals for CSS + showCSSCalibration
- en.json / ru.json: color_strip.* and targets.section.color_strips keys added
Data migration runs at startup: existing WledPictureTargets are converted to
reference a new PictureColorStripSource created from their old settings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProcessorManager: replace all isinstance checks with property-based
dispatch via base TargetProcessor (device_id, led_client,
get_display_index, update_device, update_calibration).
Remove gamma/saturation from ProcessingSettings, ColorCorrection
schema, serialization, and migration — these were never used in the
processing pipeline and are handled by postprocessing template filters.
Delete dead apply_color_correction() function.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace isinstance checks with polymorphic methods on PictureTarget
hierarchy: register_with_manager, sync_with_manager, update_fields,
and has_picture_source property.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add static_color capability to WLED and serial providers with native
set_color() dispatch (WLED uses JSON API, serial uses idle client)
- Encapsulate device-specific logic in providers instead of device_type
checks in ProcessorManager and API routes
- Add HAOS light entity for devices with brightness_control + static_color
(Adalight/AmbiLED get light entity, WLED keeps number entity)
- Fix serial device brightness and turn-off: pass software_brightness
through provider chain, clear device on color=null, re-send static
color after brightness change
- Add global events WebSocket (events-ws.js) replacing per-tab WS,
enabling real-time profile state updates on both dashboard and profiles tabs
- Fix profile activation: mark active when all targets already running,
add asyncio.Lock to prevent concurrent evaluation races, skip process
enumeration when no profile has conditions, trigger immediate evaluation
on enable/create/update for instant target startup
- Add reliable server restart script (restart.ps1)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Serial devices now route power on/off through the cached idle client
instead of opening a new serial connection (which caused PermissionError).
Adds tracked power_on state to DeviceState since Adalight has no
hardware power query.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>