Умный дом в контейнерах (ioBroker + Zigbee в Docker)

Вступление


Некоторое время пользовался несколькими стандартными реле Sonoff, управляющими светом через Google Home Mini. Но в итоге захотелось большего. Стандартного функционала не хватает, решил постепенно делать систему на чем-то более гибком. Выбрал ioBroker.
Сначала, как водится, смотрел, выбирал, проверял кусочки функционала. Когда по отдельности основное нужное заработало, начал собирать воедино. И, естественно, столкнулся с проблемами.
Основные сложности:

  • Что именно выбрать? Ведь есть много способов реализовать задуманное. И даже в выбранном решении много вариантов…
  • Нет готовых инструкций, как собрать воедино именно выбранный мной набор решений и именно в моих конфигурациях.


Что я выбрал, почему, с какими сложностями столкнулся и как их решить, и пойдет речь.
Забегая вперед, опишу, как запускал ioBroker в докере на старом ноутбуке и прокидывал в него Zigbee, чтобы взаимодействовать с датчиками Xiaomi напрямую, без шлюза. Стандартные инструкции не привожу, только свои «шишки».

Поскольку пока это не продуктив, вполне можно что-то менять. Так что буду благодарен за советы и исправления :)

Хотелки и ход моих мыслей


Были релюшки Sonoff (заведенные в eWeLink), какой-то удлинитель (Tuya SmartLife), Xiaomi gateway с несколькими датчиками (Mi Home), колонка Google Home Mini.
eWeLink и SmartLife нормально подцепились в Google Home, слушались голосовых команд типа «Включи свет над столом». Mi Home не завелся (в Google Home мало устройств Xiaomi поддерживается).
Поставил ioBroker, подцепился к Xiaomi Gateway, в принципе все заработало. Все красиво, скрипты проверил, пишутся (выбрал Node-Red), решил на этом все делать.

Однако я не профессиональный админ, могу разобраться, как готовое поставить, но тонкостей типа какие библиотеки и компоненты при этом куда ставятся не знаю (и глубоко вникать не хочу). Поэтому меня несколько напрягло, что для ioBroker надо nodejs, какие-то npm, с которыми я раньше дела не имел. Есть сложности с версиями (типа стандартным yum из репозитория ставится слишком старый nodejs и т.п.).
Ну т.е. я все запустил, но в душе остался страх, что оно хотя и работает, но я не понимаю как. И если, к примеру, при апгрейде что-то сломается, я не буду знать, как чинить. А ведь помимо ioBroker я хотел и другие системы на ноут взгромоздить.
Поставлю, к примеру, апдейт на что-то, вроде заработает. А через какое-то время окажется, что работает плохо. Придется откатывать бэкап на месяц назад. Причем бэкап не только этой системы, но и всех других, ибо я не понимаю до конца, где от какой системы исполняемые файлы, где конфигурационные, где сами данные…

Очень меня это напрягало, поэтому решил использовать Докер. Код в контейнере. Данные отдельно, директория подмонтирована на хосте. Легко бэкапить.
Вышла новая версия? Да хоть на другой виртуалке легко проверить, как новая версия контейнера с данными из этой директории заработает. Разные системы друг другу не мешают. Легко откатить бинарники какой-то системы назад. Опять же, легко перенести на что-то другое (ioBroker в контейнере и на Synology работает, и на одноплатниках). Красота!

Будут еще требования (сделать доступным через Интернет, но для безопасности не публично доступным), они будут сказываться дальше при выборе конфигураций.

Установка


Хостовая операционка не критична, скачал CentOS (по старой памяти помнил, что для всяких сетевых задач она вполне стабильно и безглючно работала). Актуальной оказалась версия Cent OS 8.
Поставил. Сделал базовые настройки типа имени хоста, fail2ban (просто привычка, хотя хост пока только в локалке). Поставил Docker. Останавливаться на этом не буду. Пример инструкции.

Пора запускать ioBroker. Но какую выбрать сеть? Host или Macvlan?
Сначала хотел Macvlan, чтобы каждый контейнер получил свой IP адрес от роутера. Но потом решил отказаться от этой затеи:

  • При Host, конечно, нужно явно указывать, какие порты пробрасывать, следить, чтобы не пересекались с другими контейнерами. Но
  • iptables придется настраивать внутри каждого контейнера. В том числе после каждого перезапуска с другими параметрами, апгрейда/замены. А в режиме Host «единая точка управления безопасностью».
  • Я все-таки планирую делать его доступным не только из домашнего WiFi. И в таком случае удобнее сделать доступным извне один хост (и а-ля port-mapping на нем), чем настраивать это для нескольких.


В настоящий момент проверил ZeroTier One. Установил его только на хост. Обращение к IP адресу этого хоста (не локальному, а выданному ZeroTier) и порту 8082 с мобилки по GPRS при запущенном клиенте ZeroTier прекрасно открывает интерфейс vis).

Так что стандартно
docker run -d --name ioBroker -p 8081:8081 -p 8082:8082 -v /opt/iobroker/:/opt/iobroker/ --device=/dev/ttyACM0 --env-file /opt/ioBroker_env.list --restart=always buanet/iobroker:latest
Упс. Что-то пошло не так.
docker logs ioBroker показывает, что на последнем шаге отсутствует подключение к внешним ресурсам. Не получается разрезолвить имя.
docker exec -it ioBroker bash показывает, что ping по IP из него прекрасно проходит, а по имени нет.

Гуглю, нахожу кучу ссылок про то, как докер неправильно подставляет DNS сервер, Правлю /etc/docker/daemon.json, разбираюсь с dnsmasq — ничего не помогает.
Закрадывается мысль, вдруг что-то блокируется на сетевом уровне. Но ни telnet, ни curl в контейнере нет, проверить не могу. Установить тоже не просто — yum install не работает же. Можно, конечно, вручную указать нужные хосты в /etc/hosts, но это слишком трудоемко, лучше проверю другие версии.

Например, тупо стопаю firewalld на хосте в надежде, что все откроется. Но нет.
Вспоминаю, что доступность порта еще можно проверить с помощью wget. И он есть в контейнере! И не может ничего скачать даже по IP. Даже к web интерфейсу домашнего роутера не может подключиться. Ну, значит точно проблема не в DNS, а в iptables.

В итоге все заработало после добавления интерфейса докера в доверенную зону:
sudo firewall-cmd --permanent --zone=trusted --change-interface=docker0
sudo firewall-cmd --reload

Вот даже интересно, это я где-то проглядел в инструкциях?
Или взял бы не CentOS 8, а что-то другое, проблемы бы не было (в других OS не фаерволится по умолчанию)?
Или это настолько для всех очевидно, что в инструкциях не пишут, один я долго тупил?

Zigbee


Итак, мой ioBroker в контейнере, и у него опубликовано только несколько портов. Сейчас это админка 8081 и vis 8082, потом добавится mqtt 1883 и, возможно, что-то для поддержки Tuya (видел такой драйвер, но пока не разбирался).
Увы, для взаимодействия с устройствами Xiaomi через его шлюз нужны мультикасты, а с этим в такой конфигурации сложности. Поэтому решил прокинуть в контейнер USB stick. Тоже обычная операция.
В командной строке вы уже видели --device=/dev/ttyACM0 именно для этого. Устройство в контейнере появилось. В ioBroker активировал штатный драйвер «Zigbee для Xiaomi и других устройств», но он не заработал.

Гугль подсказывает, что для доступа к serial порту нужно добавить пользователя в группу dialout. Захожу в контейнер, добавляю iobroker в эту группу — не помогает.
Вижу подсказки, что надо установить пакет serialport через npm.
Не могу, нет прав. Гуглю дальше.
Bluefox собственной персоной кому-то подсказывает, что это нужно делать из директории /opt/iobroker/node_modules/iobroker.javascript/ — у меня нет такой, да и все равно нет прав на установку (ну т.е. установка начинается, а потом аварийно завершается).
Наконец, до меня доходит, что надо в командной строке указать явно, в какую директрию ставить.
npm install -g serialport --production --save --prefix "/opt/iobroker"
Устанавливается, но не помогает.

Начинаю подозревать, что все-таки надо разбираться с правами доступа. Проверяю (изнутри контейнера, конечно):
test -w /dev/ttyACM0 && echo success || echo failure
Success. Т.е. все-таки Докер правильно устройство прокинул.
sudo -H -u iobroker test -w /dev/ttyACM0 && echo success || echo failure
Опаньки! Failure
bash внутри контейнера запускается под рутом, А вот из под пользователя iobroker доступа к порту нет. Несмотря на предварительное добавление его в группу dialout.
ls -l /dev/ttyACM0 выдает
crw-rw----. 1 root 18 166, 0 Nov 3 18:14 /dev/ttyACM0
Ха! Что еще за 18 вместо названия группы?

На основном хосте все правильно: crw-rw----. 1 root dialout 166, 0 Nov 3 15:15 /dev/ttyACM0
Оказывается, на основном хосте в /etc/group dialout:x:18, а в контейнере dialout:x:20
Хотя я в группу с таким названием и добавил пользователя, толку никакого, номер не тот. Так что создал еще группу с идентификатором 18, и добавил пользователя уже в нее:
groupadd -g 18 serial
usermod -a -G serial iobroker

Перестартовал все для пущей уверенности. И на этом мои разборки завершились :)
Спокойно отвязал все датчики от Xiaomi gateway, привязал к ioBroker.

Вижу их, как объекты в самом ioBroker:
p90wem95eieewuuow1fuofngsdw.png

Считываются показания в vis:
k7fbx2cnulyb2zyrcjmtr4hzrtg.png

При замыкании контактов на датчике протечки данные поступают. И картинка меняется:
2lqpz9rduv_p7am-g-rny-zbdba.png

И в Node-Red сигнал приходит. Соответственно хоть email или еще что-то шлется, засылается голос или MP3 файл на колонку GH Mini:
vqmx0eq0tbyf_biq_vpbvkj6lgk.png

Кстати, при просмотре объектов меня ждал сюрприз:
nysk2aaoyr3nv_zlcf87qqhctya.png
Это я повернул Xiaomi Cube на другую грань. Зеленым показываются последние изменения.
Flip90 поменялся — это понятно. Этот сигнал и ловится для управления. Но, оказывается, есть еще flip90_from и flip90_to — с какой на какую грань он повернулся.
Получается, что от кубика по идее можно получить еще больше сигналов управления. Например, если нарисовать стрелочки на гранях (как бы по кругу), можно отслеживать не просто «поворот на 90», но и в какую сторону (от себя или к себе, влево или вправо).

Для flip180 так же работает. И для других жестов есть подобная дополнительная информация (Top side on flip 180°, Top side on slide, Top side on tap)

Не то, чтобы было позарез нужно. Но в стандартном Mi Home информации о гранях не было. Вроде и при прежнем подключении через Xiaomi Gateway тоже не видел, не знал, что у каждой грани есть номер. Раньше только про дополнительное действие fall (свободное падение) знал, которое было, но из Mi Home исключили (видимо, слишком часто роняли).

Финал


Все, что мне нужно, работает. Дальше можно наводить красоту, писать скрипты, подключать Tuya, запускать контейнер с Blynk для других проектов…
И, возможно, что-то переделывать с учетом ваших комментариев :)

© Habrahabr.ru