Repeat.item entity_id attributes

Hi I’m trying to set up the following automation which will send a notification for each device with less than 20% battery at 7PM daily.

I’m getting this error in repeat for each step: Error: Error rendering data template: UndefinedError: 'str object' has no attribute 'attributes'

- alias: device_low_batt
  trigger:
    - platform: time
      at: '19:00:00'
  mode: parallel
  action:
    - variables:
        # List of devices with low battery
        low_batt_list: >-
          {% set sensors = states.sensor | selectattr('attributes.device_class', 'defined') 
            | selectattr('attributes.state_class', 'defined') 
            | selectattr('attributes.device_class', '==', 'battery') 
            | selectattr('attributes.state_class', '==', 'measurement') 
            | rejectattr('state', 'in', ['unavailable', 'unknown']) | list %}
          {{ sensors | selectattr("state", "is_number")
             | selectattr("state", "le", "20")
             | rejectattr("state", "==", "100")
             | rejectattr("entity_id", "search", ".*_zwave")
             | rejectattr("entity_id", "search", ".*iphone*")
             | map(attribute="entity_id") | list }}
    - choose:
      - conditions: '{{ low_batt_list | length > 0 }}'
        sequence:
          - repeat:
              for_each: '{{ low_batt_list }}'
              sequence:                      
                - service: persistent_notification.create
                  data_template:
                    title: Replace Battery
                    message: The {{ repeat.item.attributes.friendly_name.split(" battery level")[0] }} sensor battery is at {{ repeat.item.state }}%, the battery type is {{ repeat.item.attributes.battery_type }}.
                    notification_id: '{{ repeat.item.split(".")[1].split("_battery_level")[0] }}_low_batt_notification'

The low_batt_list variable you created contains a list of entity_ids.

You’re using for_each to iterate through that list of entity_ids.

The for_each's repeat.item contains an entity_id and nothing more. Therefore you can’t use repeat.item.attributes.friendly_name to get the entity’s friendly_name because all that repeat.itme contains is an entity_id like sensor.whatever.

You would have to use the expand filter to get all of the entity_id’s details.

Hi @123 thanks for you help, I’ve tried the following:

- repeat:
    for_each: '{{ low_batt_list }}'
    sequence:
      - service: persistent_notification.create
        data:
          title: Replace Battery
          message: >-
            {% set expand_name = expand("repeat.item") | map(attribute="attributes.friendly_name") | list %}
            {% set expand_state = expand("repeat.item") | map(attribute="state") | list %}
            {% set expand_batt_type = expand("repeat.item") | map(attribute="attributes.battery_type") | list %}
            The {{ expand_name.split(" battery level")[0] }} sensor battery is at {{ expand_state.split(".")[0] }}%, the battery type is {{ expand_batt_type }}.

I’m now getting this error:
Error: Error rendering data template: UndefinedError: 'list object' has no attribute 'split'

This is an example of how you check for the attributes first:

    {% set ready = entities 
      | rejectattr('state','in',('idle','off','unavailable','paused'))
      | selectattr("attributes.app_name", 'defined') 
      | map(attribute='entity_id') 
      | list %}

The error message is correct, a list doesn’t have a split method.

The first example just needed expand(sensors) in low_batt_list whereas the second one contains multiple syntax errors. Are you scrapping the first example entirely and now intend to use the dramatically different second example?

Hi @123, sticking with the 1st example (am just trying to figure this out). Realise now that should have had expand(sensors) in low_batt_list as you suggested.

So now have:

- alias: device_low_batt
  trigger:
    - platform: time
      at: '19:00:00'
  mode: parallel
  action:
    - variables:
        # List of devices with low battery
        low_batt_list: >-
          {% set sensors = states.sensor | selectattr('attributes.device_class', 'defined') 
            | selectattr('attributes.state_class', 'defined') 
            | selectattr('attributes.device_class', '==', 'battery') 
            | selectattr('attributes.state_class', '==', 'measurement') 
            | rejectattr('state', 'in', ['unavailable', 'unknown']) | list %}
          {{ expand (sensors) | selectattr("state", "is_number")
             | selectattr("state", "le", "20")
             | rejectattr("state", "==", "100")
             | rejectattr("entity_id", "search", ".*_zwave")
             | rejectattr("entity_id", "search", ".*iphone*")
             | map(attribute="entity_id") | list }}
    - choose:
      - conditions: '{{ low_batt_list | length > 0 }}'
        sequence:
          - repeat:
              for_each: '{{ low_batt_list }}'
              sequence:                      
                - service: persistent_notification.create
                  data_template:
                    title: Replace Battery
                    message: The {{ repeat.item.attributes.friendly_name.split(" battery level")[0] }} sensor battery is at {{ repeat.item.state }}%, the battery type is {{ repeat.item.attributes.battery_type }}.
                    notification_id: '{{ repeat.item.split(".")[1].split("_battery_level")[0] }}_low_batt_notification'

Am still getting this error in the repeat for each step:
Error: Error rendering data template: UndefinedError: 'str object' has no attribute 'attributes'

Try this version.

- alias: device_low_batt
  trigger:
    - platform: time
      at: '19:00:00'
  mode: parallel
  action:
    - variables:
        # List of devices with low battery
        low_batt_list: >-
          {{ states.sensor | selectattr('attributes.device_class', 'defined') 
            | selectattr('attributes.state_class', 'defined') 
            | selectattr('attributes.device_class', '==', 'battery') 
            | selectattr('attributes.state_class', '==', 'measurement') 
            | rejectattr('state', 'in', ['unavailable', 'unknown'])
            | selectattr("state", "is_number")
            | selectattr("state", "le", "20")
            | rejectattr("state", "==", "100")
            | rejectattr("entity_id", "search", ".*_zwave")
            | rejectattr("entity_id", "search", ".*iphone*")
            | map(attribute="entity_id") | list }}
    - condition: '{{ low_batt_list | length > 0 }}'
    - repeat:
        for_each: '{{ low_batt_list }}'
        sequence:                      
          - service: persistent_notification.create
            data:
              title: Replace Battery
              message: >
                {% set entity = expand(repeat.item)[0] %}
                The {{ entity.name.split(" battery level")[0] }} sensor battery is at {{ entity.state }}%, the battery type is {{ entity.attributes.battery_type }}.
              notification_id: '{{ repeat.item.split(".")[1].split("_battery_level")[0] }}_low_batt_notification'

EDIT

I had to make some changes because the for_each didn’t accept what I had originally proposed. This version expands each repeat.item individually so it can get to the entity’s name, state, and battery_type attribute.

1 Like

Amazing! That works, thanks very much for your help :+1:

1 Like

Hi @123, I’ve noticed that this doesn’t seem to pick up all sensors with low battery and I can’t seem to figure out why?

{{ states.sensor | selectattr('attributes.device_class', 'defined') 
  | selectattr('attributes.state_class', 'defined') 
  | selectattr('attributes.device_class', 'in', ['battery', 'measurement'])
  | selectattr("state", "is_number")
  | selectattr("state", "le", "20")
  | rejectattr("state", "==", "100")
  | rejectattr('state', 'in', ['unavailable', 'unknown'])
  | rejectattr("entity_id", "search", ".*_zwave")
  | rejectattr("entity_id", "search", ".*iphone*")
  | map(attribute="entity_id") | list }}

For example I have this sensor, and it should be picked up by this template, but it’s not.

This line in the template selects sensors with a state_class attribute.

  | selectattr('attributes.state_class', 'defined')

sensor.cat_flap_battery_level doesn’t have that attribute so it’s excluded.

Hmmm, I tried that, but it doesn’t seem to make any difference.

I think it’s the template, the current state of sensor.cat_flap_battery_level is 4 (%)

If I change this line…

| selectattr("state", "le", "20")

…in the template to “40”…

| selectattr("state", "le", "40")

…then the sensor is included in the result. That kind of defeats the purpose though as I’m looking for a list of sensors with less than 20% battery remaining.

I’m not sure why the state of sensor.cat_flap_battery_level is 4 but is being interpreted by the template as 40.

Ok, managed to resolve this by altering my automation to the following:

- alias: device_low_batt
  trigger:
    - platform: time
      at: '19:00:00'
  mode: parallel
  action:
    - variables:
      
        # List of battery powered devices
        batt_device_list: >-
          {{ states.sensor | selectattr("entity_id", "search", ".*_battery_level") 
            | selectattr("state", "is_number")
            | rejectattr("state", "in", ["unavailable", "unknown"])
            | rejectattr("entity_id", "search", ".*_zwave")
            | rejectattr("entity_id", "search", ".*iphone*")
            | map(attribute="entity_id") | list }}
    - condition: '{{ batt_device_list | length > 0 }}'
    - repeat:
        for_each: '{{ batt_device_list }}'
        sequence:

          # Tests whether repeat item state is not 100% and below 20%
          - condition: >-
              {% set entity = expand(repeat.item)[0] %}
              {{ entity.state | int != 100 and entity.state | int <= 20 }}

          - service: persistent_notification.create
            data:
              title: Replace Battery
              message: >
                {% set entity = expand(repeat.item)[0] %}
                The {{ entity.name.split(" battery level")[0] }} sensor battery is at {{ entity.state }}%, the battery type is {{ entity.attributes.battery_type }}.
              notification_id: '{{ repeat.item.split(".")[1].split("_battery_level")[0] }}_low_batt_notification'