
Home Assistant (HA) — это универсальная платформа с открытым исходным кодом, которая превращает ваш умный дом в единую экосистему. Её главная сила — в способности объединять устройства разных протоколов (Zigbee, Wi-Fi, Bluetooth) и производителей в единую экосистему. Но что делать, если ваше устройство не поддерживается "из коробки"? Ответ прост: создайте свою интеграцию! В этой статье я покажу, как добавить поддержку кондиционеров Hitachi через облачное API вендора.
За время работы с Home Assistant я уже несколько раз сталкивался с необходимостью доработок. В очередной раз такая необходимость возникла при покупке кондиционеров Hitachi. Сейчас все современный модели оснащены WiFi, плюсом производитель не забыл о старых моделях выпустив для них отдельный модуль который подключается к разъему для внешних термостатов.

Основной минус, что всё это работает только с проприетарным приложением, ни о каком HomeKit или других экосистемах даже и речь нет.
Когда нужна кастомная интеграция?
Устройство не поддерживается — например, новые модели или DIY-проекты.
Проприетарные системы — как в случае с кондиционерами из моего опыта, где общение с устройством возможно только через закрытое API.
Специфические требования — необходимость расширения функционала стандартных компонентов.
В этой статье я опущу подробности реверс инжиниринга протокола, там оказалось всё достаточно просто, доступа к самом устройству я получить не смог из-за HTTPS, а вот декомпиляция приложения дала возможность воспользоваться REST API облака производителя в полном объеме.
В итоге что мы имеем, REST API для отправки команд и функцию обновления статуса устройств через web socket, давайте превратим это в рабочую интеграцию.
Подготовка окружения
Необходимые инструменты
Home Assistant Core — можно экспериментировать на готовой инсталляции либо поднять тестовую копию
Python 3.10+ — HA написан на Python, и все интеграции также разрабатываются на нем, нужно конечно же базовое знание языка, но никакого rocket science, я 15 лет программирую т��лько на Java но пары часов мануалов по Python мне хватило, чтобы начать писать интеграции под HA
VS Code или PyCharm — для редактирования кода
Структура проекта
Структура
Каждая интеграция представляет собой отдельный Python пакет, находящийся в папке custom_compomets. К примеру если наша интеграция будет называться air_cloud то относительно корня корня HA путь будет выглядеть /custom_compomets/air_cloud.
Что же внутри пакета:
custom_components/ air_cloud/ __init__.py # Инициализация интеграции config_flow.py # UI конфигурации climate.py # Реализация климатического устройства manifest.json # Метаданные интеграции api.py # Обёртка для API вендора consts.py # Константы translations/ en.json # Локализация на английский ru.json # Локализация на русский
Создание manifest.json
Файл manifest.json — основной источник информации об интеграции. Пример:
{ "domain": "air_cloud", "name": "AirCloud", "version": "2.0.0", "iot_class": "cloud_push", "config_flow": true, "documentation": "https://github.com/your_repo/docs", "requirements": ["aiohttp>=3.9.3", "websockets>=10.4"], "codeowners": ["@your_username"] }
В данном файле описывается название, версия, зависимости библиотек, и прочая служебная информация, из важных моментов, поле domain это фактически уникальный идентификатор вашей интеграции.
Реализация инициализации (__init__.py)
Интеграция должна зарегистрировать себя в HA. В __init__.py для этого используются функция async_setup_entry. Функция исполняется при каждом запуске HA, где происходит загрузки настроек и устройств, для нашего примера реализация будет следующей
Добавление конфигурационного потока (config_flow.py)
Интеграция может быть сконфигурирована через файл configuration.yaml, но это неудобно и с недавнего времени не рекомендовано сообществом HA. Для настройки через UI описывается метод async_step_user в config_flow
async def async_step_user(self, user_input=None): if user_input is not None: login = user_input[CONF_EMAIL] password = user_input[CONF_PASSWORD] temp_adjust = user_input.get(CONF_TEMP_ADJUST) if await AirCloudApi(login, password).validate_credentials(): return self.async_create_entry(title=login, data=user_input) return self.async_show_form( step_id="user", data_schema=self.user_schema, errors={"base": "invalid_credentials"} ) return self.async_show_form( step_id="user", data_schema=self.user_schema )
Конфигурация устройств (climate.py)
HA работает с устройствами посредством реализации заранее заготовленный в функция в платформах switch(выключаль), light(свет), sensor(датчик) и тп. В нашем случае будет (climate) климат.
Нам необходимо сделать реализацию класса ClimateEntity описав методы взаимодействия с устройством fan_mode, turn_on, turn_off, set_temperature и тп, пример кода:
async def async_turn_on(self): self._power = "ON" await self.__execute_command() async def async_turn_off(self): self._power = "OFF" await self.__execute_command() async def async_set_fan_mode(self, fan_mode): self._update_lock = True if fan_mode == FAN_AUTO: self._fan_speed = "AUTO" elif fan_mode == FAN_LOW: self._fan_speed = "LV1" elif fan_mode == FAN_MIDDLE: self._fan_speed = "LV2" elif fan_mode == FAN_MEDIUM: self._fan_speed = "LV3" elif fan_mode == FAN_HIGH: self._fan_speed = "LV4" else: self._fan_speed = "AUTO" await self.__execute_command()
Все их тут перечислять не буду, в конце статьи будет ссылка на github, где можно будет посмотреть на код детальнее. Но смысл следующий, каждый мет��д отвечает за вызов соответствующей функции из HA, после чего происходит интерпретация полученного значения и вызов API вендора.
После описываем загрузку устройств из API создавая экземпляр класса для каждого в функции async_setup_entry
async def async_setup_entry(hass, config_entry, async_add_devices): api = hass.data[DOMAIN][API] temp_adjust = hass.data[DOMAIN][CONF_TEMP_ADJUST] entities = [] family_ids = await api.load_family_ids() for family_id in family_ids: family_devices = await api.load_climate_data(family_id) for device in family_devices: entities.append(AirCloudClimateEntity(api, device, temp_adjust, family_id)) if entities: async_add_devices(entities)
Локализация интерфейса
Для каждого языка заводится отдельный файл в формате json содержащий строки и их id.
Пример файла translations/en.json:
{ "config": { "step": { "user": { "title": "Authentication", "data": { "email": "Email", "password": "Password" } } }, "error": { "invalid_auth": "Invalid credentials" } } }
Логотип
Шаги для отображения лого интеграции, выглядят не совсем логично, но почему-то сообщество HA задумало именно так. Необходимо выполнить PR в репозиторий https://github.com/home-assistant/brands по описанным в readme правилам.
Обновление и публикация
Cамый сложный способ публикации это Pull Request непосредственно в HA, но для этого интеграция должна соответствовать достаточно строгим правилам:
Соответствие PEP8
100% покрытие mypy
Асинхронная реализация методов
Подробная документация
Основной минус обновление возможно только в формате релизов самого HA.
Вариант намного проще установка через Home Assistant Community Store (HACS), для этого лишь необходимо размести итерацию на Git Hub и добавить hacs.json в корень репозитория, пример:
{ "name": "AirCloud", "domains": ["climate"], "homeassistant": "2023.8.0" }
Таким образом через механизм релизов Git Hub вы получите возможность самостоятельно управлять релизами своей интеграции.
Заключение
Создание кастомной интеграции для Home Assistant — это не только способ добавить поддержку "нестандартных" устройств, но и отличная возможность углубиться в архитектуру умного дома. Даже если ваш код далек от идеала, не стесняйтесь делиться им с сообществом — многие пользователи будут благодарны за готовое решение, а совместными усилиями можно довести интеграцию до совершенства.
Надеюсь, эта статья поможет вам лучше понять процесс создания кастомных интеграций для Home Assistant и вдохновит вас на новые проекты!
Ссылки
Полный исходный код интеграции рассмотренной в статье можно найти на моём Github
