Skip to content

Development & Contributing

Requires Docker and Docker Compose. The speaker must be paired with the host before starting.

Terminal window
git clone https://github.com/trudenboy/sendspin-bt-bridge.git
cd sendspin-bt-bridge
docker compose up --build
docker logs -f sendspin-client
open http://localhost:8080

Without Docker (requires system Bluetooth and audio packages):

Terminal window
pip install -r requirements.txt
python sendspin_client.py
sendspin_client.py # Entry point: SendspinClient + main()
bluetooth_manager.py # BluetoothManager — BT connections via bluetoothctl
config.py # Configuration, shared lock, load_config()
state.py # Shared runtime state (list of SendspinClient instances)
services/
bridge_daemon.py # BridgeDaemon — runs inside each subprocess; stream events, sink routing
daemon_process.py # Subprocess entry point: reads args, runs BridgeDaemon, emits JSON status
bluetooth.py # Async BT helpers (D-Bus monitor)
pulse.py # PulseAudio helpers (pulsectl + pactl): find sink, move sink-inputs
routes/
api.py # Core playback & volume (6 routes)
api_bt.py # Bluetooth management (9 routes)
api_ma.py # Music Assistant integration (10 routes)
api_config.py # Configuration (9 routes)
api_status.py # Status & diagnostics (8 routes)
views.py # HTML page renders
auth.py # Optional web UI password protection
entrypoint.sh # Docker entrypoint: D-Bus, audio init
ha-addon/ # Home Assistant addon configuration
lxc/ # LXC install scripts (Proxmox & OpenWrt)

Architecture note: each Bluetooth speaker runs as an isolated asyncio subprocess (services/daemon_process.py) with PULSE_SINK=<bt_sink_name> in its environment. This gives every speaker its own PulseAudio context so audio routes to the correct speaker from the first sample, without any move-sink-input calls at startup.

Run pytest for automated unit tests:

Terminal window
python3 -m pytest tests/ -v

The test suite has 187 tests across 18 test files covering config, volume routing, device status, state management, authentication, Ingress middleware, BT services, daemon process, MA integration, and scan cooldown.

Terminal window
ruff check . # Fast Python linter
ruff format --check . # Code formatting check

Use this checklist when making changes:

  • Container starts without errors (docker logs -f sendspin-client)
  • Web UI loads at http://localhost:8080
  • Bluetooth device connects and appears in the web UI
  • Music Assistant detects the player
  • Audio plays through the Bluetooth speaker
  • Volume slider changes speaker volume
  • Auto-reconnect triggers after disconnecting (~10 s)
  • Config changes persist after container restart
  • /api/status returns valid JSON
  • main — stable, always releasable
  • Feature branches — branch off main, name feat/<description> or fix/<description>
  • Submit a PR against main

Open an issue on GitHub. Include:

  • Deployment method (Docker / HA Addon / Proxmox LXC)
  • Log output
  • Host OS, audio system (PipeWire/PulseAudio), Bluetooth adapter model
  • Steps to reproduce

Pushing a v* tag to main automatically:

  1. Builds multi-platform Docker image (linux/amd64, linux/arm64)
  2. Publishes to ghcr.io/trudenboy/sendspin-bt-bridge
  3. Syncs the version to ha-addon/config.yaml

This project grew out of loryanstrant/Sendspin-client. Thanks to the Music Assistant team for the Sendspin protocol and CLI.

  • Architecture — deep dive into the process model, IPC protocol, audio routing, Bluetooth state machine, MA integration, authentication, and graceful degradation
  • HISTORY.md — narrative history of the project’s evolution (v1 → v2, key design decisions)
  • CHANGELOG.md — full version history