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

За время работы с Home Assistant я уже несколько раз сталкивался с необходимостью доработок. В очередной раз такая необходимость возникла при покупке кондиционеров Hitachi. Сейчас все современный модели оснащены WiFi, плюсом производитель не забыл о старых моделях выпустив для них отдельный модуль который подключается к разъему для внешних термостатов.

WiFi Адаптер для кондиционеров Hitachi
WiFi Адаптер для кондиционеров Hitachi

Основной минус, что всё это работает только с проприетарным приложением, ни о каком HomeKit или других экосистемах даже и речь нет.

Когда нужна кастомная интеграция?

  1. Устройство не поддерживается — например, новые модели или DIY-проекты.

  2. Проприетарные системы — как в случае с кондиционерами из моего опыта, где общение с устройством возможно только через закрытое API.

  3. Специфические требования — необходимость расширения функционала стандартных компонентов.

В этой статье я опущу подробности реверс инжиниринга протокола, там оказалось всё достаточно просто, доступа к самом устройству я получить не смог из-за HTTPS, а вот декомпиляция приложения дала возможность воспользоваться REST API облака производителя в полном объеме.

В итоге что мы имеем, REST API для отправки команд и функцию обновления статуса устройств через web socket, давайте превратим это в рабочую интеграцию.

Подготовка окружения

Необходимые инструменты

  1. Home Assistant Core — можно экспериментировать на готовой инсталляции либо поднять тестовую копию

  2. Python 3.10+ — HA написан на Python, и все интеграции также разрабатываются на нем, нужно конечно же базовое знание языка, но никакого rocket science, я 15 лет программирую только на Java но пары часов мануалов по Python мне хватило, чтобы начать писать интеграции под HA

  3. 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

Официальная документация HA