
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
