Sensor showing different value than template itself

I’m trying to deal with strange case of template editor working properly, while the same code used in sensor is reporting unavailable value.
My sensor code for this case is:

sensor:
  - platform: template
    sensors:
      sds_task_id_0:
        friendly_name: 'Task #1 ID'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ state_attr('sensor.sds_tasks_list', 'data').tasks[0].id }}
          {% else %} 0
          {% endif %}

Attribute total of sensor.sds_tasks_list shows how many tasks should be listed (starting with index 0), so in case of this particular sensor if there is at least one task available I’m retrieving task 1 ID (with index 0) from sensor.sds_tasks_list

Value_template itself, when tested in template editor works perfectly, either returning 0, if no such task on the list, or its ID if tasks exists. However when I put this code into sensor (as provided above) it returns ID, if one exists, but reports unavailble instead of 0 if there is no such task… I have several sensors constructed same way and it is the case for all of them, so I’m doing some systematic mistake here… What might be wrong here?

It’s possible when this template sensor is initializing that sensor.sds_tasks_list has already been initialized, and doesn’t change. Remember, a template sensor (such as your sensor.sds_task_id_0) only updates when one of the referenced entities (sensor.sds_tasks_list in this case) changes.

You may need to add an automation that triggers on homeassistant starting, (possibly with a delay) forces sensor.sds_task_id_0 to update (using the homeassistant.update_entity service.)

To diagnose sensors it’s sometimes helpful to have a couple of spare input texts
Change the sensor to have an entity_id (rather than have it check the template as to what it should update on) (an entity that does not update verry often)
Then create an automation that fires to update the sensor (homeassistant.update_entity) listing the sensor. But at the same time you can capture (to the texts) what the other values are. This can provide useful information.
You can also test for sensor unavailable or just not having a value.
{% set val = okay if states(‘sensor.thing’) else ‘not_okay’ %}
Play to suit

In my opinion, that should only be done when it’s required. Otherwise it’s very easy to cause the sensor to miss updates (i.e., if the template is changed but you forget to update entity_id accordingly.) Using entity_id all the time, I believe, is a bad habit to get into.

Sorry Phil,
I meant to suggest this “only during diagnosis” doing this otherwise would be just stupid.

Fair enough. Although once someone gets something working they tend to keep it the way it is. :wink:

Also, I’ve seen people suggesting to always use entity_id, so…

BTW, “if states('sensor.thing')” will almost always evaluate to true because the state of an entity is always a string (and states() will always return a string, even if it’s 'unknown'), and a non-empty string is always evaluated as true.

As always Phil, an education. :smiley:
Maybe I should have suggested setting up another ‘test’ sensor to make that more obvious ?

So that (above) will only test for null (not ‘null’ but null) ?
So the rest of the time we can test for : - unavailable, '', unknown, etc. ???

Hm…in fact this is the problem I’m trying to deal with, that I’m receiving tooooons of errors in my log at HA start, because of these sensors, because these are uninitialized at startup. Whole setup retrieves file download status from Synology Download Station using REST sensors, that are chained together (login to DSM -> retrieve Session ID -> retrieve tasks list -> retrieve individual tasks details). So indeed some of these are unitialized on HA startup. Thats why I’m trying to a) force some of first sensors in chain to update on HA start instantly, using automation b) put conditions in sensors, so that if required value is not available to report, other predefined one. In this case if list of download tasks is not yet retrieved total attribute is 0, so I want to retrieve tasks details only on it is more thatn that.
Said that, I already have automation that runs on HA start that forces log in to SDS and update of tasks list:

automation:
  - alias: get_sid_on_start
    trigger:
    - platform: homeassistant
      event: start
    action:
    - service: homeassistant.update_entity
      entity_id: sensor.sds_login # login to SDS using user credentials and retrieves API key
    - delay: 1
    - service: homeassistant.update_entity
      entity_id: sensor.sds_tasks_list  # retrieve initial tasks list for other sensors to process

As sds_task_list is REST sensor it should update every 10 seconds afterwards - and for other values that are retrieved the same way I see that these are updated properly (like download progress that changes every 10 secs)… but it is the same case as iriginal example - data updates only for actual tasks, for not existing ones it reports unavailable instead of 0.
I also tried the trick with adding entity_id of something frequently changing… but the result is the same…

Ok, if it’s unavailable that means it did try to update but there was an error while rendering a template. There should be a warning and/or error in the log with more details as to why it failed.

Maybe try this:

sensor:
  - platform: template
    sensors:
      sds_task_id_0:
        friendly_name: 'Task #1 ID'
        value_template: >-
          {% set data_attr = state_attr('sensor.sds_tasks_list', 'data') %}
          {% if data_attr is not none and data_attr.total > 0 %}
            {{ data_attr.tasks[0].id }}
          {% else %} 0
          {% endif %}

Nope… there is a lot of errors, but related to other sensors that cannot calculate because of earlier in the chain sensors not being available (like % completed calculated as download_completed/total_size, both of source sensors not yet available).

Upon your suggestion I tried also setting first variable and then extracting valus from it, but the same result…

To me it really looks like HA first evaluate if sensor or data possibly required to calculate template value is available and responds with unavailble if not. If everything is available then template calculations are performed. Would be strange as, somehow contradictory to what we want to use templates for and what template editor does… I can’t find another explantion for now.

Nope:

Sorry, not really programmer, so it says almost nothing to me, but I get the point, was just guessing…
Yet, I’m getting unavailable instead of 0, while conditions are met and template editor properly evaluate my code :frowning:
BTW I added some files to download que and deleted some after HA restart, to soehow force sds_tasks_list change to rule out issue you mentioned in your first post regarding static entity value that could prevent sensor update… did not helped either, still exactly the same behaviour. New tasks)id were added or removed, but all inactive stay unavailable.

The point is if it’s unavailable there should be a warning or an error in the log saying why.

OK, small update, possibly could help tracing down the issue…
I finally got to the point of updating in similar way calculated sensors, that throuws so many errors into log file. Change to verify upfront if sensor should be calculate did not helped. I still get in log following errors (shown here per one sensopr):

2020-07-05 19:02:19 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.sds_task_completed_0 fails
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 272, in async_update_ha_state
    await self.async_device_update()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 463, in async_device_update
    await self.async_update()  # type: ignore
  File "/usr/src/homeassistant/homeassistant/components/template/sensor.py", line 224, in async_update
    self._state = self._template.async_render()
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 228, in async_render
    return compiled.render(kwargs).strip()
  File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 1090, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File "/usr/local/lib/python3.7/site-packages/jinja2/_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
ZeroDivisionError: float division by zero
2020-07-05 19:02:19 ERROR (MainThread) [homeassistant.helpers.entity] Update for sensor.sds_task_completed_0 fails
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 272, in async_update_ha_state
    await self.async_device_update()
  File "/usr/src/homeassistant/homeassistant/helpers/entity.py", line 463, in async_device_update
    await self.async_update()  # type: ignore
  File "/usr/src/homeassistant/homeassistant/components/template/sensor.py", line 224, in async_update
    self._state = self._template.async_render()
  File "/usr/src/homeassistant/homeassistant/helpers/template.py", line 228, in async_render
    return compiled.render(kwargs).strip()
  File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 1090, in render
    self.environment.handle_exception()
  File "/usr/local/lib/python3.7/site-packages/jinja2/environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File "/usr/local/lib/python3.7/site-packages/jinja2/_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File "<template>", line 1, in top-level template code
ZeroDivisionError: float division by zero

I can’t judge for the first one at all, second seems to be strange, as there should not be division by zero, as dividor sensor value is unavailable :smiley:

How is sensor.sds_task_completed_0 defined?

Here is the complete code for all sensors for one task (as there are few dependencies):

      sds_task_name_0:
        friendly_name: 'Download #0 name'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            "{{ state_attr('sensor.sds_task_0', 'data').tasks[0].title }}"
          {% else %} ""
          {% endif %}
      sds_task_size_0:
        friendly_name: 'Download #0 size'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ (state_attr('sensor.sds_task_0', 'data').tasks[0].size | float /1024 / 1024) | round(2) }}
          {% else %} 0
          {% endif %}
        unit_of_measurement: MB
      sds_task_downloaded_0:
        friendly_name: 'Download #0 downloaded'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ (state_attr('sensor.sds_task_0', 'data').tasks[0].additional.transfer.size_downloaded | float /1024 / 1024) | round(2) }}
          {% else %} 0
          {% endif %}
        unit_of_measurement: MB
      sds_task_completed_0:
        friendly_name: 'Download #0 completed'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ (((states('sensor.sds_task_downloaded_0') | float) / (states('sensor.sds_task_size_0') | float )) * 100) | round(1) }}
          {% else %} 0
          {% endif %}
        unit_of_measurement: '%'
      sds_task_speed_down_0:
        friendly_name: 'Download #0 speed'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ (state_attr('sensor.sds_task_0', 'data').tasks[0].additional.transfer.speed_download | float / 1024) | round(2) }}
          {% else %} 0
          {% endif %}
        unit_of_measurement: kbps
      sds_task_speed_up_0:
        friendly_name: 'Upload #0 speed'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ (state_attr('sensor.sds_task_0', 'data').tasks[0].additional.transfer.speed_upload | float / 1024) | round(2) }}
          {% else %} 0
          {% endif %}
        unit_of_measurement: kbps
      sds_task_status_0:
        friendly_name: 'Download #0 status'
        value_template: >-
          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}
            {{ state_attr('sensor.sds_task_0', 'data').tasks[0].status }}
          {% else %} 0
          {% endif %}

Is it possible the attribute value (total) is returned as a string value and not an integer?

          {% if state_attr('sensor.sds_tasks_list', 'data').total > 0 %}

If that’s the case then convert it to an integer first.

          {% if state_attr('sensor.sds_tasks_list', 'data').total | int > 0 %}

EDIT
In retrospect, it’s probably not a string because if it was then the comparison would cause an outright error.

Well, it works once all sensors are loaded, properly calculating percentage of completenes… so rather integer…

{{ (((states('sensor.sds_task_downloaded_0') | float) /
     (states('sensor.sds_task_size_0') | float )) * 100) | round(1) }}

If the state of sensor.sds_task_size_0 is not a number (e.g., it’s 'unavailable'), then the float filter will return zero. Hence divide by zero.

Eh… solved this… at least partially…
If you look at sensor.sds_task_size_0 you will see that if unavailable I mede it 0… so division by zero is obvious here :crazy_face: :man_facepalming:
So I made it 1 for start (unless available) and this removed some of errors from log.
Some are still there and this is related to exactly your point, @pnbruckner.
Regarding other errors I was getting (the main one, related to the unavailable states while these should be clearly defined in template) it gots sorted out somehow… I have no clue why, but perhaps fixing other errors in sensors chain got finally also template to work properly in sensor, not only withing template editor.
I still have to fix everything (I have 10 sets of such sensors), so once I get this done and teste I’ll report.

Anyhow, thanks a lot guys for help! That was very educative discussion! :+1: :beer:

1 Like