В процессе изучения иногда прилетают идеи, которые, как оказыватся, вполне имеют право на существование. В частности, у меня получилось вывести в интерфейс список, сортированных по значению, сенсоров батарей устройств в виде таблицы, не прибегая к использованию кастомных карточек.
Несмотря на обилие в сети информации о шаблонах, хочется поделиться, уверен, не уникальным и отнюдь не совершенным, но новым для меня, работающим решением, с разбором, как говорится, до винтика.
Понятия Entity и Sate Objects
Объекты состояния сущности (State Objects)
"state" отображает состояние сущности: включен или выключен выключатель (on/off), сработал или нет датчик, числовое значение температуры, влажности, ... , состояние дома/не дома и т.д.
Если ввести в редактор шаблонов строки, расположенные ниже, увидим текущие состояния сущностей 'sun.sun' и 'zone.home'.
{{ states('sun.sun') }}
{{ states('zone.home') }}
или
{{ states.sun.sun.state }}
{{ states.zone.home.state }}
Результат:
-> above_horizon
zoning
(В новых версиях HA, вместо "zoning", будет отображать количество персон, находящихся в зоне)
Проверка на соответствие состояния сущности (true / false):
{{ is_state('sun.sun','above_horizon') }}
-> true
"domain" (категория), группирует сущности по одинаковым признакам, для дальнейших типовых операций с ними и получения от них типовых же состояний аттрибутов. Например, в домен light попадают всевозможные светильники, люстры, умные лампочки..., а к домену binary_sensor относятся любые датчики, которые отдают только два состояния (on/off, true/false, yes/no, ...). Название домена обязательно присутствует в левой части имени любой сущности (до точки).
Для отображения домена сущности выполните:
{{ states.zone.home.domain }}
{{ states.sun.sun.domain }}
-> zone
sun
"object_id" - идентификатор объекта сущности (правая часть после точки), по сути - имя объекта сущности, записанное в нижнем регистре, без пробелов(заменяются нижним подчеркиванием) и др. пунктуации. Например, если сущность на основе потолочного светильника на кухне, то object_id может быть 'kitchen_ceiling'.
{{ states.zone.home.object_id }}
{{ states.sun.sun.object_id }}
-> home
sun
"entity_id" - полное название сущности, которое содержит в себе название домена (категории сущности) и идентификатора объекта сущности, разделённых точкой. Например, потолочный светильник на кухне состоит в домене light + вышеописанное название объекта, получится entity_id: 'light.kitchen_ceiling'.
zone.home
sun.sun
"name" - понятное имя сущности. Часто основано на аттрибуте 'friendly_name', также может походить на object_id, например: 'Kitchen Ceiling'.
{{ states.sun.sun.name }}
{{ states.zone.home.name }}
-> Sun
Дом
"attributes" - словарь с дополнительными аттрибутами, связанными с текущим состоянием сущности (для освещения (опционально): bright, color, icon, friendly_name... ).
Вывод всех аттрибутов сущности с их значениями в виде словаря {ключ : значение, ...}:
{{ states.sun.sun.attributes }}
-> {'next_dawn': '2022-04-22T00:32:19.213677+00:00', 'next_dusk': '2022-04-21T18:37:21.454765+00:00', 'next_midnight': '2022-04-21T19:56:13+00:00',
'next_noon': '2022-04-22T07:56:17+00:00', 'next_rising': '2022-04-22T02:35:15.173924+00:00', 'next_setting': '2022-04-21T18:30:20.352310+00:00',
'elevation': 32.7, 'azimuth': 168.03, 'rising': False, 'friendly_name': 'Sun'}
{{ states.zone.home.attributes }}
-> {'latitude': 00.000000, 'longitude': 00.000000, 'radius': 100, 'passive': False, 'editable': True, 'icon': 'mdi:home', 'friendly_name': 'Дом'}
Значение конкретного аттрибута сущности:
{{ state_attr('sun.sun','elevation') }}
-> 32.7
Проверка на соответствие состояния конкретного аттрибута сущности (true / false):
{{ is_state_attr('sun.sun','elevation', 39.79) }}
-> false
"last_updated" - отметка времени, когда была проведена последняя запись состояния сущности.
{{ states.sun.sun.last_updated }}
{{ states.zone.home.last_updated }}
-> 2022-04-21 10:00:29.090411+00:00
2022-04-20 16:50:59.691514+00:00
"last_changed"- отметка времени, когда было последнеее измение объекта 'state' сущности. В отличие от 'last_updated', например, при смене яркости лампочки без включения/выключения, этот объект не обновится.
{{ states.sun.sun.last_changed }}
{{ states.zone.home.last_changed }}
-> 2022-04-21 02:24:05.972487+00:00
2022-04-20 16:50:59.691514+00:00
"context" - словарь с уточняющими аттрибутами, указывающими на (опционально) источник изменения состояния сущности, идентификатор пользователя, с которым связано изменение и уникальный идентификатор самого изменения состояния, как события. Контекст используется для связывания событий и состояний в Home Assistant.
{{ states.sun.sun.context }}
{{ states.zone.home.context }}
-> Context(user_id=None, parent_id=None, id='eafd3c2d01bc9c5789161fa2a21143a8')
Context(user_id=None, parent_id=None, id='9161fa2a21146701bc9cd90b011a15c2')
Вывод основных свойств сущности в одной строке:
{{states.sun.sun }}
{{states.zone.home }}
p.s. этот формат выдаст ошибку, если 'object_id' начинается с цифры, например: 'binary_sensor.0x165f8903c747df_contact', для такого случая применяется формат с квадратными скобками:
{{ states.binary_sensor['0x165f8903c747df_contact'] }}
->
Разделители.
Все вышеупомянутые примеры были окружены разделителями - двойными фигурными скобками {{ ... }}. Они используются для вывода выражения на выходе шаблона.
В шаблонах также применяются разделители:
{% ... %} - для присвоения значений переменным (если правильнее, то ссылок на объекты ) {% set x = 10 %}, для циклов (for), условных операторов (if/elif/else), макросов и пр..
{# ... #} - для комментариев
{ ... } - (одинарные фигурные скобки) для словарей {dict}. Словарь - ассоциативный массивоподобный объект в Python, который используется и в шаблонах. Формат "ключ:значение" { key1 : value1 , key2 : value2 , key3 : value3 }. Вывод словаря аттрибутов сущности был продемонстрирован выше.
[ ... ] - (одинарные прямоугольные скобки) для списков [list]. В списках могут содержаться как числовые, так и строковые переменные, в т.ч. вперемешку: [1, 2, 3] или ["a", "b", "c"] или ["a", 1, "Python", 5] и даже список списков [[1, 2, 3], ['a', 'b', 'c']].
( ... ) - (круглые скобки) групируют выражения, а также используются для кортежей (tuple). Кортеж - альтернатива списку, защищённая от изменений.
' ... ' и " ... " - (одинарные и двойные кавычки) для обозначения строковых значений.
( . ) - разделитель точка применяется для доступа к аттрибутам объекта.
Шаблон сенсора
Для начала нам нужно выделить все сенсоры батарей. Введем в редакторе шаблонов HA код:
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery') %}
{{ var_entity }}
{% endfor %}
Здесь "for" это оператор цикла, с помощью которого переменной "var_entity" по очереди присваиваются (в виде, похожего на список, шаблона ) состояния сущностей (states) с доменом "sensor", (на это указывает оператор "in"), и, если ( if ) у этой сущности есть аттрибут "device_class" и его значение "battery", то процесс переходит к следующей строке кода. В ином случае переменной присваивается следующий объект последовательности.
Строка {{ var_entity }} выводит на экран очердное значение переменной (тот самый спископодобный шаблон), если, конечно, переменная прошла тесты оператора "if" предыдущей строки.
Строка {% endfor %} отслылает к следующему элементу последовательности или заканчивает действие цикла, если вся последовательность проверена.
Код выше выделит все сенсоры класса "батарея", в том числе, туда могут попасть батареи телефонов, пылесосов и пр..
Чтобы отсеять ненужное, а заодно и сенсоры, которые, по какой-то причине недоступны, добавим в шаблон пару условий:
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery')
and (var_entity.entity_id.endswith("_battery"))
and states(var_entity.entity_id) not in ['unavailable','unknown','none'] %}
{{ var_entity }}
{% endfor %}
Логический операор "and" возвращает "истину", если его левый и правый операнды "истинны".
Оператор "not" возвратит "истину", если его операнд (справа) "не истинен"
Существует еще оператор "or", который возвращает истину, если любой из его операндов истинны.
После оператора "and" добавляется условие: entity_id (полное имя сущности) должно заканчиваться (endswith) на "_battery".
Следующая строка добавляет условие: состояние сущности не должно быть (not in) "unavailable", "unknown" или "none".
Таким образом, у нас появился набор шаблонов, описывающих наши сенсоры батарей.
Следующий шаг - отсеивание названий датчиков и их значений, но прежде стоит обратить внимание на одну особенность цикла "for" языка Python - он хранит значение переменной только внутри цикла. После оператора "endfor", переменная цикла улетучивается. В этом легко убедиться, введя следующий код в редактор шаблонов:
{% set x = 100 %} {# Присваиваем переменной "x" значение 100 #}
{{ x }} {# Вывод переменной "x" #}
{% for x in [1, 2, 3, 4, 5] %} {# Присваиваем последовательно переменной "x" числа из списка #}
{{ x }} {# Вывод переменной "x" #}
{% endfor %} {# Следующее значение или конец цикла #}
{{ x }} {# Вывод переменной "x" #}
->
100
1
2
3
4
5
100
Чтобы можно было воспользоавться переменной из цикла, существует глобальная функция "namespace(...)" (пространство имён). Она присваивается переменной, например: "nmsp", а переменная, которая будет использоваться и в цикле и за пределами цикла, задается в виде аттрибута (через точку). Выглядит это так:
{% set nmsp = namespace() %}
{% set nmsp.y = 100 %}
Либо в одну строку:
{% set nmsp = namespace(y=100) %}
После изменения кода в минипримере, получаем доступ к последнему значению переменной цикла, уже выйдя из него.
{% set nmsp = namespace(y=100) %} {# Присвоить переменной nmsp функцию namespace(y=100), а её аттрибуту "y" значения 100 #}
{{ nmsp.y }} {# Вывод значения "y" #}
{% for x in [1, 2, 3, 4, 5] %} {# Присвоить последовательно переменной "x" числа из списка #}
{{ x }} {# Вывод переменной "x" #}
{% set nmsp.y = x %} {# Присвоить аттрибуту "y" значение переменной "x" #}
{% endfor %} {# Следующее значение или конец цикла #}
{{ nmsp.y }} {# Вывод значения "y" #}
->
100
1
2
3
4
5
5
Итак, используем функцию namespace() для создания переменной в нашем шаблоне. Поскольку нам нужны будут пары: имя и значение, создаем переменную-словарь ( пока пустой { } )
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery')
and (var_entity.entity_id.endswith("_battery"))
and states(var_entity.entity_id) not in ['unavailable','unknown','none'] %}
С помощью еще одной глобальной фунуции "dict( )", расположеной внутри цикла, заполняем нашу переменную-словарь ( nmsp.dict_nm_st ) парами - имя ( var_entity.name ) : значение ( var_entity.state ). Значение при этом прогоняем через фильтр ( | int ), чтобы преобразовать строчное значение в целочисловое.
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{ var_entity.name: var_entity.state |int(0) }) %}
{% endfor %}
Можем убедиться, что словарь наполнен:
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery')
and (var_entity.entity_id.endswith("_battery"))
and states(var_entity.entity_id) not in ['unavailable','unknown','none'] %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{var_entity.name: var_entity.state |int(0) }) %}
{% endfor %}
{{ nmsp.dict_nm_st }}
У словарей в Python есть одна особенность - их ключи и значения всегда расположены парами, но не сортированы.
Поэтому следующая задача - сделать список пар ключ : значение в порядке возрастания значений.
Создаем новую переменную " lst_dct_srt " :
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
Здесь к нашей переменной-словарю (nmsp.dict_nm_st) применен фильтр |dictsort, а в скобках указано, что фильтрация будет нечувствительна к регистру (false), и что она будет произведена по значению ('value'). В результате появляется сортированный список, в котором содержатся ключи словаря (var_entity.name) и их значения (var_entity.state). В чем сразу можем убедиться:
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery')
and (var_entity.entity_id.endswith("_battery"))
and states(var_entity.entity_id) not in ['unavailable','unknown','none'] %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{var_entity.name: var_entity.state |int(0) }) %}
{% endfor %}
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
{{ lst_dct_srt }}
Шаблон работает.
Следующий этап - сделать на основе этого шаблона датчик. Есть два нюанса, на которые следует обратить внимание:
1. В документации к шаблонам сказано "Если шаблон обращается ко всем состояниям в определенном домене, применяется ограничение скорости одного обновления в секунду". В нашем шаблоне как раз идет проверка домена "sensor", и проверять батареи каждую секунду слишком накладно для процессора и совсем не нужно. Поэтому датчик будем делать на основе триггеров, в интеграции "template", а триггеры будут привязаны к запуску HA и временным интервалам.
2. Результатом работы нашего шаблона является список, посимвольная длина которого будет варьироваться в зависимости от количества датчиков, длин имен, и значений, и, почти наверняка, у всех составит более 255 символов. А это именно то число, которое является пределом для значения state датчиков. Но выход есть. У значений аттрибутов датчиков нет ограничений по длине, этим свойством и воспользуемся.
Датчик выглядит так:
template:
- trigger:
- platform: homeassistant
event: start
- platform: time_pattern
hours: "/3"
sensor:
- name: State of battery
unit_of_measurement: entities
state: "{{ 'non' }}"
attributes:
list_of_bat: >
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for entity in states.sensor if is_state_attr(entity.entity_id, 'device_class', 'battery')
and (entity.entity_id.endswith("_battery"))
and states(entity.entity_id) not in ['unavailable','unknown','none'] %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{entity.name: entity.state |int(0) }) %}
{% endfor %}
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
{{ lst_dct_srt }}
Триггеры будут срабатывать при загрузке HA и каждые три часа. Батареи не так быстро разряжаются, поэтому частое обновление не нужно.
В состояние датчика я записал значение "non", потому что оно не требуется (по крайней мере, пока), а шаблон прописал в аттрибут "list_of_bat".
Markdown
Маркдаун - это альтернативный, облегчённый язык разметки. И, хоть он и не способен полноправно заменить кастомные карточки типа "auto entities", для меня он оказался довольно полезным в интерфейсе HA.
Работающий код, выводящий список батарей со значениями, выглядит так:
type: markdown
content: >-
Состояния батарей устройств, %
| | |
|:------- | -------:|
{% for ent_nm, st_ent in state_attr('sensor.state_of_battery', 'list_of_bat')
-%} | {{ ent_nm }} | {{ " " ~ st_ent }}
{% endfor %}
Здесь используются две строки шаблона:
1. Двум переменным ( ent_nm ) и ( st_ent ) поочередно присваиваются пары ключ-значение из сортированного списка, который является значением аттрибута " list_of_bat " нашего датчика "sensor.state_of_battery".
{% for ent_nm, st_ent in state_attr('sensor.state_of_battery', 'list_of_bat')-%}
2. Вывод значений переменных, где в строке:
| {{ ent_nm }} | {{ " " ~ st_ent }}
| - (вертикальная черта или "трубка") учавствует в оформлении таблицы маркдауна,
"  " - код, показывающий языку разметки, что здесь надо разместить два широких пробела (пробелы шириной с букву "m"),
~ - (тильда) символ, который используется в шаблонах для соединия в строке (при выводе) разных типов данных.
Остальной синтаксис формирует таблицу в маркдауне (пустые строки также обязательны).
| | | здесь, между "трубками", обычно пишутся заголовки столбцов, но я, подгоняя таблицу для удовлетворительного отображения и в браузере и в мобильном приложении, решил от заголовков избавиться. Сами трубки нужны.
|:------- | -------:| в этой строке двоеточия, указывают на стороны выравнивания текста.
Доработка
Спустя несколько дней после создания датчика, мне захотелось несколько усовершенствовать его отображение в интерфейсе. Поскольку датчиков батарей могут быть десятки, если не сотни, я подумал, что неплохо было бы иметь возможность регулировать количество отображаемых датчиков на основе значений батареек. Сделал это путем создания сущности "input_number", с выводом его в интерфейс:
input_number:
reg_list_bat:
min: 0
max: 100
step: 1
и небольшой доработки датчика:
Во-первых, добавляем триггер платформы STATE, который будет отслеживать любое изменение ползунка "input_number";
Во-вторых, вставляем две строки в шаблон, в них будет условие отбора значений датчиков батарей на основе значения "input_number".
Итоговый код выглядит так:
template:
- trigger:
- platform: homeassistant
event: start
- platform: time_pattern
hours: "/3"
- platform: state
entity_id: input_number.reg_list_bat
to:
sensor:
- name: State of battery
unit_of_measurement: entities
state: "{{ 'non' }}"
attributes:
list_of_bat: >
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery')
and (var_entity.entity_id.endswith("_battery"))
and states(var_entity.entity_id) not in ['unavailable','unknown','none'] %}
{% if var_entity.state |int(0) <= states('input_number.reg_list_bat') |int(0) %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{var_entity.name: var_entity.state |int(0) }) %}
{% endif %}
{% endfor %}
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
{{ lst_dct_srt }}
Строки с условием ( if / endif ) сравнивают значения батарей со значением "input_number", и только значения, прошедшие тест, попадают в словарь.
{% if var_entity.state |int(0) <= states('input_number.reg_list_bat') |int(0) %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{var_entity.name: var_entity.state |int(0) }) %}
{% endif %}
Иконки
И, напоследок, кому захочется добавить иконок в строках с датчиками, я доработал отображение таблицы в маркдаун:
type: markdown
content: >-
State of battery device, %
| | | |
|:----|:----|----:|
{% for ent_nm, st_ent in state_attr('sensor.state_of_battery','list_of_bat')-%}
{% if st_ent < 15 %}
{% set icon = '' %}
{% elif st_ent < 25 %}
{% set icon = '' %}
{% elif st_ent < 35 %}
{% set icon = '' %}
{% elif st_ent < 45 %}
{% set icon = '' %}
{% elif st_ent < 55 %}
{% set icon = '' %}
{% elif st_ent < 65 %}
{% set icon = '' %}
{% elif st_ent < 75 %}
{% set icon = '' %}
{% elif st_ent < 85 %}
{% set icon = '' %}
{% elif st_ent < 95 %}
{% set icon = '' %}
{% else %}
{% set icon = '' %}
{% endif %}
| {{ icon }} | {{ ent_nm }} | {{ " " ~ st_ent }}
{% endfor %}
Здесь, начиная со второй строки шаблона, идет проверка значения датчика и, в зависимости от числа, переменной "icon" присваивается специальный код отображения иконки. Например:
{% elif st_ent < 55 %}
{% set icon = '' %}
Если значение батареи датчика (st_ent) меньше 55, переменной "icon" присваивается строка '<ha-icon icon="mdi:battery-50"></ha-icon>'
При значениях датчика меньше 15% иконка будет красного ,а меньше 25% - оранжевого цветов:
{% set icon = '' %}
Вывод значений теперь осуществляется в 3 столбцах таблицы.
Заключение
На этом заканчиваю свою вторую статью. Надеюсь, она поможет начинающим строитеям "умного дома". Я затронул здесь только малую часть возможностей шаблонизатора Jinja, применительно к шаблонам Home Assistant Есть еще много фильтров, тестов, методов и прочих полезных вещей. Если кому-то интересно изучать шаблоны в таком формате, пишите в комментариях, в процессе понимания и по мере возможности буду описывать другие возможности шаблонизатора.
Source: components/sensor/__init__.py:583
Error while processing state change for input_number.reg_list_bat
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 579, in state
numerical_value = int(value)
ValueError: invalid literal for int() with base 10: 'non'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/usr/src/homeassistant/homeassistant/helpers/event.py", line 287, in _async_state_change_dispatcher
hass.async_run_hass_job(job, event)
File "/usr/src/homeassistant/homeassistant/core.py", line 607, in async_run_hass_job
hassjob.target(*args)
File "/usr/src/homeassistant/homeassistant/components/homeassistant/triggers/state.py", line 187, in state_automation_listener
call_action()
File "/usr/src/homeassistant/homeassistant/components/homeassistant/triggers/state.py", line 169, in call_action
hass.async_run_hass_job(
File "/usr/src/homeassistant/homeassistant/core.py", line 607, in async_run_hass_job
hassjob.target(*args)
File "/usr/src/homeassistant/homeassistant/components/template/__init__.py", line 166, in _handle_triggered
self.async_set_updated_data(
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 376, in async_set_updated_data
self.async_update_listeners()
File "/usr/src/homeassistant/homeassistant/helpers/update_coordinator.py", line 146, in async_update_listeners
update_callback()
File "/usr/src/homeassistant/homeassistant/components/template/trigger_entity.py", line 56, in _handle_coordinator_update
self.async_write_ha_state()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 585, in async_write_ha_state
self._async_write_ha_state()
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 649, in _async_write_ha_state
state = self._stringify_state(available)
File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 591, in _stringify_state
if (state := self.state) is None:
File "/usr/src/homeassistant/homeassistant/components/sensor/__init__.py", line 583, in state
raise ValueError(
ValueError: Sensor sensor.state_of_battery has device class None, state class None unit entities and suggested precision None thus indicating it has a numeric value; however, it has the non-numeric value: non (
Хотя я пробовал убрать указатель порогового значения заряда для отображения вообще (и строчку {% if var_entity.state |int(0) <= states('input_number.reg_list_bat') |int(0) %} {% endif %}), все равно карточку не формирует
из статьи копировал и работало несколько месяцев. Думал, что-то с обновлениями сломалось.
input_number:
reg_list_bat:
min: 0
max: 100
step: 1
template:
- trigger:
- platform: homeassistant
event: start
- platform: time_pattern
hours: "/3"
- platform: state
entity_id: input_number.reg_list_bat
to:
sensor:
- name: State of battery
unit_of_measurement: entities
state: "{{ 'non' }}"
attributes:
list_of_bat: >
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for var_entity in states.sensor if is_state_attr(var_entity.entity_id, 'device_class', 'battery')
and not (var_entity.entity_id.endswith("akkumuliatora"))
and states(var_entity.entity_id) not in ['unavailable','unknown','none'] %}
{% if var_entity.state |int(0) <= states('input_number.reg_list_bat') |int(0) %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{var_entity.name: var_entity.state |int(0) }) %}
{% endif %}
{% endfor %}
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
{{ lst_dct_srt }}
- trigger:
- platform: homeassistant
event: start
- platform: time_pattern
minutes: "/20"
- platform: state
entity_id: input_number.reject_list_bat
to:
sensor:
- name: State of battery
unit_of_measurement: entities
state: >
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for entity in states.sensor if is_state_attr(entity.entity_id, 'device_class', 'battery')
and ((entity.entity_id.endswith("_battery")) or (entity.entity_id.endswith("_battery_level")))
and states(entity.entity_id) not in ['unavailable','unknown','none'] %}
{% if states(entity.entity_id)|int(0) < 15 %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{entity.name: entity.state |int(0) }) %}
{% endif %}
{% endfor %}
{% if (nmsp.dict_nm_st | length) | int > 0 %}
{{ nmsp.dict_nm_st | length }}
{% else %}
0
{% endif %}
attributes:
list_of_bat: >
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for entity in states.sensor if is_state_attr(entity.entity_id, 'device_class', 'battery')
and ((entity.entity_id.endswith("_battery")) or (entity.entity_id.endswith("_battery_level")))
and states(entity.entity_id) not in ['unavailable','unknown','none'] %}
{% if states(entity.entity_id)|int(0) <= states('input_number.reject_list_bat')|int(0) %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{entity.name: entity.state |int(0) }) %}
{% endif %}
{% endfor %}
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
{{ lst_dct_srt }}
list_of_low_bat: >
{% set nmsp = namespace(dict_nm_st = {}) %}
{% for entity in states.sensor if is_state_attr(entity.entity_id, 'device_class', 'battery')
and ((entity.entity_id.endswith("_battery")) or (entity.entity_id.endswith("_battery_level")))
and states(entity.entity_id) not in ['unavailable','unknown','none'] %}
{% if states(entity.entity_id)|int(0) < 15 %}
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{entity.name: entity.state |int(0) }) %}
{% endif %}
{% endfor %}
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}
{{ lst_dct_srt }}
"Error while processing template: Template template= (много текста про Traceback и т.п.) TypeError: 'NoneType' object is not iterable"
Это связано с тем, что шаблон в Маркдаун срабатывает быстрее шаблона датчика, и не может обработать пустое (None) значение датчика.
Решил проблему, дополнив шаблон Маркдауна, проверкой на отсутствие значения None (без кавычек)