When I was a kid, my sister and I had a tower of VHS tapes we watched endlessly. Fast-forward to today, and my children's movie collection is vastly different. It's completely digital and dispersed across services. I wanted to recreate the tangible magic of my childhood for them.
Here's our current media routine. We have two boys, and the eldest is permitted a 30-minute TV session in the morning and another in the evening. He usually knows what he wants to watch, but he depends on us to operate the remote and navigate to the correct apps.
This got me thinking: why not blend the old with the new? I wanted a way for my son to have a more active role in choosing what to watch, despite the digital format.
Years earlier, I came across someone who had prints of his favorite music albums with an NFC tag inside of them, and I made something similar for my kids.
The end result
Here's what I ended up with:
It's a collection of laminated movie covers with an NFC tag inside. All my son has to do is pick the movie he wants to watch and put it on the NFC reader. Home Assistant will do the rest.
Here's a video of the system working:
Proof-of-concept & materials
I started by building a simple prototype. One of my electronics kits contained an NFC Reader (RC522) and some NFC tags.
I connected the NFC reader to an ESP32 board and flashed ESPHome onto it. I then placed an NFC tag on top of the reader and saw them appearing in the ESPHome logs.
That's all the confirmation I needed. I went to AliExpress and bought the "production" hardware:
I already had plenty of ESP32 boards lying around (LOLIN32 lite), but when I saw this "Super Mini" version, I had to have it. Look at this cutie:
Flashing ESPHome
Once everything arrived, I connected the NFC reader to the ESP32 using the following pins:
SS (SDA) -> GPIO7
SCK (SCK) -> GPIO4
MOSI -> GPIO6
MISO -> GPIO5
I also added a buzzer so I could beep some sounds when a tag was successfully scanned.
Next, I created an ESPHome configuration file. I had to add a few special platformio_options
to make it work on the ESP32 "Super Mini" variant, but otherwise this is a very simple 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:
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'
If you're building this yourself, you don't have to adhere to these pin numbers.
See my original ESPHome configuration for the LOLIN32 Lite here.
---
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:
platform: ESP32
board: lolin32
on_boot:
priority: 600
then:
- rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'
output:
- platform: ledc
pin: GPIO32
id: buzzer
rtttl:
output: buzzer
spi:
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
rc522_spi:
cs_pin: GPIO15
on_tag:
then:
- rtttl.stop:
- homeassistant.tag_scanned: !lambda 'return x;'
- rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'
After flashing ESPHome, the device will show up in Home Assistant with no entities. No need to panic, that's normal. It will emit tag_scanned
events. More on that later when we automate everything.
3D Printing an enclosure & collection box
To keep the electronics and wires safe from tiny hands, I designed and 3D printed a simple enclosure.
It's a box
It's a simple design. The only challenge I had was fixing the ESP32 in place. Unlike other dev boards, the ESP32C3 Super Mini has no screw holes, so I designed a simple clip mechanism.
Don't worry, the ESP32 is not floating in the air. It rests on the soldered GPIO pins on the bottom of the case.
I used Fusion360 to design and test fit all the parts (thanks to Hans Wurst for publishing a model of the Super Mini on GrabCAD).
By modeling every part, I can test if it all fits together well.
I also made a nice box to store the NFC cards:
Creating the "DVD" cards
With the reader finished, only the "DVD" cards were left. I wanted to print directly onto the PVC cards, but my printer isn't capable of that.
I went for the next best thing: I bought printable vinyl sticker paper and printed the movie covers on that.
I then cut out the stickers and stuck them to the PVC cards.
I also designed a backside for the cards, which include a summary of the movie, release date, and the studio that worked on it.
My first iteration of the cards were simply printed on paper and laminated.
With the reader finished, only the "DVD" cards were left. I printed small movie covers in booklet form, placed an NFC tag inside, and ran them through the laminator.
The booklet protects the NFC tag and allows me to recover it in the future.
Home Assistant automation
On to the last piece of the puzzle: creating an automation in Home Assistant that plays the correct movie when a tag is scanned.
We store movies on Plex and played via an Apple TV. There are two ways to play a movie on Plex:
The Plex integration is clunky and unreliable in my experience. It requires you to turn on the Apple TV, launch the Plex app, and wait for it to become available in Home Assistant before sending it a command to play a specific movie.
The Apple TV integration is near instant. All you have to do is send a "deep link", and that's it!
The deep link format is documented on the Plex forum, and is rather simple. Here's the deep link to play a given media item:
plex://play/?metadataKey=UNIQUE_MEDIA_ID&server=SERVER_ID
Your Plex server ID can be found by going to this page and looking for your server's clientIdentifier
.
Next, I needed to know what kind of data ESPHome sends to Home Assistant when a tag is scanned. I opened the Developer Tools in Home Assistant and started listening for tag_scanned
events. I put a tag on the reader and saw an event pop up:
event_type: tag_scanned
data:
tag_id: 1D-20-E2-06-96-00-00
device_id: a93f971bb9622b266286460c3f2ac640
origin: LOCAL
time_fired: "2023-11-13T14:57:06.765528+00:00"
context:
id: 01HF4JZB6DHY26TWZ903Z01BJ9
parent_id: null
user_id: null
The interesting bit here is the tag_id
. I used this ID to create a mapping between the tag's ID and the ID of a movie in Plex.
Here's the entire automation:
- alias: "NFC Reader - Plex"
description: ""
mode: single
trigger:
- platform: event
event_type: tag_scanned
condition:
- condition: or
conditions:
- condition: time
after: '05:00:00'
before: '09:00:00'
- condition: time
after: '18:00:00'
before: '19:50:00'
action:
- variables:
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:
- alias: "NFC tag is in the mapping"
condition: template
value_template: "{{ trigger.event.data.tag_id in NFC_MAPPING }}"
then:
- 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:
- 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:
- 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
- service: media_player.volume_set
data:
volume_level: 0.17
target:
entity_id: media_player.sonos_tv
The automation is simple and fast. It takes the Apple TV about 5 seconds to turn on the TV, launch the Plex app and have it play the correct movie.
I was thrilled! And so was my oldest son. He quickly realized he could use this to watch movies anytime he wanted. That wasn't the goal, so I added a condition to only allow the automation to run in the mornings and evenings.
The Apple TV also supports deep links for other services. Here are examples for the biggest streaming services:
- Netflix (use the regular URL):
https://www.netflix.com/title/80234304
- Disney+ (use regular URL):
https://www.disneyplus.com/movies/coco/db9orsI5O4gC
- YouTube (use the regular URL with
https://
replaced byyoutube://
)- Single video:
youtube://www.youtube.com/watch?v=ah3ezprtgmc
- Playlist:
youtube://www.youtube.com/watch?v=v=FkUn86bH34M&list=PLzvRQMJ9HDiQF_5bEErheiAawrJ-2zQoI&pp=iAQB
- Single video:
The only problem with these services is that they will require you to select a profile before the movie/show will start playing. With Plex, you can enable "auto login", but I haven't tried it for the other services.
Playing random episodes
One issue I had with Plex deep links is that you can’t play a random episode of a TV show. I sidestepped this issue by not making any NFC cards for shows, but that was to the dismay of my sons.
A couple of months after I made this system, I came across a post on the Plex forum explaining how you can make smart playlists that shuffle themselves every time you open them. And it even worked with the deep links!
The workaround is quite simple: start by making a “smart playlist” in Plex. Navigate to a library (movie or TV show) and change the first dropdown menu from “All” to “Advanced Filters”.
Now configure the filters. I created one smart playlist for each show (by matching the show name), but you can mix shows as well.
Finally, set change the sort order from "Title" to "Randomly". Now Plex will shuffle the playlist every time you open it, including when you use a deep link!
You can find the playlist ID by navigating to the playlist in the Plex Web UI and then looking at the URL:
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
For reference, the deep link for a playlist is slightly different to that of a movie:
plex://play/?metadataKey=%2Fplaylists%2F{PLAYLIST_ID_GOES_HERE}&server={SERVER_ID_GOES_HERE}
Future improvements
I'm thrilled with my first iteration, but I have some things one thing I'd like to tweak in the future.
On the hardware side, I want to replace the NFC reader with the PN532. It's a lot smaller, and will allow me to make the enclosure smaller.
On the automation side, I want to make it possible for tags to play random episodes of TV shows. Right now, that's not possible with Plex deep links, so I'll use the Plex Integration as a workaround.
And finally, I found printable NFC cards on Amazon that look amazing. You can insert these in any inkjet printer and have really beautiful, and uniform cards.
So why did I go through all this trouble to give my kids something tangible? Well, I see many advantages in a system like this.
First: 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.
Second, 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. They simply go to the box of cards, flip through them, pick a movie, and play it. Just like we used to do with VHS and DVD.
Third, with autonomy comes responsibility. Between certain hours, we allow them to watch TV. 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.
And finally, we have only one TV and two half-hour slots to watch it. This means they will need to learn to collaborate and compromise. 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. You can pick in the morning, I'll pick in the evening.
I have no evidence or research to back any of these claims. But I genuinely believe this could teach them valuable skills. And I feel it's way better than giving my kids unlimited access to Netflix, Disney+, YouTube, or any other streaming service.
Do you hate streaming services?
It's not that I hate streaming services. I've had a Spotify subscription for years, and I occasionally subscribe to other streaming services. It's just that I don't watch that many movies or shows, so it's hard to justify the price.
Instead, I buy our favorite movies on Blu-ray and put them on our Plex server. This has 3 major benefits.
First, my son watches a few movies over and over again (especially Pixar movies). A Disney+ subscription costs €10,99/month, but for that price I can buy 3-4 Blu-rays on second hand websites. They're very cheap and once you buy a disc, it's yours forever.
Secondly, Blu-ray discs come with a lot of extra content. I especially love the "behind the scenes" or "deleted scenes" that most discs come with. These aren't always available on streaming services.
And finally, if you're a non-English speaker, Blu-rays will include a dubbed version in your native language. This is a big plus for the kids, especially when they're as young as mine are now.
Conclusion & downloads
We're really happy with this system, and I'm looking forward to when my youngest will be able to use it as well.
Want to build your own? Here's my Fusion360 design file that you can use or improve upon.