NFC Movie Library

Hi everyone!

Wanted to share a project I made for my kids: a movie library based on NFC tags.
I wanted to bring back the physicality of VHS tapes and DVDs.

The idea is simple: they browse through our catalog, pick a card, put it on the scanner, and watch a movie.

On the hardware side, I used an ESP32 (C3 Super Mini) and the RC522 NFC reader. The ESP32 is flashed with ESPHome for seamless integration with Home Assistant and local control.

ESPHome Configuration
---
substitutions:
  devicename: "nfc-scanner"
  friendly_name: "NFC Scanner"

packages:
  esphome: !include common/esphome.yaml
  api: !include common/api.yaml
  logger: !include common/logger.yaml
  wifi: !include common/wifi.yaml

esphome:
  # Magic variables to get the ESP32C3 Super Mini to work
  platformio_options:
    board_build.f_flash: 40000000L
    board_build.flash_mode: dio
    board_build.flash_size: 4MB
  on_boot:
    priority: 600
    then:
      - rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'

esp32:
  variant: ESP32C3
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

status_led:
  pin:
    number: GPIO8
    inverted: true

output:
  - platform: ledc
    pin: GPIO3
    id: buzzer

rtttl:
  output: buzzer

spi:
  clk_pin: GPIO4
  mosi_pin: GPIO6
  miso_pin: GPIO5

rc522_spi:
  cs_pin: GPIO7
  on_tag:
    then:
      - rtttl.stop:
      - homeassistant.tag_scanned: !lambda 'return x;'
      - rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'




When an NFC tag is scanned, ESPHome emits a tag_scanned event to Home Assistant. This is being used to play the correct movie on our Apple TV via Plex. To do that, I maintain a “mapping” between tag IDs and Plex IDs.

The automation only works between certain hours to prevent the kids from abusing the system and watching TV all the time :wink:

Home Assistant automation
- alias: "NFC Reader - Plex"
  description: ""
  mode: single
  trigger:
  - platform: event
    event_type: tag_scanned

  # Only allow movies to be played in the morning and evening.
  # Broad time window to allow for some flexibility in schedule.
  condition:
  - condition: or
    conditions:
    - condition: time
      after: '05:00:00'
      before: '09:00:00'
    - condition: time
      after: '18:00:00'
      before: '19:50:00'  # Bed time!
  action:
  - variables:
	  # Map the ID of each tag to a Plex ID. The "name" attribute is
	  # not used but nice to have for debugging.
      NFC_MAPPING:
        53-77-08-69-71-00-01:
          name: Ratatouille
          plex_id: 37353
        53-72-08-69-71-00-01:
          name: Coco
          plex_id: 3135
		# ...
  - if:
    # Make sure the scanned tag is in the mapping
    - alias: "NFC tag is in the mapping"
      condition: template
      value_template: "{{ trigger.event.data.tag_id in NFC_MAPPING }}"
    then:
    # Turn on the Apple TV (and the TV itself) when it's in standby
    - if:
      - condition: state
        entity_id: media_player.appletv_living
        state: standby
      then:
      - service: media_player.turn_on
        data: {}
        target:
          entity_id: media_player.appletv_living
      - delay:
          seconds: 5

	# Take the Plex ID from the mapping and send a deep link to the 
	# Apple TV. This will automatically open the Plex app and start
	# or resume the given movie.
    - service: media_player.play_media
      target:
        entity_id: media_player.appletv_living
      data:
        media_content_type: url
        media_content_id: "plex://play/?metadataKey=%2Flibrary%2Fmetadata%2F{{ NFC_MAPPING[trigger.event.data.tag_id].plex_id }}&server=xxxxxxxxxxxxxx"

	# Set an appropriate (low) volume on our Sonos Beam
	# Volume controls on Apple TV only support up/down when using an eARC
	# speaker.
    - service: media_player.volume_set
	  data:
	    volume_level: 0.17
	  target:
	    entity_id: media_player.sonos_tv



Using “physical” media has many benefits in my opinion:

  • You have a limited choice of movies to watch. When I was a kid, we didn’t have an infinite catalog of movies to watch. We had a handful of VHS tapes and DVDs. We watched the same movies over and over again, each time discovering new details we would have missed otherwise.

  • It gives my kids autonomy. They can decide what they want to watch and are not dependent on us to operate the remote and navigate to the correct app. (Autonomy within certain time periods, we don’t want them watching a lot of TV)

  • With autonomy comes responsibility. When allowed, they can watch any movie they like. They can even switch between movies. But the timeframe is fixed. They can watch a good chunk of one movie, or they can watch a tiny bit of 10 different movies. It’s completely up to them.

  • They will need to learn to collaborate and compromise. We only have one TV and two half-hour slots to watch it. They must quickly agree on a movie or forfeit their viewing time. Or perhaps they use their creativity to come up with a system to decide who gets to choose the movie, eg: “You can pick in the morning, I’ll pick in the evening.”

Other benefits: it’s cheaper to buy second hand Blurays than keeping a Netflix or Disney+ subscription. My kids keep watching the same movies over and over again. Plus, the Blurays come with dubbed versions (horrible, I knnow) and many extras that are often fun to watch. I specifically love behind the scenes content.


My kids love this system, and it’s super reliable. The only issue I’m having is with TV Shows. I currently use Plex deep links to play a specific movie on my Apple TV. If I want to play TV Shows, I need to go through the Plex integration, but for some reason it complains that my Apple TV doesn’t accept playback controls.

→ I still have this issue, but I found a workaround by using smart playlists and regular deep links: NFC Movie Library - #9 by Savjee


You can read the full write-up here. My 3D models are also available if you want to recreate this setup.

9 Likes

Love the idea. Keep up the good work

This is fantastic, great work!

Hello @Savjee i´m trying to recreat this NFC Tag Reader, but i´m having trouble with installing the code onto the ESP

Can you provide more information what code are you having inside esphome: !include common/esphome.yaml
api: !include common/api.yaml
logger: !include common/logger.yaml

Sure! These files are shared among pretty much all of my ESPHome devices.

esphome.yaml

---
esphome:
  name: $devicename
  comment: $friendly_name
  build_path: .builds/$devicename

api.yaml

---
# Enable Home Assistant API
api:
  encryption:
    key: !secret esphome_encryption_key

ota:
  password: !secret esphome_api_password

logger.yaml

---
# Enable logging
logger:

wifi.yaml

---
# Wi-Fi component package
# https://esphome.io/components/wifi.html

wifi:
  ssid: !secret wifi_iot_ssid
  password: !secret wifi_iot_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: ${friendly_name} AP
    password: !secret esphome_fallback_ap_password
1 Like

Trying to get something like this working but not really understanding deep links… what goes after server? Is it the token, the ip address, the name of the server? I have tried all of those as well as the Machine identifier and can’t even get them to work in a web browser - ha. Just verifying what it’s supposed to be. thank you!

1 Like

Oh wait you explain in your writeup… and I did go to the devices page, but it lists so many different clientIdentifiers and all of them are different, even for the same device. Must be something odd with my setup… or how did you choose the one to use? There are two different ones listed for my server and neither works.

1 Like

You probably have more than 1 Plex server attached to your account.

An alternative way to get the server token is by opening your Plex server in a web browser.
The URL will look like this:

http://192.168.2.216:32400/web/index.html#!/server/xxxxxxxxxxxxxxxxxxxxxxx/details?key=%2Flibrary%2Fmetadata%2F5&context=source%3Acontent.library~0~0

The server ID is right after the /server/ part.

Update: I still can’t the Plex integration to work with my Apple TV. It keeps complaining about not accepting playback controls. However, I found a way to play a random TV show episode with Plex deep links!

You can create a smart playlist that automatically shuffles itself when opened. Instructions to make a playlist are simple: [TIP] --- how to create an autoplaylist with random sort - Tips, Tricks & How-Tos - Plex Forum

Once that playlist is created, copy the ID from the URL. Example:

http://192.168.2.1:32400/web/index.html#!/server/{SERVER_ID}/playlist?key=%2Fplaylists%2F{PLAYLIST_ID}&context=source%3Acontent.playlists.video~0~0

Now adapt the automation to add support for plex_id and playlist_id attributes:

- alias: "NFC Reader - Plex"
  description: ""
  mode: single
  trigger:
  - platform: event
    event_type: tag_scanned

  # Only allow movies to be played in the morning and evening.
  # Broad time window to allow for some flexibility in schedule.
  condition:
  - condition: or
    conditions:
    - condition: time
      after: '05:00:00'
      before: '09:00:00'
    - condition: time
      after: '18:00:00'
      before: '19:50:00'  # Bed time!
  action:
  - variables:
	  # Map the ID of each tag to a Plex ID. The "name" attribute is
	  # not used but nice to have for debugging.
      NFC_MAPPING:
        53-77-08-69-71-00-01:
          name: Ratatouille
          plex_id: 37353
        53-72-08-69-71-00-01:
          name: Coco
          plex_id: 3135
        04-D3-F2-FD-9F-61-81:
          name: Bing
          playlist_id: 4586
        04-36-F6-32-5F-61-80:
          name: Bumba
          playlist_id: 4587
		# ...

  - if:
    # Make sure the scanned tag is in the mapping
    - alias: "NFC tag is in the mapping"
      condition: template
      value_template: "{{ trigger.event.data.tag_id in NFC_MAPPING }}"
    then:
    # Turn on the Apple TV (and the TV itself) when it's in standby
    - if:
      - condition: state
        entity_id: media_player.appletv_living
        state: standby
      then:
      - service: media_player.turn_on
        data: {}
        target:
          entity_id: media_player.appletv_living
      - delay:
          seconds: 5
	
    # If the matched tag has a "plex_id", play it as movie.
    - if:
        - condition: template
          value_template: "{{ \"plex_id\" in NFC_MAPPING[trigger.event.data.tag_id] }}"
      then:
        - action: media_player.play_media
          data:
            media_content_type: url
            media_content_id: >-
              plex://play/?metadataKey=%2Flibrary%2Fmetadata%2F{{NFC_MAPPING[trigger.event.data.tag_id].plex_id}}&server=xxxxxxxxxxxxxx
        target:
          entity_id: media_player.appletv_living
		  
    # If the matched tag has a "playlist_id", play a random item of it.
    - if:
        - condition: template
          value_template: "{{ \"playlist_id\" in NFC_MAPPING[trigger.event.data.tag_id] }}"
      then:
        - action: media_player.play_media
          data:
            media_content_type: url
            media_content_id: >-
              plex://play/?metadataKey=%2Fplaylists%2F{{NFC_MAPPING[trigger.event.data.tag_id].playlist_id}}&server=xxxxxxxxx
        target:
          entity_id: media_player.appletv_living

	# Set an appropriate (low) volume on our Sonos Beam
	# Volume controls on Apple TV only support up/down when using an eARC
	# speaker.
    - service: media_player.volume_set
	  data:
	    volume_level: 0.17
	  target:
	    entity_id: media_player.sonos_tv

That’s it! Every time the kids scan an NFC card of a TV Show, Plex will re-shuffle the playlist and start playing episodes in a random order.

(Also updated my original blog post in case you want more details)

Thank you for sharing this project @Savjee ! I really like it and started to copy it for my kid.
I just ordered the vinyl stickers, looks like a really good solution - I’ve been trying with different normal stickers but didn’t really like that. I was impressed with your design, did you make those yourself or find them somewhere online?

For anyone trying to implement this on an Android TV (Shield): I couldn’t get Plex deeplinking to work on android tv - and the plex integration wasn’t too reliable in my testing.
So I went with VLC. You can SMB to the share where you host your Plex files, and deeplink using vlc://smb://

Hi Rend!

I made the design for the cards myself. I’m not a designer, so I kept things simple and clean. Here’s the source file (Pixelmator): https://file.io/epakIJ17MekG

1 Like