The os_listener field existed in the backend model but was never
exposed in the UI. It defaulted to false, so OS notifications were
captured to history but never fired the visual effect. Now shows
a toggle at the top of the notification section, defaults to ON
for new sources.
Audio sources: type + device/parent/channel/band detail
Weather sources: provider + coordinates (updates on geolocation)
Sync clocks: "Sync Clocks · Nx" (updates on speed slider)
Automations: scene name + condition count/logic
Scene presets: "Scenes · N targets" (updates on add/remove)
Pattern templates: "Pattern Templates · N rects" (updates on add/remove)
All follow the same pattern: name auto-generates on create, stops
when user manually edits the name field.
Multichannel, Mono, and Band Extract each get their own subtab and
panel within the Audio group, replacing the single combined Audio
Sources tab. Cross-links from CSS, value sources, and command palette
updated to navigate to the correct subtab.
New audio source type that filters a parent source to a specific frequency
band (bass 20-250Hz, mid 250-4kHz, treble 4k-20kHz, or custom range).
Supports chaining with frequency range intersection and cycle detection.
Band filtering applied in both CSS audio streams and test WebSocket.
New standalone WeatherSource entity with pluggable provider architecture
(Open-Meteo v1, free, no API key). Full CRUD, test endpoint, browser
geolocation, IconSelect provider picker, CardSection with test/clone/edit.
WeatherColorStripStream maps WMO weather codes to ambient color palettes
with temperature hue shifting and thunderstorm flash effects. Ref-counted
WeatherManager polls API and caches data per source.
CSS editor integration: weather type with EntitySelect source picker,
speed and temperature influence sliders. Backup/restore support.
i18n for en/ru/zh.
Composite layers now support optional start/end LED range (toggleable)
and reverse flag, making composite a superset of mapped source. Layers
are collapsible with animated expand/collapse and consistent 0.85rem
font sizing. Delete button restyled as ghost icon button.
Also includes minor dashboard CSS overflow fixes.
- Add /api/v1/system/shutdown endpoint that triggers clean uvicorn exit
- Persist all 15 stores to disk during shutdown via _save_all_stores()
- Add force parameter to BaseJsonStore._save() to bypass restore freeze
- Restart script now requests graceful shutdown via API (15s timeout),
falls back to force-kill only if server doesn't exit in time
- Broadcast server_restarting event over WebSocket before shutdown
- Frontend shows "Server restarting..." overlay instantly on WS event,
replacing the old dynamically-created overlay from settings.ts
- Add server_ref module to share uvicorn Server + TrayManager refs
- Add i18n keys for restart overlay (en/ru/zh)
Set WLED_RESTART=1 env var on tray restart so __main__.py skips
opening a new browser tab — the user already has one open.
Add important TODO item to eliminate WLED naming throughout the app
(package rename, i18n, build scripts, etc.).
Populate <select> <option> elements from gradient entities before
creating IconSelect — the trigger display needs matching options to
sync correctly. Add _syncSelectOptions helper used by all three
palette pickers (gradient, effect, audio).
Both actions show a confirmation dialog before proceeding.
Restart uses os.execv to re-launch the process in-place.
Shutdown stops the server and exits the tray.
Replace the gradient stop editor (canvas, markers, stop list) in the
CSS editor gradient section with a simple gradient entity selector.
Gradients are now created/edited exclusively in the Gradients tab.
Fix effect and audio palette pickers to populate from gradient entities
dynamically instead of hardcoded HTML options.
Unify all gradient/palette pickers via _buildGradientEntityItems().
Also: rename "None (use own speed)" → "None (no sync)" for sync clocks.
Add i18n keys for gradient selector and missing error messages.
release.yml: add fallback for existing releases on tag re-push.
installer.nsi: add .onInit file lock check, use LaunchApp function
instead of RUN_PARAMETERS to fix NSIS quoting bug.
build-dist.ps1: copy start-hidden.vbs to dist scripts/.
start-hidden.vbs: embedded Python fallback for installed/dev envs.
Update ci-cd.md with version detection, NSIS best practices, local
build testing, Gitea vs GitHub differences, troubleshooting.
Update frontend.md with full entity type checklist and common pitfalls.
Fix ~68 pre-existing strict null errors across 13 feature modules.
Add non-null assertions for DOM element lookups, null coalescing for
optional values, and type guards for nullable properties. Zero tsc
errors now with --noEmit.
Add full gradient editor modal with name, description, visual stop
editor, tags, and dirty checking. Gradient editor now supports ID
prefix to avoid DOM conflicts between CSS editor and standalone modal.
Fix color picker popover clipped by template-card overflow:hidden.
Fix gradient canvas not sizing correctly in standalone modal.
Add gradient_id field to color strip sources for referencing reusable
gradient entities. Improve audio stream processing and effect stream
with new parameters.
Add pystray-based system tray icon with Open UI / Restart / Quit
actions. Add __main__.py for `python -m wled_controller` support.
Update start-hidden.vbs with embedded Python fallback for both
installed and dev environments.
New notification effects:
- Chase: light bounces across strip with Gaussian glow tail
- Gradient flash: bright center fades to edges with exponential decay
Queue priority: notifications with color_override get high priority and
interrupt the current effect.
Also fixes transient preview for notification sources — adds WebSocket
"fire" command so inline preview works without a saved source, plus
auto-fires on preview open so the effect is visible immediately.
Effects: add 7 new procedural effects (rain, comet, bouncing ball, fireworks,
sparkle rain, lava lamp, wave interference) and custom palette support via
user-defined [[pos,R,G,B],...] stops.
Gradient: add easing functions (linear, ease_in_out, step, cubic) for stop
interpolation, plus noise_perturb and hue_rotate animation types.
Daylight: add longitude field and NOAA solar equations for accurate
sunrise/sunset based on latitude, longitude, and day of year.
Candlelight: add wind simulation (correlated gusts), candle type presets
(taper/votive/bonfire), and wax drip effect with localized brightness dips.
Also fixes editor preview to include all new fields for inline LED test.
Three new processing template filters for both picture and color strip sources:
- HSL Shift: hue rotation (0-359°) + lightness multiplier via vectorized RGB↔HSL
- Contrast: LUT-based contrast adjustment around mid-gray (0.0-3.0)
- Temporal Blur: exponential moving average across frames for motion smoothing
Add 5 WebGL shader background effects (Aurora, Plasma, Digital Rain,
Starfield, Warp Tunnel) via a new bg-shaders.ts engine that shares
a dedicated canvas. Add 5 style presets (Sakura, Ocean, Copper, Vapor,
Monolith) with distinctive font pairings. Remove CSS particles effect
in favor of shader-based alternatives. Fix dot grid visibility and
tune all shader intensities for subtle ambient appearance.
Add style presets (font + color combinations) and background effect
presets as a new Appearance tab in the settings modal. Style presets
include Default, Midnight, Ember, Arctic, Terminal, and Neon — each
with curated dark/light theme colors and Google Font pairings.
Background effects (Dot Grid, Gradient Mesh, Scanlines, Particles)
use a dedicated overlay div alongside the existing WebGL Noise Field.
All choices persist to localStorage and restore on page load.
Scene name and fallback scene in automation cards are now clickable,
navigating to the corresponding scene preset card. Also renders the
deactivation mode label which was previously set but never displayed.
Auth is now optional: when `auth.api_keys` is empty, all endpoints are
open (no login screen, no Bearer tokens). Health endpoint reports
`auth_required` so the frontend knows which mode to use.
Backup/restore fixes:
- Auto-backup uses atomic writes (was `write_text`, risked corruption)
- Startup backup skipped if recent backup exists (<5 min cooldown),
preventing rapid restarts from rotating out good backups
- Restore rejects all-empty backups to prevent accidental data wipes
- Store saves frozen after restore to prevent stale in-memory data
from overwriting freshly-restored files before restart completes
- Missing stores during restore logged as warnings
- STORE_MAP completeness verified at startup against StorageConfig
Camera engine and video stream support now gracefully degrade when
opencv-python-headless is not installed. The app starts fine without
it — camera engine simply doesn't register and video streams raise
a clear ImportError with install instructions.
Saves ~45MB for users who don't need camera/video capture.
- Notification history: replace text buttons with icon buttons, use modal-footer for proper positioning
- CSS test: reject 0-LED picture sources with clear error message, show WS close reason in UI
- Calibration: distribute LEDs by aspect ratio (16:9 default) instead of evenly across edges
- Value source schedule: replace native time input with custom HH:MM picker matching automation style
- Remove "No ... yet" empty state labels from all CardSection instances
Create contexts/ci-cd.md documenting release pipeline, build scripts,
CI runners, and versioning. Reference it from CLAUDE.md context table.
Add mandatory pre-commit lint check rule to CLAUDE.md.
Add Windows installer, Docker volume mount, and first-time setup
instructions to the Gitea release body. Fix Docker registry URL.
Add CI/Release sync rule to CLAUDE.md.
Add compileall step to build-dist-windows.sh that generates .pyc files
for both app source and site-packages. Saves ~100-200ms on startup by
skipping parse/compile on first import.
- Replace winsdk (~35MB) with winrt packages (~2.5MB) for OS notification
listener. API is identical, 93% size reduction.
- Replace wmi (~3-5MB) with ctypes for monitor names (EnumDisplayDevicesW)
and camera names (SetupAPI). Zero external dependency.
- Migrate cv2.resize/imencode/LUT to Pillow/numpy in 5 files (filters,
preview helpers, kc_target_processor). OpenCV only needed for camera
and video stream now.
- Fix DefWindowProcW ctypes overflow on 64-bit Python (pre-existing bug
in platform_detector display power listener).
- Fix openLightbox import in streams-capture-templates.ts (was using
broken window cast instead of direct import).
- Add mandatory data migration policy to CLAUDE.md after silent data
loss incident from storage file rename without migration.
Strip unnecessary files from site-packages:
- Remove pip, setuptools, pythonwin (not needed at runtime)
- OpenCV: remove unused extra modules and data
- numpy: remove tests, f2py, typing stubs
- Remove all .dist-info directories and .pyi type stubs
- Remove winsdk type stubs
- Replace 7z with msiextract (msitools) to extract tkinter from
python.org's individual MSI packages (tcltk.msi + lib.msi)
- Fix build step numbering to /9
- Docker job continues on login failure (registry may not be enabled)
- Show makensis output for debugging
- Replace nuget approach (doesn't contain tkinter) with extracting
from the official Python amd64.exe installer using 7z
- Remove MUI_ICON/MUI_UNICON (no .ico file available, use NSIS default)
- Add p7zip-full to CI dependencies
- installer.nsi: per-user install to AppData, Start Menu shortcuts,
optional desktop shortcut and autostart, clean uninstall (preserves
data/), Add/Remove Programs registration
- build-dist-windows.sh: runs makensis after ZIP if available
- release.yml: install nsis in CI, upload both ZIP and setup.exe
- Fix Docker registry login (sed -E for https:// stripping)
Download _tkinter.pyd, tkinter package, and Tcl/Tk DLLs from the
official Python nuget package and copy them into the embedded Python
directory. This enables the screen overlay visualization during
calibration in the portable build.
- Move startup banner into main.py so it shows the actual configured
port instead of a hardcoded 8080 in the launcher scripts
- Wrap tkinter import in try/except so embedded Python (which lacks
tkinter) logs a warning instead of crashing the overlay thread
Windows: install-autostart.bat (Startup folder shortcut),
uninstall-autostart.bat. Linux: install-service.sh (systemd unit),
uninstall-service.sh.
Both launchers now use python -m wled_controller.main so port is
read from config/env instead of being hardcoded to 8080.
Windows embedded Python ignores PYTHONPATH when a ._pth file exists.
Add ../app/src to the ._pth so wled_controller is importable.
Fixes ModuleNotFoundError on portable builds.
Replace Windows runner requirement with cross-compilation:
download Windows embedded Python + win_amd64 wheels from PyPI,
package into the same ZIP structure as build-dist.ps1.
All 4 release jobs now run on ubuntu-latest.
Restructure release.yml into 4 jobs:
- create-release: shared Gitea release with download table
- build-windows: existing portable ZIP (unchanged)
- build-linux: new tarball with venv + run.sh launcher
- build-docker: push image to Gitea Container Registry
Add build-dist.sh as Linux equivalent of build-dist.ps1.
Docker tags: version + latest (stable only, no latest for alpha/beta/rc).