"No aggregated item, sequence was empty" on template

Hi,

I have this template that checks every hour if, in the next 24 hours, the temperature will be higher or lower than the values I've set through some input_text helpers. I'm using the state of the sensor to display / hide a visual alert on my dashboard and the result_temperatures and result_datetime attributes to indicate the temperature that triggered the alert information.

- sensor:
  - name: Temperature alert trigger 
  # compare all sensor.forecast_24_hours temperatures to both temp_alert values 
    unique_id: sensor.temp_alert_trigger
    attributes: 
      result: >-
        {% set temperatures = state_attr('sensor.forecast_24_hours', 'forecast') %}
        {% set alert_value_max = states('input_text.temp_alert_value_max') %}
        {% set alert_value_min = states('input_text.temp_alert_value_min') %}
        {% set ns = namespace(temp_list = []) %}
        {% for temp in temperatures %}
            {% if temp.temperature | int > alert_value_max | int %}
              {# {{temp.temperature}} > {{alert_value}} #}
              {% set ns.temp_list = ns.temp_list + [(temp.temperature,temp.datetime)] %}
              {% set ns.type = "max" %}
            {% endif %}
            {% if temp.temperature | int < alert_value_min | int %}
              {# {{temp.temperature}} < {{alert_value}} #}
              {% set ns.temp_list = ns.temp_list + [(temp.temperature,temp.datetime)] %}
              {% set ns.type = "min" %}
            {% endif %}
        {% endfor %}
        {% if ns.type == "max" %} 
          {{ ns.temp_list | max }}
        {% else %}
          {{ ns.temp_list | min }}
        {% endif %}
        
      result_temperatures: '{{ "0" if this.attributes.result is not defined else this.attributes.result[0] }}'
      result_datetime: "{{ '0' if this.attributes.result is not defined else as_timestamp(this.attributes.result[1], 0) | timestamp_custom('%B %d at %H:%M', 0) }}"
    state: '{{ "off" if this.attributes.result == "" else "on" }}'

It works as I need it (though it's my first template, and I'm pretty sure it's not optimized, or shall I say messy; any recommendations are more than welcome :)).

But when there are no values in the list (meaning no temperature higher or lower than expected in the next 24 hours) I get a warning ""No aggregated item, sequence was empty"

  • Template variable warning: No aggregated item, sequence was empty. when rendering '{% set temperatures = state_attr('sensor.forecast_24_hours', 'forecast') %} [...]'
  • Template variable warning: str object has no element 0 when rendering '{{ "0" if this.attributes.result is not defined else this.attributes.result[0] }}'
  • Template variable warning: str object has no element 1 when rendering '{{ '0' if this.attributes.result is not defined else as_timestamp(this.attributes.result[1], 0) | timestamp_custom('%B %d at %H:%M', 0) }}'

First, I don't understand what I'm doing wrong in the conditions result_temperatures: '{{ "0" if this.attributes.result is not defined else this.attributes.result[0] }}' (and result_datetime:). How can I check if this.attribut.result has values before trying to access them? I thought I was doing just that, but obviously not.

Also how can I prevent the warning from being fired if result is empty (which is a normal behavior most of the time) ? I've tried to understand how I could use the availability: template, but I don't get how to use it in my code, and I'm not sure if it's the way to go, because from what I understood, the template won't be evaluated if the list turns out to be empty, but I need the state to be updated if the list becomes empty.

Thank you for any help that might point me in the right direction.

What I think happens is this (take it with a grain of salt :wink: ):

  1. the warning comes from {{ ns.temp_list | max }} and empty list; it doesn't seem to be allowed, and the output is undefined
  2. undefined returned from a variable or attribute renders as empty string
  3. that's why "0" if this.attributes.result is not defined is almost*) never true, and it tries go get a first/second character from empty string
  1. *) you still need to guard against undefined attribute value though, because template sensors become unavailable with attributes cleared when the platform reloads or HA starts.

You could try this:

- sensor:
  - name: Temperature alert trigger
  # compare all sensor.forecast_24_hours temperatures to both temp_alert values
    unique_id: sensor.temp_alert_trigger
    attributes:
      result: >-
        {% set temperatures = state_attr('sensor.forecast_24_hours', 'forecast') or {} %}
        {% set alert_value_max = states('input_text.temp_alert_value_max') %}
        {% set alert_value_min = states('input_text.temp_alert_value_min') %}
        {% set ns = namespace(temp_list = []) %}
        {% for temp in temperatures %}
            {% if temp.temperature | int > alert_value_max | int %}
              {# {{temp.temperature}} > {{alert_value}} #}
              {% set ns.temp_list = ns.temp_list + [(temp.temperature,temp.datetime)] %}
              {% set ns.type = "max" %}
            {% endif %}
            {% if temp.temperature | int < alert_value_min | int %}
              {# {{temp.temperature}} < {{alert_value}} #}
              {% set ns.temp_list = ns.temp_list + [(temp.temperature,temp.datetime)] %}
              {% set ns.type = "min" %}
            {% endif %}
        {% endfor %}
        {% if ns.temp_list|count > 0 %}
          {% if ns.type == "max" %}
            {{ ns.temp_list | max }}
          {% else %}
            {{ ns.temp_list | min }}
          {% endif %}
        {% else %}
          None
        {% endif %}

      result_temperatures: '{{ "0" if this.attributes.result|default(none) is none else this.attributes.result[0] }}'
      result_datetime: "{{ '0' if this.attributes.result|default(none) is none else as_timestamp(this.attributes.result[1], 0) | timestamp_custom('%B %d at %H:%M', 0) }}"
    state: '{{ "off" if this.attributes.result|default(none) is none else "on" }}'

Ah, and with the state switching on/off, you could make it a binary_sensor.

I would probably structure this a bit differently.

  • If the state in going to to "on/off", use a Binary Sensor.
  • Switching to a trigger-based method will allow you to process the data in the variables block and eliminate the need to use this.
  • Instead of using a loop and multiple namespaces to do the comparison then paring down to the max or min, I would find the min and max temperatures of the set of forecasts before doing the comparison.
- triggers:
    - trigger: state
      entity_id:
        - sensor.forecast_24_hours
      attribute: forecast
    - trigger: state
      entity_id:
        - input_text.temp_alert_value_max
        - input_text.temp_alert_value_min
      not_to:
        - unknown
        - unavailable
  variables:
    temperatures: "{{ state_attr('sensor.forecast_24_hours', 'forecast') }}"
    sorted: "{{ temperatures | sort(attribute='temperature') | list }}"
    min_forecast: "{{ sorted[0] }}"
    max_forecast: "{{ sorted[-1] }}"
    alert_value_max: "{{ states('input_text.temp_alert_value_max')|int }}"
    alert_value_min: "{{ states('input_text.temp_alert_value_min')|int }}"
    results: |
      {% if max_forecast.temperature > alert_value_max %}
        {{ [max_forecast.temperature, max_forecast.datetime] }}
      {% elif min_forecast.temperature < alert_value_min %}
        {{ [min_forecast.temperature, min_forecast.datetime] }}
      {% else %}
        No Alerts
      {%endif%}
  binary_sensor:
    - name: Temperature alert trigger
      state: '{{ results != "No Alerts" }}'
      attributes:
        result_temperatures: '{{ "0" if results == "No Alerts" else results[0] }}'
        result_datetime: "{{ '0' if results == "No Alerts" else as_timestamp(results[1], 0) | timestamp_custom('%B %d at %H:%M', 0) }}"

Yeah, I 100% agree with Didgeridrew. Trigger-based template sensors are more reliable and better suited for complex calculations. They preserve all values ​​across HA restarts, avoiding the "unavailable --> available again" state and its associated issues; there's a clear distinction between previous state and freshly calculated vaues (in short: variable name or attribute name gives you the fresh value, while this.attributes.whatever - the old one).

I think this should illustrate main differences (regarding state and attributes)

   attributes:
     attr0: "{{ attr1 }}"			      // accessing attr1 before "declaration", always undefined
     attr1: "{{ now().second }}"		  // "declaration" of attr1, each time returning a new value
     attr2: "{{ this.attributes.attr1 }}" // returns an old/previous value of attr1
     attr3: "{{ attr1 }}"			      // returns a freshly calculated value, attr3 equals attr1
   state: "{{ attr1 }}"				      // always undefined, attr1 is not visible here, but variables are!

Thank you very much to both of you, code is also a lot clearer that way, I'll use it as a reference for my next templates. :slightly_smiling_face: