Тема шаблонов (Templates) в HA всегда вызывает много вопросов, причем порой и у пользователей системы со стажем. Здесь я хочу собрать некоторое кол-во примеров с комментариями, которые помогут лучше понять принцип работы и синтаксис темплейтов.
Для погружения в тему, начать стоит как всегда с изучения официальной документации, а так же руководства на шаблонизатор Jinja2, именно его под капотом использует HA.
Шаблоны можно применять во множестве мест, начиная от объектов (например, создав собственный sensor или switch) и заканчивая скриптами, автоматизациями и интерфейсом lovelace. С их помощью можно получать необходимые данные из объектов (сущностей) — стейты и атрибуты, при необходимости модифицировать их, а так же реализовывать всевозможную логику (переменные, циклы, проверки и т.д.). Это по-настоящему мощный инструмент, открывающий массу возможностей.
Один из главных инструментов для работы с шаблонами находится в разделе «Панель разработчика» — «Шаблоны» (/developer-tools/template), здесь можно в он-лайн режиме проверить корректность составленного шаблона и оценить его результат. Не забывайте про него, это сохранит множество времени и нервов при отладке сложных конструкций, а так же поможет лучше понять как все это работает.

Коротко про синтаксис
Обратите внимание на оформление, в шаблонах могут использоваться строки с разным обрамлением:
{{ }}— Expressions (выражения), эти конструкции после вычисления возвращают какой-то объект.
Пример:{{ as_timestamp(now()) | timestamp_custom ('%W') }}вернет номер текущей недели.
А{{ (state_attr('light.0x86bd7fffe616b72_light', 'brightness') }}— атрибут brightness (яркость) лампы.{% %}— Statements (не знаю как правильно перевести), в этих конструкциях размещается все то, что содержит в себе какую-то «логику»: переменные, проверки if\else, циклы и т.д.
Пример:{% if is_state('group.family_persons', 'not_home') %}Никого нет{% endif %}вернет текст «Никого нет» если стейт у группы group.family_persons равен not_home.
По умолчанию, каждая строка шаблона на выходе будет оканчиваться символом новой строки «\n».
Причем даже те строки, которые ничего не выводят (например, объявление переменной — {% set a = 123 %}).
Об этом необходимо помнить и учитывать при составлении темплейта.
К примеру, если шаблон используется для вывода чего-либо на экран, можно потратить уйму времени в борьбе с лишними пустыми строками на выходе.
Для управления этим поведением в jinja2 существует специальный механизм:
{{- }}или{%- %}знак минуса в левой части уберет символ новой строки до текущей.{{ -}}или{% -%}знак минуса в правой части уберет символ новой строки после текущей.
Помимо этого, стоит уделить время ресурсу yaml-multiline.info, на котором можно наглядно разобраться с вопросом форматирования и переноса строк в YAML’е (символы |, > и т.д.), это бывает полезно при форматировании многострочных шаблонов.
Отдельно стоит упомянуть о том, какие операторы можно использовать в шаблонах.
Полную информацию об этом стоит искать в документации на jinja, а вкратце это:
- Математические (сложение, вычитание, умножение и т.д.)
- Сравнения (больше, меньше, равно и т.д.)
- Логические (и, или, не)
Помимо этого, в выражениях можно использовать фильтры jinja и HA, передавая значения в них через символ «|».
Пример: ((states('sensor.0x158d0003230618_pressure') | float) / 1.333) | round(2)
Важные моменты в документации, на которые стоит обратить особое внимание:
- 4 Важных правила написания шаблонов
- Особая форма записи объектов, имена которых начинаются с чисел
- Приоритет операторов в шаблонах
— ~ —
Примеры
Сенсоры и их атрибуты
С помощью шаблонов можно создавать как бинарные (on\off), так и обычные сенсоры:
binary_sensor:
- platform: template
sensors:
# Создаем выделенный сенсор для водонагревателя, отражающий его статус (вкл\выкл)
boiler_status:
device_class: power
# Сенсор примет состояние 'on', если стейт объекта switch.tplink_smartplug_01 будет 'on'
value_template: "{{ is_state('switch.tplink_smartplug_01', 'on') }}"
# Собственный датчик протечки, меняющий свое состояние в зависимости от input_boolean
neptun_water_leakage:
friendly_name: Датчики протечки Нептун
device_class: 'moisture'
value_template: >
# Если input_boolean.neptun_activated = 'on', то у нас протечка
{{ is_state('input_boolean.neptun_activated', "on") }}
sensor:
- platform: template
sensors:
# Создаем выделенный сенсор на основе атрибута другого объекта
gismeteo_temperature:
unit_of_measurement: °C
# В этот сенсор будет попадать данные из атрибута temperature (температура)
value_template: '{{ state_attr("weather.gismeteo","temperature") }}'
# Сенсор уровня воды в увлажнителе воздуха (в %)
smartmi_humidifier_01_water_level:
friendly_name: "Остаток воды"
value_template: >
# Здесь сырые данные из атрибута depth переводятся в проценты
{{ ((state_attr('fan.xiaomi_miio_device', 'depth') / 120) * 100) | int }}
В примерах выше используются простые выражения (Expressions) с использованием встроенных в HA функций is_state() и state_attr(). С полным перечнем функций и правилами их применения лучше всего ознакомится в документации.
Более интересный пример:
sensor:
- platform: template
sensors:
date_formatted:
friendly_name: 'Date (DD.MM.YYYY)'
value_template: "{{ as_timestamp(states('sensor.date_time_iso')) | timestamp_custom('%d.%m.%Y') }}"
icon_template: mdi:calendar
attribute_templates:
day_of_week: >-
{% set day_num = ["Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"] %}
{% set day_of_week = day_num[now().weekday()] %}
{{ day_of_week }}
Здесь в качестве стейта (state) сенсора date_formatted будет выведен результат выражения {{ as_timestamp(states('sensor.date_time_iso')) | timestamp_custom('%d.%m.%Y') }} и дополнительно, в виде атрибута — день недели, получаемый с помощью ряда операций.

В качестве значения сенсора можно использовать произвольный текст, в этом примере в зависимости от состояния датчика двери (геркона) в стейт будет записан текст «Открыта» или «Закрыта» (речь про дверь):
sensor:
- platform: template
sensors:
entrance_door_status:
value_template: >
{% if is_state('binary_sensor.0x158d00031c790f_contact', "on") %}Открыта
{% elif is_state('binary_sensor.0x158d00031c790f_contact', "off") %}Закрыта
{% else %}Unavailable
{% endif %}
Более комплексный пример:
- platform: template
sensors:
local_pressure_mmhg:
value_template: >
{% set pressure = states('sensor.0x158d0003230618_pressure') | float(default=-1) %}
{% if pressure != -1 and (((states('sensor.0x158d0003230618_pressure') | float) / 1.333) | round(2)) > 500 %}
{{ ((states('sensor.0x158d0003230618_pressure') | float) / 1.333) | round(2) }}
{% else %}
{{ states('sensor.local_pressure_mmhg') }}
{% endif %}
Темплейты можно использовать практически во всех возможных местах, например в именах будущих сенсоров:
- platform: template
sensors:
tplinksmartplug01_amps:
friendly_name_template: "{{ states.switch.tplink_smartplug_01.name}} Current"
value_template: '{{ state_attr("switch.tplink_smartplug_01","current_a") | float }}'
unit_of_measurement: 'A'
tplinksmartplug01_watts:
friendly_name_template: "{{ states.switch.tplink_smartplug_01.name}} Current Consumption"
value_template: '{{ state_attr("switch.tplink_smartplug_01","current_power_w") | float }}'
unit_of_measurement: 'W'
Или меняя иконку сенсора в зависимости от различных условий:
mirobot_1s_battery:
friendly_name: "Xiaomi Vacuum Cleaner 1S"
device_class: battery
unit_of_measurement: '%'
icon_template: >
{% if state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') %}
{% if state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 98 %}
mdi:battery
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 85 %}
mdi:battery-90
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 75 %}
mdi:battery-80
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 65 %}
mdi:battery-70
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 50 %}
mdi:battery-50
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 35 %}
mdi:battery-30
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') >= 25 %}
mdi:battery-20
{% elif state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') <= 15 %}
mdi:battery-10
{% else %}
mdi:battery-outline
{% endif %}
{% endif %}
value_template: "{{ state_attr('vacuum.xiaomi_vacuum_cleaner', 'battery_level') }}"
Switches
В выключателях так же есть возможность использовать шаблоны.
Например, можно создать выключатель телевизора с разными действиями на включение и выключение:
switch:
- platform: template
switches:
samsungtv_40c5100:
# Считать выключатель включенным, если binary_sensor.samsungtv_40c5100 = 'on'
value_template: "{{ is_state('binary_sensor.samsungtv_40c5100', 'on') }}"
# Выключатель доступен если выполняется условие ниже, иначе он становится unavailable
availability_template: "{{ is_state('binary_sensor.smartir_01_status', 'on') }}"
turn_on:
service: switch.turn_on
data:
entity_id: switch.smartir_01_tv_samsung_power
turn_off:
service: script.turn_on
data:
entity_id: script.power_off_samsungtv_40c5100
# Иконка у выключателя будет меняться в зависимости от состояния сенсора binary_sensor.samsungtv_40c5100
icon_template: >-
{% if is_state('binary_sensor.samsungtv_40c5100', 'on') %}
mdi:television
{% else %}
mdi:television-off
{% endif %}
Скрипты и автоматизации
Самое широкое применение шаблоны встречают в скриптах и автоматизациях.
Скрипт для запуска уборки роботом-пылесосом конкретной комнаты (в интерфейсе input_select.room_to_vacuum это выпадающий список с комнатами):
script:
start_vacuum_room:
alias: 'Clean Selected Room [Mi Robot]'
sequence:
- service: script.turn_on
data_template:
entity_id: >-
{% if is_state("input_select.room_to_vacuum", "Прихожая") %}
script.start_vacuum_hallway
{% elif is_state("input_select.room_to_vacuum", "Детская") %}
script.start_vacuum_nursery
{% elif is_state("input_select.room_to_vacuum", "Гостиная") %}
script.start_vacuum_living_room
{% elif is_state("input_select.room_to_vacuum", "Кухня") %}
script.start_vacuum_kitchen
{% elif is_state("input_select.room_to_vacuum", "Спальня") %}
script.start_vacuum_bedroom
{% endif %}
Автоматизация, меняющая мощность работы робота-пылесоса:
automation:
- alias: 'Set cleaning mode'
trigger:
platform: state
entity_id: input_select.vacuum_power
action:
- service: >
{% if trigger.to_state.state == 'Silent' %}
script.set_vacuum_power_silent
{% elif trigger.to_state.state == 'Standard' %}
script.set_vacuum_power_standard
{% elif trigger.to_state.state == 'Medium' %}
script.set_vacuum_power_medium
{% elif trigger.to_state.state == 'Turbo' %}
script.set_vacuum_power_turbo
{% elif trigger.to_state.state == 'Gentle' %}
script.set_vacuum_power_gentle
{% endif %}
Оповещение о произошедшей ошибке.
С помощью шаблона можно получить текст ошибки из атрибута error:
automation:
- alias: 'Оповещение об ошибке'
initial_state: true
trigger:
platform: state
entity_id: vacuum.xiaomi_vacuum_cleaner
to: "error"
action:
- service: notify.telegram
data_template:
message: |
Mi Robot: Произошла *ошибка*!
{{ state_attr('vacuum.xiaomi_vacuum_cleaner', "error") }}
Автоматизация меняющая яркость лампы по двойному клику на кнопку (по кругу):
automation:
- alias: 'Яркость света в детской'
initial_state: true
trigger:
platform: state
entity_id: sensor.0x158d00033efd9e_action
to: 'double'
action:
service: light.turn_on
data_template:
entity_id: light.detskaia
transition: '0.5'
brightness: >
{%- if (state_attr('light.detskaia', 'brightness') | int) <= 3 %}
51
{% elif (state_attr('light.detskaia', 'brightness') | int) <= 51 %}
102
{% elif (state_attr('light.detskaia', 'brightness') | int) <= 102 %}
153
{% elif (state_attr('light.detskaia', 'brightness') | int) <= 153 %}
204
{% elif (state_attr('light.detskaia', 'brightness') | int) <= 204 %}
255
{% elif (state_attr('light.detskaia', 'brightness') | int) <= 255 %}
3
{% endif %}
Плавное включение света в заданное в интерфейсе HA время (будильник):
automation:
- alias: Sunrise Lighting (Bedroom)
initial_state: true
trigger:
platform: template
# Триггером служит совпадение времени в sensor.time и input_datetime.sunrise_in_bedroom
value_template: "{{ states('sensor.time') == (states('input_datetime.sunrise_in_bedroom')[:5]) }}"
condition:
condition: and
conditions:
- condition: state
entity_id: binary_sensor.workday_sensor
state: 'on'
- condition: sun
before: sunrise
before_offset: "00:30:00"
action:
- service: light.turn_on
entity_id: light.spalnia_stol
data:
effect: SunriseBW
Постепенное увеличение громкости:
script:
googlehome3792_increase_volume:
alias: Increase volume by 5%
sequence:
- service: media_player.volume_set
entity_id: media_player.googlehome3792
data_template:
volume_level: >
{% set level = (state_attr('media_player.googlehome3792', 'volume_level') | float) + (0.05 | float) %}
{% if level < 1 %} {{ level }}
{% else %} 1
{% endif %}
automation:
- alias: Increase volume loop - Childrens Room
initial_state: false
trigger:
platform: time_pattern
minutes: "/3"
action:
- service: script.turn_on
entity_id: script.googlehome3792_increase_volume
Изменение громкости у выбранного из выпадающего списка источника:
automation:
- alias: 'Громкость радио'
trigger:
platform: state
entity_id: input_number.volume_radio
action:
service: media_player.volume_set
data_template:
entity_id: >
{% if is_state("input_select.output_device", "Гостинная (TV)") %} media_player.gostinaia
{% elif is_state("input_select.output_device", "Гостинная (Home Mini)") %} media_player.googlehome9967
{% elif is_state("input_select.output_device", "Детская (TV)") %} media_player.detskaia
{% elif is_state("input_select.output_device", "Детская (Home Mini)") %} media_player.googlehome3792
{% endif %}
volume_level: '{{ states.input_number.volume_radio.state }}'
Отправка оповещений о приходе или уходе из дома:
automation:
- alias: 'Home Presence Alert'
initial_state: true
trigger:
platform: state
entity_id: person.alexander, person.irina
condition:
condition: and
conditions:
- condition: template
value_template: "{{ trigger.to_state.state != trigger.from_state.state }}"
action:
- service: notify.telegram
data_template:
message: >
{{ trigger.to_state.attributes.friendly_name }}
{% if trigger.to_state.state == 'home' %}дома!
{% else %}скорее всего вне дома.
{% endif %}
В примере выше сообщение будет выглядеть примерно так — «Александр скорее всего вне дома.»
Шаблон соберется в одну строку, т.к. перед ним указан символ «>», означающий замену символов новой строки на пробелы.
Еще одно оповещение, на этот раз о температуре в комнате (холодно или жарко):
automation:
- alias: Termo alert [Living Room]
trigger:
- platform: template
value_template: "{{ (states('sensor.0x158d0003230618_temperature') | float) < 22 }}"
for:
minutes: 5
- platform: template
value_template: "{{ (states('sensor.0x158d0003230618_temperature') | float) > 25 }}"
for:
minutes: 5
action:
- service: notify.telegram
data_template:
message: >-
В *Гостиной*
{% if (trigger.to_state.state | float) > 23 -%} жарко,
{% elif (trigger.to_state.state | float) < 23 -%} холодно,
{% endif -%} температура: *{{ trigger.to_state.state }}°C*
Команда для телеграм-бота, возвращающая имена находящихся дома:
automation:
- alias: 'Telegram Bot - Who is home?'
trigger:
platform: event
event_type: telegram_command
event_data:
command: '/whoishome'
action:
service: telegram_bot.send_message
data_template:
target: '{{ trigger.event.data.user_id }}'
message: |
Сейчас дома:
{%- set entites = expand('group.family_persons') %}{% for prs in entites %}{% if prs.state == "home" %}
{{ prs.attributes.friendly_name }}{% endif %}{% endfor %}
{% if is_state("group.family_persons", "not_home") %}Никого нет{% endif %}
Здесь, в отличии от предыдущего примера, перед шаблоном указан символ «|», означающий сохранение всех переносов строк в шаблоне. Лишняя пустая строка из вывода убрана с помощью конструкции «{%-«.
Еще одна команда для бота, присылающая текущую погоду:
- alias: 'Telegram Bot - Weather'
trigger:
platform: event
event_type: telegram_command
event_data:
command: '/weather'
action:
- service: telegram_bot.send_photo
data_template:
target: '{{ trigger.event.data.user_id }}'
file: '/config/www/weather_icons/{{ states("weather.gismeteo") }}.webp'
caption: |
Температура {{ state_attr('weather.gismeteo', 'temperature') }}°C
Влажность {{ state_attr('weather.gismeteo', 'humidity') }}%
Давление {{ states('sensor.gismeteo_pressure_mmhg') }} mmHg
Здесь, в зависимости от стейта сенсора weather.gismeteo (sunny, rainy, snowy и т.д.) отправляется картинка соответствующая текущей погоде (картинки подготовлены заранее).
— ~ —
Я постарался собрать здесь как можно более разнообразные примеры использования шаблонов в HA, но т.к. эта тема невероятно обширная, получилось это у меня слабо… =)
В будущем я надеюсь дополнить заметку другими интересными (с разных точек зрения) примерами.