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>
- Draw dashed orange line on target FPS sparkline showing hardware max FPS
- Prevent loadTargetsTab polling from rebuilding DOM while a button action
(start/stop/overlay/delete) is in flight; add reentry guard on the
refresh function itself
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add dynamic "Hardware max ≈ N fps" recommendation below FPS slider,
computed from LED count (WLED: protocol timing) or baud rate (serial).
Reuses shared _computeMaxFps from devices.js with named constants.
- Fix gradient looking different across targets sharing the same stream:
configure() now uses max LED count across all consumers; _fit_to_device
uses np.interp linear interpolation instead of truncate/tile.
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>
Replace the three FPS text labels (actual/current/target) with a
Chart.js sparkline chart + compact label, matching the dashboard style.
FPS history (30 samples) persists across poll rebuilds. Pipeline timing
breakdown moved inside the metrics grid directly under the FPS chart.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Non-picture sources (static, gradient, color_cycle) returned empty
timing from get_last_timing(), causing timing_total_ms to be null and
hiding the entire timing section in the UI. Now timing_total_ms falls
back to send_ms when no CSS pipeline timing exists. Frontend timing
bar/legend segments are conditionally rendered to avoid null labels.
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>
- Add 5 new animation effects (strobe, sparkle, pulse, candle, rainbow
fade) to both static and gradient color strip streams
- Fix FPS drops (30→25) by using 5ms re-poll on frame skip instead of
full frame_time, preventing synchronization misses between animation
thread and processing loop
- Double-buffer animation output arrays to eliminate per-frame numpy
allocations and reduce GC pressure
- Use uint16 integer math for gradient brightness scaling instead of
float32 intermediates
- Update animation type dropdowns and locale strings (en + ru)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix WGC capture_frame() returning stale frames (80k "frames" in 2s)
by tracking new-frame events; return None when no new frame arrived
- Add draw_border config passthrough with Win11 22H2+ platform check
- Add high_resolution_timer() utility (timeBeginPeriod/EndPeriod)
- Switch all processing loops from time.time() to time.perf_counter()
- Wrap all loops with high_resolution_timer() for ~1ms sleep precision
- Add animation speed badges to static/gradient color strip cards
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix Toggle All button always showing Start: /picture-targets list
endpoint does not include processing state; now fetches
/picture-targets/{id}/state per-target in parallel in both
loadProfiles() and toggleProfileTargets()
- Replace pause icons (⏸) with stop icons (⏹) in dashboard
- Change profile automation toggle tooltip from 'Disabled' (status)
to 'Disable' (action); add profiles.action.disable i18n key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Modal content now constrained to viewport height with scrollable body,
preventing dialogs from overflowing on small screens
- Overlay (👁️) button hidden for targets using static/gradient/color_cycle
sources — calibration overlay only applies to picture-type sources
Co-Authored-By: Claude Sonnet 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>
- Disable Chart.js animations on real-time FPS and perf charts
- Dashboard: delta-update profile badges on state changes instead of full DOM rebuild
- Dashboard: cache querySelector results in Map for metrics update loop
- Dashboard: debounce poll interval slider restart (300ms)
- Calibration: debounce ResizeObserver and span drag via requestAnimationFrame
- Calibration: batch updateCalibrationPreview canvas render into rAF
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New match types for application conditions:
- "fullscreen": app has a fullscreen window on any monitor (detected via
EnumWindows, works even when another window is focused on a different
display)
- "topmost_fullscreen": app is the focused foreground window AND fullscreen
Optimizes profile evaluation to only call expensive detection methods when
needed: WMI process enumeration (~3s) is skipped when no condition uses
"running" mode; foreground/fullscreen checks (<1ms each) are called
selectively based on active match types.
Filters false positives from fullscreen detection by excluding desktop/shell
process windows, tool windows, and non-activatable overlay windows.
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>
Replace hardcoded updateAllText() calls with languageChanged event
pattern so feature modules subscribe independently. Guard all API
calls behind apiKey checks to prevent unauthorized requests when not
logged in. Fix login modal localization, hide tabs when logged out,
clear all panels on logout, and treat profiles with no conditions as
always-true.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When multiple rectangles overlap, the currently selected one is now
tested first for both click and hover, keeping its edges and body
interactive even when another rectangle sits on top.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap running and stopped target lists under a parent Targets group.
Fix narrow-screen layout by keeping action buttons inline and hiding
metrics below 768px.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace text FPS labels with Chart.js sparklines on running targets,
use emoji icons for metrics, add in-place DOM updates to preserve
chart animations, and add a 1-10s poll interval slider that controls
all dashboard timers. Uptime now ticks every second via client-side
interpolation regardless of poll interval.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds brightness (0.0-1.0) to KeyColorsSettings, applies scaling in the
KC processing pipeline after smoothing, exposes a slider on the WebUI
target card, and creates a HA number entity for KC target brightness.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a Number platform to the HAOS integration so each LED target
with a brightness-capable device gets a 0-255 slider in Home
Assistant. Coordinator now fetches device list and brightness on
each poll cycle. Also enable chart animation in perf-charts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The 2-second auto-refresh was destroying the DOM and closing the
native color picker popup. Now checks if an input inside the targets
panel is focused before refreshing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: GET /api/v1/system/performance endpoint using psutil (CPU/RAM)
and nvidia-ml-py (GPU utilization, memory, temperature) with graceful
fallback. Frontend: Chart.js line charts with rolling 60-sample history
persisted to sessionStorage, flicker-free updates via persistent DOM
and diff-based dynamic section refresh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each dashboard section (Profiles, Running, Stopped) now has a chevron
toggle that collapses/expands the section content. Collapsed state is
persisted in localStorage so it survives page reloads and WebSocket
re-renders. Stop All button uses event.stopPropagation() to avoid
triggering the section toggle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add core/modal.js with reusable Modal class that handles open/close,
body locking, backdrop close, dirty checking, error display, and a
static stack for ESC key management. Migrate all 13 modals across 8
feature files to use the base class, eliminating ~200 lines of
duplicated boilerplate. Replace manual ESC handler list in app.js
with Modal.closeTopmost(), fixing 3 modals that were previously
unreachable via ESC. Remove 5 unused initialValues variables from
state.js. Fix target editor to auto-select first device/source and
auto-generate name like the KC editor does.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetch devices list alongside targets and profiles in loadDashboard,
then look up device_type from the devices map instead of relying on
state.device_name which is only available for running targets.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add missing common.loading i18n key to en/ru locales. Add close button
and ESC key support to the overlay spinner so users can cancel running
capture tests. Uses AbortController to abort the in-flight fetch request.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the single 7034-line app.js with 17 ES module files organized
into core/ (state, api, i18n, ui) and features/ (calibration, dashboard,
device-discovery, devices, displays, kc-targets, pattern-templates,
profiles, streams, tabs, targets, tutorials) with an app.js entry point
that registers ~90 onclick globals on window. No bundler needed — FastAPI
serves modules directly via <script type="module">.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>