v2.0.0
Breaking change (Sendspin)
The Sendspin client was rebuilt on aiosendspin 9.x. The deprecated pre-encryption wire protocol and mDNS auto-discovery are gone, and the Music Assistant server address is now required in config.json:
"sendspin": { "connection": { "server_host": "<MA-SERVER-IP>" } }Existing config.json files must add sendspin.connection.server_host before the client can connect. Run script/migrate_config.py after updating: it warns when the key is missing (it cannot guess your MA IP) and strips the other obsolete sendspin.connection keys. Devices without Sendspin enabled are unaffected. The rebuilt client adds Noise-encrypted pairing with a PIN spoken through the speaker (Piper, espeak-ng fallback), persistent player identity, and a sounddevice output stage with server-synchronized buffering. Requires Python >= 3.12 and libportaudio2.
Re-founded on the upstream core
This release lands the re-foundation: upstream modules (satellite.py, entity.py, models.py, player/, wake_word.py, peripheral API) are used as-is and the fork's differentiating features live as add-on modules. Future upstream releases merge cleanly again. From upstream this brings the full ESPHome device-page entity set (mic auto gain, noise suppression, mic volume, per-slot wake word and stop word sensitivities), dual music/TTS players with ducking and announcements, output-only mode, external wake word downloads, MWW/OWW model switching from the UI, and the WebSocket peripheral API (port 6055).
main is now the re-founded stack. Devices still tracking the old upstream-core branch keep working: their updater self-migrates to main on the first update after this release.
Kernel-independent ReSpeaker 2-Mic HAT audio (v1 and v2)
Audio runs on mainline kernel drivers (snd-soc-wm8960 for v1, snd-soc-tlv320aic3104 for v2) via device-tree overlays compiled at install time:
- No DKMS, no kernel headers, no per-kernel builds, any kernel >= 5.4; kernel upgrades can no longer break audio
- One smart installer auto-detects the HAT revision by I2C address (0x1a = v1, 0x18 = v2) and removes stale installs of the other revision
- Same ALSA card ID (
seeed2micvoicec) as before, so existing LVA configs keep working; legacy DKMS installs upgrade in place - v1 gets proper MCLK wiring on the codec node (fixes "No MCLK configured" PCM open failures), which also enables the WM8960 PLL for the 44.1 kHz family
Self-updating fleet
script/update_lva rewritten: in-place git fetch + checkout instead of move-aside + fresh clone. Untracked per-device files (preferences, Sendspin credentials, piper voices, per-model threshold files, external wake words, config.json) are never touched. The script re-executes the freshly fetched copy, so update logic always runs at the version being deployed. Adds --branch, --force, and --rollback. Now defaults to main.
Fixed
- Tray client stale state after MQTT (re)connects: the daemon now publishes a consolidated retained
lva/<device_id>/statetopic on every voice transition, and the tray derives its displayed state from it. State changes while muted are also published now. - HA mute switch going stale on non-HA mute changes (tray, GPIO, XVF3800 hardware buttons): mute state is now published to all connected API clients.
Upgrade notes
- Run
script/migrate_config.py(orscript/update_lvawith your usual flags) after updating. - If you use Sendspin, add
sendspin.connection.server_hosttoconfig.json. config.jsonis no longer tracked by git; per-device files survive updates.
Full details in CHANGELOG.md.
Testing: 625 passed / 1 skipped with all install extras (Python 3.12); verified on Raspberry Pi Zero 2 W (2-Mic HAT v1 + v2), Orange Pi Zero 2 W (XVF3800), and desktop tray clients.