Забытые технологии древних — картинка в картинке на ТВ для умного дома
Привет, Хабр! Сегодня я расскажу о своем опыте автоматизации умного дома, а именно о выводе изображения PiP с камеры на тв.
Пример вывода изображений с камер
Обычно ОС (webOS, Tizen и тд) тв закрыта и не предполагает кого-либо внешнего управления, например, включение\выключение или вывод картинки в картинке. Но на Android TV с этим проще — можно запустить любое приложение, а наделив его правами SYSTEM_ALERT_WINDOW, вывести его поверх любых других (в том числе видео плеера или изображения с HDMI), этим и воспользуемся.
Дано два телевизора: Xiaomi Q1 на Android TV к нему подключены Nvidia Shield и Xbox и LG TV на webOS с подключенным к нему FireTV Stick — цель вывести видео с камер при звонке в дверь. Умный дом управляется через Home Assistant.
Настройка андроид тв\приставки
Приложение для вывода потока с камеры нужно устанавливать именно на ТВ, а не на тв приставку, так как независимо от подключенного внешнего источника изображения (тв приставка или игровая консоль и тп), всплывающее изображение будет выведено независимо и поверх любого источника. Но если тв приставка это единственный источник сигнала, то можно и на нее.
Для начала на Android TV необходимо выдать права разработчика, для этого в настройках ОС, нужно несколько раз кликнуть по Номеру сборки, а после этого в параметрах разработчика нужно включить Отладку и тут же, не отходя от кассы, можно телевизор\приставку добавить в HomeAssistant через интеграцию Android Debug Bridge.
Добавление Android TV в Home Assistant
![Добавление устройства в HA](https://habrastorage.org/r/w1560/getpro/habr/upload_files/d0e/bf3/2a1/d0ebf32a12ac376fb6e2a665bc67f1a4.png)
Добавление устройства в HA
![Запрос на отладку](https://habrastorage.org/r/w1560/getpro/habr/upload_files/16a/5e1/b5f/16a5e1b5fdce607c2edbff90f9d8613d.png)
Запрос на отладку
![все домашние Андроид ТВ добавлены](https://habrastorage.org/r/w1560/getpro/habr/upload_files/44b/9e7/a3f/44b9e7a3f0737dc6c5e60e8dafefd976.png)
все домашние Андроид ТВ добавлены
Теперь скачиваем APK приложение PiPup и устанавливаем на ТВ командами:
adb connect YOUR_ANDROID_TV_IP_ADDRESS
adb install app-debug.apk
adb shell appops set nl.rogro82.pipup SYSTEM_ALERT_WINDOW allow
Команды ADB можно вызывать прямо из Home Assistant
![a779351f216e09c6bfa038dd876649f8.png](https://habrastorage.org/r/w1560/getpro/habr/upload_files/a77/935/1f2/a779351f216e09c6bfa038dd876649f8.png)
Приложение PiPup открывает на устройстве веб порт 7979
и принимает POST JSON данные, которые необходимо вывести на экране тв, например:
{
"duration": 30,
"position": 0,
"title": "Your awesome title",
"titleColor": "#0066cc",
"titleSize": 20,
"message": "What ever you want to say... do it here...",
"messageColor": "#000000",
"messageSize": 14,
"backgroundColor": "#ffffff",
"media": { "image": {
"uri": "https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/cfcc3137009463.5731d08bd66a1.png", "width": 480
}}
}
У
media: "video"
есть недостаток, по крайней мере на Amazon FireTV, проигрываемое основное видео на тв приставке автоматически ставится на паузу, поэтому я вообще решил не использовать такой тип медиа.
Проверить можно так:
curl -d "@post.json" -H "Content-Type: application/json" -X POST http://ANDROID_TV_IP_ADDRESS:7979/notify
Настройка умного дома
Веб запросы в HA вызываются через интеграцию rest_command, создадим удобные сервисы (для отправки уведомления в виде текста, изображения и веб содержимого), для этого нужно сохранить код ниже в файле notification_on_tv.yaml по пути /homeassistant/rest_commands
pipup_text_on_tv:
url: http://{{ ip }}:{{ port | default(7979) }}/notify
content_type: 'application/json'
verify_ssl: false
method: 'post'
timeout: 20
payload: >
{
"duration": {{ duration | default(20) }},
"position": {{ position | default(0) }},
"title": "{{ title | default('') }}",
"titleColor": "{{ titleColor | default('#50BFF2') }}",
"titleSize": {{ titleSize | default(12) }},
"message": "{{ message }}",
"messageColor": "{{ messageColor | default('#fbf5f5') }}",
"messageSize": {{ messageSize | default(14) }},
"backgroundColor": "{{ backgroundColor | default('#0f0e0e') }}"
}
pipup_image_on_tv:
url: http://{{ ip }}:{{ port | default(7979) }}/notify
content_type: 'application/json'
verify_ssl: false
method: 'post'
timeout: 20
payload: >
{
"duration": {{ duration | default(20) }},
"position": {{ position | default(0) }},
"title": "{{ title | default('') }}",
"titleColor": "{{ titleColor | default('#50BFF2') }}",
"titleSize": {{ titleSize | default(10) }},
"message": "{{ message }}",
"messageColor": "{{ messageColor | default('#fbf5f5') }}",
"messageSize": {{ messageSize | default(14) }},
"backgroundColor": "{{ backgroundColor | default('#0f0e0e') }}",
"media": {
"image": {
"uri": "{{ url }}",
"width": {{ width | default(640) }}
}
}
}
pipup_url_on_tv:
url: http://{{ ip }}:{{ port | default(7979) }}/notify
content_type: 'application/json'
verify_ssl: false
method: 'post'
timeout: 20
payload: >
{
"duration": {{ duration | default(20) }},
"position": {{ position | default(0) }},
"title": "{{ title | default('') }}",
"titleColor": "{{ titleColor | default('#50BFF2') }}",
"titleSize": {{ titleSize | default(10) }},
"message": "{{ message }}",
"messageColor": "{{ messageColor | default('#fbf5f5') }}",
"messageSize": {{ messageSize | default(14) }},
"backgroundColor": "{{ backgroundColor | default('#0f0e0e') }}",
"media": {
"web": {
"uri": "{{ url }}",
"width": {{ width | default(640) }},
"height": {{ height | default(480) }}
}
}
}
и прописать этот файл в основном файле конфигурации HA /homeassistant/configuration.yaml
rest_command: !include_dir_merge_named rest_commands
После этого нужно перезагрузить HA и мы сможем вызывать наши новые сервисы для вывода текста, изображений или веб содержимого, например:
service: rest_command.pipup_image_on_tv
data:
title: Привет
message: Хабр
ip: 192.168.0.94
titleColor: red
position: 0
url: https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/cfcc3137009463.5731d08bd66a1.png
Вывод live видео с камер
Для вывода потока с подключенных камер HA использует внутренний проксирующий сервис на основе ffmpeg, который отдает фронтенду поток в виде «бесконечной» картинки с заголовком multipart/x-mixed-replace; boundary=--frameboundary
ссылку на поток можно получить через атрибуты устройства камера — entity_picture, который (к нашей радости) содержит также одноразовый токен доступа, это, в свою очередь, дает доступ к картинке без авторизации, пример:
http://HA_IP:8123/api/camera_proxy/camera.doorbell_repeater_b8b7?token=fd31891d4e34b6a45bf26fbba48af42f71f6abd0d797994fe529c280536e5e78
Атрибуты подключенных камер
![654e052ddfb1a413001ffa0e4f29a91e.png](https://habrastorage.org/r/w1560/getpro/habr/upload_files/654/e05/2dd/654e052ddfb1a413001ffa0e4f29a91e.png)
К сожалению такой поток «бесконечной» картинки нельзя просто так отдать в наш новый сервис pipup_image_on_tv — ничего не выведется (компонента не умеет работать с такими картинками). Так же не сработает если отдать в pipup_url_on_tv, нужна обертка в виде простой веб страницы с этой картинкой.
HA умеет хранить и отдавать статичные пользовательские ресурсы, которые лежат по пути /homeassistant/www, создадим там страницу с выводом картинки с камеры, например, /homeassistant/www/fullscreen/my_fullscreen_panel.html
Fullscreen Panel
ID камеры на страницу будем передать через параметр camera_entity, а url к картинке (с токеном) будем получать через встроенный в HA REST сервис /api/states/, для доступа к нему в HA нужно получить постоянный авторизационный токен, тут http://homeassistant.local:8123/profile/security
И прописать его в HTML тут (осторожно: этот ключ дает полный доступ к HA, но для внутренней сети это нестрашно)
const response = await fetch('/api/states/' + entity_id, {
headers: {
'Authorization': 'Bearer СЕКРЕТНЫЙ_КОД',
'Content-Type': 'application/json'
}
});
Еще, атрибут camera_entity содержит только статичную картинку, поэтому в пути нужно заменитьcamera_proxy на camera_proxy_stream, что уже сделано у меня в коде.
Теперь в браузере на PC, вызвав http://HA_IP:8123/local/fullscreen/my_fullscreen_panel.html?camera_entity=camera.doorbell_repeater_b8b7
должна появится картинка с камеры на весь экран.
Автоматизации
Создадим красивый скрипт обертку, для вызова статичной страницы с передачей ID камеры и текста, здесь у меня (при вызове скрипта) параллельно выводится изображение с камер на два домашних тв (192.168.0.215, 192.168.0.94) используем сервис pipup_url_on_tv.
alias: Показать live с камеры с переданным ID и текстом
sequence:
- parallel:
- service: rest_command.pipup_url_on_tv
metadata: {}
data:
ip: 192.168.0.215
title: "{{ title }}"
url: >-
http://192.168.0.170:8123/local/fullscreen/my_fullscreen_panel.html?camera_entity={{
camera_entity }}&nocache={{ range(1, 51) | random }}
width: 320
height: 200
- service: rest_command.pipup_url_on_tv
metadata: {}
data:
ip: 192.168.0.94
title: "{{ title }}"
url: >-
http://192.168.0.170:8123/local/fullscreen/my_fullscreen_panel.html?camera_entity={{
camera_entity }}&nocache={{ range(1, 51) | random }}
width: 320
height: 200
description: ""
icon: mdi:camera
fields:
camera_entity:
selector:
entity: {}
name: Сущность камеры
required: true
title:
selector:
text: null
name: Сообщение
Также, на всякий случай, я создал автоматизацию, которая запускает сервис PiPup на тв (при переходе из спящего режима), если вдруг телевизор решит его прибить:
alias: Перезапуск сервиса картинка в картинке на XiaomiTV, если тв прибьет его
description: ""
trigger:
- platform: device
device_id: ID ANDROID TV
domain: media_player
entity_id: Player ID
type: idle
condition: []
action:
- service: androidtv.adb_command
target:
device_id: ID ANDROID TV
data:
command: >-
ps -ef | grep -v grep | grep pipup || shell am start
--activity-task-on-home -n nl.rogro82.pipup/.MainActivity
mode: single
Ну и, как пример, основная автоматизация, когда курьер принес заказ:
alias: Курьер пришел
description: ""
trigger:
- platform: state
entity_id:
- sensor.smartintercom_line_status
to: Открытие двери
condition:
- condition: state
entity_id: switch.smartintercom_delivery
state: "on"
action:
- service: script.alice_play_text
data:
message: Кажется курьер пришел, открыла дверь
- wait_for_trigger:
- platform: state
entity_id:
- binary_sensor.camera_hub_g2h_e718_motion_sensor
to: "on"
timeout:
hours: 0
minutes: 4
seconds: 0
milliseconds: 0
continue_on_timeout: false
- service: script.alice_play_text
data:
message: Курьер у входной двери
- service: script.live_stream
metadata: {}
data:
camera_entity: camera.camera_hub_g2h_e718_camera_stream_management0
title: Курьер у входной двери
mode: single
И автоматизация при звонке в дверной замок:
alias: Включение экрана планшета при звонке и показ live с камеры звонка
description: ""
trigger:
- platform: device
device_id: bba92ec78885fa381662ae945f2ee231
domain: homekit_controller
type: doorbell
subtype: single_press
condition: []
action:
- service: script.door_tablet_screen_on
metadata: {}
data: {}
- service: script.live_stream
metadata: {}
data:
camera_entity: camera.doorbell_repeater_b8b7
title: Звонят в дверь
mode: single
Мира!