Commit Graph

548 Commits

Author SHA1 Message Date
alexei.dolgolyov 92585e7c19 fix(build): bundle bettercam/dxcam/windows-capture in installer
Lint & Test / test (push) Successful in 2m5s
The Windows installer was only shipping mss as a screen-capture
backend, so EngineRegistry.get_available_engines() reported just
['mss', 'camera', 'demo'] on installed builds. Picture sources
configured to use bettercam/dxcam/wgc were rejected at the
test/ws handshake with HTTP 403 (close-before-accept on
"Engine '<x>' not available").

Add the three Windows screen-capture wheels to WIN_DEPS so the
installer build matches a 'pip install -e .' dev environment.
2026-04-08 23:15:10 +03:00
alexei.dolgolyov 0e09eaf43b fix(launcher): set TCL_LIBRARY/TK_LIBRARY for embedded Python
Embedded Python ships with tcl8.6/ and tk8.6/ next to python.exe, but
Tcl's auto-detection searches <exe>/../lib/tcl8.6 — a path that doesn't
exist in our layout. Without these env vars, tkinter.Tk() raises
"Can't find a usable init.tcl", breaking the screen overlay (and any
other tk-based UI) on installed builds.
2026-04-08 23:14:58 +03:00
alexei.dolgolyov adfc39f9d1 chore: release v0.3.0
Build Release / create-release (push) Successful in 3s
Build Release / build-linux (push) Successful in 1m23s
Build Release / build-docker (push) Successful in 2m19s
Lint & Test / test (push) Successful in 3m10s
Build Release / build-windows (push) Successful in 3m29s
v0.3.0
2026-04-08 12:41:28 +03:00
alexei.dolgolyov d037a2e929 fix(tray): replace tkinter messagebox with Win32 MessageBoxW
Lint & Test / test (push) Successful in 2m3s
The packaged embedded Python distribution does not ship the tcl/tk
runtime, so tkinter.messagebox.askyesno crashed with 'Can't find a
usable init.tcl' when the user clicked Shutdown or Restart in the
tray menu. Use ctypes + user32.MessageBoxW instead — no tcl/tk,
no extra dependencies.
2026-04-08 12:16:32 +03:00
alexei.dolgolyov fc8ee34369 fix(launcher): start-hidden.vbs must be ASCII + CRLF, use python.exe
Lint & Test / test (push) Successful in 1m40s
Three separate bugs in the VBS launcher wedged together:

1. The previous fix added a UTF-8 em-dash in a comment. wscript.exe
   on Windows refused to execute the file with "Execution of the
   Windows Script Host failed. (Not enough memory resources are
   available to complete this operation.)" — a misleading error that
   actually means "I could not parse this file as ANSI VBScript".
   Fix: keep the file pure ASCII, convert to CRLF.

2. The launcher was invoking pythonw.exe. WshShell.Run spawning
   pythonw.exe inside the wscript host exited immediately (no process,
   no log). python.exe with WindowStyle=0 works reliably and matches
   the pattern used by the Media Server sibling app's VBS launcher,
   which has been running on this machine without issue.

3. The env vars (PYTHONPATH, WLED_CONFIG_PATH) must be set before the
   child process spawns, otherwise config.py falls back to the CWD
   default path that does not exist at install time.
2026-04-08 00:15:49 +03:00
alexei.dolgolyov e262a8b004 fix(launcher): set PYTHONPATH and WLED_CONFIG_PATH in start-hidden.vbs
Lint & Test / test (push) Successful in 2m1s
The VBS launcher (used by Start Menu, desktop, and autostart shortcuts
created by the NSIS installer) ran pythonw.exe without setting any env
vars. LedGrab.bat sets PYTHONPATH and WLED_CONFIG_PATH; the VBS did not.

With CWD set to the install root, config.py fell through to its default
lookup (./config/default_config.yaml), which does not exist there — the
real file is at app/config/default_config.yaml. The server silently ran
with built-in defaults on every shortcut launch: no devices, wrong data
dir, nothing persisted where the user expected.

The fix uses WshShell.Environment("Process") to set env vars on the
current VBS process, which child processes spawned via .Run inherit.
Kept CurrentDirectory = appRoot to preserve prior behavior for anyone
depending on CWD-relative paths inside the app.
2026-04-08 00:02:56 +03:00
alexei.dolgolyov d4ffe2e985 refactor: drop packaging dependency, inline version parsing
Lint & Test / test (push) Successful in 3m9s
The only user of 'packaging' was version_check.py — two small functions
(normalize_version, is_newer) that just need to parse "1.2.3-alpha.1"
and compare PEP 440-style versions. That's well within stdlib reach.

- Inline a NamedTuple-based Version with kind/pre_num ordering
  (dev < alpha < beta < rc < release), same regex-normalized format
- Define a local InvalidVersion exception
- Remove packaging>=23.0 from pyproject.toml dependencies

Why now: the Windows cross-build uses a hard-coded DEPS array in
build-dist-windows.sh, which was never updated when 'packaging' was
added on March 25. Result: importable from pip-installed dev envs,
missing from the portable installer — tray icon appeared but uvicorn
died with ModuleNotFoundError: No module named 'packaging'.

Removing the dep entirely is cleaner than adding one more hard-coded
entry to the Windows DEPS list. Tests (678 passing) and a manual test
matrix covering dev/alpha/beta/rc/release ordering all pass.
2026-04-07 23:54:27 +03:00
alexei.dolgolyov feb91ad281 fix(build): fix shell syntax error in smoke_test_imports heredoc
Lint & Test / test (push) Successful in 2m6s
The 'cmd <<EOF || { ... }' pattern confuses bash's parser — the closing
brace of the inline error block collides with the function's closing
brace. Rewrote to capture the python script into a local var via
$(cat <<EOF), then run it with -c and a plain 'if !' guard.
2026-04-07 23:41:42 +03:00
alexei.dolgolyov 17c5c02993 fix(build): keep .py sources + make smoke test skip uninstalled modules
Lint & Test / test (push) Successful in 1m36s
- compile_and_strip_sources: stop deleting .py files after compileall.
  OpenCV's loader does literal file I/O on cv2/config.py (not a Python
  import), so stripping it breaks `import cv2` with "missing
  configuration file: ['config.py']". Other packages may do similar
  file-based introspection tricks — the ~30% size win isn't worth
  playing whack-a-mole with broken installers. We already hit this
  with numpy.linalg and zeroconf._services; enough incidents.
- smoke_test_imports: only assert importability for modules whose
  top-level dir actually exists in site-packages. Pillow for example
  is a Windows-only dep, and was failing the Linux build spuriously.
  Rewrote as a heredoc for readability.
2026-04-07 23:37:54 +03:00
alexei.dolgolyov fd6776aeac fix(build): stop stripping zeroconf/_services + add import smoke test
Lint & Test / test (push) Successful in 2m39s
- build-common.sh: remove zeroconf/_services from the strip list.
  zeroconf's compiled Cython _listener.pyd imports from _services
  internally, so stripping it broke `import zeroconf` at runtime with
  ModuleNotFoundError — same class of bug as the numpy.linalg strip.
- build-common.sh: add smoke_test_imports() that imports every top-level
  dependency against the stripped site-packages. Catches "we stripped
  something that was actually needed" regressions at build time instead
  of on a user's machine after install.
- build-dist.sh: wire smoke test into the Linux flow (runs real imports).
- build-dist-windows.sh: cross-build can't load win_amd64 .pyd files with
  the host python, so instead verify that the known-required submodule
  dirs (numpy.linalg/lib/matrixlib/ma, zeroconf._services) exist after
  cleanup. Fails loud if any future strip-rule removes them.
2026-04-07 23:32:50 +03:00
alexei.dolgolyov 9f34ffb0a0 fix(build): stop stripping numpy.lib/linalg from site-packages
Lint & Test / test (push) Successful in 2m57s
numpy's own __init__.py imports lib and matrixlib (which in turn imports
numpy.linalg via defmatrix.py). Removing any of these submodules to save
dist size makes `import numpy` raise ModuleNotFoundError on the target,
which cascades into every wled_controller import.

Symptom: v0.0.0.dev0 Windows installer showed a tray icon but uvicorn
died silently in its background thread and port 8080 never listened —
browser got ERR_CONNECTION_REFUSED.

Keep stripping: polynomial, distutils, f2py, typing, _pyinstaller.
These are genuinely unused by numpy's own import chain.
2026-04-07 23:17:12 +03:00
alexei.dolgolyov b5842e6424 fix(build): normalize non-PEP440 versions, fix .py/compileall ordering, wipe NSIS payload dirs
Lint & Test / test (push) Successful in 3m18s
- build-common.sh: detect_version() normalizes non-PEP440 labels (e.g. 'dev',
  'nightly') to 0.0.0.dev0 so stamping pyproject.toml doesn't break pip install.
- build-common.sh: split .py deletion out of cleanup_site_packages into a new
  compile_and_strip_sources() that runs 'compileall -b' FIRST, then removes
  sources. compileall now fails loud instead of silently no-op'ing.
- build-dist.sh: add missing compile_and_strip_sources call on Linux site-packages
  (previous tarballs shipped empty packages with no .py and no .pyc).
- build-dist-windows.sh: reorder so compile_and_strip_sources runs right after
  cleanup_site_packages, not after .py files have already been deleted.
- installer.nsi: RMDir /r payload dirs (python/, app/, scripts/) and delete
  LedGrab.bat at the top of SecCore before File /r. NSIS File /r MERGES into
  existing dirs, so upgrades left half-old/half-new state that surfaced as
  'version mismatch' or duplicate-package ImportErrors. data/ and logs/ remain
  untouched to preserve user config.
2026-04-07 23:04:38 +03:00
alexei.dolgolyov 7a9c368448 refactor: split color-strips.ts into focused modules under color-strips/ folder
Lint & Test / test (push) Successful in 2m6s
Monolithic 3060-line color-strips.ts split into 11 modules:
- index.ts (core orchestrator, modal, type switching, editor, save, CRUD)
- cards.ts (card rendering for all source types)
- game-event.ts (game event mappings, presets, UI)
- gradient.ts (gradient entity modal + CRUD)
- audio.ts (audio viz widgets, load/reset state)
- math-wave.ts (wave layers + waveform selects)
- mapped.ts (mapped zone helpers)
- color-cycle.ts (color cycle add/remove/render)
- composite.ts, notification.ts, test.ts (previously extracted, moved into folder)
2026-04-05 12:54:15 +03:00
alexei.dolgolyov ce53ca6872 feat: add card glare effect to dashboard and perf chart cards
Lint & Test / test (push) Has been cancelled
Extend cursor-tracking spotlight to .dashboard-target and
.perf-chart-card elements with a smaller 100px radius suited
for compact cards.
2026-04-05 12:23:39 +03:00
alexei.dolgolyov b04978af58 feat: add music sync viz modes and auto_gain audio filter
Lint & Test / test (push) Has been cancelled
Add 4 new audio visualization modes powered by MusicAnalyzer:
- pulse_on_beat: BPM-synced pulsing with smooth beat phase
- energy_gradient: bass/mid/treble mapped to scrolling gradient
- spectrum_bands: three VU zones for frequency bands
- strobe_on_drop: state-driven strobe on detected musical drops

MusicAnalyzer provides BPM estimation (median IBI), beat phase tracking,
asymmetric energy envelope, 3-band frequency splitting, and drop
detection state machine (idle/buildup/drop/recovery).

Add auto_gain audio filter for automatic level normalization via rolling
peak tracking with configurable target level and response time.

Deprecate auto_gain on Audio Value Source (use the filter instead).
2026-04-05 01:40:34 +03:00
alexei.dolgolyov 6e8b159126 fix: weather CSS card shows empty source name after hard refresh
Lint & Test / test (push) Has been cancelled
Weather sources cache was not fetched during streams tab load, so the
card renderer could not resolve weather source names. Also widened
lat/lon inputs from 80px to 100px to fit longer coordinates.
2026-04-05 00:49:28 +03:00
alexei.dolgolyov ace24715c8 feat: add math_wave color strip source type
Lint & Test / test (push) Has been cancelled
Mathematical wave generator that produces per-LED colors from
configurable waveform layers (sine, triangle, sawtooth, square) with
superposition, mapped through a gradient palette. Supports sync clocks,
bindable speed, and up to 8 wave layers.

- Storage model with wave validation and apply_update
- Numpy-vectorized stream with gradient LUT color mapping
- API schemas (create/update/response) and route registration
- Frontend editor with dynamic wave layer rows, gradient picker,
  speed widget, and IconSelect waveform selectors
- i18n in en/ru/zh
2026-04-05 00:41:07 +03:00
alexei.dolgolyov edc6d27e2e fix: replace HA test icon with refresh, make automation rules collapsible
Lint & Test / test (push) Has been cancelled
Use refresh icon instead of flask for HA test connection buttons.
Add collapse/expand chevron to automation rule rows (collapsed by default).
2026-04-04 21:28:51 +03:00
alexei.dolgolyov b7da4ab6b5 feat: add Integrations tab and responsive icon-only tabs
Lint & Test / test (push) Successful in 1m48s
Move HA sources, weather sources, game integration, and MQTT settings
into a dedicated Integrations top-level tab with tab-registry pattern.
Collapse tab labels to icon-only at narrow desktop widths (<=1100px)
to prevent toolbar overflow.
2026-04-02 15:29:38 +03:00
alexei.dolgolyov 99460a8043 fix: make pystray a core dependency on Windows instead of optional extra
Lint & Test / test (push) Successful in 2m5s
The tray icon should always appear on Windows, not only when installed
with the [tray] extra.
2026-04-02 14:22:18 +03:00
alexei.dolgolyov 89990f8d63 chore: remove processed-audio-sources plan files 2026-04-02 13:42:37 +03:00
alexei.dolgolyov 0cc0aaa411 feat: processed audio sources with composable filter pipeline
Replace hardcoded MonoAudioSource/BandExtractAudioSource with a
composable ProcessedAudioSource + AudioProcessingTemplate + AudioFilter
system. 11 audio filters: channel extract, band extract, peak hold,
gain, noise gate, envelope follower, spectral smoothing, compressor,
inverter, beat gate, delay. Full frontend UI with filter editor,
tree navigation, and i18n support.
2026-04-02 13:42:18 +03:00
alexei.dolgolyov af2c89c8df fix: audio tree structure, filter i18n, and IconSelect for filter options
Restructure audio tree nav into Capture (Sources + Engine Templates)
and Processed (Sources + Filter Templates) subgroups.
Add missing i18n description keys for all 11 audio filters.
Wrap plain select filter options with IconSelect grids.
2026-04-02 13:37:50 +03:00
alexei.dolgolyov d04192ffb7 fix: add reference check before deleting audio processing template
Prevent deleting templates that are still referenced by
ProcessedAudioSource entities. Returns 400 with source names.
2026-04-01 23:28:58 +03:00
alexei.dolgolyov 992495e2e4 fix: isolate tests from production database
Tests that imported wled_controller.main at module level caused the real
production database (data/ledgrab.db) to be opened before test fixtures
could patch the config. This led to silent data loss.

Patch the global config singleton at conftest module level (before any
test imports main.py) to redirect all DB access to a temp directory.
2026-04-01 19:01:56 +03:00
alexei.dolgolyov 6b0e4e5539 feat(processed-audio-sources): phase 8 - frontend design consistency review
Fix audio source modal error class (modal-error), use Modal.showError(),
reorder audio source card description, remove redundant APT filter count
badge, clean up unused imports in audio-sources.ts.
2026-03-31 23:11:17 +03:00
alexei.dolgolyov ce1f4847f3 feat(processed-audio-sources): phase 7 - testing and polish
Fix test_list_filters test (filter_id field name mismatch).
Add tests for audio filters, template store, and source store.
All 678 tests pass, ruff clean, tsc clean, esbuild clean.
No dead code remaining from old source types.
2026-03-31 22:50:02 +03:00
alexei.dolgolyov 1ce0dc6c61 feat(processed-audio-sources): phase 6 - frontend source type cleanup
Rewrite audio source editor modal for capture/processed types only.
Remove old multichannel/mono/band_extract HTML sections and i18n keys.
Clean up legacy DOM section null-checks in audio-sources.ts.
2026-03-31 19:40:37 +03:00
alexei.dolgolyov 553463935e feat(processed-audio-sources): phase 5 - frontend audio processing templates
Add Audio Processing Templates management UI to Streams tab:
- Template editor modal with filter list via FilterListManager
- CardSection with reconciliation for template cards
- DataCache instances for templates and audio filter defs
- Audio filter icon mappings in filter-list.ts
- i18n keys in en/ru/zh locales
2026-03-31 19:32:17 +03:00
alexei.dolgolyov ab43578049 feat(processed-audio-sources): phase 4 - runtime filter integration
Add AudioFilterPipeline for chained filter execution on AudioAnalysis.
Wire filter pipelines into AudioColorStripStream, AudioValueStream,
and WebSocket test endpoint. Add hot-update support via
ProcessorManager.refresh_audio_filter_pipelines(). Thread
AudioProcessingTemplateStore through dependency injection hierarchy.
2026-03-31 19:15:29 +03:00
alexei.dolgolyov 353c090b42 feat(processed-audio-sources): phase 3 - processed audio source model
Replace MultichannelAudioSource with CaptureAudioSource, add
ProcessedAudioSource (audio_source_id + audio_processing_template_id),
remove MonoAudioSource and BandExtractAudioSource entirely.
Update store resolution to walk processed chains collecting template IDs.
Update all API schemas, routes, and frontend references.
2026-03-31 19:01:46 +03:00
alexei.dolgolyov eb94066386 feat(processed-audio-sources): phase 2 - implement 11 audio filters
Add all audio filters that transform AudioAnalysis data:
- Channel Extract, Band Extract (migration from old source types)
- Peak Hold, Gain, Noise Gate, Envelope Follower
- Spectral Smoothing, Compressor, Inverter, Beat Gate, Delay
All registered via AudioFilterRegistry with option schemas.
2026-03-31 18:43:36 +03:00
alexei.dolgolyov 86a9d344e6 feat(processed-audio-sources): phase 1 - audio filter framework
Add the foundation for audio processing filters, mirroring the existing
picture filter/postprocessing template system:
- AudioFilter base class, AudioFilterRegistry, AudioFilterOptionDef
- AudioProcessingTemplate dataclass + SQLite-backed store
- audio_filter_template meta-filter with recursive resolution
- Full REST API: CRUD templates + filter registry discovery
- Dependency injection wired in dependencies.py and main.py
2026-03-31 17:35:39 +02:00
alexei.dolgolyov c59107c7c7 feat: refactor MQTT from global config to multi-instance entity model
Lint & Test / test (push) Successful in 1m32s
MQTT broker connections are now managed as entities (like HA sources)
instead of a single global config. Each MQTTSource gets its own
runtime with auto-reconnect, ref-counted via MQTTManager.

Backend:
- MQTTSource dataclass + MQTTSourceStore (SQLite)
- MQTTRuntime (per-broker connection, refactored from MQTTService)
- MQTTManager (ref-counted pool, same pattern as HAManager)
- CRUD API at /api/v1/mqtt/sources + test + status endpoints
- MQTTRule gains mqtt_source_id field for source selection
- Automation engine acquires/releases MQTT runtimes automatically
- Legacy MQTTService kept for backward compat during transition

Frontend:
- MQTT source cards in Streams > Integrations tab
- Create/edit modal with test button
- Dashboard integration cards with health-dot indicators
- Removed MQTT tab from settings modal
2026-03-31 18:02:19 +03:00
alexei.dolgolyov e7c9a568dc feat: HA source cards use health-dot indicators
Lint & Test / test (push) Successful in 1m39s
Replace unstyled status-dot classes with the existing health-dot
pattern (green/red glow) matching LED device cards. Tooltip shows
connection status and entity count.
2026-03-31 16:05:01 +03:00
alexei.dolgolyov b36ddfd395 Merge branch 'feature/game-integration'
Lint & Test / test (push) Successful in 1m49s
Game integration system: receive real-time events from games
(CS2, Dota 2, LoL, etc.) and drive LED effects via color strip
streams and value source bindings.
2026-03-31 14:23:38 +03:00
alexei.dolgolyov 492bdb95e3 feat: game integration system
Receive real-time events from games (CS2, Dota 2, LoL, etc.) and drive
LED effects through the existing color strip and value source pipelines.

Core:
- GameEventBus (thread-safe pub/sub) with standardized 23-type event vocabulary
- GameAdapter ABC + AdapterRegistry + MappingAdapter (YAML-driven)
- Built-in adapters: CS2 GSI, Dota 2 GSI, LoL Live Client, Generic Webhook
- Community YAML adapters: Minecraft, Valorant, Rocket League
- GameEventColorStripStream with 5 effects (flash/pulse/sweep/color_shift/breathing)
- GameEventValueSource with EMA smoothing and timeout
- 4 built-in effect presets (FPS Combat, MOBA Health, Racing, Generic Alert)
- Auto-setup for Valve GSI games (Steam path detection, cfg file writing)
- Demo capture engine exposed to non-demo mode

Frontend:
- Game tab in Streams tree navigation with integration cards
- Game integration editor modal with adapter picker, config fields, event mappings
- game_event source type in CSS and ValueSource editors
- Setup instructions overlay (markdown rendered)
- Live event monitor and connection test

API:
- Full CRUD for game integrations
- Event ingestion endpoint (adapter-level auth)
- Adapter metadata, presets, auto-setup, status/diagnostics endpoints
2026-03-31 13:17:52 +03:00
alexei.dolgolyov b6713be390 feat: system_metrics value source type
Lint & Test / test (push) Successful in 1m32s
New value source that monitors host hardware via psutil/pynvml:
cpu_load, cpu_temp, gpu_load, gpu_temp, ram_usage, disk_usage,
network_rx, network_tx, battery_level, fan_speed.

Each metric normalizes to 0.0-1.0 with configurable ranges, poll
interval, EMA smoothing, and sensor_label for multi-sensor systems.
Conditional editor fields show/hide based on selected metric.

Also fixes: WS test crash when raw_value streams lack _min_ha attr,
toast timer overlap on rapid calls, SW cache bump to v34.
2026-03-30 18:22:58 +03:00
alexei.dolgolyov db5008aaeb feat: system theme option + fix toast timer overlap
Lint & Test / test (push) Successful in 1m27s
Add third theme mode (system) that follows OS prefers-color-scheme.
Theme button cycles dark → light → system with monitor icon.
Listens for OS preference changes in real time when in system mode.

Fix showToast clearing previous timer so rapid calls don't cause
the toast to disappear early.
2026-03-30 13:55:38 +03:00
alexei.dolgolyov 4b7a8d75f4 feat: value source card crosslinks + gradient_map test shows input value
Lint & Test / test (push) Successful in 1m23s
Add navigateToCard crosslinks for ha_entity (→ HA source), gradient_map
(→ input value source + gradient entity), and css_extract (→ color strip).
Gradient map test now charts the input interpolation factor instead of
output luminance, making the 0–1 chart meaningful.
2026-03-30 03:23:05 +03:00
alexei.dolgolyov f6c25cd15f feat: color value source test visualization
Lint & Test / test (push) Successful in 1m6s
WS now sends color RGB data for color-type streams. Test modal renders
a color history swatch strip below the chart, colors the chart line and
fill area with the current output color, and shows rgb() in the stats.
Works for static_color, animated_color, and adaptive_time_color sources.
2026-03-30 03:12:57 +03:00
alexei.dolgolyov 0a8737157c feat: HA value source test — raw value axis + behavior IconSelect
Lint & Test / test (push) Successful in 1m9s
- WS sends raw_value and raw_range for HA entity streams
- Canvas draws right-side Y-axis labels: configured min/max range
  and current raw HA value (green, positioned at correct Y)
- Replace plain <select> for scene behavior with IconSelect (moon/sun)
2026-03-30 03:06:44 +03:00
alexei.dolgolyov 11d5d6b5e1 fix: device card header layout — URL badge overflow and hide button gap
Lint & Test / test (push) Successful in 1m24s
Move URL badge from card-title to card-subtitle row to prevent
overlap with top-right action buttons. Widen card-header padding-right
to 60px for 2-button clearance. Reorder hide button to first position
in top-actions so power and trash stay visually adjacent.
2026-03-30 02:05:45 +03:00
alexei.dolgolyov 384362ccf1 feat: new value source types (HA entity, gradient map, strip extract) + UI fixes
Lint & Test / test (push) Successful in 1m27s
New value source types:
- ha_entity: reads numeric values from HA entity state/attribute, normalizes
  via min/max range, applies EMA smoothing. EntitySelect for HA connection
  and entity selection with live entity list fetching.
- gradient_map: maps a float value source (0-1) through a gradient entity.
  EntitySelect for both input source and gradient with inline previews.
- css_extract: extracts single color by averaging LED range from a color
  strip source. EntitySelect for source selection.

Value source type picker:
- Filter tabs (All / Numeric / Color) above the icon grid
- showTypePicker extended with filterTabs + onFilterChange support

Palette selectors converted to EntitySelect:
- Effect palette, gradient preset, and audio palette selectors now use
  command-palette style EntitySelect with gradient strip previews

Tab indicator fixes:
- Icon now updates on tab switch (was passing no args to updateTabIndicator)
- Visible with any background effect active, not just Noise Field
- Noise Field is the default background effect for new users

Dashboard section collapse fix:
- Split header into clickable toggle (chevron+label) and non-clickable
  actions area — buttons no longer trigger collapse/expand

Discriminated union fix (422 errors):
- source_type/target_type now always included in update payloads for:
  CSS editor, LED target, HA light target, simple calibration,
  advanced calibration
2026-03-29 20:38:22 +03:00
alexei.dolgolyov ea812bb4d5 feat: check if port is busy before starting the server
Lint & Test / test (push) Successful in 1m16s
2026-03-29 14:21:35 +03:00
alexei.dolgolyov a9e6e8cb82 fix: KC color strip test preview — use LiveStreamManager instead of raw engine
Lint & Test / test (push) Successful in 1m28s
BetterCam (DXGI) only supports one capture session per display, so creating
a second engine for the KC test produced no frames when a target was already
running. Now uses LiveStreamManager.acquire() which ref-counts and reuses
the running capture.

Also removed double postprocessing — ProcessedLiveStream already applies
filters, so re-applying them in the KC test halved the resolution (960x400
→ 240x100).
2026-03-29 14:11:01 +03:00
alexei.dolgolyov 78ce6c84d7 fix: composite layer opacity/brightness widgets + CSS layout
Lint & Test / test (push) Successful in 1m7s
- Fix opacity widget empty space (CSS selector .composite-layer-opacity → .composite-layer-opacity-container)
- Replace brightness select dropdown with BindableScalarWidget (slider + VS toggle)
- Legacy brightness_source_id auto-converted to BindableFloat on load
- Add .composite-layer-brightness-container CSS rule
2026-03-29 00:42:42 +03:00
alexei.dolgolyov 8a17bb5caa feat: BindableFloat — universal value source binding for all scalar properties
Lint & Test / test (push) Successful in 1m20s
Introduce BindableFloat abstraction that allows any numeric property to be
either a static value or dynamically driven by a ValueSource. Backward-compatible
serialization: plain float when unbound, {value, source_id} dict when bound.

Backend:
- storage/bindable.py — BindableFloat dataclass + bfloat() helper
- 25+ scalar properties converted across all entity types
- Runtime VS acquisition in ColorStripStreamManager for CSS bindings
- All stream hot loops use self.resolve() for live values
- KeyColorsColorStripStream now inherits ColorStripStream

Frontend:
- BindableScalarWidget (slider + VS picker toggle) for all editors
- TypeScript BindableFloat type + helpers
- Graph editor edges for all bindable properties
- Audio source channel IconSelect grid

Fixes: daylight longitude, candlelight wind_strength/candle_type from_dict
2026-03-29 00:33:24 +03:00
alexei.dolgolyov 5f70302263 feat: use custom app icon for shortcuts and installer
Lint & Test / test (push) Successful in 1m15s
- Generate icon.ico from icon-512.png (16-256px sizes)
- Set MUI_ICON/MUI_UNICON for installer wizard
- Point all shortcuts to icon.ico instead of pythonw.exe
- Add DisplayIcon registry entry for Add/Remove Programs
2026-03-28 18:41:12 +03:00
alexei.dolgolyov 40751fecb7 feat: HA light target live color preview — per-entity swatches via WebSocket
Lint & Test / test (push) Successful in 1m24s
- Cache per-entity colors in HALightTargetProcessor._update_lights()
- Broadcast colors_update to WS clients at target's update_rate
- WS endpoint: /api/v1/output-targets/{target_id}/ha-light/ws
- Frontend: connect WS when target runs, update swatch colors live
- Card shows colored boxes per mapped entity with entity name labels
2026-03-28 18:28:16 +03:00