I’ve used the excellent Tag Reader project to implement a podcast player feature which keeps track of the last played episode for each podcast. First, here’s a bit of background to my use case:
Background
I have two children, ages 6 and 4. Their bedtime routine is that we get them ready for bed and then sit in one of their rooms and read a story with them. We have also recorded a bunch of stories using MyStoryTime, and so after reading a story with them we can ask Google to play another story as they fall asleep on their own (this was the key step that allowed us to leave the children to go to sleep on their own and gave us our evenings back!). When I assembled my first tagreader I set it up using the standard media player automation and printed a bunch of NFC cards so that my youngest could play the stories himself (here’s the javascript/css template I’ve used to create my cards).
All good so far, but now my eldest likes to listen to a podcast at bedtime. We had been casting the podcast from our phones but that means I have to clutter my podcast app with his content, and when his mum does bedtime she doesn’t know which episodes I’ve already played. So, Home Assistant to the rescue.
Podcast Player
For each podcast, I create a feedpaser sensor which retrieves the RSS feed for the podcast twice per day. Here’s an example:
- platform: feedparser
scan_interval: 43200
name: podcast_sound_explorers
feed_url: https://feeds.megaphone.fm/soundexplorers
I also create a counter to keep track of the next unplayed episode:
The important thing to remember is that the most recent episode is always the first element of the RSS feed so to play them in the correct order you need to start at the end of the feed. I initialise the counter to a big number (more than the number of episodes), and my automation detects this when the tag for the podcast is first scanned, and sets it equal to the number of episodes.
My automation makes use of variables and steals a lot of features from the tagreader media player so there’s an array containing the configuration for each podcast and that’s the only part that needs updating to add a new podcast. The rest of the automation is data driven and never needs to change. It also supports multiple tagreaders and speakers so will play the podcast on the speaker that’s in the same room as the tagreader that was scanned.
- id: '1604341003387'
alias: 'NFC: Podcast Player'
mode: restart
variables:
media_players:
4dd136882e204aec5b9809887714f25b: media_player.bedroom_2_speaker
43ddd1e400d41d9d547f7bc24a95f86d: media_player.reuben_s_room_speaker
tags:
04-51-6F-AA-ED-6C-80:
title: Maddie's Sound Explorers
podcast_counter: counter.podcast_maddie_s_sound_explorers
sensor: sensor.podcast_sound_explorers
04-31-6F-AA-ED-6C-80:
title: Marvellous Musical
podcast_counter: counter.podcast_marvellous_musical
sensor: sensor.podcast_marvellous_musical
04-4D-6F-AA-ED-6C-80:
title: Peace Out
podcast_counter: counter.podcast_peace_out
sensor: sensor.podcast_peace_out
trigger:
platform: event
event_type: tag_scanned
condition:
- '{{ trigger.event.data.tag_id in tags }}'
- '{{ trigger.event.data.device_id in media_players }}'
action:
- variables:
number_of_podcasts: '{{ state_attr(tags[trigger.event.data.tag_id].sensor, ''entries'')
| length }}'
podcast_counter: '{{ tags[trigger.event.data.tag_id].podcast_counter }}'
podcast_sensor: '{{ tags[trigger.event.data.tag_id].sensor }}'
media_player_entity_id: '{{ media_players[trigger.event.data.device_id] }}'
- choose:
- conditions:
- condition: template
value_template: '{{ states(podcast_counter) | int >= number_of_podcasts |
int }}'
sequence:
- service: counter.configure
data:
value: '{{ number_of_podcasts - 1}}'
entity_id: '{{podcast_counter}}'
default: []
- service: tts.google_translate_say
data:
entity_id: '{{ media_player_entity_id }}'
message: '{{tags[trigger.event.data.tag_id].title}} {{state_attr(podcast_sensor,''entries'')[states(podcast_counter)
| int].title }}'
- delay: 00:00:02
- wait_template: '{{ states(media_player_entity_id) == ''idle'' }}'
continue_on_timeout: true
timeout: '10'
- service: media_player.play_media
data:
media_content_id: '{{ (state_attr(podcast_sensor,''entries'')[states(podcast_counter)
| int].links | selectattr(''rel'',''eq'',''enclosure'') | list)[0].href }}'
media_content_type: music
entity_id: '{{ media_player_entity_id }}'
- choose:
- conditions:
- condition: template
value_template: '{{ states(podcast_counter) | int == 0 }} '
sequence:
- service: counter.configure
data:
value: '{{ number_of_podcasts | int - 1 }}'
entity_id: '{{podcast_counter}}'
default:
- service: counter.decrement
data:
entity_id: '{{podcast_counter}}'
So when a tag is scanned the following happens:
- Set
number_of_podcasts
to the number of items in the rss feed for this podcast - Assign
podcast_counter
andpodcast_sensor
to the respective counter and sensor for this podcast - If the counter value is greater than
number_of_podcasts
reset it so that it starts with the first episode (the last element in the rss feed) - Read the podcast title (that I set up in the
tags
array) and the episode title (from the rss feed), using Google TTS on the correct media player - Wait for the media player to return to
idle
state after reading the titles - Play the episode indicated by the counter
- If the counter was zero (i.e. the most recent episode was just played) reset it back to the end, otherwise decrease the counter by 1.
I’ve been using this for a week or so and it’s working well but as with most of these things I’m sure there are different ways to implement this so I’d welcome any feedback.