- Add warm, cool, neon, pastel built-in gradients (promoted from frontend presets)
- Change gradient seeding to add missing built-ins on every startup (not just first run)
- Add searchable option to IconSelect component for filtering by name
- Enable search on gradient, effect palette, and audio palette pickers
- Simplify modal titles: "Add Gradient" / "Edit Gradient" instead of "Add Color Strip Source: Gradient"
- Update INSTALLATION.md and .env.example
- Replace truncated plaintext release notes with full-screen overlay
rendered via `marked` library
- Server reconnection does a hard page reload instead of custom event
- Create utils/image_codec.py with cv2-based image helpers
- Replace PIL usage across all routes, filters, and engines with cv2
- Move Pillow from core deps to [tray] optional in pyproject.toml
- Extract shared build logic into build-common.sh (detect_version, cleanup, etc.)
- Strip unused NumPy/PIL/zeroconf/debug files in build scripts
_save_all() in BaseSqliteStore did DELETE FROM table + INSERT all in-memory items
on every shutdown. Since SQLite stores use write-through caching (every CRUD writes
immediately), this was redundant. Worse, if in-memory state had fewer items than
the DB, the DELETE wiped rows and only partial data was reinserted.
- Make _save_all() a no-op (DB is always up to date via write-through)
- Replace self._save() with self._save_item() in 6 seed/default creation methods
- Remove _save_all_stores() shutdown hook (replaced with log-only message)
All store tests were passing file paths instead of Database objects
after the JSON-to-SQLite migration. Updated fixtures to create temp
Database instances, rewrote backup e2e tests for binary .db format,
and fixed config tests for the simplified StorageConfig.
Replace 22 individual JSON store files with a single SQLite database
(data/ledgrab.db). All entity stores now use BaseSqliteStore backed by
SQLite with WAL mode, write-through caching, and thread-safe access.
- Add Database class with SQLite backup/restore API
- Add BaseSqliteStore as drop-in replacement for BaseJsonStore
- Convert all 16 entity stores to SQLite
- Move global settings (MQTT, external URL, auto-backup) to SQLite
settings table
- Replace JSON backup/restore with SQLite snapshot backups (.db files)
- Remove partial export/import feature (backend + frontend)
- Update demo seed to write directly to SQLite
- Add "Backup Now" button to settings UI
- Remove StorageConfig file path fields (single database_file remains)
- Add unfreeze_saves() to base_store.py and call it in e2e cleanup.
The backup restore flow calls freeze_saves() which sets a module-level
flag that silently disables all store _save() calls. Without reset,
this poisoned all subsequent persistence tests (9 failures).
- Fix os_listener toggle to use toggle-switch/toggle-slider CSS classes
instead of nonexistent switch/slider classes (was showing plain checkbox).
- Add mandatory test run to CLAUDE.md pre-commit checks.
All 341 tests now pass.
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.