Issue when referencing attributes in a template sensor

Hello,

I am currently creating a template sensor that converts pollutant concentrations into an European Air Quality Index level. Since the levels are defined per pollutant, I wanted to have the level descriptions as an attribute in a template. However, this doesn’t work as I want due to an attribute error.

This is the specific code that is not working:

- sensor:
  - name: European Air Quality Index
    state: >-
      {{ this.attributes.level_descriptions[this.attributes.eaqi_value_overall] }}
    attributes:
      level_descriptions: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor'] }}

This is what I see in the logs:

Logger: homeassistant.helpers.template_entity
Source: helpers/template_entity.py:329
TemplateError('UndefinedError: 'homeassistant.util.read_only_dict.ReadOnlyDict object' has no attribute 'level_descriptions'') while processing template 'Template("{{ this.attributes.level_descriptions[this.attributes.eaqi_value_overall] }}")' for attribute '_attr_native_value' in entity 'sensor.european_air_quality_index'

I don’t understand why there is an issue with one attribute (level_descriptions) but not the other (eaqui_value_overall) since the code below does work:

- sensor:
  - name: European Air Quality Index
    state: >-
      {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor']
      [this.attributes.eaqi_value_overall] }}

Also, when looking at the developer template tool, the following code works perfectly fine as well and gives me the right result:

{{ states.sensor.european_air_quality_index.attributes.level_descriptions
[states.sensor.european_air_quality_index.attributes.eaqi_value_overall] }}

Do you have an idea what might cause this issue?

Thanks in advance,

Best regards,

Raphaël

Because the state option is computed before the attributes option.

Change your sensor’s state template so that it doesn’t reference its own attributes.

@123 Are you sure? In my second example, the state also depends on the attributes and it is not a problem. Why would it be different in both code snippets?

Where is the attribute eaqi_value_overall defined? It’s not shown in the example you posted above.

It is defined in the exact same section, a few lines below. Here is the full yaml before my issue happened.

- sensor:
  - name: European Air Quality Index
    device_class: aqi
    state: >-
      {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor']
      [this.attributes.eaqi_value_overall] }}
    attributes:
      level_descriptions: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor'] }}
      concentration_pm_2_5: "{{states.sensor.luchtmeetnet_pm25_level.state_with_unit}}"
      eaqi_value_pm_2_5: >-
        {{ [10, 20, 25, 50, 75] | select('lt',
        states.sensor.luchtmeetnet_pm25_level.state
        | float) | list | count }}
      eaqi_level_pm_2_5: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor']
        [this.attributes.eaqi_value_pm_2_5] }}
      concentration_pm_10: "{{states.sensor.luchtmeetnet_pm10_level.state_with_unit}}"
      eaqi_value_pm_10: >-
        {{ [20, 40, 50, 100, 150] | select('lt',
        states.sensor.luchtmeetnet_pm10_level.state
        | float) | list | count }}
      eaqi_level_pm_10: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor']
        [this.attributes.eaqi_value_pm_10] }}
      concentration_no2: "{{states.sensor.luchtmeetnet_no2_level.state_with_unit}}"
      eaqi_value_no2: >-
        {{ [40, 90, 120, 230, 340] | select('lt',
        states.sensor.luchtmeetnet_no2_level.state
        | float) | list | count }}
      eaqi_level_no2: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor']
        [this.attributes.eaqi_value_no2] }}
      concentration_o3: "{{states.sensor.luchtmeetnet_o3_level.state_with_unit}}"
      eaqi_value_o3: >-
        {{ [50, 100, 130, 240, 380] | select('lt',
        states.sensor.luchtmeetnet_o3_level.state
        | float) | list | count }}
      eaqi_level_o3: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor']
        [this.attributes.eaqi_value_o3] }}
      eaqi_value_overall: >-
        {{ [
          this.attributes.eaqi_value_pm_2_5,
          this.attributes.eaqi_value_pm_10,
          this.attributes.eaqi_value_no2,
          this.attributes.eaqi_value_o3,
          ] | max }}
      dominant_pollutant: >-
        {% if this.attributes.eaqi_value_overall %}
        {{ [
          ["PM2.5", this.attributes.eaqi_value_pm_2_5],
          ["PM10", this.attributes.eaqi_value_pm_10],
          ["NO2", this.attributes.eaqi_value_no2],
          ["O3", this.attributes.eaqi_value_o3],
        ] | selectattr('1','eq',this.attributes.eaqi_value_overall)
        | map(attribute=0)|join(', ') }}
        {% endif %}

When you first created this Template Sensor, was the template for state different from the way it is now?

I made quite a few trials and errors, but the only time it breaks is when I replace my list of levels by a reference to this.attributes.level_descriptions.
If I revert it and reload it, it works fine.
It seems like there is a qualitative difference between this attribute and the others. I don’t know if this is because the other attributes are dependent on another sensor and therefore are computed at a different moment, or if this is because it’s a list rather than a string or numerical value.
I have tried reading your first reply, but I don’t know where I should put my list of levels then. Should it be in another template?

Simple example demonstrating that state is evaluated before attributes.

- sensor:
  - name: Example Foo
    state: "{{ this.attributes.foo }}"
    attributes:
      foo: "{{ now() }}"

If you want the Template Sensor’s state template to report an air quality index, all of the calculations you are currently performing in the Template Sensor’s attributes must be duplicated in its state template.

I still don’t understand why some attributes are working fine and others not.
I’m following precisely the documentation at Template - Home Assistant and the following template works perfectly, so my example seems to contradict your example.

- sensor:
  - name: Test
    state: "{{this.attributes.foo}}"
    attributes:
      foo: "{{ states.sun.sun.state }}"

Check your Log for error messages "has no attribute foo".

Nope. No error when the attribute is linked to a state.

In the end, I solved my problem by adding the exact thing that was missing from the documentation. The template below works great just by adding the default jinja filter

- sensor:
  - name: European Air Quality Index
    device_class: aqi
    state: >-
      {{ (this.attributes.level_descriptions | default([]))
        [ this.attributes.eaqi_value_overall ]
      }}
    attributes:
      level_descriptions: >-
        {{ [ 'Good', 'Fair', 'Moderate', 'Poor', 'Very poor', 'Extremely poor'] }}

The latest example you posted, yet again, lacks the definition for the eaqi_value_overall attribute thereby making it an incomplete example for other users.

The addition of the default filter doesn’t change the fact that there’s an order to how a template’s configuration is processed. It cannot process state and attributes concurrently because it could potentially introduce a race condition if their templates referenced one another. Therefore they are processed in a specific order (state first) and, generally speaking, it’s inadvisable to reference a value that hasn’t been defined yet (i.e. it generates an error message).

The inclusion of default doesn’t change any of these facts. All it does is ensure that if a particular variable isn’t defined yet (like this.attributes.level_descriptions) it will supply a default value and thereby prevent the generation of an error message.

The other thing to keep in mind is that the this object contains information for the entity’s existing pre-computation state. In other words, before it even starts to compute the new value for state, and any attributes, the this object contains the entity’s existing values. The only time this contains no information about the entity’s current state is the very first time the Template Sensor is created.

That’s why this is often used in a Template Sensor to report it’s existing value instead of the newly computed value (which may be something undesirable like unavailable, zero, or a negative value).

1 Like

At least it solves my problem, which was I came here in the first place.
The rest of the example is the same, so if another user complains that I am providing incomplete information, they just have to scroll up, since the definition of the eaqi_value_overall attribute hasn’t changed and is also reliant on this.
I don’t know if I am generating a race condition, but since this is consistent with the official documentation which does recommend using |default, I believe this shouldn’t be too much of a problem. I’ll come back and complain if my RaspberryPi fails overnight.

I never said you were and if you have the suspicion that you might, then it’s likely you have misinterpreted my explanation.

From what I understood in your explanation, I have not solved the root cause of the problem, which is that there are some undefined states and attributes that just disappear when the template is re-refreshed several times, but I wasn’t looking to solve the root cause, I just wanted the entity to show correctly in the UI.
I am still wondering why, in some cases, I didn’t get to see these undefined states nor any errors in logs, while in other cases, this just breaks the visible status, but I think it’s not worth for us to spend too much time arguing.

I don’t know about you but I am not arguing. I explained how the system works and why the attributes can be undefined. You apparently either don’t accept or understand the explanation because you’re still asking:

Nevertheless, I agree with you that there’s no further value in continuing the discussion. Slap a default on it and call it done.