Control your hi-fi system from anywhere — a hardware knob on your couch, your phone, or just ask Claude.
This bridge connects your music sources (Roon, LMS, UPnP) to any control surface you prefer. No vendor lock-in: mix and match sources, add HQPlayer DSP processing, and control it all from one place.
Once the bridge is running, control your system from:
- Web UI — Built-in at
http://your-bridge:8088 - roon-knob — ESP32-S3 hardware knob with OLED display
- iOS & Apple Watch — In alpha testing. Get in touch if you'd like to try it.
- Claude & AI agents — Via the built-in MCP server (see MCP Server below)
docker pull muness/unified-hifi-control:latestDownload the SPK package from Releases:
unified-hifi-control_*_apollolake.spk— Intel x86_64 (DS918+, DS920+, etc.)unified-hifi-control_*_rtd1296.spk— ARM64 (DS220+, DS420+, etc.)
Download the QPKG package from Releases:
unified-hifi-control_*_x86_64.qpkg— Intel/AMD x86_64unified-hifi-control_*_arm_64.qpkg— ARM64
Add this repository URL in LMS Settings → Plugins → Additional Repositories:
https://raw.githubusercontent.com/open-horizon-labs/unified-hifi-control/v3/lms-plugin/repo.xml
Then install "Unified Hi-Fi Control" from the plugin list. The plugin automatically downloads and manages the bridge binary.
Pre-built binaries available for Linux (x64, arm64, armv7), macOS (x64, arm64), and Windows from Releases.
# docker-compose.yml
services:
unified-hifi-control:
image: muness/unified-hifi-control:latest
network_mode: host # Required for Roon/UPnP discovery
volumes:
- ./data:/data
environment:
- CONFIG_DIR=/data
# - UHC_PORT=8088 # Bridge port (default: 8088)
# - RUST_LOG=info # Log level: trace, debug, info, warn, error
restart: unless-stoppeddocker compose up -d
# Access http://localhost:8088| Variable | Description | Default |
|---|---|---|
UHC_PORT |
Bridge HTTP port | 8088 |
CONFIG_DIR |
Directory for config/state files | /data |
RUST_LOG |
Log filter (e.g., info, debug, unified_hifi_control=debug) |
debug |
LMS_HOST |
Auto-configure LMS backend (used by LMS plugin) | — |
LMS_PORT |
LMS server port | 9000 |
Legacy aliases: PORT (→ UHC_PORT), LOG_LEVEL (→ RUST_LOG)
Note: Port 8088 is also HQPlayer's default. If running both on the same host, change one.
If you route audio through HQPlayer for upsampling or filtering, this bridge lets you control HQPlayer's DSP settings (profiles, filters, shapers) alongside your zone controls.
Note: You need to set up audio routing to HQPlayer separately (via Roon, LMS/BubbleUPnP, or OpenHome). This bridge exposes the DSP controls, not the audio path.
- Open the web UI at
/hqplayer - Enter your HQPlayer host IP and ports (native: 4321, web: 8088)
- Link zones to HQPlayer instances — each zone can use a different HQPlayer
- Zone now-playing info will include HQPlayer pipeline status
┌─────────────────────────────────────────────────────────────────────┐
│ Unified Hi-Fi Control Bridge │
│ ┌────────┐ ┌────────┐ ┌──────────┐ ┌────────┐ ┌──────────┐ │
│ │ Roon │ │ Lyrion │ │ OpenHome │ │ UPnP │ │ HQPlayer │ │
│ │ │ │ /LMS │ │ │ │ /DLNA │ │ DSP │ │
│ └────────┘ └────────┘ └──────────┘ └────────┘ └──────────┘ │
│ │
│ HTTP API + SSE │
└─────────────────────────────────────────────────────────────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
ESP32 Web UI
Knob
Control your hi-fi with natural language. The bridge includes an MCP server so Claude can search, play, queue music, adjust volume, and switch HQPlayer profiles.
- Start the bridge (
docker compose up -d) - Add to your MCP config (Claude Code, ChatGPT, BoltAI, etc.):
{
"mcpServers": {
"unified-hifi-control": {
"type": "http",
"url": "http://<your-bridge-host>:8088/mcp"
}
}
}| Tool | Description |
|---|---|
hifi_zones |
List available zones (Roon, LMS, OpenHome, UPnP) |
hifi_now_playing |
Get current track, artist, album, play state |
hifi_control |
Play, pause, next, previous, volume control |
hifi_search |
Search library, TIDAL, or Qobuz (Roon only) |
hifi_play |
AI DJ: search and play/queue in one command (Roon only) |
hifi_status |
Overall bridge status |
hifi_hqplayer_status |
HQPlayer Embedded status and pipeline |
hifi_hqplayer_profiles |
List saved HQPlayer profiles |
hifi_hqplayer_load_profile |
Switch HQPlayer profile |
hifi_hqplayer_set_pipeline |
Change filter, shaper, dither settings |
Search and play are currently Roon-only. Transport controls work with all adapters. LMS search/play contributions welcome!
Ask Claude: "Play some jazz piano" or "Queue Hotel California" or "What's playing?" or "Turn the volume down"
Firmware Updates (roon-knob)
The bridge automatically downloads new roon-knob firmware from GitHub every 6 hours. Knobs check /firmware/version on startup and OTA update from the bridge.
| Variable | Description | Default |
|---|---|---|
FIRMWARE_AUTO_UPDATE |
Enable/disable auto-download | true |
FIRMWARE_POLL_INTERVAL_MINUTES |
Check interval | 360 (6 hours) |
Version History (for nerds)
| Version | Stack | Notes |
|---|---|---|
| v1 | Node.js | Proof of concept — validated the idea of a unified control surface |
| v2 | Node.js | Production release — in-memory event bus, multi-backend support |
| v3 | Rust | Complete rewrite — native packages (Synology, QNAP, LMS plugin), 10x smaller memory footprint, single static binary |
The v3 rewrite was motivated by packaging requests (NAS users wanted native packages, not Docker) and the opportunity to dramatically reduce resource usage. The Rust binary uses ~15MB RAM vs ~150MB for Node.js.
Development
- Rust 1.84+ with
wasm32-unknown-unknowntarget - Dioxus CLI
rustup target add wasm32-unknown-unknown
cargo install dioxus-cli --locked
cp scripts/pre-commit .git/hooks/make css # Build Tailwind CSS
dx build --release --platform web --features web # Build server + WASM
cd target/dx/unified-hifi-control/release/web
PORT=8088 ./unified-hifi-control # Run at http://localhost:8088For hot reload during development:
PORT=8088 dx serve --release --platform web --features web --port 8088cargo test --workspace
cargo fmt --check && cargo clippy -- -D warningsNote: Use dx build, not cargo build — the web UI requires the WASM bundle that only dx produces.
As of v2.5.0, this project is licensed under the PolyForm Noncommercial 1.0.0 license.
Versions up to and including v2.4.1-prior-license were released under a custom source-available license (see LICENSE-PRIOR).
For commercial licensing inquiries, see COMMERCIAL-LICENSE.md.