Home Assistant integration for Qobuz music streaming, with rich library browsing, playback control, and Qobuz Connect support.
Important disclaimer: This is an unofficial integration using reverse-engineered and community-documented API patterns (no official public Spotify-like Web API exists for Qobuz). It is fully AI-generated code. Use at your own risk. Qobuz may change endpoints without notice, and this may violate their Terms of Service. No warranty, no official support from Qobuz or the author. Credentials are handled securely but you should use a dedicated or limited account if concerned.
Compatible with latest Home Assistant (2025+ patterns).
- Media player entity with browse media (playlists, favourites → tracks), signed stream URLs when the player bundle secret can be scraped
- Qobuz Connect (Phase 3): WebSocket controller session — discovers Connect renderers, transfer playback (
select_source/ service), play/pause via Connect when the session is active (protobuf protocol aligned with qonductor) - REST polling for library + “now playing” via
/player/getStatewhere available - Config flow with re-auth
- Diagnostics support
Manual HACS steps (if the button above doesn't work):
- Go to HACS → Integrations → three-dot menu → Custom repositories
- Add
https://github.com/Razor5284/HA-Qobuzas category Integration - Install "Qobuz"
- Restart Home Assistant
- Settings → Devices & Services → Add Integration → search "Qobuz"
You authenticate with a browser session token (not your password — Qobuz blocks automated login). After setup, a media_player and account/subscription sensors appear under one device.
- Polling interval (default 30s)
Advanced overrides (app id / secret) are not exposed in the options flow yet; the integration scrapes current credentials from the official web player bundle when possible.
When /qws/createToken succeeds, the integration opens a regional QConnect WebSocket, authenticates with the issued JWT, joins as a controller (“Home Assistant”), and listens for renderer add/remove/active events. You can:
- See sources = discovered Connect devices (speaker/TV/Cast endpoints registered with Qobuz)
- Transfer playback to a device (media player → Source, or
qobuz.transfer_playback) - Play / Pause via Connect when the WS session is connected (otherwise state still follows REST polling)
Next/previous track skipping uses QConnect CtrlSrvrSetPlayerState with the target queue item (queue must be synced from Connect).
See docs/adr/0001-qobuz-connect-approach.md and docs/adr/0002-phase3-qconnect-controller.md.
- Versioning follows semantic versioning (e.g. 0.1.0 initial).
- Full test suite + CI (pytest, ruff) ensures long-term maintainability.
- See
CONTRIBUTING.md(to be added) for running tests and contributing fixtures.
Inspired by the excellent SpotifyPlus integration for scope and UX patterns.
See GitHub releases for detailed changes.
v0.11.6 — Connect stability, browse-to-play, and media controls:
- Queue deltas: handle
SRVR_CTRL_QUEUE_TRACKS_ADDED,QUEUE_CLEARED,QUEUE_VERSION_CHANGED, and server acks for shuffle/loop/volume/max audio quality; persistqueue_hash/action_uuidfor shuffle writes - Shuffle: build
CtrlSrvrSetShuffleModefromqconnect_payload_pb2(notqconnect_queue_pb2) - Repeat: build
CtrlSrvrSetLoopModefrom payload protobuf; exportCtrlSrvrSetLoopModefromconnect/generated/__init__.py - UI refresh:
async_refresh_playback()on the coordinator (playback only, no full library poll) after Connect transport;_on_connect_updateschedules refresh withloop.call_soon_threadsafeso HA never seesasync_create_taskfrom a worker thread - Browse → play: longer queue wait loop, safer behaviour when the WebSocket closes mid-wait (
ConnectionClosedErrorno longer bubbles to the UI) - Media player:
media_position/ duration from Connect,RepeatMode/ shuffle properties,SEEK,VOLUME_SET,async_media_seek,async_set_volume_level; repeat toggle usesgetattr(repeat, "value", repeat)for HA enums - Service:
qobuz.set_streaming_quality→CtrlSrvrSetMaxAudioQuality
v0.11.4 — Connect playback UX (browse play, shuffle/repeat, devices):
- Browse → Play: when Connect is connected, play_media clears the session queue (when a queue version exists), QueueAddTracks for the chosen track, then starts playback at index 0 (falls back to stream URL only if Connect is not ready)
- Shuffle / repeat: wired to CtrlSrvrSetShuffleMode and CtrlSrvrSetLoopMode; media player uses an immediate coordinator refresh after transport so the UI tracks Connect faster than debounced
async_request_refresh - Next / previous: no longer bumps the local queue index until the server sends RendererStateUpdated (avoids UI lying when the command is ignored while paused)
- Devices: accept ADD_RENDERER without an embedded
DeviceInfopayload; treat QConnect max uint64 renderer id as “no active renderer” for ACTIVE_RENDERER_CHANGED and RENDERER_STATE_UPDATED; reset local renderer/queue cache on each new WebSocket session; UPDATE_RENDERER fallback match by display name + INFO log when unmatched (helps debug missing AVRs such as Denon)
v0.11.3 — Protobuf + Home Assistant event loop compatibility:
- Fix:
DeviceInfo.capabilitiesand nested QConnect payloads useCopyFrom— required with protobuf 5.x on newer HA/Python; the previousinfo.capabilities = capsassignment raised Assignment not allowed to message field "capabilities", which aborted the Connect join on every attempt - Fix: TLS default certificate store is built via
async_add_executor_jobbeforewebsockets.connect, avoiding blocking call to load_default_certs warnings on Python 3.14+
v0.11.2 — Connect discovery and HA now-playing sync:
- Fix:
CtrlSrvrAskForQueueState/CtrlSrvrAskForRendererStatenow send queue_uuid and session_id (fromSrvrCtrlSessionState), matching qonductor — restores device list and queue/renderer snapshots when joining as a controller - Fix:
source_listno longer treats an empty device list as missing; Connect updates trigger a coordinator refresh so track metadata updates without waiting for the poll interval - Fix: Coordinator merges Connect when REST returns an idle but truthy body (e.g.
{}), not onlyNone - Improvement: Ignore RendererStateUpdated from non-active renderers once the active renderer is known; apply RendererState fields only when set (partial protobuf updates)
- Improvement: WebSocket User-Agent; join as DEVICE_TYPE_LAPTOP; handle SrvrCtrlUpdateRenderer and placeholder renderers before AddRenderer
v0.11.0 — Qobuz Connect token + HA playback fallback:
- Fix:
/qws/createTokenmatches the web player: POST formjwt=jwt_qws(literal) +app_id, session inX-User-Auth-Tokenonly — restores Connect WebSocket for accounts that previously saw 400/503 on token creation - Improvement: Coordinator surfaces Connect metadata when renderer state is still
UNKNOWN(after WS connect), and ignores explicitSTOPPEDso stale queue hints are not shown as now playing - Docs / tooling:
scripts/test_api.pydocuments/player/getState503 as common and probescreateTokenthe same way as the integration - Note: REST
/player/getStateis still polled when available; it remains 503 for many accounts — Connect carries now playing in that case
v0.10.0 — Connect playback state, metadata & track skipping:
- Fix: Connect WebSocket now processes
RENDERER_STATE_UPDATED,QUEUE_STATE, andSESSION_STATEmessages — playback state (playing/paused), track metadata, and device info are now properly surfaced to the media player entity - Fix: When the REST
/player/getStateendpoint returns no data (common for most accounts), the coordinator falls back to Connect WebSocket state and fetches track metadata viaget_track_info()API - New: Next/previous track via Qobuz Connect protocol (sends
CtrlSrvrSetPlayerStatewith target queue item) - New: On WebSocket connect, client immediately requests current renderer and queue state so existing playback is picked up
- New:
sourceattribute and source list now reliably reflect Connect devices and the active playback device
v0.9.0 — Full QConnect WebSocket controller (device list, transfer, play/pause via Connect).
If you see an error like:
Got status code 404 when trying to download .../archive/refs/heads/f530bf8.zip
This usually means HACS tried to fetch a non-existent branch or commit hash.
Fix:
- In HACS → Custom repositories, remove the entry if it points to a specific commit/hash.
- Re-add using exactly:
https://github.com/Razor5284/HA-Qobuz(no branch or commit suffix). - Make sure the repository's default branch on GitHub is
main(ormaster) and contains the latest code. - For the most reliable installs, create a GitHub Release (see "Development, Versioning & Releasing" below). HACS prefers releases.
After fixing, click "Redownload" in the integration card in HACS.
If /qws/createToken fails, check that your session token is valid and review logs for API errors. When Connect starts correctly you should see Qobuz Connect WebSocket connected at INFO level.
We follow a lightweight GitHub Flow + Semantic Versioning process.
main— stable, releasable code only.- Feature branches:
feature/short-descriptionorfix/issue-xxx. - Never commit directly to
main; always open a PR.
- Follow SemVer:
MAJOR.MINOR.PATCH. - The single source of truth for the integration version is
custom_components/qobuz/manifest.json. - Also keep
hacs.jsonin sync if it contains version constraints.
-
Prepare the release
- Update the version in
custom_components/qobuz/manifest.json(e.g.0.1.0→0.2.0). - Update
README.mdchangelog / features if needed. - (Optional) Add an entry to a
CHANGELOG.md. - Run tests and lint:
ruff check . && pytest tests/.
- Update the version in
-
Create the release on GitHub
- Push your changes to
main. - Go to the repository on GitHub → Releases → Draft a new release.
- Choose a tag version (e.g.
v0.2.0— thevprefix is recommended). - Target branch:
main. - Write release notes (what's new, breaking changes, Connect improvements, etc.).
- Publish the release.
- Push your changes to
-
HACS users will see the update
- HACS automatically detects new releases and offers updates.
- Users on the default branch will also get the latest
maincode when they redownload.
- Make sure the GitHub repository is public (or users have a GitHub token configured in HACS for private repos).
- The "Add to Home Assistant" badge in this README points to the correct repository.
Following this process ensures reliable HACS installs and a clean upgrade path for users.
Repository: https://github.com/Razor5284/HA-Qobuz