Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

README.md

rockbox-sdk#

PyPI Python Downloads License AsyncIO Typed Ruff mypy GitHub

Async Python SDK for Rockbox Zig — a typed, batteries-included client for the GraphQL API exposed by rockboxd.

import asyncio
from rockbox_sdk import RockboxClient, PlaybackStatus

async def main():
    async with RockboxClient(host="localhost") as client:
        track = await client.playback.current_track()
        if track:
            print(f"Now: {track.title}{track.artist}")
        if await client.playback.status() == PlaybackStatus.PAUSED:
            await client.playback.resume()

asyncio.run(main())

Highlights#

  • Async-first — built on httpx + websockets. Use await everywhere.
  • Domain-namespaced APIclient.playback.*, client.library.*, client.sound.*, …
  • Typed responses — every reply is a Pydantic model with snake_case fields.
  • Real-time eventsconnect() opens a WebSocket and forwards track:changed / status:changed / playlist:changed to listeners.
  • Builder APIRockboxClient.builder().host(...).port(...).build().
  • Plugin system — Jellyfin-style install/uninstall lifecycle.
  • Python-friendly — context manager, decorator listeners, dataclass inputs.

Install#

uv add rockbox-sdk
# or
pip install rockbox-sdk

Requires Python 3.10+ and a running rockboxd (default port 6062).

Try it in the REPL#

The SDK is async-first. The recommended REPL is IPythonawait works at the top level, and you get tab-completion on models, inline docs with ?, and %timeit for benchmarking:

uv run ipython
In [1]: from rockbox_sdk import RockboxClient, PlaybackStatus
In [2]: client = RockboxClient(host="localhost", port=6062)
In [3]: await client.playback.status()
Out[3]: <PlaybackStatus.PLAYING: 1>
In [4]: track = await client.playback.current_track()
In [5]: track.title, track.artist
Out[5]: ('Money', 'Pink Floyd')
In [6]: await client.sound.get_volume()
Out[6]: VolumeInfo(volume=-12, min=-74, max=6)
In [7]: await client.library.search("daft punk")
In [8]: await client.aclose()

You can also test offline — models, enums, and the builder don't need a server:

In [1]: from rockbox_sdk import RockboxClient, Track, InsertPosition
In [2]: Track.model_validate({"title": "Money", "albumArt": "x.jpg"}).album_art
Out[2]: 'x.jpg'
In [3]: RockboxClient.builder().host("nas.local").build()._config.resolve_http_url()
Out[3]: 'http://nas.local:6062/graphql'

If you prefer the stdlib REPL, python -m asyncio also supports top-level await, or wrap each call in asyncio.run(...) in a plain python session:

uv run python -m asyncio
>>> from rockbox_sdk import RockboxClient
>>> client = RockboxClient()
>>> await client.playback.status()

Subscriptions (await client.connect()) keep firing in the background between prompts in both REPLs.

Configure#

from rockbox_sdk import RockboxClient

# Direct kwargs
client = RockboxClient(host="192.168.1.42", port=6062)

# Or fluent builder
client = (
    RockboxClient.builder()
    .host("nas.local")
    .port(6062)
    .timeout(15)
    .build()
)

# Or full URL override
client = RockboxClient(
    http_url="http://nas.local:6062/graphql",
    ws_url="ws://nas.local:6062/graphql",
)

Always call await client.aclose() when you're done — or use it as an async context manager:

async with RockboxClient() as client:
    ...

Domains#

Namespace What it does
client.playback Transport (play/pause/seek), play helpers
client.library Albums, artists, tracks, search, likes, scan
client.playlist The active queue (insert/remove/shuffle/start)
client.saved_playlists Persistent playlists & folders
client.smart_playlists Rule-based playlists & listening stats
client.sound Volume control
client.settings Global EQ / replaygain / crossfade / shuffle / …
client.system Version, runtime info
client.browse Filesystem & UPnP browser
client.devices Cast / source device discovery
client.bluetooth Bluetooth pairing & scanning (Linux only)

Real-time events#

from rockbox_sdk import RockboxClient, TRACK_CHANGED, STATUS_CHANGED

async with RockboxClient() as client:
    await client.connect()  # opens the WebSocket

    @client.on(TRACK_CHANGED)
    async def on_track(track):
        print(f"▶ {track.title}{track.artist}")

    @client.on(STATUS_CHANGED)
    def on_status(raw_status):
        print(f"◐ status = {raw_status}")

    await asyncio.Event().wait()  # run forever

Convenience wrappers exist (client.on_track_changed(...), client.on_status_changed(...), client.on_playlist_changed(...)).

Plugins#

A plugin is anything matching the RockboxPlugin protocol — a name, version, install(context), and optionally uninstall():

from rockbox_sdk import RockboxClient, PlaybackStatus, RockboxPlugin

class SleepTimer:
    name = "sleep-timer"
    version = "1.0.0"
    description = "Stop playback after N minutes"

    def __init__(self, minutes: int) -> None:
        self.minutes = minutes
        self._task: asyncio.Task | None = None

    def install(self, ctx):
        async def fire():
            await asyncio.sleep(self.minutes * 60)
            await ctx.query("mutation { hardStop }")

        self._task = asyncio.create_task(fire())

        @ctx.events.on("status:changed")
        def cancel_on_stop(status: int):
            if status == PlaybackStatus.STOPPED and self._task:
                self._task.cancel()

    def uninstall(self):
        if self._task:
            self._task.cancel()

async with RockboxClient() as client:
    await client.connect()
    await client.use(SleepTimer(30))

Raw GraphQL escape hatch#

data = await client.query(
    "query Volume { volume { volume min max } }"
)

Examples#

See examples/ for runnable scripts mirroring the TypeScript SDK examples:

  • 01-basic-playback.py
  • 02-now-playing.py
  • 03-library-search.py
  • 04-queue-management.py
  • 05-volume-control.py
  • 06-plugin-sleep-timer.py

Run with:

uv run python examples/01_basic_playback.py

License#

MIT License. See LICENSE for details.