NFC tagreader podcast player

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:

  1. Set number_of_podcasts to the number of items in the rss feed for this podcast
  2. Assign podcast_counter and podcast_sensor to the respective counter and sensor for this podcast
  3. 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)
  4. 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
  5. Wait for the media player to return to idle state after reading the titles
  6. Play the episode indicated by the counter
  7. 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.

4 Likes

That’s a great project. About your template project, I can various size of image. You didn’t crop or resize them before generating the card?

Hi, you can use pretty much any size of image. The stylesheet will reduce it to 125px high, and if it’s too wide the extra will be cropped off.

This is the CSS class that does it: https://github.com/tdroza/nfc-cards/blob/538b06eb7a5f63e3abd0a62b9d15601b527bf911/css/styles.css#L36

Want to setup the same.
Just got the tag readers done but can’t seem to figure out the podcast portion. How do you program the NFC tags to link? I have tried the service to write a random code and also used the native tag portion in home assistant. My reader works with music and the tag reader blueprint. Just can’t seem to get this working.

You write the tag using the Home Assistant app - let it autogenerate an ID. Then scan the tag and grab the tag id (from Settings > Tags, or listen to the tag_scanned event in Developer Tools) and use that in the tags: section of the automation. My kids still use these every day but it’s a while since I’ve set up a new tag. I vaguely remember that the tag id that HA reports was different (or in a different format) if I scanned it with my phone compared to scanning it with the tagreader so make sure you use the tagreader to get the right id.

Hope that helps

It did help, I am wondering if this can be triggered by a script or a button.
I would almost like a way to play in two rooms at the same time off the same podcast “feed”. Might have a room with no tag reader but we want to play the next available podcast from the series. Been mucking around to make it into a script but its not working (do to me no idea what I am doing)