В детстве у нас с сестрой была гора VHS-кассет, которые мы бесконечно пересматривали. Современная видеоколлекция моих детей сильно от них отличается. Она полностью цифровая и разбросана по разным сервисам. Я хотел поделиться с ними магией осязаемости носителя из моего детства.
У меня двое пацанов, старшему разрешается посмотреть полчаса телевизор с утра и ещё полчаса вечером. Обычно он точно знает, что хочет посмотреть, но ему приходится ждать, пока кто-то из родителей возьмёт пульт и откроет нужное приложение.
Это заставило меня задуматься: почему бы не объединить новое и старое? Я хотел, чтобы у сына была более активная роль в выборе того, что он хочет посмотреть, несмотря на цифровой формат.
Несколько лет назад я увидел, как кто-то распечатал любимые музыкальные альбомы с NFC-меткой внутри, и решил сделать что-то подобное для своих детей.
▍ Конечный результат
Вот, что я в результате создал:
Это коллекция ламинированных обложек фильмов и мультфильмов с NFC-меткой внутри. Сыну остаётся только выбрать нужный фильм и положить его на считыватель NFC. Всем остальным займётся Home Assistant.
Вот видео того, как работает система:
▍ Proof-of-concept и материалы
Я начал с изготовления простого прототипа. В одном из моих наборов электроники нашёлся считыватель NFC (RC522) и несколько NFC-меток.
Я подключил считыватель NFC к плате ESP32 и прошил на неё ESPHome. Затем я положил NFC-метку на считыватель и увидел, что она появляется в логах ESPHome.
И это всё, что мне нужно было проверить. Я зашёл на AliExpress и купил оборудование для «продакшена»:
- 50 NFC-карт из ПВХ (NTAG215): €9,43.
- ESP32-C3 (Super Mini): €2,93.
У меня уже было множество плат ESP32 (LOLIN32 lite), но когда я увидел версию Super Mini, то обязан был её купить. Посмотрите на эту красоту:
▍ Прошиваем ESPHome
Когда все детали прибыли, я подключил считыватель NFC к ESP32 с помощью следующих контактов:
SS (SDA) -> GPIO7
SCK (SCK) -> GPIO4
MOSI -> GPIO6
MISO -> GPIO5
Ещё я добавил динамик, чтобы можно было издавать писк при успешном сканировании метки.
Затем я создал файл конфигурации ESPHome. Мне пришлось добавить несколько особых platformio_options
, чтобы он заработал на версии ESP32 Super Mini, но в целом это оказалась очень простая конфигурация.
---
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:
# Магические переменные, необходимые, чтобы заработала ESP32C3 Super Mini
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'
Если вы будете собирать устройство самостоятельно, то необязательно использовать те же номера контактов.
---
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'
После завершения прошивки ESPHome устройство отобразится в Home Assistant, но без записей. Не нужно паниковать, это нормально. Оно будет отправлять события tag_scanned
. Подробнее об этом я расскажу позже, когда мы всё автоматизируем.
▍ 3D-печать корпуса и коробки для видеоколлекции
Чтобы защитить электронику и провода от шаловливых детских ручек, я спроектировал и распечатал простой корпус.
Это корпус
У него очень простая конструкция. Единственная сложность возникла с креплением ESP32. В отличие от других плат разработки, у ESP32C3 Super Mini нет отверстий под винты, поэтому я спроектировал простой механизм с зажимами.
Не волнуйтесь, ESP32 не висит в воздухе. Он опирается на припаянные контакты GPIO внизу корпуса.
Для проектирования и компоновки всех деталей я использовал Fusion360 (благодарю Ханса Вурста за публикацию модели Super Mini на GrabCAD).
Смоделировав каждую деталь, можно проверить, хорошо ли они сочетаются друг с другом
Ещё я распечатал красивую коробочку для хранения NFC-карт:
▍ Создание карт «DVD»
После завершения разработки считывателя мне оставалось лишь изготовить карты «DVD». Я хотел печатать непосредственно на ПВХ-картах, но мой принтер на это неспособен.
Поэтому я выбрал чуть менее оптимальный вариант: купил виниловую бумагу для наклеек и распечатал обложки на ней.
Затем я вырезал наклейки и приклеил их на ПВХ-карты.
Ещё я задизайнил обратную сторону карт, на которой изложен краткий сюжет фильма, дата выпуска и студия.
Буклет защищал NFC-метку и позволял извлечь её в будущем.
▍ Автоматизация Home Assistant
И последняя часть проекта: создание автоматизации Home Assistant, которая будет воспроизводить нужный фильм после сканирования метки.
Мы храним фильмы в Plex и воспроизводим их через Apple TV. В Plex есть два способа воспроизведения видео:
- Через интеграцию с Apple TV, при помощи «глубоких ссылок» Plex.
- Через интеграцию Plex с Home Assistant.
По моему опыту, интеграция Plex слишком неудобна и ненадёжна. Нужно включить Apple TV, запустить приложение Plex и дождаться, пока оно станет доступным в Home Assistant; лишь после этого можно отправить команду воспроизведения конкретного фильма.
Интеграция с Apple TV работает практически мгновенно. Достаточно лишь отправить «глубокую ссылку»!
Формат «глубоких ссылок» (deep link) задокументирован на форуме Plex, он довольно прост. Вот «глубокая ссылка» для воспроизведения медиа:
plex://play/?metadataKey=UNIQUE_MEDIA_ID&server=SERVER_ID
ID сервера Plex можно найти на этой странице или поискав clientIdentifier
вашего сервера.
Дальше мне нужно было понять, какие данные ESPHome отправляет Home Assistant при сканировании метки. Я открыл Developer Tools в Home Assistant и начал прослушивать события tag_scanned
. Положив метку на считыватель, я увидел событие:
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
Самое интересное здесь — это tag_id
. Этот ID я сопоставил с ID фильма в Plex.
Вот как выглядит полная автоматизация:
- 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:
# Сопоставляем ID каждой метки с Plex ID. Атрибут "name"
# не используется, но удобен для отладки.
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:
# Проверяем, находится ли отсканированная метка в mapping
- alias: "NFC tag is in the mapping"
condition: template
value_template: "{{ trigger.event.data.tag_id in NFC_MAPPING }}"
then:
# Включаем Apple TV (и сам телевизор), когда он находится в режиме ожидания
- 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
# Если сопоставленная метка имеет "plex_id", то воспроизводим его как фильм.
- 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
# Если сопоставленная метка имеет "playlist_id", воспроизводим случайный пункт из плейлиста.
- 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
# Устанавливаем подходящую (низкую) громкость на Sonos Beam
# При работе с динамиком eARC элементы управления громкостью
# Apple TV поддерживают только команды вверх/вниз.
- service: media_player.volume_set
data:
volume_level: 0.17
target:
entity_id: media_player.sonos_tv
Автоматизация проста и быстра. Apple TV требуется примерно пять секунд, чтобы включить телевизор, запустить приложение Plex и начать воспроизводить через него нужный фильм.
Я был впечатлён! И мой старший сын тоже. Он сразу понял, что можно использовать эту систему в любое время. Я стремился не к этому, поэтому добавил условие, позволяющее запускать автоматизацию только по утрам и вечерам.
Apple TV поддерживает «глубокие ссылки» и для других сервисов. Вот примеры для самых популярных сервисов потокового воспроизведения:
- Netflix (используется обычный URL):
https://www.netflix.com/title/80234304
- Disney+ (используется обычный URL):
https://www.disneyplus.com/movies/coco/db9orsI5O4gC
- YouTube (используется обычный URL, в котором
https://
заменено наyoutube://
)- Отдельное видео:
youtube://www.youtube.com/watch?v=ah3ezprtgmc
- Плейлист:
youtube://www.youtube.com/watch?v=v=FkUn86bH34M&list=PLzvRQMJ9HDiQF_5bEErheiAawrJ-2zQoI&pp=iAQB
- Отдельное видео:
Единственная проблема с этими сервисами заключается в том, что перед началом воспроизведения они требуют выбрать профиль. В случае Plex можно включить «auto login», но для других сервисов я этого не пробовал.
▍ Воспроизведение случайных эпизодов
У меня возникла проблема с «глубокими ссылками» Plex — невозможность воспроизведения случайного эпизода сериала. Я обошёл эту трудность, просто не став делать NFC-карты для сериалов, к разочарованию моих сыновей.
Спустя пару месяцев после разработки этой системы я нашёл пост на форуме Plex о том, что можно создавать умные плейлисты, перемешивающиеся при каждом открывании. И это работало даже с «глубокими ссылками»!
То есть, решить проблему оказалось довольно просто: создать «умный плейлист» в Plex. Перейти в библиотеку (фильм или сериал) и изменить значение первого раскрывающегося меню с «All» на «Advanced Filters».
Далее нужно сконфигурировать фильтры. Я создал по одному умному плейлисту для каждого сериала, но можно и перемешивать разные сериалы.
Затем я поменял порядок сортировки с «Title» на «Randomly». После этого Plex будет перемешивать плейлист при каждом открытии, в том числе и при переходе по «глубокой ссылке»!
ID плейлиста можно найти, перейдя к плейлисту в Plex Web UI и скопировав 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
«Глубокая ссылка» для плейлиста немного отличается от ссылки для фильма:
plex://play/?metadataKey=%2Fplaylists%2F{PLAYLIST_ID_GOES_HERE}&server={ЗДЕСЬ_SERVER_ID}
▍ Дальнейшее развитие проекта
Меня впечатлила даже первая версия, но в будущем я бы хотел кое-что изменить.
Во-первых, я хочу заменить считыватель NFC на PN532. Он намного меньше и позволит сделать корпус поменьше.
▍ Почему физические носители — это здорово
Почему же я заморочился всем этим, чтобы у моих детей было что-то осязаемое? Мне кажется, у такой системы есть множество преимуществ.
Первое: ограниченный выбор фильмов. В детстве у меня не было бесконечного каталога фильмов, только с десяток VHS-кассет и DVD. Мы смотрели одни и те же фильмы снова и снова, каждый раз обнаруживая новые детали, которые бы в противном случае упустили.
Второе: это даёт моим детям независимость. Они сами могут решать, что хотят посмотреть, не прося нас брать пульт и запускать приложение. Они просто подходят к коробке с картами, выбирают фильм и включают его. Точно так же, как мы делали с VHS и DVD.
Третье: свобода порождает ответственность. Мы разрешаем им смотреть телевизор в определённые часы. Они могут смотреть любой фильм, даже переключаться между ними. Но временной промежуток ограничен. Они могут посмотреть большую часть одного фильма или понемногу из десяти фильмов. Выбор полностью за ними.
Наконец, у нас есть только один телевизор и два получасовых интервала на его просмотр. То есть им придётся учиться договариваться и идти на компромиссы. Они должны быстро выбрать фильм, или потерять это время просмотра. Или, например, подойти к этой задаче творчески и придумать систему, определяющую, кто будет выбирать фильм: ты можешь выбирать утром, я выбираю вечером.
Я не могу подтвердить все эти утверждения доказательствами или исследованиями. Но я искренне верю, что это поможет им освоить ценные навыки. И я считаю, что это намного лучше, чем давать детям неограниченный доступ к Netflix, Disney+, YouTube или любому другому потоковому сервису.
▍ Я ненавижу потоковые сервисы?
Конечно, нет. У меня уже много лет есть подписка на Spotify, и время от времени я подписываюсь на другие потоковые сервисы. Дело в том, что я смотрю не так много фильмов и сериалов, так что их стоимость себя не оправдывает.
Вместо них я покупаю любимые фильмы на Blu-ray и сохраняю их на сервер Plex. Это даёт мне три важных преимущества.
Во-первых, мой сын снова и снова смотрит одни и те же фильмы (особенно мультфильмы Pixar). Подписка на Disney+ стоит €10,99 в месяц, но за такую цену я могу купить с рук 3-4 диска Blu-ray. Они очень дешёвые, а после покупки диска он ваш навечно.
Во-вторых, на дисках Blu-ray есть множество дополнительного контента. Особенно мне нравятся разделы «За кулисами» («behind the scenes») и «Удалённые сцены» («deleted scenes»), которые есть на большинстве дисков. В потоковых сервисах их не всегда можно найти.
Наконец, на Blu-ray есть дублированные версии на разных языках. Для детей это большой плюс, особенно если они маленькие.
▍ Заключение и файлы
Я очень доволен этой системой, и жду, когда мой младший сможет пользоваться ею.
Если хотите изготовить что-то подобное сами, то вот мой файл Fusion360, который можно использовать или доделать.
Автор: ru_vds