Variable in automation not working as expected

I have an automation to monitor low batteries that’s been working fine. I want to optimise the automation by removing some duplication, by storing a list of filtered entities in a variable. The odd thing is that while a test version of this works in the template editor, it doesn’t when actually executing the automation. Note that I’m not triggering the automation manually: I’ve modified the trigger to run every minute for testing purposes.

This is the working automation:

- alias: "Check For Low Batteries"
  initial_state: true
  variables:
    threshold: 15
  trigger:
    platform: time
    at: "09:00:00"
  condition: >-
    {{ states.sensor
         | selectattr('attributes.monitor','eq',True)
         | selectattr('attributes.device_class','eq','battery')
         | map(attribute='state')
         | map('int')
         | select('le', threshold)
         | list
         | count
       > 0
    }}
  action:
    - service: notify.mobile_app_ceres
      data:
        title: "Batteries"
        message: >
          {%- set batteries = states.sensor
                                | selectattr('attributes.monitor','eq',True)
                                | selectattr('attributes.device_class','eq','battery')
                                | map(attribute='entity_id')
          %}
          The following devices have less than {{ threshold }}% charge:
          {%- for b in batteries %}
            {%- if states(b) | int < threshold and not is_state(b, 'unavailable') %}
           - {{ state_attr(b, 'friendly_name') | replace(' Battery', '') }}: {{ states(b) | int }}%
            {%- endif -%}
          {%- endfor %}
        data:
          group: "batteries"
          url: homeassistant://navigate/lovelace/devices

This is the optimised version:

- alias: "Check For Low Batteries"
  id: "5ff59069-6f3c-4f9d-bc66-b50bea8e184e"
  initial_state: true
  variables:
    # test threshold to be reverted
    threshold: 85
    monitored_batteries: >-
      {{ states.sensor
           | selectattr('attributes.monitor', 'defined')
           | rejectattr('state', 'in', ['unavailable', 'unknown', 'none'])
           | selectattr('attributes.monitor', 'eq', True)
           | selectattr('attributes.device_class', 'eq', 'battery')
      }}
  # test trigger to be reverted
  trigger:
    platform: time_pattern
    minutes: "/1"
  condition: >-
    {{
      monitored_batteries
        | map(attribute='state')
        | map('int')
        | select('le', threshold)
        | list
        | count
        > 0
    }}
  action:
    - service: notify.mobile_app_ceres
      data:
        title: "Batteries"
        message: >
          {%- set batteries = monitored_batteries | map(attribute='entity_id') %}
          The following devices have less than {{ threshold }}% charge:
          {%- for b in batteries %}
            {%- if states(b) | int < threshold and not is_state(b, 'unavailable') %}
           - {{ state_attr(b, 'friendly_name') | replace(' Battery', '') }}: {{ states(b) | int }}%
            {%- endif -%}
          {%- endfor %}
        data:
          group: "batteries"
          url: homeassistant://navigate/lovelace/devices

This is the error from the log:

2022-06-16 16:41:00 WARNING (MainThread) [homeassistant.components.automation] Error evaluating condition in 'Check For Low Batteries':
In 'condition':
  In 'template' condition: UndefinedError: 'str object' has no attribute 'state'

And the same error in the trace:

Also note the |- above – not sure why it’s included or where it comes from. It may be relevant, but I don’t know. I know what it normally means when used in YAML.

I thought that perhaps the variable(s) get serialised between steps, so I tried this in the template editor to try and replicate the error at least, but there’s no error (in Python a string can be handled like a list):

{% set foo = "bar" %}
{{ foo | map(attribute='state') }}

This works fine in the template editor:

variables don’t resolve complex objects, like state objects. You have to output simple objects, like a list, dictionary, int, float, str, bool. List and dictionary can only contain simple objects, i.e. list, dictionary, int, float, str, bool.

To account for that, output entity_ids in monitored batteries. Expand in your condition. And use the entity_id list in your message.

- alias: "Check For Low Batteries"
  id: "5ff59069-6f3c-4f9d-bc66-b50bea8e184e"
  initial_state: true
  variables:
    # test threshold to be reverted
    threshold: 85
    monitored_batteries: >-
      {{ states.sensor
           | selectattr('attributes.monitor', 'defined')
           | rejectattr('state', 'in', ['unavailable', 'unknown', 'none'])
           | selectattr('attributes.monitor', 'eq', True)
           | selectattr('attributes.device_class', 'eq', 'battery')
           | map(attribute='entity_id') | list
      }}
  # test trigger to be reverted
  trigger:
    platform: time_pattern
    minutes: "/1"
  condition: >-
    {{
      expand(monitored_batteries)
        | map(attribute='state')
        | map('int')
        | select('le', threshold)
        | list
        | count
        > 0
    }}
  action:
    - service: notify.mobile_app_ceres
      data:
        title: "Batteries"
        message: >
          {%- set batteries = monitored_batteries %}
          The following devices have less than {{ threshold }}% charge:
          {%- for b in batteries %}
            {%- if states(b) | int < threshold and not is_state(b, 'unavailable') %}
           - {{ state_attr(b, 'friendly_name') | replace(' Battery', '') }}: {{ states(b) | int }}%
            {%- endif -%}
          {%- endfor %}
        data:
          group: "batteries"
          url: homeassistant://navigate/lovelace/devices

Thanks for confirming and the solution that still achieves my goal!

EDIT: Just one thing, @petro: Seems like map('entity_id') should be map(attribute='entity_id'). I get an error otherwise on my 2022.5 instance.

1 Like

yep, is a typo

1 Like