Skip to content

MatFaes/Bluezard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bluezard

BlueZ Audio Receiver Daemon

Bluezard turns a headless Linux machine into a managed Bluetooth audio receiver with a clean web interface. It is Raspberry Pi friendly, but not Pi-specific: the real requirements are BlueZ, BlueALSA, ALSA, systemd/D-Bus, Python 3.11, and a Bluetooth adapter. It handles Bluetooth device management, A2DP audio routing via BlueALSA, and provides a live admin UI — without PipeWire, PulseAudio, or any desktop-session dependency.

Bluezard dashboard — Now Playing and Audio Pipeline

Dashboard — Now Playing, stream metrics, Audio Pipeline

Documentation: Setup · Bluetooth · Troubleshooting · UI screenshots


Features

  • One-tap pairing — "Add device" makes the Pi visible for 3 minutes; auto-trusts after pairing
  • Dashboard layout — sidebar navigation (Dashboard, Devices, Settings), Now Playing card, Audio Pipeline summary, persistent bottom player bar
  • Now Playing — real-time track title, artist, album from AVRCP (Spotify, Apple Music, etc.)
  • Stream metrics — active codec (with quality indicator), sample rate, BlueALSA buffer delay, and SBC throughput estimate while a device is streaming
  • Playback controls — Play / Pause / Next / Previous from the web UI and bottom player bar
  • Volume control — per-device ALSA volume slider, synced with external changes
  • Multi-output routing — assign each device to a different ALSA output from Devices in advanced mode
  • One device at a time — optionally disconnect others when a new device starts streaming (on by default)
  • PWA — installable on mobile, with persistent token login and reconnecting state
  • Responsive UI — desktop sidebar plus mobile-friendly layout and PWA install
  • Bluetooth adapter control (power, discoverable, pairable)
  • Device scanning, pairing, connecting, removing; last-seen timestamps
  • Live WebSocket event streaming (D-Bus signals → browser)
  • A2DP-only audio routing (HSP/HFP excluded — no accidental headset audio)
  • ALSA native output — no resampling, full DAC capability preserved
  • Diagnostics modal plus advanced System, Players, Audio Outputs, and Live Events sections
  • REST API with interactive docs at /api/docs
  • Health check endpoint at /health (no auth required)
  • Optional token authentication for LAN use
  • Systemd-native — auto-start, auto-restart, no root web process

Requirements

Hardware

  • Any always-on Linux host with working Bluetooth and audio output. Raspberry Pi is the primary target, but not a hard requirement.
  • A Pi 3 or newer is a practical Raspberry Pi baseline; Pi 4/5 is not required.
  • 32-bit or 64-bit OS both work, as long as the OS provides Python 3.11 and BlueALSA packages.
  • Built-in Bluetooth is fine. A USB Bluetooth adapter can improve range or avoid interference.
  • Any ALSA playback device: USB DAC, HAT, HDMI, or built-in audio. A USB DAC/HAT is recommended for quality and reliability.

Software

  • Raspberry Pi OS Bookworm or Debian Bookworm, 32-bit or 64-bit. Other Debian-like systems can work if they provide compatible BlueZ/BlueALSA packages.
  • systemd and the system D-Bus, because Bluezard runs as a service and talks to BlueZ over D-Bus.
  • BlueZ (bluetooth.service) and BlueALSA (bluealsa.service plus bluealsa-aplay).
  • Python 3.11+ with venv.
  • ALSA utilities (aplay, amixer) for output detection and volume.
  • qrencode is optional but recommended to show the phone login QR code.

The installer resolves distro package names automatically. In Debian this is usually bluez-alsa-utils; on Raspberry Pi OS it may be bluealsa.


Quick Install

git clone https://github.com/MatFaes/Bluezard.git
cd Bluezard
sudo bash scripts/install.sh

The install script will:

  1. Install all system packages
  2. Detect your Bluetooth adapter and ALSA output card
  3. Create a bluezard system user
  4. Install the Python environment in /opt/bluezard/
  5. Create /var/lib/bluezard for UI-managed runtime state
  6. Enable and start the services
  7. Display a QR code to open the web UI directly on your phone

Open: http://<bluezard-host>:8088

See docs/setup.md for the full installation guide.


Update

sudo bash /opt/bluezard/scripts/update.sh

Pulls the latest release from GitHub, redeploys, and restarts. Your .env and runtime state are never touched.


Configuration

cp .env.example .env
nano .env
Variable Default Description
BLUEZARD_HOST 127.0.0.1 Bind address. Use 0.0.0.0 for LAN access
BLUEZARD_PORT 8088 HTTP port
BLUEZARD_TOKEN (empty) Auth token (empty = no auth)
BLUEZARD_REQUIRE_TOKEN false When a token is set, also require it for localhost. Enabled by the installer for LAN binding
BLUEZARD_BLUEZ_ADAPTER hci0 HCI adapter name
BLUEZARD_DEFAULT_PCM (empty) Default ALSA output (e.g. plughw:C20,0) — set by install script
BLUEZARD_STATE_DIR /var/lib/bluezard Writable runtime state directory for UI-managed settings
BLUEZARD_EXCLUSIVE_SINK true Disconnect others when a new device starts streaming
BLUEZARD_PLAYER_MODE connected connected runs players only while devices are connected; always_ready pre-starts players for trusted A2DP devices
BLUEZARD_LOG_LEVEL INFO DEBUG / INFO / WARNING / ERROR
BLUEZARD_DEBUG false Verbose debug + auto-reload

Bluetooth Pairing

  1. Open the Bluezard web UI
  2. On Dashboard, click + Add device — the Bluetooth adapter becomes visible for 3 minutes
  3. On your phone/tablet: open Bluetooth settings → select the Pi
  4. When pairing succeeds, Bluezard opens Devices so you can connect it

By default, Bluezard starts bluealsa-aplay only while a trusted A2DP device is connected.


Audio Routing

Bluezard manages bluealsa-aplay processes dynamically for trusted A2DP devices. This replaces the static bluealsa-aplay.service.

  • Default output: set by BLUEZARD_DEFAULT_PCM (configured at install time)
  • Per-device override: use the output selector in Devices → Device details → Output
  • Routing overview: Settings → Audio Outputs shows detected outputs and active overrides
  • Overrides are persisted in /var/lib/bluezard/state.json across restarts

To verify which ALSA card to use:

aplay -l

Audio Codecs

Bluezard displays the active Bluetooth codec in the stream metrics panel (codec name with a quality indicator: green for AAC/aptX/LDAC, amber for SBC).

Codec negotiation is handled automatically by BlueALSA and the remote device — the best codec supported by both sides is selected at connection time.

Codec quality ranking

Codec Quality Typical devices
LDAC Excellent Sony Android devices
aptX HD Very good High-end Android
aptX Good Mid/high-end Android
AAC Good iPhone, iPad, Mac
SBC Baseline All devices (mandatory fallback)

Codec support in BlueALSA

The bluez-alsa-utils package from Debian Bookworm only includes SBC. A dedicated install script builds BlueALSA from source and enables AAC, aptX, and aptX-HD:

sudo bash scripts/bluealsa-install.sh

The script:

  1. Installs build dependencies (libfdk-aac-dev, libopenaptx-dev, etc.)
  2. Clones and compiles BlueALSA from source
  3. Installs binaries to /usr/local/bin/ (system package in /usr/bin/ untouched)
  4. Creates a systemd override that reads Bluezard's UI-managed codec settings
  5. Restarts bluealsa.service

If you already have the sources checked out:

sudo bash scripts/bluealsa-install.sh --source=/path/to/bluez-alsa

LDAC note: LDAC decoding (receiving audio from a phone) requires libldacBT-dec, which is not available in Debian repos. LDAC is therefore not supported as an A2DP sink codec.

AAC note: libfdk-aac is patent-encumbered, which is why Debian ships BlueALSA without it by default. The library is available in the non-free component.


Screenshots

Full-size captures: docs/README.md.

Dashboard — playback, stream metrics, pipeline summary

Dashboard

Devices — connect, pair, device details

Devices

Settings — default output and advanced diagnostics

Settings

Mobile — PWA layout with bottom player bar

Mobile dashboard


Architecture

Phone / Tablet ──BT A2DP──► BlueZ ──BlueZ-ALSA──► bluealsa-aplay ──ALSA──► USB DAC
                                 │                  (per-device,
                              D-Bus                  managed by
                                 │                   Bluezard)
                            Bluezard
                         (management UI)
                              │
                         Web Browser

Bluezard manages. BlueALSA plays.


Development Deploy

bash scripts/deploy-dev.sh pi@raspi.local          # fast: static + Python files
bash scripts/deploy-dev.sh pi@raspi.local --full   # full pip reinstall + restart

Manual / Development Setup

# Install system packages
sudo apt-get install -y bluetooth bluez bluez-alsa-utils \
  python3 python3-venv python3-pip alsa-utils qrencode

# On Raspberry Pi OS, the BlueALSA package may be named:
# sudo apt-get install -y bluealsa

# Add your user to bluetooth and audio groups
sudo usermod -aG bluetooth,audio $USER
newgrp bluetooth

# Python setup
python3 -m venv venv
source venv/bin/activate
pip install -e .

# Copy and edit config
cp .env.example .env

# Run
bluezard

REST API

Interactive docs: http://localhost:8088/api/docs

If BLUEZARD_TOKEN is set, provide it with X-Bluezard-Token, Bearer auth, or the bluezard_token cookie. Localhost is exempt unless BLUEZARD_REQUIRE_TOKEN=true.

To re-display the phone login URL and QR code on the Pi:

bash ~/Bluezard/scripts/show-qr.sh --host rpi-player.local
# Status
curl http://localhost:8088/api/status

# Health check (no auth)
curl http://localhost:8088/health

# Playback controls (play, pause, stop, next, previous)
curl -X POST http://localhost:8088/api/devices/AA:BB:CC:DD:EE:FF/media/next

# Per-device volume (0–100)
curl http://localhost:8088/api/devices/AA:BB:CC:DD:EE:FF/volume
curl -X POST http://localhost:8088/api/devices/AA:BB:CC:DD:EE:FF/volume \
  -H "Content-Type: application/json" -d '{"volume": 75}'

# Per-device output routing
curl -X POST http://localhost:8088/api/devices/AA:BB:CC:DD:EE:FF/output \
  -H "Content-Type: application/json" -d '{"pcm": "plughw:C20,0"}'

# Settings
curl http://localhost:8088/api/settings
curl -X POST http://localhost:8088/api/settings \
  -H "Content-Type: application/json" -d '{"exclusive_sink": true}'

# Managed player processes
curl http://localhost:8088/api/players

# Live stream metrics (BlueALSA PCM — sample rate, delay, codec)
curl http://localhost:8088/api/stream/metrics
curl 'http://localhost:8088/api/stream/metrics?address=AA:BB:CC:DD:EE:FF'

# Full diagnostics
curl http://localhost:8088/api/diagnostics

Systemd Services

Service Purpose
bluetooth.service BlueZ stack
bluealsa.service BlueZ-ALSA bridge daemon
bluezard.service Bluezard web manager + per-device audio routing

bluealsa-aplay.service is not used — Bluezard manages bluealsa-aplay processes directly for trusted A2DP devices.

# Status
systemctl status bluezard bluealsa bluetooth

# Restart Bluezard (also restarts all audio players)
sudo systemctl restart bluezard

# Follow Bluezard logs
journalctl -fu bluezard

Security

  • Bluezard binds to 127.0.0.1 by default — not accessible over LAN without configuration.
  • No root required for the web process (bluezard system user in bluetooth and audio groups).
  • Set BLUEZARD_TOKEN before enabling BLUEZARD_HOST=0.0.0.0 for LAN access.
  • Token validated with secrets.compare_digest (timing-safe).
  • REST and WebSocket endpoints both enforce the token when authentication is enabled.
  • /opt/bluezard is installed root-owned; only /var/lib/bluezard is writable by the service.
  • UI-managed settings are stored in /var/lib/bluezard/state.json, not written back into .env.
  • No shell execution, no SQL, no file uploads, no eval.

For production LAN use, consider placing Bluezard behind an nginx reverse proxy with HTTPS.


Diagnostics

Open the Diagnostics button in the web UI, or use the API:

In advanced mode, Settings → System also shows WebSocket, Bluetooth, BlueALSA, device, player, output override, and runtime state status.

curl http://localhost:8088/api/diagnostics | python3 -m json.tool
curl http://localhost:8088/api/diagnostics/alsa
curl http://localhost:8088/api/diagnostics/logs/bluealsa

See docs/troubleshooting.md for the full troubleshooting guide.


Project Structure

bluezard/
├── bluezard/
│   ├── main.py          FastAPI app, routes, WebSocket
│   ├── bluez.py         BlueZ D-Bus manager (dbus-next, asyncio)
│   ├── player.py        Per-device bluealsa-aplay process manager
│   ├── models.py        Pydantic models
│   ├── config.py        Settings (pydantic-settings)
│   ├── auth.py          Token authentication
│   ├── state.py         Runtime state store (/var/lib/bluezard/state.json)
│   ├── websocket.py     WebSocket broadcast manager
│   ├── diagnostics.py   ALSA / systemd diagnostics + volume
│   ├── stream_metrics.py BlueALSA PCM metrics for the dashboard
│   ├── templates/
│   │   └── index.html   Single-page web UI
│   └── static/
│       ├── style.css    Dark-theme CSS (sidebar dashboard layout)
│       ├── app.js       Vanilla JS frontend
│       ├── sw.js        Service Worker (PWA offline shell)
│       ├── manifest.json  PWA manifest
│       ├── icon-192.png PWA icon
│       ├── icon-512.png PWA icon
│       └── favicon.ico  Browser icon
├── systemd/
│   └── bluezard.service
├── docs/
│   ├── README.md        Documentation index and UI gallery
│   ├── setup.md
│   ├── bluetooth.md
│   ├── troubleshooting.md
│   └── screenshots/     UI screenshots (see README inside)
├── scripts/
│   ├── install.sh
│   ├── show-qr.sh
│   ├── update.sh
│   ├── uninstall.sh
│   ├── deploy-dev.sh
├── tests/
│   ├── test_auth.py
│   ├── test_player.py
│   ├── test_state_and_config.py
│   └── test_stream_metrics.py

License

Apache 2.0 — see LICENSE.


Contributing

Issues and pull requests welcome. Please:

  • Target Debian Bookworm / Python 3.11+
  • Keep ALSA-native — no PulseAudio/PipeWire dependencies
  • No new D-Bus parsing hacks — use typed dbus-next calls
  • Test on real hardware when touching D-Bus / ALSA code

About

Bluetooth Audio Receiver for Raspberry Pi and Linux. Web UI for BlueZ and BlueALSA with A2DP sink support, playback controls and multi-output audio routing.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors