Bug fix release targeting database reliability, search accuracy, and UI edge cases. The v2only datastore gets fixes for duplicate dynamic thresholds, broken partial common name search, and stale DB handles after connection pool recycling. MQTT users gain stable source identifiers that survive restarts, and two new API endpoints expose audio sources over REST. Support dump uploads, which silently broke in the previous build due to an upstream sentry-go regression, are restored.
New Features
Audio Source API Endpoints
Two new REST endpoints expose the active audio sources from the engine registry: GET /api/v2/system/audio/sources (authenticated, all source types) and GET /api/v2/streams/sources (public, streams only). Both return {"sources": [{"id", "name", "type", "state"}]}. Previously the only way to discover sources was via the SSE stream at /api/v2/streams/audio-level (#2913).
Stable MQTT Source Identity
sourceId in MQTT detection payloads is now derived from a SHA-256 hash of the connection string instead of a random UUID. The format stays {type}_{hex8} (e.g., rtsp_a3f1b2c4) but is now deterministic across restarts. A new sourceName field carries the configured stream display name for human-readable filtering in Home Assistant (#2916, fixes #2799).
Note: sourceId values will change once after upgrading. If your Home Assistant automations filter on a specific sourceId, update them to the new value or switch to the new sourceName field, which is always stable.
Bug Fixes
Database & Search
- Duplicate dynamic thresholds in v2only mode -
GetAllDynamicThresholds()returned emptyModelNamebecause the v2 schema stores thresholds by label ID without a model column. The API merge produced different composite keys for the same species, doubling every entry. Thresholds now include the model relationship and use consistent lowercase species names (#2907, fixes #2902). - Partial common name search broken in v2only mode - the v2only datastore only matched scientific names via SQL LIKE, so searching "rastas" would not find "räkättirastas". Added in-memory common name scanning with NFC normalization for correct diacritical matching (#2919, fixes #2917).
- Audio source display name not updating on device swap -
GetOrCreate()returned the stale display name when a different USB microphone landed on the same ALSA device path. Now compares and updates the stored name when it differs (#2924, fixes #2921). - "Database is closed" errors not retried - the error was not classified as transient, so retry logic never kicked in during connection pool recycling. DB handles are now cleared on close to prevent stale references (#2928).
Audio & Performance
- Per-frame mutex contention in audio routing - cached buffer pool pointers on route structs using
atomic.Pointer, eliminating 3 per-frame mutex acquisitions in the hot path. Adds per-frame timing instrumentation and enriched Sentry diagnostics for frame drop events (#2904). - Equalizer crash on plain HTTP - four bare
crypto.randomUUID()calls in the equalizer UI threwTypeErrorwhen accessed over non-HTTPS connections. Added the establishedMath.randomfallback (#2905).
Dashboard & UI
- Restart progress replaced by "unexpected error" - the restart store treated all
ApiErrorinstances as HTTP rejections, making the recovery polling branch unreachable. Network errors during restart now correctly trigger polling instead of resetting the UI (#2906). - Dashboard banner not visible to guest users - the settings store initialized as "loaded" even when
loadSettings()was intentionally skipped for guests, causing the layout derivation to treat empty defaults as server state. Added adataLoadedflag to distinguish the two cases (#2923, fixes #2526). - Incorrect placeholder image URL - fixed the fallback thumbnail path returned by the analytics backend (#2922 by @rexxars).
Notifications
- Alert rule test fires not reaching push providers - the push dispatch loop filtered out test notifications, so clicking "Test" on an alert rule never sent to ntfy, Discord, Telegram, or other providers. The "Test Provider" button on Channels worked because it used a different code path (#2929, fixes #1902).
Telemetry
- Support dump uploads silently broken - sentry-go v0.46.0 introduced a telemetry processor that drops event attachments. Support dump zip files uploaded from Settings > Support were arriving in Sentry without the attachment. Disabled the new buffer path to restore the legacy transport that handles attachments correctly (#2918).
Internationalization
- Hungarian locale reverting to English on reload - the hardcoded
SUPPORTED_LOCALESarray inindex.htmlwas missinghu, causing the early-boot script to overwrite the stored preference. Locale list is now derived at build time from the actual translation files. Also added missing Danish, Latvian, and Swedish locales to the backend fallback list (#2915, fixes #2914).