Статья

Статус батарей устройств в виде таблицы в Markdown

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

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

Понятия Entity и Sate Objects

Сущность(Entity) - это представление в HA какой-либо функции физического или виртуального устройства, сценария, автоматизации и даже персоны. Например, когда в систему интегрируется физический датчик температуры и влажности, эта самая система выделяет из него (условно) три сущности: датчик температуры, датчик влажности и датчик батареи. Все эти сущности имеют определённые состояния и свойства, которые, в виде объектов данных, структурируются определенным образом для удобного их восприятия и применения пользователем и системой.
Объекты состояния сущности (State Objects)

Объекты состояния сущности (State Objects)

"state" отображает состояние сущности: включен или выключен выключатель (on/off), сработал или нет датчик, числовое значение температуры, влажности, ... , состояние дома/не дома и т.д.

Если ввести в редактор шаблонов строки, расположенные ниже, увидим текущие состояния сущностей 'sun.sun' и 'zone.home'.

yaml
Копировать
{{ states('sun.sun') }}
{{ states('zone.home') }}

или

{{ states.sun.sun.state }}
{{ states.zone.home.state }}

Результат:

-> above_horizon
   zoning

(В новых версиях HA, вместо "zoning", будет отображать количество персон, находящихся в зоне)

Проверка на соответствие состояния сущности (true / false):

yaml
Копировать
{{ is_state('sun.sun','above_horizon') }}

-> true

"domain" (категория), группирует сущности по одинаковым признакам, для дальнейших типовых операций с ними и получения от них типовых же состояний аттрибутов. Например, в домен light попадают всевозможные светильники, люстры, умные лампочки..., а к домену binary_sensor относятся любые датчики, которые отдают только два состояния (on/off, true/false, yes/no, ...). Название домена обязательно присутствует в левой части имени любой сущности (до точки).

Для отображения домена сущности выполните:

yaml
Копировать
{{ states.zone.home.domain }}
{{ states.sun.sun.domain }}

-> zone
   sun

"object_id" - идентификатор объекта сущности (правая часть после точки), по сути - имя объекта сущности, записанное в нижнем регистре, без пробелов(заменяются нижним подчеркиванием) и др. пунктуации. Например, если сущность на основе потолочного светильника на кухне, то object_id может быть 'kitchen_ceiling'.

yaml
Копировать
{{ 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'.

yaml
Копировать
{{ states.sun.sun.name }}
{{ states.zone.home.name }}

-> Sun
   Дом

"attributes" - словарь с дополнительными аттрибутами, связанными с текущим состоянием сущности (для освещения (опционально): bright, color, icon, friendly_name... ).

Вывод всех аттрибутов сущности с их значениями в виде словаря {ключ : значение, ...}:

yaml
Копировать
{{ 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'}
yaml
Копировать
{{ states.zone.home.attributes }}
-> {'latitude': 00.000000, 'longitude': 00.000000, 'radius': 100, 'passive': False, 'editable': True, 'icon': 'mdi:home', 'friendly_name': 'Дом'}

Значение конкретного аттрибута сущности:

yaml
Копировать
{{ state_attr('sun.sun','elevation') }}

-> 32.7

Проверка на соответствие состояния конкретного аттрибута сущности (true / false):

yaml
Копировать
{{ is_state_attr('sun.sun','elevation', 39.79) }}

-> false

"last_updated" - отметка времени, когда была проведена последняя запись состояния сущности.

yaml
Копировать
{{ 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', например, при смене яркости лампочки без включения/выключения, этот объект не обновится.

yaml
Копировать
{{ 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.

yaml
Копировать
{{ 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', для такого случая применяется формат с квадратными скобками:

yaml
Копировать
{{ 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 код:

yaml
Копировать
{% 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 %} отслылает к следующему элементу последовательности или заканчивает действие цикла, если вся последовательность проверена.

Код выше выделит все сенсоры класса "батарея", в том числе, туда могут попасть батареи телефонов, пылесосов и пр..

Чтобы отсеять ненужное, а заодно и сенсоры, которые, по какой-то причине недоступны, добавим в шаблон пару условий:

yaml
Копировать
{% 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", переменная цикла улетучивается. В этом легко убедиться, введя следующий код в редактор шаблонов:

yaml
Копировать
{% 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", а переменная, которая будет использоваться и в цикле и за пределами цикла, задается в виде аттрибута (через точку). Выглядит это так:

yaml
Копировать
{% set nmsp = namespace() %}
{% set nmsp.y = 100 %}

Либо в одну строку:

{% set nmsp = namespace(y=100) %}

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

yaml
Копировать
{% 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() для создания переменной в нашем шаблоне. Поскольку нам нужны будут пары: имя и значение, создаем переменную-словарь ( пока пустой { } )

yaml
Копировать
{% 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 ), чтобы преобразовать строчное значение в целочисловое.

yaml
Копировать
{% set nmsp.dict_nm_st = dict(nmsp.dict_nm_st, **{ var_entity.name: var_entity.state |int(0) }) %}
{% endfor %}

Можем убедиться, что словарь наполнен:

yaml
Копировать
{% 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 " :

yaml
Копировать
{% set lst_dct_srt = nmsp.dict_nm_st |dictsort(false, 'value') %}

Здесь к нашей переменной-словарю (nmsp.dict_nm_st) применен фильтр |dictsort, а в скобках указано, что фильтрация будет нечувствительна к регистру (false), и что она будет произведена по значению ('value'). В результате появляется сортированный список, в котором содержатся ключи словаря (var_entity.name) и их значения (var_entity.state). В чем сразу можем убедиться:

yaml
Копировать
{% 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 датчиков. Но выход есть. У значений аттрибутов датчиков нет ограничений по длине, этим свойством и воспользуемся.

Датчик выглядит так:

yaml
Копировать
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.

Работающий код, выводящий список батарей со значениями, выглядит так:

yaml
Копировать
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".

yaml
Копировать
{% for ent_nm, st_ent in state_attr('sensor.state_of_battery', 'list_of_bat')-%}

2. Вывод значений переменных, где в строке:

yaml
Копировать
| {{ ent_nm  }} | {{  "  " ~ st_ent }}

| - (вертикальная черта или "трубка") учавствует в оформлении таблицы маркдауна,

"  " - код, показывающий языку разметки, что здесь надо разместить два широких пробела (пробелы шириной с букву "m"),

~ - (тильда) символ, который используется в шаблонах для соединия в строке (при выводе) разных типов данных.

Остальной синтаксис формирует таблицу в маркдауне (пустые строки также обязательны).

| | | здесь, между "трубками", обычно пишутся заголовки столбцов, но я, подгоняя таблицу для удовлетворительного отображения и в браузере и в мобильном приложении, решил от заголовков избавиться. Сами трубки нужны.

|:------- | -------:| в этой строке двоеточия, указывают на стороны выравнивания текста.

Доработка

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

yaml
Копировать
input_number:
    reg_list_bat:
      min: 0
      max: 100
      step: 1

и небольшой доработки датчика:

Во-первых, добавляем триггер платформы STATE, который будет отслеживать любое изменение ползунка "input_number";

Во-вторых, вставляем две строки в шаблон, в них будет условие отбора значений датчиков батарей на основе значения "input_number".

Итоговый код выглядит так:

yaml
Копировать
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", и только значения, прошедшие тест, попадают в словарь.

yaml
Копировать
{% 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 %}

Иконки

И, напоследок, кому захочется добавить иконок в строках с датчиками, я доработал отображение таблицы в маркдаун:

yaml
Копировать
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" присваивается специальный код отображения иконки. Например:

yaml
Копировать
{% elif st_ent < 55 %}
  {% set icon = '' %}

Если значение батареи датчика (st_ent) меньше 55, переменной "icon" присваивается строка '<ha-icon icon="mdi:battery-50"></ha-icon>'

При значениях датчика меньше 15% иконка будет красного ,а меньше 25% - оранжевого цветов:

yaml
Копировать
{% set icon = '' %}

Вывод значений теперь осуществляется в 3 столбцах таблицы.

Заключение

На этом заканчиваю свою вторую статью. Надеюсь, она поможет начинающим строитеям "умного дома". Я затронул здесь только малую часть возможностей шаблонизатора Jinja, применительно к шаблонам Home Assistant Есть еще много фильтров, тестов, методов и прочих полезных вещей. Если кому-то интересно изучать шаблоны в таком формате, пишите в комментариях, в процессе понимания и по мере возможности буду описывать другие возможности шаблонизатора.




К сожалению, выложенный код для Mardown изобилует спецсимволами, и неверно воспринимается движком сайта. В итоге символы либо не отображаются, либо отображаются не те, что в изначальном коде. Я приведу скрин последнего кода с иконками для Markdown в виде картинки:
Над проблемой работают
Карточка стала пустой, в чем там может быть дело? В редакторе шаблонов лист заполняется.
Трудно сказать. F5 в браузере прОбовали? Почистить КЭШ и пр.. У меня всё работает без изменений. Код у себя не менял.
пробовал, в другом браузере пробовал открыть. В журнале сервера есть вот такая вот ошибка

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. Может в коде, описывающем этот input_number, опечатка, вместо нуля буква О или что-то в этом роде? Не видя кода не смогу толково подсказать.
извините, что гружу этим
из статьи копировал и работало несколько месяцев. Думал, что-то с обновлениями сломалось.
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 (без кавычек)

Вернуться назад
Вернуться назад