Skip to content

About

speakerctrl`is the central control daemon for SpeakerOS. It turns a Raspberry Pi into a single-purpose, appliance-style network speaker.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

speakerctrl

speakerctrl is the central control daemon for SpeakerOS.

It turns a Raspberry Pi into a single-purpose, appliance-style network speaker by orchestrating audio sources, networking state, and system policy. speakerctrl does not implement audio protocols itself — instead, it coordinates well-tested Linux services and enforces clear, predictable behaviour.


Disclaimer

This project was vibe coded as an exercise in learning agentic coding tools. It works well and I've been using it, but I'm not vouching for the code quality.


Design philosophy

SpeakerOS and speakerctrl are designed as appliances, not general-purpose computers. Reliability comes first — the system should boot predictably, recover from errors, and keep working months later without intervention. There are no hidden modes or magic behaviours. The system adapts to the user, not the other way around. Rather than reimplementing complex subsystems, speakerctrl builds on PipeWire, WirePlumber, BlueZ, and standard Linux service management.


What speakerctrl does

  • Allows multiple active audio sources simultaneously
    • Bluetooth (A2DP sink)
    • Spotify Connect
    • AirPlay 1
  • Monitors system state via D-Bus and PipeWire
  • Ensures audio services remain discoverable and available
  • Manages Wi-Fi connectivity and AP fallback mode
  • Serves a local web UI for setup and status
  • Automatic fallback to setup mode if Wi-Fi connection fails for 3 minutes after boot

What speakerctrl does not do

  • Implement Bluetooth, AirPlay, or Spotify protocols
  • Perform audio mixing or DSP
  • Replace PipeWire or WirePlumber

Target platform

  • Raspberry Pi 4 Model B
  • Raspberry Pi OS Lite (64-bit), Bookworm
  • systemd, PipeWire, WirePlumber

Project structure

speakerctrl/
├── crates/
│   ├── speakerctrl-core/      # State machine and policy logic
│   └── speakerctrl-daemon/    # Main daemon (includes embedded web UI)
├── packaging/                 # systemd units, Debian packaging
├── docs/                      # Architecture and development notes
└── README.md

Configuration

All persistent data lives under /persist/speakerctrl/.

Human-editable configuration is stored in TOML (config.toml); machine-managed state is stored in JSON (state.json). See docs/ for details on the configuration schema, persistence layout, and D-Bus interface.

Example config.toml:

version = 1
device_name = "SpeakerOS"
log_level = "info"
log_to_file = true
log_max_size_bytes = 5242880    # 5MB

Status

v0.4.2 — Fix AP fallback not triggering when WiFi network unavailable.

Recent improvements include:

  • AP fallback now triggers correctly when the configured WiFi network is unavailable
  • Recovery now reconciles Bluetooth and PipeWire sources consistently
  • Factory reset clears in-memory Wi-Fi credentials and re-enters setup mode
  • Wi-Fi scans prefer fresh results to avoid stale lists

Building and packaging

cargo build --release           # Build for host
./build_package.sh              # Create .deb for host architecture
./build_package.sh aarch64      # Cross-compile for Raspberry Pi

Cross-compilation requires aarch64-linux-gnu-gcc and the Rust target:

rustup target add aarch64-unknown-linux-gnu
sudo apt install gcc-aarch64-linux-gnu

Development notes

For local testing without a mounted /persist, set:

SPEAKERCTRL_PERSIST_ROOT=/tmp/speakerctrl-persist

Hardware validation guidance lives in docs/TEST_PLAN.md.


License

Licensed under the Apache License, Version 2.0. See the LICENSE file for details.

About

speakerctrl`is the central control daemon for SpeakerOS. It turns a Raspberry Pi into a single-purpose, appliance-style network speaker.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages