Python SDK for Rockbox — async GraphQL client with subscriptions and plugins
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rockbox_sdk-0.1.0.tar.gz.
File metadata
- Download URL: rockbox_sdk-0.1.0.tar.gz
- Upload date:
- Size: 20.6 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c556cd54cc302f2e66b7a2b1ad3e684464aee1b2c3d7d4c8e2d2acbf1fe808ab
|
|
| MD5 |
68c94ab9bb6671c8dfb5277d728b485a
|
|
| BLAKE2b-256 |
ad92687288bc82d1e29f5b657aa5a52536c39bae92eff00e1e85192892e218cb
|
File details
Details for the file rockbox_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: rockbox_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 29.0 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dcbd88f1f429187c76c9508a389deadfff5b86b2b98110e77ec0ec483078ccbf
|
|
| MD5 |
37763b970e579ad0911f718bf738b6c6
|
|
| BLAKE2b-256 |
e5efeed9d16d3a3d073fbca3f6adb0f3efc62ff00a68ad75f0fbb9a4e171316a
|