Датчики MQTT с общим топиком: has no attribute и unknown

Если в сообщении на общем MQTT-топике нет нужного ключа, шаблоны датчиков выдают предупреждения вида 'dict object' has no attribute 'Occupancy', а состояние может теряться. В трех ветках форума помогло разделение значений по отдельным топикам с сохранением сообщений у брокера.

Как выглядит проблема

Предупреждения Template variable warning сообщают об отсутствии ключа в пришедшем сообщении и не означают сломанную интеграцию.

  • Шаблонные датчики Occupancy (присутствие) и Illuminance (освещенность) на общем топике пишут в журнал предупреждения об отсутствующем атрибуте. В ветках форума это датчики Vallhorn за Tasmota Zigbee-мостом.
  • Датчики контроллера Siemens LOGO 8.4, разделяющие один топик LOGO_Pub, выдают ошибки отсутствующего ключа и становятся unknown - их состояние неизвестно - после перезапуска Home Assistant.
  • Состояние датчика теряется, когда на тот же топик приходит сообщение с другим параметром.

Типичные записи в журнале выглядят так.

WARNING (MainThread) [homeassistant.helpers.template] Template variable warning: 'dict object' has no attribute 'Occupancy' when rendering '{{ value_json['ZbReceived']['0x4D91']['Occupancy'] }}'
2024-10-24 11:21:59.260 ERROR (MainThread) [homeassistant.helpers.template] Error parsing value: 'dict object' has no attribute 'I4' (value: {"state":{"reported":{"$logotime":1729768864}}}, template: {{ value_json['state']['reported']['I4']['value'][0] }})

После перезапуска Home Assistant датчики LOGO выдают ошибки ValueError и сообщения о неудачном обновлении сущности.

ValueError: Sensor sensor.ai2 has device class 'None', state class 'None' unit 'V' and suggested precision 'None' thus indicating it has a numeric value; however, it has the non-numeric value: 'unknown' (<class 'str'>)
Exception raised while updating state of sensor.ai2, topic: 'LOGO_Pub' with payload: b'{"state":{"reported":{"$logotime":1729772603}}}'
Exception raised while updating state of sensor.ai1, topic: 'LOGO_Pub' with payload: b'{"state":{"reported":{"$logotime":1729772603}}}'

Сообщения от Tasmota Zigbee-моста содержат объект ZbReceived. У Siemens LOGO часть сообщений несет только отметку времени $logotime без значений датчиков.

Причина

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

ПричинаКак проверитьЧто сделать
В сообщении на общем топике нет ключа, который читает шаблон датчикаСравнить ключ в шаблоне с содержимым сообщения в записи журналаАвтоматизацией разнести значения по топикам с сохранением сообщений и указать каждому датчику свой state_topic - топик состояния

Рабочее решение

Автоматизация разделяет сообщения общего топика по свойствам и публикует значения с retain: true - брокер сохраняет сообщение. Такой разделитель называют демультиплексором. Сообщения без нужных ключей, например с одной отметкой времени, нужно пропускать. Ниже пример для Tasmota Zigbee из ветки форума.

alias: zigbee bridge demultiplexer
trigger:
  - platform: mqtt
    topic: tele/zigbee-bridge/#
    variables:
      address: "{{ trigger.payload_json.ZbReceived.keys() | first }}"
      properties:
        - Occupancy
        - Illuminance
      property: >
        {{ set(trigger.payload_json.ZbReceived[address].keys()).intersection(set(properties))
          | first | default(none, true) }}
      msg: >
        {{ trigger.payload_json.ZbReceived[address][property]
          if property is not none else property }}
condition:
  - condition: template
    value_template: "{{ property is not none }}"
action:
  - service: mqtt.publish
    data:
      topic: "zb/{{ trigger.topic.split('/')[2] }}/{{ property | lower }}"
      payload: "{{ msg }}"
      retain: true

В примере имя устройства берется из третьего сегмента исходного топика, а название свойства переводится в нижний регистр. Как устроены автоматизации и переменные триггера, разобрано в гайде по автоматизациям.

Дальше MQTT-датчики в YAML подписываются на свои топики. Базовая настройка сенсоров и двоичных сенсоров разобрана в гайде по MQTT и Mosquitto.

  - name: "Vallhorn Landing Illuminance"
    device_class: illuminance
    unit_of_measurement: lx
    state_topic: "zb/vallhorn-landing/illuminance"

  - name: "Vallhorn Hall Illuminance"
    device_class: Illuminance
    unit_of_measurement: lx
    state_topic: "zb/vallhorn-hall/illuminance"
  - name: "PIR Vallhorn Landing"
    state_topic: "zb/vallhorn-landing/occupancy"
    device_class: occupancy
    value_template: "{{ iif(value|int(0) == 1, 'ON', 'OFF') }}"

  - name: "PIR Vallhorn Hall"
    state_topic: "zb/vallhorn-hall/occupancy"
    device_class: occupancy
    value_template: "{{ iif(value|int(0) == 1, 'ON', 'OFF') }}"

Шаблон двоичного датчика превращает числовые 1 и 0 в ON и OFF.

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

Если не помогло

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

Не сработали:

  • Попытка посмотреть содержимое сообщения срезом {{ value_json[:250] }} - получена ошибка KeyError: slice(None, 250, None).
  • Обход шестнадцатеричного адреса через первый элемент ZbReceived с одновременным отказом от qos - вариант value_template: "{{ ((value_json['ZbReceived'].values())|first)['Illuminance'] }}" не сработал.
  • Защита доступа к Occupancy с сохранением this.state и числовыми payload_on: 1 и payload_off: 0.
  • Булев шаблон Occupancy без payload_on и payload_off - тоже без результата.
  • Шаблонный датчик на MQTT-триггере вместо MQTT-сенсора - не сработал.

Частичный результат дали два варианта:

  • Проверка is defined с откатом на this.state, например {{ value_json.state.reported.I1.value[0] if value_json.state.reported.I1 is defined else this.state }} - эффект частичный в двух ветках.
  • Временное снятие device_class и показ среза {{ value[:250] }} - один частичный результат в одной ветке.

Еще предложили посмотреть первый элемент ZbReceived без Occupancy и device_class, перенести конфигурацию освещенности под sensor и добавить unit_of_measurement и unique_id. У каждого из этих двух вариантов одна ветка, итоги неизвестны.

Частые вопросы

Чем различаются value и value_json?

value содержит исходное сообщение как текст, а value_json - результат разбора JSON. По документации текстовая переменная доступна всегда, а разобранные данные - только при корректном JSON во входящем сообщении. Поэтому для просмотра исходного текста в обсуждении использовали срез value. Обе переменные описаны в документации по шаблонам.

Какие значения задавать payload_on и payload_off?

Эти опции задают строками. Они определяют значения сообщения для состояний включено и выключено у двоичного MQTT-датчика. В документации двоичных MQTT-датчиков есть пример со значениями ‘1’ и ‘0’ в кавычках. В журнале Vallhorn также встречались сообщения No matching payload found с результатами шаблона ‘on’, ‘True’ и ‘False’.

Чем смотреть полезную нагрузку MQTT?

В одной ветке форума предложили MQTT Explorer для просмотра сообщений брокера, итог неизвестен. В сообщениях Vallhorn встречаются объект ZbReceived и поля Occupancy и Illuminance. У Siemens LOGO значения находятся внутри state и reported, а часть сообщений содержит лишь $logotime. При просмотре полезно сопоставить эти поля с ключом, к которому обращается шаблон датчика.

Как перечитать MQTT-сущности после правки YAML?

Используйте действие mqtt.reload - оно перечитывает MQTT-сущности из YAML-конфигурации. В одной ветке форума советовали перезагрузить сущности после изменения определений, но результат остался неизвестным. Там упоминалось название Manually Created MQTT Entities; в документации MQTT-интеграции для этой операции указано действие mqtt.reload. Именно его можно использовать как ориентир при применении изменений конфигурации.

Что проверяет шаблон доступности AI2?

Он проверяет наличие AI2 и то, что его значение является числом. В одной ветке форума предложили такой вариант для LOGO, итог неизвестен. В документации MQTT-сенсора шаблон доступности извлекает доступность устройства из availability_topic, а результат сравнивается с сообщениями доступности. В предложении для AI2 проверка обращалась к первому элементу value внутри AI2.

Андрей Ковалев, редактор. Собирает решения из веток форума Home Assistant и сверяет команды и настройки с официальной документацией.