Getting started
roon-internal-api is the TypeScript side of this experiment. It lives in the
roon-internal-api/
directory of the repo. It’s not published to npm — use it from a clone. Expect rough
edges; this is a proof-of-concept.
The latest tagged version is v0.1.1. See Releases for changes, compatibility notes, and the validation performed on Roon 2.73 build 1696.
Install
Section titled “Install”git clone https://github.com/arthursoares/roon-api-reverse-engineeringcd roon-api-reverse-engineering/roon-internal-apinpm installnpm run buildnpm test -- --runInBandWhat you need from your own Core
Section titled “What you need from your own Core”Two values identify your Core. They aren’t secrets (there’s no auth here), but they’re specific to your install, so supply your own rather than copying mine:
| Value | What it is | How to find it |
|---|---|---|
host | Core IP / hostname | Roon → Settings → About, or your router |
serverBrokerId | 16-byte Core id (hex) | read it off a capture of the handshake — see Contributing |
Connect and read
Section titled “Connect and read”The RoonClient facade is the front door. A minimal, read-only program:
import { RoonClient } from './src';
async function main(): Promise<void> { const host = process.env.ROON_HOST; const brokerId = process.env.ROON_SERVER_BROKER_ID; if (!host || !brokerId || !/^[0-9a-f]{32}$/i.test(brokerId)) { throw new Error('Set ROON_HOST and ROON_SERVER_BROKER_ID (32 hex characters)'); }
const roon = new RoonClient({ host, serverBrokerId: Buffer.from(brokerId, 'hex'), }); try { await roon.connect();
console.log('Library oid:', roon.serviceOid('Library').toString()); console.log('Zone oid:', roon.zoneByName('Living Room')?.toString());
const album = roon.findByTitle('AlbumLite', 'Kind of Blue'); console.log(album ? `found, oid=${album.oid}` : 'not loaded'); } finally { roon.close(); }}
main().catch(console.error);Run the included version with both variables set:
ROON_HOST=192.168.1.50 ROON_SERVER_BROKER_ID=0123456789abcdef0123456789abcdef \ npx ts-node examples/demo.tsOn connect() the client does the handshake, resolves the root service, and starts
ingesting the streaming object graph — so zones, devices, now-playing, and loaded library
content are queryable via the helpers below. How reliable this is beyond my own setup, I
genuinely don’t know.
Core helpers on RoonClient
Section titled “Core helpers on RoonClient”| Method | Purpose |
|---|---|
connect() / close() | open / close the session |
serviceOid(name) | object id of a singleton service ('Library', 'Transport'…) |
zoneByName(name) / endpointByName(name) | resolve a zone / endpoint oid |
findByTitle(type, title) | find a loaded object by title (e.g. 'AlbumLite') |
titleOf(obj) | best-effort display title of an object |
call(service, method, params, args, oid?) | low-level escape hatch to any method |
structArg(typeName, fields) | build a by-val struct argument |
For doing things — favorites, playback, edits, search — see Recipes. For the full generated method surface, see the generated API.