Skip to content

SendSpin Multi-Room Audio Renderer Integration - #2

Open
kiwipaulrob wants to merge 214 commits into
masterfrom
sendspin-advanced
Open

SendSpin Multi-Room Audio Renderer Integration#2
kiwipaulrob wants to merge 214 commits into
masterfrom
sendspin-advanced

Conversation

@kiwipaulrob

@kiwipaulrob kiwipaulrob commented Jul 24, 2026

Copy link
Copy Markdown
Owner

SendSpin Multi-Room Audio Client for moOde

Metadata Architecture (final)

Home Assistant REST API (HASS_TOKEN)
  → polls media_player.moode_sendspin every 3s
  → writes artist~~~title~~~album~~~duration~~~cover_path~~~codec
  → sendspin-meta.php endpoint serves to JS poller

Fallback: Device detection (fuser) → "Now Playing" indicator
Future: SendSpin protocol metadata@v1 listener is built and dormant — activates when MA populates the role fields

19 installer components | Clean architecture

Key Features

  • Renderer page — ON/OFF toggle, name field, Resume MPD, Edit/Restart
  • Config page — audio format, log level, version check + CLI update
  • Metadata display — album art, artist, title on moOde homepage
  • Uses _audioout — same ALSA device as AirPlay/Spotify/MPD
  • Matches renderer patterns — vol.sh -restore, CDSP sync, sspactive, sendFECmd
  • **No HA optional — metadata works with HA long-lived token

NOT READY FOR UPSTREAM — needs fresh-install test

moodeaudio and others added 30 commits June 17, 2026 08:57
Causes MPD lsinfo to fail with a parse error and affects several scripts.
- playerlib.js renderFolderView(): data-path element needs encodeHTMLEntities()
- PHP scripts containing lsinfo commands need escapeDblQuotes()
This commit adds SendSpin client support to moOde, allowing it to act
as a synchronized audio endpoint in a SendSpin multi-room system
(e.g., Music Assistant).

Features:
- Configure → Renderers UI integration
- Service ON/OFF toggle with auto-save
- Custom endpoint name configuration
- Manual restart with confirmation dialog
- MPD coexistence (auto-stop/resume)
- Direct hardware ALSA configuration (avoids dmix IPC issues)
- Systemd timeout for reliable startup
- All code quality fixes applied (safe array access, proper escaping)

Installation:
  curl -fsSL .../moode-sendspin-installer.sh | sudo bash

Or minimal install:
  curl -fsSL ... | sudo bash -s -- --minimal

Tested on moOde 9.4.2 with Music Assistant 2.x
Release 1: Core integration
…aDSP, buffer tuning

Release 2 builds on the core integration (Release 1) with:

Features:
- Now playing metadata display (title, artist, album, cover art)
  via SendSpin hook-start/hook-stop environment variables
- Two-way volume synchronisation with Music Assistant
  via hook-set-volume + reverse sync CLI
- CamillaDSP loopback support (auto-detects DSP state, routes
  through hw:Loopback when enabled, direct hw when disabled)
- ALSA buffer tuning (period_time 1160, buffer_time 4640) for
  sub-millisecond sync precision
- Version check and update button (checks PyPI for newer versions)
- Systemd hardening (LimitRTPRIO, LimitMEMLOCK for real-time priority)
- Pre-play and post-play hooks (spspre.sh, spspost.sh)

Files added:
- hooks/sendspin-metadata.sh    - Metadata capture from SENDSPIN_* env vars
- hooks/sendspin-volume-sync.sh - Volume sync (MA -> moOde + reverse)
- hooks/spspre.sh               - Pre-play: CamillaDSP detection + ALSA routing
- hooks/spspost.sh              - Post-play: state cleanup
- hooks/sendspin-version-check.sh - PyPI version comparison
- etc/alsa/conf.d/sendspin.conf - Buffer-tuned ALSA config
- moode-sendspin-r2-installer.sh - R2 installer with --check/--uninstall
- SENDSPIN_RELEASE2_ROADMAP.md  - Full feature roadmap

Dependencies:
- Release 1 (sendspin-integration) must be installed
- SendSpin CLI 7.5.0+ (hook support)
- moOde 9.4.2+

Builds on sendspin-integration branch
… block

The nested-brace ALSA syntax (type hw / card N / device N) was not being
parsed correctly by ALSA on the Pi, causing the sendspin PCM device to
not register. Switched to the string-based format used by moOde's own
config files (pcm 'plughw:0,0') which works reliably.

Also removed period_time/buffer_time from ALSA config - these parameters
are not valid in the slave block of type plug and were causing the PCM
definition to be rejected entirely.

Tested on Pi: service stable, port 8928 listening, Music Assistant
connected successfully after fix.
- Python daemon connects as metadata@v1 client (no audio playback)
- Uses aiosendspin ClientListener + SendspinClient from sendspin-cli venv
- Receives track metadata (title, artist, album, artwork_url, progress)
  directly from SendSpin protocol - works with ANY server
- Writes to moOde's ~~~ metadata format
- Downloads cover art automatically
- Runs on separate port 8929 (audio daemon stays on 8928)
- Includes systemd service file

Based on SendSpin protocol spec: github.com/Sendspin/spec
Hook-based approach abandoned - hooks only pass connection info,
not track metadata. This is the correct 'metadata sink' pattern.
Wraps the incoming WebSocket with a logging proxy that captures every
TEXT and BINARY message before the aiosendspin parser processes it.
This will reveal exactly what Music Assistant sends on track changes,
connection, and playback state transitions.

Also syncs debug logging level and per-field attribute dumps with the
version already deployed on the Pi.
…k data

Music Assistant does not populate the SendSpin metadata@v1 role fields.
All metadata arrives null. Confirmed via raw WebSocket message logging.

Solution: Poll Home Assistant REST API (media_player.moode_sendspin entity)
every 3 seconds for title, artist, album, duration, and artwork URL.
Cover art downloaded via HA proxy URL and cached locally.

SendSpin listener kept for connection monitoring only.
HA polling runs independently of SendSpin connection state.

Tested: track changes captured in real-time, cover art downloading correctly.
- SENDSPIN_RELEASE2_ROADMAP.md: Feature 1 marked DONE with full implementation
  details, actual vs planned approach, verified working status, updated deps
- SENDSPIN_PR.md: Future Enhancement section replaced with implemented solution
  description including MA bug details and HA polling workaround
- README-sendspin.md: Release 2 file listing updated, added metadata sink
  section with how-it-works, requirements, and service management commands
- Rewrite sendspin-display.js: poll metadata endpoint directly, drop broken engineCmd hook
- Fix metadata-sink: re-write file if cleared externally (spspost.sh race)
- Add --hardware-volume false to sendspin.service (software volume for SMSL DAC)
- Create moode-worker.service systemd unit (replace rc.local for worker.php)
- Create ssp-config.php + ssp-config.html (SendSpin settings page with version/update)
- Add Edit button to SendSpin section in ren-config.html
- Fix overlay CSS: pointer-events for click-through, vmin-responsive cover sizing
- Restore spspre.sh original ALSA config (remove softvol attempt)
… BUG-03 restart on save, BUG-04 DB read order, ISSUE-01 session via stored ID, ISSUE-04 absolute path, ISSUE-05 async update, ISSUE-06 session fallback on ssp, MINOR-04 input validation, STRUCT-06 PIDFile cleanup
…SUE-03: Add cfg_sendspin table and service regeneration to installer
…rsion, checkSendspinUpdate, updateSendspin, generateSendspinService, getSendspinMetadata)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants