rockbox-sdk
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. Useawaiteverywhere. - Domain-namespaced API —
client.playback.*,client.library.*,client.sound.*, … - Typed responses — every reply is a Pydantic model with snake_case fields.
- Real-time events —
connect()opens a WebSocket and forwardstrack:changed/status:changed/playlist:changedto listeners. - Builder API —
RockboxClient.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 IPython — await 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.py02-now-playing.py03-library-search.py04-queue-management.py05-volume-control.py06-plugin-sleep-timer.py
Run with:
uv run python examples/01_basic_playback.py
License
MIT License. See LICENSE for details.
Metadata
Release files for rockbox-sdk 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| rockbox_sdk-0.1.0.tar.gz | 20.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| rockbox_sdk-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 49.6 kB
Release files / rockbox_sdk-0.1.0.tar.gz
| Download URL | rockbox_sdk-0.1.0.tar.gz |
|---|---|
| Size | 20.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c556cd54cc302f2e66b7a2b1ad3e684464aee1b2c3d7d4c8e2d2acbf1fe808ab
|
|
BLAKE2b-256 checksum How to use checksums |
ad92687288bc82d1e29f5b657aa5a52536c39bae92eff00e1e85192892e218cb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / rockbox_sdk-0.1.0-py3-none-any.whl
| Download URL | rockbox_sdk-0.1.0-py3-none-any.whl |
|---|---|
| Size | 29.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
dcbd88f1f429187c76c9508a389deadfff5b86b2b98110e77ec0ec483078ccbf
|
|
BLAKE2b-256 checksum How to use checksums |
e5efeed9d16d3a3d073fbca3f6adb0f3efc62ff00a68ad75f0fbb9a4e171316a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.6 {"installer":{"name":"uv","version":"0.11.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|