Я расскажу историю об одной из систем, которые я не только размещаю дома, но и написал сам – умный дом. Расскажу как и почему я наколхозил себе эту систему, что из этого получилось, почему Not Invented Here – это не диагноз, а самое главное - какие выводы я сделал из этого проекта.
Первичная мотивация
Что двигало мной, когда я начинал проект? Та же, что и с селфхостингом в целом – мне хотелось большего контроля над техникой и автономной работы без интернета. Ну серьёзно, это странно - управлять бойлером через мобильное приложение, которое подключается к Wi-Fi, лезет в интернет на сервера производителя, меняет там значение, а потом бойлер получает это новое значение с сервера производителя, подключаясь к тому же самому Wi-Fi. Обанкротится компания - получу кирпич вместо устройства. Неприятный риск!
Выбор устройств
Сегодня я использую девять "умных" устройств:
- 3 рекуператора Vakio Smart Base. Умеют гонять воздух взад-вперёд на улицу и обратно.
- 2 тёплых пола, которые управляются термостатами Lytko.
- Бойлер Electrolux, с которым провозился полтора года – формально он не поддерживает локальный MQTT, его пришлось "взломать".
- 2 светильника Yeelink, которые про MQTT не знают, управляются через TCP-пакеты, обратной связи нет, полагаюсь только на вычисленное состояние, но всё равно вписались в архитектуру.
- Термометр, который собирает статистику по температуре в спальной. Скоро его заменит Vakio Atmosphere 2.0, дорогущий, зараза, почти 12 000 рублей. Прямо пока писал эту статью в палате роддома, смотрел на новорождённого сына и понял, что микроклимат в доме для него важен настолько, что цель оправдывает средства.
Выбор протокола связи
Устройства работают через Wi-Fi. На этапе проектирования я много читал о нестабильности Wi-Fi соединений, постоянных "отвалах". У меня на квартиру 3 роутера, роуминга нет, каждое устройство подключено к ближайшему к нему роутеру. Отвалов нет, перезагрузок нет. В самом начале не учёл, на стороне коммутатора не включил STP, мучился от потерь при переключении устройств между роутерами. После включения все проблемы исчезли.
ZigBee можно было бы пощупать, но я в нём совершенно не разбираюсь, а заехать в новую квартиру хотелось как можно скорее – устройства нужно было выбрать и учитывать в дизайн-проекте. Я не отбросил ZigBee окончательно – заглядываюсь на датчики влажности для почвы, причём нужно ≈20 штук – сперва обкатаю в квартире на цветах, потом использую на даче, для газона. Увы, описания устройств на маркетплейсах совершенно не объясняют как они работают. Вероятно, в какой-то момент я просто научусь паять и колхозить всякое из ESP32, вместо того, чтобы ждать, пока нужная мне вещь появится на рынке.
Реализованные сценарии
Мой умный дом умеет:
- Не нагревать бойлер до максимума в 75ºC, после принятия душа вечером. Вместо этого бойлер переходит в эконом режим нагрева до 50ºC на ночь, а к 9 утра возвращается к максимуму. Ни я, ни жена всё равно не полезем в душ, а 50ºC хватает на мытьё рук.
- Включать вентиляцию в рабочем кабинете перед душными рабочими созвонами, интеграции рабочим календарём нет, мне достаточно критерия "будни" + "конкретное время"). Рекуператор проветривает в спальной перед сном и, увы, выключается на ночь, потому что Vakio Base Smart всё равно слышно даже на минимальной скорости и это мешает спать.
- Включать вентиляцию посильнее, на вторую скорость из семи, когда наступает время робота-пылесоса кататься по квартире. Всё равно шумно, а так - чуть больше свежего воздуха.
- Показывать в веб-интерфейсе три кнопки "Провентилировать на максимуме в течение 5 минут" для каждого помещения.
- Экономить на нагреве тёплого пола, пока нас нет дома, корректировка определяется по числу активных пользовательских устройств.
- Вечером напоминать идти спать, выключая подвесной светильник над рабочим местом. Он сам включается, когда на улице становится темно, без датчика освещения, хватает расписания.
Следующее что хочу добавить – усиление вентиляции при повышении уровня CO2 в комнате. Ещё бы увлажнитель с MQTT найти и вопрос микроклимата будет закрыт.
Почему же я не взял готовое
- Home Assistant крутой, но это слишком большая штука, чтобы я досконально понимал, что в нём происходит и насколько это безопасно.
- Мне было интересно поразбираться в устройстве протокола MQTT, а не надстроек над ним.
- Вдобавок было интересно, насколько простым я смогу сделать умный дом.
- Я люблю DIY и колхозить, а потом опенсорсить.
Компоненты архитектуры
Окей, готовое не взял, но и в полный абсолютизм с переизобретением всего я не впадал. Я хотя бы использую готовый MQTT-брокер и готовую библиотеку для работы с ним.
- MQTT-брокер. Эдакая Enterprise Service Bus, только поменьше и для дома. Я использую Mosquitto [1], он написан на C, компактный, нересурсоёмкий, был в репозиториях ОС. В качестве альтернативы был EMQX, но он показался слишком уж большим, сложным и отказоустойчивым.
- Движок правил и расписаний. Прикрутил Pydantic-модели к YAML-конфигу, в котором хранятся правила для всех устройств, написал ≈100 строчек на Python, чтобы интерпретировать правила на уровне "к какому устройству относится", "действует ли сейчас". Раз в минуту сервер отсматривает текущее состояние системы и при срабатывании правила меняет состояние устройства.
- Подписка на топики в MQTT-брокере и создание блокировок. Временные ручные настройки, которые вносятся нажатием на физическую кнопку на самом устройстве или через веб-интерфейс имеют приоритет над расписанием. Обработчик расписания игнорирует топики, которые заблокированы временными правилами. Структура блокировок повторяет структуру состояния устройства и сопоставляет их друг с другом. Для реализации я использовал aiomqtt [2].
- Сенсоры. Раньше был сбор информации о внешнем мире - чтобы если на улице слишком холодно, автоматически включать зимний режим на рекуператорах. Увы, отвалилось и перешло на ручное управление. Сейчас я собираю информацию о числе активных пользовательских устройств, через DHCP-leases, с роутера и использую её для коррекции температуры тёплого пола. Пользователи живут в отдельном инстансе DHCP-сервера, со своим IP-пулом, поэтому отделить их от IoT-устройств легко.
- Веб-интерфейс. Я взял aiohttp и частично реализовал jsonrpc-сервер. На фронтенде использовал готовую реализацию jsonrpc-клиента и немного кода на ванильном JS, который собираю вебпаком. Сервер также отдаёт текущее состояние, которое отправляется в домашний мониторинг, которому недавно исполнилось два года). Я использую grafana + influxdb на отдельной машине, метрики тянутся с неё по крону.
- Сеть из 4 Mikrotik с DHCP, VLAN, Firewall и так далее. Где это возможно у устройств ограничил доступ к Интернету, локальной сети и остальным IoT устройствам, за исключением MQTT-брокера.
Всё это написал на Python, работает в одном процессе, единственном экземпляре, в котором есть несколько asyncio-задач. У задач общие память и состояние, никакого IPC, можно считать что MQTT-брокер - единственное место, в котором сохраняется какое-то состояние. Высокой нагрузки дома нет, требований к масштабированию тоже, поэтому такая архитектура достаточна. Проблем с гонками я пока не наблюдал и целенаправленно от них не защищался, они могут быть, но их критичность пока не обдумана.
Мониторинг и статистика
Я упоминал о мониторинге. Алертинга нет никакого. На этапах разработки и отладки - выручал, как дополнение к mqtt-explorer [3], собирая статистику для ретроспективного анализа, правильно ли всё работает. Сейчас я в него заглядываю сильно реже. Что я собираю:
- Текущую и целевую температуры тёплого пола.
- Текущую и целевую температуры + режим работы бойлера.
- Скорость работы рекуператоров.
- Текущую температуру в спальной.
- Число активных устройств с разбивкой по категориям.
Проблемы проекта
После версии 0.4.0 проект [4] погряз в хардкоде и я не могу опубликовать доработки. Я прибил веб-интерфейс гвоздями к своим устройствам. Это привело к противоречию, которое я ещё не устранил:
- Если расширять API и сделать интерфейс динамичным - интерфейс станет неудобным.
- Чтобы сделать интерфейс и динамичным и удобным - надо расширять возможности конфигурирования.
- Если расширю возможности конфигурирования - система станет сложной, а этого я не хочу.
Из-за бойлера пришлось понизить версию MQTT. Новый Mosquitto ругается на SSL, старый – кормит бойлер самоподписным сертификатом с тем же CN и не жалуется, но не поддерживает MQTT v5.
Нет системы плагинов. Было бы здорово подключать новые виды устройств модулями. Всего-то надо унаследоваться от базового класса BaseDevice, дописать правильный конфиг, но это ж надо будет тащить в проект pluggy, разбираться как им пользоваться. Эта система никому, кроме меня, не нужна, пока она поддерживает только 3-5 видов устройств.
Потребление памяти распухло - уже 50мб, а поначалу получалось ≈25мб. Уже не тянет на микро-софт (ой), но переписывать ради 25мб на язык вроде Go или Rust тоже нет смысла.
Я не решил вопрос дистрибуции и обновления. Я обновляю систему скриптом, который подпуливает dev-ветку с домашнего git-хостинга и перезапускает systemd-сервис, для публичного проекта это непривлекательно.
Выводы
А всё равно это было весело. Ну и хрен с ним, что оно не стало успешным open-source проектом, без звёздочек на Github тоже можно жить. Я делал это для себя, мне оно нравится, а самое главное – выполняет свою функцию. Не бойтесь колхозить, если вам это доставляет удовольствие. Нарушайте устоявшиеся архитектурные практики, не делайте идеально. Нет ничего страшного, в том, что вы не достигнете гигантских масштабов – на 10000 устройств и 1000000 топиков на одном экземпляре, если вам это не надо.
Ссылки
- Mosquitto - компактный MQTT-брокер.
- AioMQTT - python-библиотека для асинхронной работы с MQTT
- MQTT-Explorer – незаменимая в изучении устройств и отладки взаимодействия с ними программа.
- MQTT-Automator – последняя не поросшая хардкодом версия моего умного дома.