A Home Assistant integration for OwnTone, the open-source music server that plays to AirPlay and other speakers.
It gives you a media player for the server and one for every speaker, plus actions to play a stream URL on chosen speakers, pause, resume or stop, set volume, fade volume over a set time, and add or remove speakers, with instant updates when OwnTone's websocket is available.
This is an unofficial community project. It is not affiliated with or endorsed by OwnTone.
Built for setups like: play an internet stream (a radio station, or a relay of a YouTube live stream) on a few AirPlay speakers, fade it out over 30 minutes at bedtime, and get a notification when a stream fails to start.
I'm a pretty cruddy programmer, and I really don't have the skills or time to develop something like this myself. I had Claude.ai write this code under my direction. I'm putting it here if anyone else finds it useful.
- Master media player with play, pause, stop, next, previous, seek, shuffle, repeat, volume, clear queue, and
play_media(replace, add, play next, or play now). It shows the current track, artist, album, and artwork. - A media player per speaker. Turning one on or off selects or deselects that output in OwnTone; each has its own volume. New speakers appear automatically.
- Play audio on specific speakers, optionally setting the volume first, with a way to handle failures in your script.
- Fade volume up or down, either over a total duration you choose (the timing is worked out for you) or in a set number of steps.
- Add / remove speakers without disturbing the others.
- Push updates with polling fallback. Changes show up straight away over OwnTone's websocket; if it isn't available the integration polls instead.
- An OwnTone server reachable from Home Assistant (developed against OwnTone 29.3).
- Home Assistant 2026.9 or newer (the version it was developed and tested on).
- Optional, for instant updates: OwnTone's websocket enabled. It is controlled by
websocket_portin OwnTone'sowntone.conf(3688 by default) and must be enabled there.
- Install HACS if you haven't already.
- Click the badge above, or in HACS open the ⋮ menu → Custom repositories, add
https://github.com/ncbax/ot-conductorand choose the type Integration. - Find OT Conductor in HACS, Download it, and restart Home Assistant.
- Go to Settings → Devices & services → Add integration and choose OT Conductor.
Copy the custom_components/ot_conductor folder from this repository into your Home Assistant config/custom_components/ folder, restart, then add the integration as in step 4 above.
The setup form asks for:
| Field | Default | Meaning |
|---|---|---|
| Host | IP address or hostname of the OwnTone server | |
| Port | 3689 | OwnTone's web/API port |
| Push updates port | 3688 | OwnTone's websocket port. Enter 0 to turn push updates off and use polling only |
The form checks that OwnTone answers, then tests the push port with a real connection. If that test fails, either enable websocket_port in owntone.conf and restart OwnTone, correct the port, or enter 0.
While push updates work, the integration polls only every 5 minutes as a safety net (it also finds newly discovered speakers). Without push it polls every 15 seconds. The master player's update_mode attribute shows which one is active.
| Entity | What it is |
|---|---|
| Master media player | OwnTone's player: playback, queue, master volume, now-playing information. Attributes: selected_outputs, update_mode. |
| One media player per speaker | On = the speaker is selected, off = it isn't. Volume per speaker. While selected, it shows what the player is playing. |
The entity IDs are generated from names, for example media_player.ot_conductor_master and media_player.<speaker name>, so check Settings → Entities for the real ones. Turning off the last selected speaker also stops playback.
Every action returns a result you can capture with response_variable. Wherever an action asks for outputs, pick speakers from the list in the UI. The Master media player means all outputs. In YAML you can also give speaker names or IDs, or all.
| Action | What it does | Main fields |
|---|---|---|
ot_conductor.play |
Optionally selects speakers and sets volume, then plays a URI | uri, outputs, volume, clear, start, raise_error |
ot_conductor.stop |
Stops playback | |
ot_conductor.pause |
Pauses playback, keeping your place in the queue (stop ends playback instead) |
|
ot_conductor.resume |
Continues playing what is in the queue, from where pause left off |
|
ot_conductor.set_volume |
Sets or steps volume on several speakers at once | outputs, and volume (0-100) or step (-100 to 100) |
ot_conductor.fade |
Fades volume to a target | outputs, to, from, duration or steps and interval, wait |
ot_conductor.add_outputs |
Turns on speakers, leaving the others as they are | outputs |
ot_conductor.remove_outputs |
Turns off speakers, leaving the others as they are | outputs, stop_playback |
A URI for play can be a stream URL, or an OwnTone library item such as a playlist, artist, or album.
Give a total duration and OT Conductor works out the timing so the volume moves evenly and arrives exactly when the time is up. By default it uses one step per volume point, never faster than one change per 0.5 seconds. Each speaker is calculated from its own starting volume, so they all finish together. Without a duration, steps (default 10) and interval (default 1 second) apply. A duration and an interval can't be used together.
- action: ot_conductor.fade
data:
outputs: [media_player.living_room, media_player.kitchen]
to: 0
duration: "00:30:00"
wait: true
- action: ot_conductor.pause # or ot_conductor.stopWith wait: true the action holds for the whole fade. Without it the fade runs in the background. Starting a new fade, or setting the volume, on a speaker cancels the fade running there. Fades don't survive a Home Assistant restart. Pausing or stopping doesn't cancel a running fade.
By default, if OwnTone can't add the stream, play stops the script with an error that names the URI, the HTTP status and OwnTone's message. Turn Raise error on failure off and the action finishes normally and sets the response variable to ok: false instead, so the script can decide what to do:
sequence:
- action: ot_conductor.play
data:
uri: "http://relay.local:8000/lofi.mp3"
outputs: [media_player.living_room]
raise_error: false
response_variable: result
- if: "{{ result is not defined or not result.ok }}"
then:
- action: notify.notify
data:
message: >-
Stream failed: {{ result.error if result is defined else 'no result was returned' }}The failed result includes stage (select_outputs, set_volume or queue_add), error, kind (http, timeout or unreachable), status, detail, and the uri. Problems with the inputs, such as an unknown speaker, always raise an error.
| Problem | What to try |
|---|---|
| The setup form says it can't connect to the push port | Enable websocket_port in owntone.conf and restart OwnTone, check that the port is reachable from Home Assistant, or enter 0 to use polling only. |
| The form says OwnTone's websocket is disabled | OwnTone reports no websocket port. Enable it in owntone.conf, or enter 0. |
update_mode says polling |
The websocket isn't connected. Check the Home Assistant log for "Push updates unavailable". |
| "Unknown output(s)" | The speaker name or ID isn't one OwnTone currently reports. The error lists the available names. |
| A speaker's state looks out of date | Changes made outside Home Assistant show up on the next update: instantly with push, within 15 seconds to 5 minutes otherwise. |
play fails with an HTTP 500 |
OwnTone couldn't open the stream. Check the stream's source; for a relay, look at its log. |
To see more detail, add this to configuration.yaml:
logger:
logs:
custom_components.ot_conductor: debug- One OwnTone server per Home Assistant.
- OwnTone has a single player and queue shared by all selected speakers, so speakers can't play different things.
- Fades run inside Home Assistant and stop if it restarts.
- No media browser.
- yt-audio-relay: turns YouTube live streams into stable MP3 URLs that OwnTone can play.
Bug reports are welcome in the issue tracker. Please include your Home Assistant and OwnTone versions and the relevant lines from the log.
MIT. See LICENSE.