The solution I came up with follows. I’ve just finished it, so I can’t say anything towards long-term viability. But, in theory, it should be alright.
What not worked: Using the native media_player.play_media
support with media_content_id
is easy. But has a big caveat: The devices are not in-sync. The two Sonos devices are pretty much synchronized, but the Denon devices is somewhat quicker. Sketch: A script
with a sequence
and two media_player.play_media
s; one for the Sonos devices (entity_id: [media_player.a, media_player_b]
and one for the Denon device. (It took me quite some time – and a lucky find – that Sonos wants media_content_type: music
, but Denon wants media_content_type: url
…).
So, back to a working strategy: Use Airplay – because that’s in sync.
I compiled forked-daapd (with websockets), enabled MPD support in the forked-daapd config and included it as a new media player to Home Assistant.
media_player:
- platform: mpd
host: 127.0.0.1
port: 6600
name: Raspi Forked DAAPD
Inset: This forked-daapd/MPD media player has great support in Home Assistant by @kalkih forked-daapd-card (see also his forum entry)!
With this new MPD-Airplay device media_player.raspi_forked_daapd
, we can easily exploit native Home Assistant support for playing media:
service: media_player.play_media
data:
entity_id: media_player.raspi_forked_daapd
media_content_id: "https://wdr-1live-live.icecastssl.wdr.de/wdr/1live/live/mp3/128/stream.mp3"
media_content_type: music
But there are two things, which – to my knowledge – are not supported:
- Enabling Airplay devices (/speakers) on the forked-daapd backend (because they might have been disabled previously)
- Set volume of each Airplay device individually to some default (especially the Denon music player needs to lower volume significantly for anything music compared to TV and movie)
@kalkih’s Lovelace card supports both features, but AFAIK they are not scriptable from Home Assistant directly. But exploiting forked-daapd’s well-documented API is easy with Home Assistant’s rest_command
s. The following two commands will enable an Airplay device (activate_speaker
) and set volume (set_volume
). The port, in this case, is not the MPD-feature port of forked-daapd but the actual forked-daap port.
rest_command:
activate_speaker:
url: "http://localhost:3689/api/outputs/{{ id }}"
method: PUT
payload: '{"selected": true}'
set_volume:
url: "http://localhost:3689/api/outputs/{{ id }}"
method: PUT
payload: '{"volume": {{ volume }} }'
We are nearly there. We basically have everything to create a Universal Media Player, except a state. The Universal Media Player is dedicated for this one radio station stream. When the radio stream is played, the Universal Media Player should be on, when not, it should be off – we can reuse this for the Homekit integration. So we create a input_boolean
to hold the state whether or not the radio station is currently playing in the setup.
input_boolean:
playing_1live:
name: "Currently Playing 1LIVE Airplay Broadcast"
icon: mdi:speaker
Let’s put everything together.
- Create a script which starts everything up: Enable the Airplay devices, set their volume to our default, and play the stream MP3; also, change the state of our boolean.
- Create a script which stops everything. We just need to stop the
raspi_forked_daapd
media player – and change the state of our boolean.
Here are the scripts:
script:
1live_broadcast_start:
alias: "1LIVE Radio: Prepare and Play"
icon: mdi:radio
sequence:
- alias: "Enable speaker Schlafzimmer"
service: rest_command.activate_speaker
data: { 'id': '57717330314888' }
- alias: "Enable speaker Wohnzimmer"
service: rest_command.activate_speaker
data: { 'id': '24924472818' }
- alias: "Enable speaker Kueche"
service: rest_command.activate_speaker
data: { 'id': '132116598949134' }
- alias: "Play to All"
service: media_player.play_media
data:
entity_id: media_player.raspi_forked_daapd
media_content_id: "https://wdr-1live-live.icecastssl.wdr.de/wdr/1live/live/mp3/128/stream.mp3"
media_content_type: music
- alias: "Set volume Schlafzimmer"
service: rest_command.set_volume
data: { 'id': '57717888', 'volume': '20' }
- alias: "Set volume Wohnzimmer"
service: rest_command.set_volume
data: { 'id': '249472818', 'volume': '22' }
- alias: "Set volume Kueche"
service: rest_command.set_volume
data: { 'id': '1321168949134', 'volume': '20' }
- alias: "Set 1LIVE Airplay Broadcast state to ON"
service: input_boolean.turn_on
data:
entity_id: input_boolean.playing_1live
1live_broadcast_stop:
alias: "1LIVE Radio: Stop Broadcasting"
icon: mdi:radio
sequence:
- service: media_player.media_pause
data:
entity_id: media_player.raspi_forked_daapd
- alias: "Set 1LIVE Airplay Broadcast state to ON"
service: input_boolean.turn_off
data:
entity_id: input_boolean.playing_1live
(The IDs can be retrieved from the forked-daapd API.)
Both scripts should be called from the Universal Media Player. The state of the player should be extracted from the input_boolean
(not raspi_forked_daapd
directly, because that could play something else).
media_player:
- platform: universal
name: "1LIVE Player"
children:
- media_player.raspi_forked_daapd
commands:
turn_on:
service: script.1live_broadcast_start
turn_off:
service: script.1live_broadcast_stop
attributes:
state: input_boolean.playing_1live
volume_level: media_player.raspi_forked_daapd|volume_level # probably not needed
Done! Well, nearly. We want to integrate this to iOS:
homekit:
filter:
include_entities:
- media_player.1live_player
entity_config:
media_player.1live_player:
name: "1LIVE"
feature_list:
- feature: on_off
Tadaa. Let me know if there’s stuff to improve.