Jinja loop scoping – Average temperature sensor

Prior to 0.36, I had a simple template sensor that looped over a list of sensor names, added them up and divided by the length of the list – simple!
It seems that with 0.36, this is no longer working and now only uses the last item iterated, as is to be expected with Jinja’s loop scope, it seems.

For the life of me, I can no longer write a template that takes a list of temperature sensors and generates an average output. It appears the “Sum” Jinja filter is the way to go but I cannot get my head around how to pull the state value as an attribute to use in “Sum”.

If anyone has ideas, or code, to get this extremely simple template sensor working, it would be much appreciated.

1 Like

This is how I do it:

But not tested it on 0.36 yet

Thanks – your code is pretty much same as mine. Set a variable with 0 outside the loop, add to it from within and divide by length once iterating over the last item.

Just copied and pasted your full code from this thread and swapped the sensor names. Issue persists.

If you upgrade to 0.36, I’d expect your sensor to also report issues. In my case, because it divides by the (length + 1), the output was ~7ºc. Yours might look okay as sum will end up being 1 at the end of the loop.

Just for reference, here’s mine:

indoor_temperature:
    friendly_name: "Indoor Temperature"
    entity_id:
      - sensor.living_room_thermostat_temperature
      - sensor.temperatureandhumidity_1_1
      - sensor.temperatureandhumidity_2_1
    unit_of_measurement: "°C"
    value_template: >
        {% set sensors = ["sensor.living_room_thermostat_temperature", "sensor.temperatureandhumidity_1_1", "sensor.temperatureandhumidity_2_1"] %}
        {% set output = 0 %}
        {% for state in sensors %}
            {% set output = output + states(state)|float %}
            {% if loop.last %}
                {{ (((output / sensors|length) * 2)|round(0)) / 2 }}
            {% endif %}
        {% endfor %}

Here’s how I am doing it, if this helps:

#
#
#   Average Home Temp
#
#
     average_main_temp:
       friendly_name: 'Average Main Floor Temp'
       unit_of_measurement: "°F"
       value_template: >-
         {{ ((float(states.sensor.aeotec_multisensor_6_temperature_7_1.state) + float(states.sensor.aeotec_multisensor_6_temperature_8_1.state) + float(states.sensor.main_floor_temperature.state) + float(states.sensor.office_temperature.state)) / 4) | round(1) }}

     average_upstairs_temp:
       friendly_name: 'Average Second Floor Temp'
       unit_of_measurement: "°F"
       value_template: >-
         {{ ((float(states.sensor.upstairs_temperature.state) + float(states.sensor.upstairs_temperature_2.state) + float(states.sensor.second_floor_temperature.state)) / 3) | round(1) }}

It works fine here on 0.37.0.dev0.
Have you tried to debug it at http://127.0.0.1:8123/dev-template ?

Yup. Template is how I’ve been testing it (including yours).
Definitely didn’t see this behaviour pre-0.36. Might see what happens with 0.37 and re-evaulate as necessary.

As for rpitera’s option – that works, but it’s so simple that it’s messy. I’d have to manually update the sensor count, and specifically define the sensors. Mine and Daniel’s options automate it somewhat.

1 Like

The right answer is always what is right for your use case. Every home and homeowner is different and so every solution is as well.

That’s what makes Home Assistant so great.

I confirm the same issue after upgrading from 35.3 to 36.0.
I use loop for in template sensor that counts number of sensors with status ‘online’:

    value_template: >-
      {% for state in states.sensor -%}
        {% if loop.first %}
          {% set devnum = 0 %}
        {% endif -%}
        {%- if state.state == "online" %}
          {% set devnum = devnum + 1 %}
        {%- endif -%}
        {% if loop.last %}
          {{ devnum }}
        {% endif -%}
      {%- endfor -%}

Now I’m getting these errors in my log:

17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined
17-01-18 22:41:02 homeassistant.components.sensor.template: UndefinedError: ‘devnum’ is undefined

Well, it looks like it is a new issue/feature of jinja2, see details here https://github.com/pallets/jinja/issues/641

Yeah, that was partially my worry as that is how the loop scoping is advertised with Jinja.
Looks like me and a few others managed to catch a glimpse of an ‘issue’ that worked in our advantage.

If anyone has any ideas on how to achieve a similar loop without this scoping problem, it’d be appreciated. :+1:

I’ve come up with a solution, it’s not entirely as tidy as I’d like it to be but it works in 0.36.
Essentially, it’s just a sum filter applied to an array of the states of various temperature sensors and cuts out the loop altogether.

The second line’s multiply by 2 and divide by 2 is just to create a decimal that is either .0 or .5 similar to the standard temperature readings from a Nest thermostat.

{% set sensors = [states.sensor.living_room_thermostat_temperature.state|float, states.sensor.temperatureandhumidity_1_1.state|float, states.sensor.temperatureandhumidity_2_1.state|float] %}
{{ (((sensors|sum / sensors|length) * 2)|round(0)) / 2 }}

I am trying to do something simliar:

{%- set data = namespace(entities=[]) -%}
{%- for state in states.sensor -%}
{% if 'power' in state.entity_id %}
{%- set data.entities = data.entities + [state.state | float] -%}
{% endif %}
{%- endfor -%}
{{ data | sum(attribute='entities') }}

but I am getting an ‘Unknown error rendering template’ error, thoughts?

Change the last line to:

{{ data.entities | sum }}

You can also write the template like this:

{%- set data = namespace(entities=[]) -%}
{%- for state in states.sensor if 'power' in state.entity_id %}
  {%- set data.entities = data.entities + [state.state | float] -%}
{%- endfor -%}
{{ data.entities | sum }}

If you wanted to calculate the average:

{%- set data = namespace(entities=[], qty=0) -%}
{%- for state in states.sensor if 'power' in state.entity_id %}
  {%- set data.entities = data.entities + [state.state | float] -%}
  {%- set data.qty = data.qty + 1 -%}
{%- endfor -%}
{{ (data.entities | sum) / data.qty }}

EDIT

One more variation, but only if all the desired sensors have their device_class set to power and use the same units (W or kW).

{% set x = states.sensor
   | selectattr('attributes.device_class', 'eq', 'power')
   | map(attribute='state') | map('float') | list %}
{{ (x | sum) / (x | count) }}
1 Like

yeah, I had tried

{{ data.entities | sum }}

without success but now it magically works.

I ended up with:

{%- set data = namespace(entities=[]) -%}
{%- for state in states.sensor -%}
{% if ('power' in state.entity_id and 'W' in state.attributes.unit_of_measurement) %}
{%- set data.entities = data.entities + [state.state | float] -%}
{% endif %}
{%- endfor -%}
{{ data.entities | sum }}

Thank you for your help! :slight_smile:

Do these sensor’s not have their device_class set to power?

They are all zwave switches and devices with a ‘power’ entity_id… and no, they do not have a device_class attr.

Go to Configuration > Customize and see if it allows you to set the device_class for the zwave sensor.

It looks like it can, but what is the value of it? Why does it matter?

It allows for the use of simpler templates that don’t rely on selecting entities by matching sub-strings within names or attributes.

Question for you: Do your power sensors all report using the same unit of measurement?

I’m asking because this will match ‘W’ and ‘kW’:

'W' in state.attributes.unit_of_measurement

which can affect the accuracy of the summation.

ok, what is the device class for kW vs W?