[Из песочницы] Умный дом без пультов, но с кубиком

Проблема


18gvunttmqvenjpg.jpg

На время ремонта новой квартиры я вернулся в квартиру, в которой не жил 10 лет и решил поупражняться в дисциплине «Умный дом», чтобы со знанием дела обустраивать уже новую квартиру. Техника в этой квартире тоже не самая умная и не самая новая, ибо была произведена ~10 лет назад. Больше всего в квартире меня напрягало обилие инфракрасных пультов, они терялись и нагружали мозг подбором нужного. До недавнего времени было принято на пультах делать огромное количество малоиспользуемых кнопок. А еще пульты не эстетичны и не гигиеничны, одни угловатые, другие с скруглёнными гранями, кнопки желтеют, в них копятся остатки жизнидеятельности обитателей на которых образуется «подкнопочный биоциноз». В итоге по дому разбросано десяток пультов с кучей ненужных кнопок, для простой операций приходилось искать по две-три штуки, их вид царапает взор, хотелось удобного тактильного и модного голосового управления.


Цель

На замену куче пультов хотелось взять современное устройство с приятным дизайном, чтобы разница по сравнению с пультами бросалась в глаза. Ответной частью контроллера должно стать устройство умеющее обучаться инфракрасным сигналам с существующих пультов. Беглый поиск и маркетинг xiaomi сделали своё грязное дело и взор мой пал на Xiaomi universal IR remotecontroller и Xiaomi Mi Smart Home Magic Cube, а чтобы эти ребята могли общаться пришлось купить Xiaomi smart home gateway 2


cube_03-1000x1000.jpg

Приключения

В теории всё казалось просто, вот кубик, вот излучатель IR сигналов, вот их предводитель — ZigBee гейт. Но жизнь, как обычно, богаче.

Уже при подключении этого всего стало весело, без интернета это всё не настроить, все устройства требуют связи с серверами xiaomi, кроме того, продаваемые устройства произведены для китайского рынка и для их работы в родном приложении Mi Home требуется установить регион «Китай». Всё бы ничего, но если у вас регион Китай то используйте и китайские сервера, выбора не предоставляется. Я не параноик, не против чтобы КГБ товарища Си Цзиньпина знало когда и какие инфракрасные сигналы я посылаю своим колонкам и телевизорам. На практике оказалось, что почти все способности умных устройств гвоздями прибиты к серверам и без их ведома почти ничего не делают, сами сервера далеко (в Китае) в итоге послав из приложения Mi Home 10 сигналов увеличивающих громкость колонок приходилось ждать секунд 5 до начала увеличения звука, и еще секунд 30 до завершения обработки всех 10 сигналов, да и сами сигналы излучались не быстренько, а рывками, звук увеличивается на 1 затем ждет секунду еще на два, снова ожидание итд. Удобство так себе, врагу не пожелаешь…

Беда из Китая не приходит одна, кроме тормозов натирать стала невозможность прикрутить умный кубик к управлению звуком.


Кубик управляется жестами:

  • Встряхнуть
  • Подвинуть на плоскости
  • Повернуть на 90/180 градусов вокруг горизонтальной оси
  • Повернуть вокруг вертикальной оси
  • И др…


xiaomi_cube_switch_editor_01.png

Все действия триггеры — то есть сделал действие, пришел сигнал о том, что оно случилось, но для поворота вокруг вертикальной оси кубик умеет мерять еще и градус поворота, поэтому жест поворота удобно использоваться для управления звуком, повернул на 90 градусов по часовой стрелке — звук увеличился на 10 пунктов, повернул на 45 против часовой стрелки — звук уменьшился на 5 итд. В родном приложении нет возможности реализовать такое поведение в связки с инфракрасным излучателем, лучшее что умел мой комплект — это управление яркостью светодиодов на гейте ZigBee, это настолько-же прикольно насколько бесполезно, да еще и с 3х секундной задержкой имени Китайских серверов.

Чтобы одолеть сию оказию начал смотреть как устройства дружат с опенсорсными проектами типа Home Assistant, оказалось что да, есть такая возможность, но в Xiaomi явно решили придушить её, спрятав токены для общения с излучателем и закрыв порты через которые идет общение с ZigBee гейтом. Security token для общения с IR излучателем были по советам из интернетов выужены из логов на андроид устройстве, для этого пришлось установить приложение Mi Home годичной давности в котором был такой баг. С xiaomi gateway было чуть сложнее, пришлось подпаиваться к его UART RX/TX для того чтобы с консоли открыть порты через которые с ним общается Home Assistant.

Сам Home Assistant надо где-то запускать, под эту задачу подошел Synology DS718+ который использовался у меня как DLNA медиацентр и торрентокачалка. Это устройство умеет запускать Docker контейнеры и в нем легко завёлся контейнер с Home Assistant. Home Assistant штука крутая, но в отличии от Mi Hime всю умность дома в нем нужно писать ручками в yaml. Освоив DSL понимаемый Home Assistant и поплевавшись от еще одного средства программирования не требующего знания ЯП удалось состряпать костыль нужной формы.

Код автоматизации:

- id: volume_up
  alias: Volume up
  trigger:
  - event_data:
      action_type: rotate
      entity_id: binary_sensor.cube_0000000103ec74
    event_type: xiaomi_aqara.cube_action
    platform: event
  condition:
    condition: template
    value_template: "{{ trigger.event.data.action_value > 0 }}"
  action:
  - service_template: "script.bbk_vol_up_{{ (trigger.event.data.action_value / 3) | int }}"

Эта автоматизация отлавливает событие поворота по часовой стрелке и вызывает скрипт в имя которого подставляется кол-во сигналов увеличения звука которые должен послать инфракрасный излучатель. Как вы наверное догадались, для каждого угла поворота требуется свой скрипт, на их создание была призвана копипаста, она и народила мне вот таких скриптов 20 штук:


bbk_vol_up_10:
  sequence:
  - data:
      command:
      - bbk_vol_up
      delay_secs: 0.04
      num_repeats: 10
    entity_id: remote.xiaomi_miio_192_168_2_62
    service: remote.send_command


Конфигурация излучателя с перехваченными сигналами с пульта колонок выглядят вот так:

remote:
  - platform: xiaomi_miio
    host: 192.168.2.62
    token: 392cbed325e862ffff983c4575b7a6f2
    commands:
      bbk_power:
        command: raw:nE5m0wlk0msxmEsmszmEsm0wAHKaABkAoQAcAL1MZtOQEFmM3ARMCHwCymM2m4ENgXEAioHlgy+AwoL3gvYAh4L0gGGEBYLCAL0BR4CHhU4B+wQ8TCAA
      bbk_vol_up:
        command: raw:nMwmUwlk0mk1mEsms3ADKbABlM5hLJtMAD2ALKazkAdgAymM3AYMBApjNgKTmgGLABuBD4IaAEKA1YJrgyuBDwJvgZeEx4C/geHMZsA4YCDgxoAgwR3hU+A1ICJTCAA=
      bbk_vol_down:
        command: raw:nE5m0wlk0msxmEsmszmEsm0wAHKaABkAoQAcAL1MZtOQEFmM3ARMCHwIfAdQBFZuAQYFfAfuBCoHngveEx4C/gL+Cj4R2ANfLJqAYQUChU3MIAA=

Ура! Звук управляет поворотом кубика вокруг вертикальной оси, а включаются и телевизор и колонки поворотом кубика вокруг горизонтальной оси на 90 градусов.

Из Home Assistant все управляется, но хочется же еще и голосом этим рулить, Google Assistant подключается в Home Assistant, но тоже не без приключений. GA не показывает в интерфейсе скрипты, зато показывает выключатели. Понятие выключателя не ложилось на мой телевизор, так как не существовало программного способа понять включен он или выключен. Однако я нашел у телевизора разьём для Ethernet кабеля, но не нашел описания протокола общения по нему, поэтому решил использовать признак наличия IP адреса телевизора в сети в качестве сенсора, если IP телевизора в сети есть — он включен, если нет выключен. Чтобы реализовать такой сенсор пришлось написать простенький custom_component для Home Assistant:


import logging
from homeassistant.helpers.entity import Entity
from pythonping import ping

logger = logging.getLogger(__name__)

def setup_platform(hass, config, add_entities, discovery_info=None):
    entities = []
    for name, ip in config['hosts'].items():
        entities.append(IPSensor(name, ip))

    add_entities(entities)

class IPSensor(Entity):
    def __init__(self, name, ip):
        self._name = name
        self._ip = ip
        self._state = None
        self._is_on = None

    @property
    def name(self):
        return self._name

    @property
    def state(self):
        return self._state

    @property
    def state_attributes(self):
        return {'ip_address': self._ip}

    @property
    def device_class(self):
        return 'connectivity'

    @property
    def is_on(self):
        return self._is_on

    def update(self):
        result = ping(self._ip, count=1, timeout=0.1)
        self._is_on = result.success()
        self._state = 'on' if result.success() else ‘off'


Затем связать инфракрасный излучатель с сенсором в переключатель вот так:

switch:
  - platform: template
    switches:
      toshiba_tv:
        value_template: "{{ is_state('binary_sensor.toshiba_tv', 'on') }}"
        turn_on:
          service: script.toshiba_power
        turn_off:
          service: script.toshiba_power


И вот такой вот переключатель стал отображаться в Google Home и управляться с голоса.

P.S. Упражнение с умным домом прошло успешно, пошел закупать новые устройства!
P.P. S. А ZigBee хаб теперь зачем-то освещает прихожую…

Было


datllfwsuxaymb2ambzwp-20faq.jpeg

Стало


cobolyyfof7zpaqji3wfgx0dkfw.jpeg

© Habrahabr.ru