Pull to refresh

Первое знакомство с Home Assistant

Reading time14 min
Views222K

Home Assistant – популярное приложение с открытым исходным кодом для организации умного дома. Первый опыт автора в работе с Home Assistant основывается на попытке интеграции в него ‘умной рисоварки‘. Автор постарается описать основные компоненты и возможности данного приложения, с которыми ему привелось пошагово познакомиться. Статья является в чем-то обзором, в чем-то руководством для желающих начать свое знакомство с Home Assistant.

Тем, у кого мало свободного времени, советую пропустить присказку – первую главу – и перейти сразу ко второй. Вам нужно знать только, что работать мы будем с умной китайской рисоваркой от Xiaomi.

Умная рисоварка

Рисоварка, очевидно, — это устройство для приготовления риса. Вики демонстрирует нам керамические рисовые пароварки из Британского музея, датирующиеся 1250 г. до н.э. В 1945 году корпорация Mitsubishi стала первой в Японии компанией, производящей домашнюю электрическую рисоварку. Наша модель — Rice Cooker от Xiaomi – может готовить не только рис. “Это великолепное устройство для приготовления не только риса, но других типов блюд. Оно может готовить и супы, и пирожные, и многое другое” — говорится в рекламе. Но самое главное — это наличие wi-fi модуля, ПО с возможностями автоматизации и 200+ программно установленных рецептов. “Путь к умному дому через желудок – это правильно”, подумал автор, и решился.

Xiaomi Rice Cooker, как и подобает цифровому устройству, внешне очень привлекательна, радует округлостью форм и общим минимализмом. Для её настройки и использования производитель предлагает приложение Mi Home. После регистрации Mi account, программа легко отыскивает новое устройство, и вы регистрируете его в вашей локальной сети. Интерфейс приложения не самый плохой, предоставляет базовые средства для автоматизации, может принимать уведомления от устройств. Однако, есть существенные недостатки. Не всех может порадовать отправление информации разработчику о каждом клике пользователя. И неприятное выражение находит часто упоминаемый нынче национальный калорит. Вместо 200+ рецептов на иностранные языки переведено и доступно всего лишь четыре. Остальное – исключительно для китайского народа. Когда ваша ‘умная’ рисоварка не способна выполнять все обещаные кулинарные обязанности, тут, согласитесь, становится грустно. Побродя некоторое время по интернетам, погрустневший автор наткнулся на следующий интересный проект (вечных благ автору). Который оказался посвящен разработке модуля для некоего Home Assistant.

Home Assistant

Сперва, немного общей информации. Как нам говорят на домашней странице HA, ”Это ПО с открытым кодом для автоматизации умного дома, ориентирующееся на локальное управление и конфиденциальность. Развиваемый трудом открытого сообщества энтузиастов, он отлично подходит для работы на Raspberry Pi или локальном сервере.” Проекту более пяти лет, он использует python и лицензию Apache 2.0. Версия релиза на момент написания этих строк – 0.99.3.

Для управления устройствами HA использует отдельные модули (integrations, или components). Создать такой довольно просто. На сайте можно найти каталог основных (одобренных и поддерживаемых сообществом) модулей. Среди общего их количества (1485 штук) попадаются совершенно разнообразные, в каталоге значятся имена amazon, google, xiaomi, и даже один раз yandex.
Попробуем установить HA в виртуальное окружение на линукс десктопе. Нам понадобится python3 и менеджер пакетов pip.
python3 -m venv homeassistant # Создаем виртуальное окружение
cd homeassistant
source bin/activate # Активируем виртуальное окружение
python3 -m pip install homeassistant # Устанавливаем Home Assistant
hass --open-ui # Запускаем Home Assistant

После этого на http://localhost:8123 станет доступнен графичекий интерфейс HA. При первом входе потребуется создать аккаунт пользователя. Веб-интерфейс HA довольно объемен. Пара важных элементов, о которых стоит упомянуть в самом начале, это закладка Configuration → General, где вы легко можете перезагрузить файлы конфигурации или сам сервер. А также страница Info в списке Developers tools, где можно посмотреть логи ошибок.

Все необходимые пользователю данные HA хранит, в случае линукс, в папке настроек “~/.homeassistant”. Файлы настройки записаны в формате YAML, и основной из них – это “configuration.yaml”. Он объединяет данные модулей, автоматизаций, etc. Возможность импорта позволяет разбить настройки на отдельные логически организованные файлы. Модули же хранятся в подпапках “components” (встроенные) и “custom_components”.

Этих знаний для установки нового модуля нам должно быть достаточно. Копируем с репозитория папку “xiaomi_cooker” в нашу “~/.homeassistant/custom_components”. Согласно описанию, добавляем настройки модуля в файл “configuration.yaml”:
configuration.yaml
xiaomi_cooker: # Имя модуля
    name: 'Akari' # Имя устройства
    host: 192.168.1.10 # IP адрес устройства
    token: '4921def609273302248d040a24243a25' # Замуты протокола Xiaomi
    model: chunmi.cooker.normal2 # Модель устройства


Готово. После перезагрузки HA в разделе General → Integrations веб-интерфейса появится запись о новом модуле.

Любой модуль представляет собой некоторый набор объектов (entities) и сервисов (services, по сути — функции). Объекты хранят различные принимаемые от устройств данные. Например, sensor.xiaomi_cooker_temperature – температуру рисоварки, sun.sun – положение солнца. Данные объекта выражаются одним основным значением — статусом (state), и произвольным набором дополнительных аттрибутов (attributes). Сервисы используются для передачи команд и значений устройствам. Например, xiaomi_cooker.start – команда начала работы рисоварки, или homeassistant.check_config – инициализация поиска ошибок в файлах настроек HA. В списке Developer Tools веб-интерфейса находится раздел Services, где можно просмотреть доступный вам список сервисов и поиграться с их вызовами. Рядом есть раздел States, где, соответственно, можно просмотреть и поизменять значения объектов. Нужно заметить, что изменения значений объектов в разделе States имеют односторонний характер. Т.е. если, например, поменять здесь состояние объекта lights.state с off на on, на истинном состоянии устройства это не отразится, и при следующем же обновлении данных от устройства значение объекта будет перезаписано в реальное.

Automation

Основным инструментом управления умного дома являются автоматизации (automation). Добавлять и редактировать их можно с помощью графического интерфеса в разделе General → Automation или непосредственно в файле “automations.yaml”. Основная функция автоматизаций – вызов сервисов при достижении тех или иных условий. Базовые инструменты задания условий вызова значения объектов, в т.ч. данные о времени. Есть и чуть более специфические решения, как, например, события (events). В качестве простого примера автоматизации приведем код, выполняющий обновление данных о плюсанутости автора с периодом в 15 минут:
automations.yaml
 - id: '1565889970818' # ID автоматизации
   alias: Umpiro decharging # Имя
   trigger: # Условия срабатывания
   - platform: time_pattern # Тип условия - временной
     minutes: /15 # Задание условия (в нашем случае – условие выполняется каждые 15 минут)
   condition: [] # Дополнительные условия срабатывания
   action: # Выполняемые автоматизацией действия
   - data: # Аргументы, передаваемые при вызове сервиса
       entity_id: sensor.umpiro_charge # Аргумент (ключ: значение)
     service: homeassistant.update_entity # Имя вызываемого сервиса


Нужно заметить, что пока еще не все доступные автоматизации (например, приведенную выше) можно сконфигурировать без редактирования yaml-кода, через графический интерфейс, но разработчики говорят об активной работе над устранением этого недостатка.

Templating

После автоматизаций будет самое время рассказать о шаблонах (templating). Различные элементы настроек в yaml-файлах позволяют использовать вставки на скриптовом языке jinja2. Многие из этих элементов объединены общим названием ‘Templates’, как то service_template или trigger_template. Используя шаблоны, мы получаем доступ к значениям объектов в HA и возможность использовать эти данные в комплексных математических и логических выражениях, что значительно расширяет наш потенциал. В качестве примера, приведем записанный в “configuration.yaml” чуть подусложненный код упомянутого ранее sensor.umpiro_charge. Это template_sensor, i.e. “сенсор, формирующий данные на основании значений других объектов”. Наш сенсор будет представлять собой некоторый аналог постепенно разряжающейся батарейки:
configuration.yaml
sensor: # Название модуля
    platform: template # Тип модуля - шаблон
    sensors: # Список объектов
        # Название объекта – sensor.umpiro_charge
        umpiro_charge:
            # Задает форму отображения объекта в UI (опционально)
            unit_of_measurement: '%'
            # Шаблон, задающий значение объекта
            value_template: >-
                # Получение значения объекта – конвертация фильтром ‘int’
                # + проверка на соответствие типа
                {% if states('sensor.umpiro_charge')|int('unknown') is number %}
                    # запись в переменную нового значения объекта
                    # на основе текущего значения и времени последнего обновления
                    {% set value = (states('sensor.umpiro_charge')|int - (as_timestamp(now()) - as_timestamp(states.sensor.umpiro_charge.last_updated))/60/15)|round %}
                    # Тонкая работа фильтрами для ограничения плюсанутости
                    {{ [[0, value]|max, 100]|min }}
                {% else %}
                    # Значение, используемое HA по умолчанию
                    # для не определенных состояний объектов
                    {{ 'unknown' }}
                {% endif %}
            # ID стороннего объекта, при изменение значения которого
            # будет обновляться значение данного объекта
            entity_id: []
            friendly_name: 'Charge' # Имя объекта
            # Задает форму отображения объекта в UI (опционально)
            device_class: battery


entity_id мы оставили пустым, поскольку уже добавили автоматизацию, которая будет самостоятельно вызывать обновление данных объекта.

Python Scripts

В свою очередь, для создания новых сервисов простым инструментом являются пайтон-скрипты (python scripts). После добавления в “configuration.yaml” строчки: “python script:”, все файлы с расширением “.py”, которые мы поместим в папку “~/.homeassistant/python_scripts”, станут доступны в качестве сервисов с именами “python_scripts.<file_name>”. Их код выполняется в заранее заданном окружении, где переменные data и hass дают нам доступ к аргументам вызова сервиса, а также объектам и сервисам HA. В качестве примера приведем код файла “charge_set.py” для сервиса “python_scripts.charge_set”. Его функцией будет установка заряда нашей батарейки:
python_scripts/charge_set.py
# Получение первого аргумента вызова 
name = data.get('name', 'sensor.umpiro_charge')
# Получение второго аргумента вызова
new_state = data.get('charge', '100')
# Получение данных объекта
attributes = hass.states.get(name).attributes
# Изменение данных объекта
hass.states.set(name, new_state, attributes)



Creating integration

Все что мы сделали с помощью шаблонов и пайтон-скриптов, возможно, было бы проще осуществить написанием отдельного модуля. Как уже говорилось, неофициальные модули хранятся в папке “custom_components”. В будущем нам понадобится объект, хранящий информацию о текущем рецепте для нашей рисоварки, и сервис, позволяющий изменять эти данные. На основе примера из документации создадим для этого новый модуль, “overmind”. Первый шаг – это файл custom_components/overmind/__init__.py:
custom_components/overmind/__init__.py
# Домен для регистрации сервиса
DOMAIN = 'overmind'
# Значение по умолчанию для нашего объекта (рецепта)
DEFAULT_RECIPE = {
"title": "Habr post",
"description": "Post an article on habr.com",
 "profile": "471822"
}

# Функция настройки модуля
def setup(hass, config):
    # Установка значений объекта
    hass.states.set('overmind.current_recipe', 'on', DEFAULT_RECIPE)

    # Функция с описанием сервиса установки рецепта
    def handle_set(call):
    # Получение значения аргумента
    recipe = call.data.get(‘recipe’, DEFAULT_RECIPE)
    # Выполнение задачи - изменение значения объекта
    hass.states.set('overmind.current_recipe', 'set', recipe)

# Регистрация сервиса    
hass.services.register(DOMAIN, 'recipe_set', handle_set)
return True


После этого сообщим о новом модуле файлу настроек “configuration.yaml”, добавив в него строчку с названием модуля: “overmind:”. Задача решена.

Lovelace UI

Так называется используемый HA фронтенд. Этот графический интерфейс, через который обычному пользователю предлагается управлять умным домом, является заглавной страницей веб-интерфейса HA. Интерфейс LUI формируется из карточек (сards) разнообразых типов, которые могут отражать значения объектов, служить для вызова функций и прочих задач. Карточки можно распределять по страницам (view), по аналогии с браузерными закладками. Настройка удобно организована через тот же графический интерфейс, но доступна и посредством yaml-кода, для чего там же присутствует встроенный текстовый редактор. Рекомендую заглянуть на страницу https://demo.home-assistant.io/, где приведено несколько различных примеров настройки LUI, и где их легко можно посмотреть, пощелкать и поизменять.


Пример настройки графического интерфейса

Говоря о недостатках интерфейса, к сожалению, разработчики сами признаются, что проект пытается усидеть одновременно на стульях десктопа и смартфона. LUI, по умолчанию, любит самостоятельно определять расположение и размеры карточек, что иногда может превращать нормально выглядящую на мониторе страницу в полную кашу на экране смартфона, и наоборот. Присутствуют некоторые простые инструменты для упорядочения интерфейса, но и они, по моему опыту, не всегда эффективны.

Думаю, не имеет большого смысла описывать создание интерфейса посредством графических инструментов, поэтому я приведу несколько примеров в виде использованного мной yaml-кода. Создав для нашей рисоварки отдельную страницу (view), мы постараемся заполнить её самыми необходимыми элементами так, чтобы это не вызывало отторжения при пользовании с экрана смартфона.

Тут же опробуем те самые простые инструменты упорядочения интерфеса, это – horizontal-stack и vertical-stack. Сперва, создадим vertical-stack из карточек типов entity-button и sensor. Первая будет служить для запуска нашей рисоварки, вторая – для отображения значения температуры:
vertical-stack
cards: # Список карточек стека
  - type: entity-button # Тип первой карточки в стеке
      entity: sensor.xiaomi_cooker_state # Объект, связанный с данной карточкой
      icon: 'mdi:selection' # Иконка для данной кнопки
      icon_height: 100px # Размер иконки
      name: Offline # Имя под кнопкой
      show_icon: true #
      show_name: true #
      tap_action: # Действие при нажатии
      action: call-service # Действие – вызов сервиса
      service: script.turn_on # Сервис – вызов скрипта
      service_data: # Аргументы вызова сервиса
      entity_id: script.order_cooker_start # Аргумент – название скрипта
      hold_action: # Действие при удержании
      action: none #
  - type: sensor # Тип второй карточки в стеке
      entity: sensor.xiaomi_cooker_temperature # Объект, связанный с данной карточкой
      name: Temper # Имя карточки
type: vertical-stack # Тип карточки – вертикальный стек


Home Assistant включает в себя архив иконок Material Design Icons, которые, через соответствующие имена (например, mdi:selection), можно использовать в элементах настроек. Скрипт (в данном случае, не python-, а yaml-), который мы использовали для вызова сервиса, это еще один удобный инструмент HA.

Теперь объединим приведенный выше vertical-stack с карточкой портрета нашей в теперь уже horizontal-stack. Все будет так же просто:
horizontal-stack
сards: # Список карточек стека
  - type: picture # Тип карточки
    image: /local/akari_r.jpg # Адрес файла изображения
  - # Тут мы помещаем написанный выше код вертикального стека
type: horizontal-stack # Тип карточки – горизонтальный стек


Здесь нужно отметить строчку ‘image:’. Все файлы, которые мы помещаем в папку ‘~/.homeassistant/www’ становятся доступными по ссылке http://localhost/local/filename.

Следующим шагом мы немного поработаем над созданной нами кнопкой вызова сервиса. Для нас было бы удобно, если бы она работала как тумблер, т.е. на включение/выключение, а не так, как это сделано сейчас. Этого можно добиться через использование карточки типа conditional, отображение которой на экране можно регулировать через задание определенных условий. Ниже приведен код для карточки, которая является кнопкой выключения рисоварки и видна только при условии, если рисоварка находится в процессе приготовления блюда:
conditional
card: # Описание содержания карточки
     entity: sensor.xiaomi_cooker_state #
          icon: 'mdi:star-box-outline' #
          icon_height: 100px #
          name: Running #
          tap_action: #
            action: call-service #
            service: xiaomi_cooker.stop #
          type: entity-button #
      conditions: # Условия отображения
          # Объект, значение которого проверяется
          entity: sensor.xiaomi_cooker_mode
          # Значение объекта, необходимое для выполнения условия
          state: Running
type: conditional # Тип карточки


Переписав подобным образом ранее созданный код кнопки влючения, и объединив его с этим, мы получим одну кнопку, работающую одновременно на включение и выключение.

Дополним наш интерефейс еще одной карточкой — с отображением времени до окончания приготовления (аналогично карточке температуры), и еще одной – с деталями приготовляемого рецепта (custom:recipe-card). В итоге получим что-то такое:

Custom Cards

Home Assistant помимо богатства встроенного набора типов карточек, конечно же, предоставляет возможность создавать и свои. Такие карточки называются пользовательскими карточками (custom cards), и для их создания используется javascript. Здесь можно ознакомиться с двумя простыми примерами кода. В сети несложно найти готовые карточки, созданные различными энтузиастами, а если хочется поэкспериментировать самому, то полезно будет узнать, что существуют и специальные js-модули, созданные для упрощения работы над написанием новых. Мой опыт обращения с javascript продолжает желать меньшего, поэтому, в качестве примера, я приведу только небольшую часть кода карточки, используемой для выбора и отображения текущего рецепта.
www/recipe-card.js
import {
// Импорт из стороннего модуля lit-element
    LitElement,
    html,
    css
} from "https://unpkg.com/lit-element@latest/lit-element.js?module";

// Класс с описанием новой карточки
class RecipeCard extends LitElement { 
    // Получение доступа к элементам HA
    static get properties() {
        return {
            hass: {},
            config: {}
        };
    }
    // Пример вызова сервиса
    callServiceEx() {
        this.hass.callService('persistent_notification', 'create', {message: 'example'});
    }
    // Получение html-кода карточки
    render() {
        // Получение информации об объекте, связанном с данной карточкой
        const entityId = this.config.entity;
        // Получение состояния объекта
        const state = this.hass.states[entityId];
        // Получение аттрибута объекта
        const titleStr = state ? state.attributes.title : 'unavailable';
        const descrStr = state ? state.attributes.description : 'unavailable';
        // Формирование кода карточки
        return html`
            <ha-card>
                <div class="flex" style="margin-top:16px;">
                    <div class="icon">
                        <ha-icon icon="mdi:bowl"></ha-icon>
                    </div>
                    <div class="header">
                        <span class="name">Recipe</span>
                    </div>
                </div>
                <div class="flex info" style="margin-bottom:16px;">
                    <span class="recipe-title">${titleStr}</span>
                    <span class="recipe-descr">${descrStr}</span>
                </div>
            </ha-card>
        `;
 }
// Задание имени новой карточки
customElements.define('recipe-card', RecipeCard);



Для использования новой карточки нужно будет добавить в начале файла настроек LUI следующий код:
recipe-card
resources: # Задание внешних ресурсов
 - type: module # Тип ресурса – модуль
   url: /local/recipe-card.js # Расположение ресурса

и среди списка карточек:
 - type: 'custom:recipe-card' # Тип карточки
   entity: overmind.current_recipe # Объект, связанный с данной карточкой



Notifications

Необходимой частью умного дома является отправка сообщений пользователю. В HA такие сообщения называются notifications (уведомления) и существует два базовых типа уведомлений. Первый – это внутренние уведомления (persistent notifications). Для их отправки используется встроенный сервис «persistent_notification.create». Список таких сообщений доступен через иконку колокольчика в графическом интерфейсе, они используют markdown разметку и по сути довольно просты.

Другим, более интересным, инструментом является встроенный модуль notify, который через установку дополнительных модулей позволяет передавать сообщения, используя сторонние платформы. В качестве примера рассмотрим модуль для telegram.

Для использования модуля нам, прежде всего, будет необходимо создать бота в самом telegram. При настройке нам понадобится chat_id нашего пользователя и API token бота. Как получить эти данные – детально рассказано по ссылке выше, будем считать, что они у нас готовы. Переходя непосредственно к установке модуля, сперва, как мы уже делали, скопируем его исходники в папку components, а затем добавим его настройки в файл “configuration.yaml”:
configuration.yaml
telegram_bot: # Настройки модуля telegram
   platform: polling # Тип модуля
   api_key: XXXXXXXXXXXXX # API Token вашего бота
   allowed_chat_ids:
       XXXXXXX # chat_id вашего пользователя 
   proxy_url: socks5://XXXXXXXXXXXXX # Адрес нашего любимого прокси
   proxy_params:
       username: umpiro
       password: umpiro_password

плюс настройки модуля notify:
notify: # Настройка модуля notify
    name: notify_send # Название сервиса (в нашем случае – будет notify.notify_send)
    platform: telegram # Тип модуля
    chat_id: XXXXXXX # chat_id вашего пользователя



Модуль telegram позволяет нам отправлять сообщения, картинки, или видео. В качестве примера, создадим автоматизацию для отправки сообщения с картинкой, уведомляющее нас об окончании приготовления блюда.
automations.yaml
 - id: '1571074941908' # ID автоматизации
   alias: EOC # Имя автоматизации
   trigger: # Условия срабатывания автоматизации
      # Объект, определяющий срабатывание
    - entity_id: sensor.xiaomi_cooker_mode
      from: Running # Исходное состояние объекта
     # Тип автоматизации – изменение состояния объекта
      platform: state
      to: Waiting # Конечное состояние объекта
   condition: [] # Дополнительные условия
   action: # Выполнияемые автоматизацией действия
    - service: notify.notify_send # Действие – вызов сервиса
      data: # Аргументы, передаваемые при вызове сервиса
         title: End of Cooking # Заголовок
         message: "EOC" # Сообщение
         data: # Дополнительные данные сообщения
           photo: # Тип данных - изображение
              # Файл изображения
            - file: /home/umpiro/.homeassistant/www/cat.jpg
              # Заголовок изображения
              caption: “EOC”



Послесловие

Home Assistant может заинтересовать тех, кто хочет попробовать организовать локальное управление умным домом. Это широкий, интересный, открыто и активно развивающийся за счет усилий коммьюнити проект. Разнообразие инструментов Home Assistant не может не радовать (некоторые важные я не успел упомянуть). В качестве минуса можно назвать относительную запутанность и неполноту документации.
P.S.
Недавно, 10 октября произошел, не знаю можно ли это назвать релизом, переход проекта от версии 0.99 к версии 0.100.
Цитата из Release Notes:
Welcome to the release notes of yet another wonderful release! No, we’re not going for 1.0, we’re doing 0.100! We feel like we’re not ready yet with our goals for 1.0, but we’re making progress every day. For a sneak peak of what we’re thinking about, check our blog Simple mode in Home Assistant 1.0.
Tags:
Hubs:
Total votes 14: ↑12 and ↓2+10
Comments24

Articles