← Back to list

🔊 Odio: Introducing the Missing Piece to Unleash Linux Multimedia

A free REST API + Home Assistant integration to control MPRIS players, PipeWire audio, and systemd user services

Mathieu Réquillart · 2026-02-15 19:20 · 1 claps · 6.6 min read
#linux #multimedia #home-assistant #golang #dbus
Open on Medium ↗
Wiki topics: 🔓 · Open Source 🎵 · Music & Audio

🔊 Odio: Introducing the Missing Piece to Unleash Linux Multimedia

Introduction

For over a decade, I’ve kept old hardware alive on Linux: homemade NAS and HTPC since 2013, a Raspberry Pi B+ audio server running 24/7 since 2020 that I documented here. Big thanks to the Debian and Openmediavault projects to make this possible.

Like many, I built a multimedia setup I’m proud of. But cleanly integrating it into Home Assistant always felt hacky, with scattered integrations, SSH scripts, and fragile glue.

go-odio-api v0.4 is built for people like us. An ultra-lightweight Go daemon exposing a clean REST API to control everything in your Linux user session: MPRIS players (Spotify, VLC, Firefox, MPD), PulseAudio/PipeWire, systemd user services. No root. No hacks. Just Linux primitives.​

The 3 Pillars (Modular Backends)

1. MPRIS Control (enabled by default)

Lists all compatible players in real time: Spotify, VLC, Firefox, MPD (with mpd-mpris or mpDris2), etc.

Full actions: play/pause/stop/next/prev, volume, seek, shuffle/loop


GET /players # instant inventory
POST /players/{player}/play
POST /players/{player}/volume # {“volume”: 0.5}
POST /players/{player}/seek # {“offset”: 1000000}

2. PulseAudio/PipeWire Management (enabled by default)

Server info, client listing, volume/mute per client or general output in real time. Limited PipeWire support via pipewire-pulse

Endpoints:


GET /audio/server
POST /audio/server/volume # {“volume”: 0.5}
GET /audio/clients
POST /audio/clients/{client}/mute 

3. systemd USER Services (opt-in, security-critical, central to power)

This backend is a real game-changer — and potentially the riskiest if I screw up the security. But it’s absolutely essential for home automation power users because you need to start/stop/restart services remotely: launching Kodi media centers, firing up Firefox kiosks for Netflix/YouTube, restarting pulseaudio, MPD or shairport-sync when they glitch. Here’s the idea.

How it works (explicit whitelist required):

systemd:
 enabled: true
 user:
   — mpd.service # your music daemon (w/ MPRIS)
   — shairport-sync.service # AirPlay receiver (see my Medium article) 
   — spotifyd.service # headless Spotify (needs MPRIS build)
   — firefox-kiosk@netflix.com.service # Netflix kiosk → auto MPRIS player
   — firefox-kiosk@youtube.com.service # YouTube kiosk → auto MPRIS player
   — kodi.service # full media center (w/ MPRIS addon)
   — vlc.service # VLC daemon
 system: # monitoring only!
 — bluetooth.service # watch system-wide BT status
 — upmpdcli.service # UPnP renderer

Endpoints:

GET /services # ALL whitelisted units + real-time status
POST /services/user/mpd.service/start # launch music daemon
POST /services/user/kodi.service/restart # reboot media center 
POST /services/user/firefox-kiosk@netflix.com.service/stop # kill Netflix kiosk

The Security Model

Layer 1 is dead simple: systemd backend is disabled by default. You must explicitly set systemd.enabled: true AND provide a whitelist of units in config.yaml. No units listed = zero exposure. That’s the bare minimum.

The real protection: Odio enforces user-session mutations only through a an application-layer lockdown, regardless of your system configuration.

Let’s say you messed up your dbus policies through SSH trying to let regular users start system services, or your polkit rules are wide open, Odio’s will still refuses to execute the operation.

You can monitor system-wide units like bluetooth.service or upmpdcli.service (they show live status in GET /services), but never start/stop/restart them. For that, you’ll still need SSH and a CLI.

Remote Kodi restarts, Netflix kiosk launches, MPD service restarts. All possible without opening a giant systemd security hole. Application-layer enforcement makes it safe. Other protections:

  • bind: localhost by default, single interface or all interfaces, exposing Odio is your choice.
  • Odio opens only the required sytemd bus based on config
  • Odio throughs a fatal error if you try to run it as root.

There’s no authentication, yet, but Odio is meant for trusted LAN only anyway, not Internet exposure.

Without this backend secured properly, you’d be screwed for real home automation use cases. With it, I really hope every one can hack around safely.

Why This Design?

Odio is built around the principle of “dumb core, smart integrations” Unix style. The API stays deliberately simple, stable, and reliable. The real magic comes from the integrations.

The User Session Bet

The architecture makes a critical choice: all multimedia services run as systemd user units (systemctl --user), not system-wide daemons.

Why? Because this pattern has been a Linux best practice since systemd v208. Media services handle per-user resources: audio sinks, display sessions, D-Bus contexts, that belong in user space.

This is the key unlock: by running everything in the user session, Odio talks to a single, unified D-Bus session bus. PulseAudio/Pipewire, MPRIS players (Spotify, VLC, Firefox, MPD, Kodi), user systemd units, all expose an interface on that bus. Odio doesn’t need to know what Spotify is. Or VLC. Or your custom MPD setup. It just listens to the D-Bus session for MPRIS-compatible players and exposes them via HTTP. You add a new player? It shows up automatically. Zero code change. Zero config. This is what makes Odio universal without being bloated.

Test in 2 Minutes

Build from source (no prebuilt binaries on purpose for now):


git clone https://github.com/b0bbywan/go-odio-api
cd go-odio-api
go build -o odio-api .
./odio-api

Or Docker (build your own image):


git clone https://github.com/b0bbywan/go-odio-api
cd go-odio-api 
docker build -t odio-api .
docker compose up

Immediate test: Start a youtube or vlc video, spotify or mpd player and test

curl http://localhost:8018/players # your MPRIS players
curl http://localhost:8018/audio/clients # your Pulse clients
curl -X POST http://localhost:8018/players/mpd/pause # it works

Installation: systemd user service


# ~/.config/systemd/user/odio-api.service
[Unit]
Description=Odio API
After=sound.target network-online.target
Wants=sound.target network-online.target

[Service] 
ExecStart=/usr/local/bin/odio-api
Restart=always
RestartSec=12

[Install]
WantedBy=default.target

systemctl --user enable --now odio-api.service

Integrations & Roadmap

Odio’s real power comes from integrations. The API is just plumbing infrastructure. The magic happens when you connect it to actual control interfaces.

odio-ha: The Home Assistant Integration

I’ve already built the first proper integration to prove what go-odio-api can do: **odio-ha**.

It exposes:

  • Audio control: media_player.odio_audio_receiver for global PulseAudio/PipeWire volume/mute
  • Per-service control: separate media_player per systemd service (mpd, kodi, shairport-sync, etc.) with power on/off, volume, state tracking
  • MPRIS players (in progress): auto-discovered players (Spotify, VLC, Firefox, MPD) with full playback control and metadata

The real power — integration inheritance:

Once odio-ha exposes your services, you can map them to native HA integrations. Example: MPD runs on your audio server (Odio manages the service), but you want rich features (playlists, album art). Just map the mpd odio service to your existing mpd integration when you set up Odio-ha Integration

  • Odio handles service lifecycle (start/stop/restart)
  • HA’s MPD integration handles rich playback
  • Everything centralized on the right host — your media server

Same for Kodi, Spotify Connect, Snapcast, or any player. Odio becomes the hub that makes all your HA integrations point to the correct machine.

All local. No cloud. No SSH scripts. Automations and voice control just work.

Current state: odio-apiv0.4 + odio-hav0.2.2 (add the repository in HACS) gives you clean control over audio and services.

Odio Integration v0.3.0

Odio Integration v0.3.0

Odio entity example

Odio entity example

What’s next

Bluetooth Backend (target v0.5): Turn your Linux box into a fully controllable Bluetooth speaker:

  • Toggle Bluetooth power, pairing mode via API
  • Accept connections from phones, tablets, laptops
  • Playback control and metadata exposed through MPRIS (play/pause/next/prev, track info)
  • The speaker appears as a media_player entity in Home Assistant via odio-ha
  • Full remote control: no bluetoothctl/bt-agent/custom script hell

Fake a commercial Bluetooth speaker, but with full API control

Built-in dashboard (target v0.6):

Simple web UI served by go-odio-api itself on http://localhost:8018/ui/. I don’t need to tell more, just let screenshot do the talking. There’s a good chance it becomes my HTPC homepage at login.

Dev UI version

Dev UI version

PR are on the way !

Later: SSE, Authentication, Wayland Remote Control, Photos Casting…

Join the Project

Odio is very early stage. I pushed the very first commit on January 25, 2026, barely three weeks ago. It’s a Proof of Concept and experiment with Claude Code that went much better than expected. Video players weren’t even in the initial scope.

v0.4.0 works out of the box, but there’s a long road ahead. Expect bugs. I’ve mobilized 12 years of Linux multimedia tinkering and 15 years of startup experience to turn that POC into production-grade software: low footprint and portability across architectures and distributions for my old hardware, increasing test coverage since I broke v0.1.2, exhaustive config/README, and solid debug logs for integration developement... I’m building this to be as universal and lasting as possible, not just for my use case, but for yours too. And I can’t do this alone.

I need your help to keep this on the right path.

Does it work on your setup? What breaks? What’s missing? What pain points does your current stack have that we could solve together?

Try it: github.com/b0bbywan/go-odio-api - github.com/b0bbywan/odio-ha Run it on your hardware. Tell me what works and what doesn’t. Show me your setup. If you want to contribute code, even better. Go is a wonderful language for this use case.

This is what I want to build: the multimedia remote platform Linux has been missing. Universal. Local. Free. Built on stable primitives that already work.

But it only becomes that if it solves your problems, not just mine.

Let’s build it together.

[embed]odio - Open-source Raspberry Pi audio streamer. Self-hosted, Home Assistant native. odio turns any Raspberry Pi into a free self-hosted audio streamer. AirPlay, Bluetooth, Spotify, Tidal, multi-room…odio.love

Fun fact: no me di cuenta de que “Odio” significa hate en español, pero me encanta el nombre, y hasta hice que el número de puerto coincidiera con él, así que lo mantendré.


메타데이터
post_id
f3a0baae4f5e
slug
odio-introducing-the-missing-piece-to-unleash-linux-multimedia-f3a0baae4f5e
url
https://medium.com/@mathieu-requillart/odio-introducing-the-missing-piece-to-unleash-linux-multimedia-f3a0baae4f5e
canonical_url
https://medium.com/@mathieu-requillart/odio-introducing-the-missing-piece-to-unleash-linux-multimedia-f3a0baae4f5e
author_url
https://medium.com/@mathieu-requillart
status
ok
fetched_at
2026-06-22 05:41:33