How to handle unknown or unavailable states in template entities or automations

* Template warning: 'int' got invalid input 'unknown' when rendering template ...
* Template warning: 'float' got invalid input 'unavailable' when rendering template ...
...
but no default was specified. Currently 'float' will return '0', however this template will fail to render in Home Assistant core 2022.1

I guess I should change something in my templates, but how? If the value is not available, it doesn’t make sense to calculate with this value. So it should probably set the template entity to unknown.

This also includes states for automations:

* Error in 'xyz' trigger: In 'numeric_state' condition: no entity specified

So I should add this to all my templates?

{{ ,  default = "unknown" }}
          {% if (states('sensor.wind_m_s') | float < 5) %}
            {{ (10 * 3.6 * states('sensor.wind_m_s') | float )| round(1),  default = "unknown" }}
          {% else %}
            {{ "unknown" }} //erratic value
          {% endif %}

What happens if the if statement fails?

What about conditions in automations?
* Error in 'xyz' trigger: In 'numeric_state' condition: no entity specified

I don’t see the benefit of doing this, could someone please explain?

If the value is not available the template will be unknown anyway, right?

The benefit is that, when you update to 2022.1 and beyond, your templates will still work.

No, you should do what is explained in the post linked above and the warning you received

If you want your templates to behave the same as it has in the past just add a default of 0 to your float() filters:

{% if (states('sensor.wind_m_s') | float(0) < 5) %}
  {{ (10 * 3.6 * states('sensor.wind_m_s') | float(0)) | round(1) }}
{% else %}
  {{ "unknown" }}
{% endif %}
2 Likes

I didn’t even start before 2022.1 so for me it was always like that.

If a value is unknown it makes sense to say it is unknown. But it is already unknown, so why should I want it to be 0? So this is even redundant:
{{ , default = "unknown" }}

{% if (states('sensor.wind_m_s') | float(0) < 5) %}
The state is in that case not set, which makes it unknown, which again is correct.

Or if a template condition is having such an error it is false, which again is correct.

is the word ‘unknown’ greater than or less than 5?

When you convert the word ‘unknown’ to a number, what should that number be?

That’s what default is. It’s the value of the left side of the equation x < 5 when x fails to convert to a valid number.

The original plan was to error out templates in version 2022.1. They forgot about making that change. When they remember, your templates will start to fail.

But isn’t that what it is supposed to do? I can’t just assume states that I don’t know. In that case it will go unknown, right? Or am I missing something?

Perspective is likely the challenge here.

I have far more binary_sensor templates than any other domain. You cannot have true/false/unknown and be binary. “isn’t that what it is supposed to do?” I’d say no. A binary template should not be unknown.

If you have a string-based template sensor then “supposed” to could be any value you desire. If that is “unknown”, great!

But the vast majority of us like our binary sensors to be binary and our number templates to be a number not a string.

You’re missing something. The calculation will fail when the float can’t convert. And you’ll get an error. That’s the error you are seeing. Supplying a default will guard against it.

Edit: or supply a availability template that guards against it.

But if it is missing it should throw an error?

If I don’t have a relevant sensor value why would I calculate with a dummy value?

I think this is misplaced approach. More below…

For good reason; determinism. Aka, when an error occurs, you control the result.

Let’s use an example. If my “door unlock motion sensor” stops responding for any reason, I may choose to have a “fail-open” design and let people out. Others mights want a “fail-safe” design and lock people in. The key here isn’t choosing fail-open or fail-safe, but knowing which you have chosen.

Likewise if you have a heating or cooling circuit that relies upon a temperature sensor, do you want to turn on or off that cooling or heating circuit if the temperature sensor stops responding?

I prefer to keep my freezer food frozen even if the sensor breaks. Or I might prefer to turn the oven off in the same scenario to prevent burning food.

The key here is determinism and choice. Not the example itself. So picking apart any example misses the point.

Which leads us to the goal of monitoring sensors. I agree, that’s a good thing. But don’t do it using sensor values, use a monitoring automation or other system that reports unknown or unavailable sensors. Don’t force that on automation logic for heating/cooling/doors/whatever that isn’t designed to monitor and only designed to trigger something.

I hope this makes sense.

It does make sense.

But my use case/approach seems different.

I actually trigger on the error to ensure proper operation and also notification.

Also the default won‘t help if the the condition fails (e.g. in the if statement).

I have made wide use of the availability: template. That way, the sensor state is unavailable rather than HA throwing template warnings/errors into my logs, which usually occurs during startup as various components are initializing. This particular one though was throwing a template error at startup, sometimes two 5 seconds apart and was bugging me as I had an availability template for the MQTT sensor and even tried adding utcnow() != 'unknown' but it turned out I simply needed to default the as_timestamp() function…

  - trigger:
    - platform: time_pattern
      seconds: "/5"
    sensor:
      - name: "HASS Agent My PC Idle Time"
#        availability: '{{ utcnow() != "unknown" and states("sensor.lastactive") != "unknown" }}'
#        state: '{{ as_timestamp(utcnow()) - as_timestamp(states("sensor.lastactive"))|float(default=0) }}'
        state: '{{ as_timestamp(utcnow(),0) - as_timestamp(states("sensor.lastactive"),0) }}'
        unit_of_measurement: "Seconds"
2 Likes

I wanted to post a summary of this old thread because it helped me fix a problem, but it took a long while to figure out how. Everyone seemed to be talking at crossed purposes. Hopefully this will help someone else get to a solution quicker.

The problem

Davinci’s original problem involved sensors that were supposed to return numeric values, but actually returned strings like "unknown" or "unavailable". These values then caused errors when used in template functions that expected inputs that could be converted into numeric values.

Digeridrew and Petro suggested adding default parameters into the functions that threw the errors, but this didn’t seem to make sense to Davinci: using a default value of the correct type makes the error go away, but then means that the template that uses those functions will produce a valid but incorrect answer. Defaulting to the string "unknown" just produces more errors when the template is expected to output a number.

Petro and Dbrunt also mentioned using availability: templates. If these templates evaluate to a value like false, the sensor will have the state "unavailable" and its icon will be greyed-out in the UI. You can use these to flag a template entity as unavailable when the values it depends on are unavailable or unknown. However, this doesn’t solve the whole problem either. Even when the template sensor is flagged as unavailable, it will still throw errors if "unknown" or "unavailable" values are passed into numeric functions in the template.

The Solution

To fix this issue robustly, you need to use both default values and availability: templates at the same time. In an ideal world, if the availability: template flagged the sensor as unavailable, then Home Assistant wouldn’t try to render the state: template, and you wouldn’t get errors. Unfortunately, it seems to still process the state: template, even though it will discard its output. This means you need to use defaults in any numeric function in the state: template that could potentially receive an unexpected string value like “unknown” or “unavailable”. Similarly, if you are expecting JSON inputs, the state: template needs to handle missing data elements without throwing errors, e.g. by checking that objects exist before trying to access their attributes.

You then need to set the availability: template to check for bad inputs to your template, so that the output of the state: template will be discarded and replaced by "unavailable", rather than a plausible but wrong value based on default inputs.

For me, something like this worked:

 template:
   sensor:
    - name: Test Sensor
      unit_of_measurement: "%"
      state_class: measurement
      availability: |-
        {{ states("sensor.unreliable_sensor") != "unavailable"
        and states("sensor.unreliable_sensor") != "unknown" }}
      state: |-
        {{ states("sensor.unreliable_sensor") |float(default = 100) / 100 }}

Remember that even once you have set this up properly, if your template sensor was supposed to output a numeric value rather than a string, you will still have to make sure that any automations or other templates that it is used in can also handle receiving "unavailable" as their input.

9 Likes

Brilliant, thanks a lot! This had been driving me crazy and spamming my logs, and I have now finally figured this out, thanks largely also to you summarizing it well!

I’m finding this to be a huge pain right now. I am using the PetKit integration, and it has a series of sensors that reset at midnight, including the last time the cat used the litter box and daily counts of litter, feeder, and fountain use.

I wanted to just make simple sensors to keep the last valid date (i.e. did not reset at midnight). It’s taken hours upon hours to do this. The actual core logic was simple. However, the behavior of HA when the PetKit integration resets its date, at full system restarts and template reloads are all different and very hard to track down and every variant of checking “unknown”, “none”, and “unavailable” seems to catch one case just to have another take down my automation.

For example, this litter box version still doesn’t work, going to “unknown” at midnight instead of keeping the last value:

  - trigger:
      - platform: state
        entity_id: 
          - sensor.pet_last_weight_measurement
        not_to:
          - unknown
          - unavailable
          - none
    sensor:
      - name: "Last valid Pet weight"
        unique_id: "last_valid_pet_weight"
        state: >
          {% set value = states('sensor.pet_last_weight_measurement') %}
          {% set last_value = states('sensor.last_valid_pet_weight') %}

            {% if value in ['unknown', 'unavailable', 'none'] or value | float(0) == 0 %}
              {{ last_value }}
          {% else %}
            {{ value | float }}
          {% endif %}
        unit_of_measurement: "lb"

I finally think I got the feeder version to work, which has so much checking logic in it the actual “business logic” is hard to follow.

 - trigger:
      - platform: state
        entity_id: sensor.pet_daily_eating_count
        not_to:
          - unknown
          - unavailable
          - none
    sensor:
      - name: "Last Valid Pet Feeder Eating Time"
        unique_id: "last_valid_pet_feeder_eating_time"
        device_class: timestamp
        state: >
          {% set prev = states('sensor.last_valid_pet_feeder_eating_time') %}
          {% set from = trigger.from_state %}
          {% set to = trigger.to_state %}

          {% if from is not none and to is not none
              and from.state not in ['unknown', 'unavailable', 'none']
              and to.state not in ['unknown', 'unavailable', 'none'] %}
            {% set from_val = from.state | int(0) %}
            {% set to_val = to.state | int(0) %}
            {% if to_val > from_val %}
              {{ now() }}
            {% elif prev not in ['unknown', 'unavailable', 'none'] %}
              {{ prev | as_datetime }}
            {% else %}
              unknown
            {% endif %}
          {% elif prev not in ['unknown', 'unavailable', 'none'] %}
            {{ prev | as_datetime }}
          {% else %}
            unknown
          {% endif %}

The challenge is compounded by not having a way to log from a template, so I can’t log to figure out what value is exactly what when the template runs. Also it’s hard to simulate the various values that might arrive on restarts, etc. so I just have to wait until events happen and the entire template fails and start to deduce what might be the problem. Hence the extreme boilerplate code as I’m programming in the dark.

It does seem like a trigger’s from_state might be none or it’s from_state.state might be unknown, etc. There’s a lot to check and little to help guide what might actually be happening.

I can’t imagine not being a serious programmer to even attempt to get something like this right. The “you have to be a rocket scientist” topic is quite relevant to this case above (and to be clear I love HA, but I’ve poured hours into these very simple cases that should have taken minutes).

EDIT: I tried what seemed like a more “foolproof” method for a litter template last used sensor, looking at only states of itself and the sensor it was referencing, only to have it fail again. But turns out after some manipulation that a separate problem was that the last_weight_measurement from the PetKit integration is a date string, which has to be converted into a datetime and then into local timestamp in order to get recognized as a device_class timestamp.

While debugging existing unavailable/unknown problems, this was confusing! This was tracked down through the System log.

  - trigger:
      - platform: state
        entity_id: 
          - sensor.pet_last_use_date
    sensor:
      - name: "Last Valid Pet Litter Use Date"
        unique_id: "last_valid_pet_litter_use_date"
        device_class: timestamp
        state: >
          {% set value = states('sensor.pet_last_use_date') %}
          {% set last_value = states('sensor.last_valid_pet_litter_use_date') %}

          {% if value in ['unknown', 'unavailable', 'none'] %}
            {{ last_value }}
          {% else %}
            {{ value }}
          {% endif %}

With these entries in the logbook:

Became unknown triggered by state of Pet Last use date became unavailable
11:46:09 AM - 1 hour ago

Changed to March 24, 2025 at 4:47 AM
7:43:31 AM - 5 hours ago

Turns out this works at least for now!

  - trigger:
      - platform: state
        entity_id: 
          - sensor.pet_last_use_date
    sensor:
      - name: "Last Valid Pet Litter Use Date"
        unique_id: "last_valid_pet_litter_use_date"
        device_class: timestamp
        state: >
          {% set value = states('sensor.pet_last_use_date') %}
          {% set last_value = states('sensor.last_valid_pet_litter_use_date') %}

          {% if value in ['unknown', 'unavailable', 'none'] %}
            {{ last_value }}
          {% else %}
            {{ value | as_datetime | as_local }}
          {% endif %}

HA is great for the power that it gives you, but wielding that power does often take more effort than one might want to give. This is why platforms like HomeKit–which is quite powerful in its own right if you take a good look at it–have so many limitations.

Feel free to DM me about your automations; if we come up with something that works, I’ll follow up in this post so that others can benefit from the solution.

This is really hard to debug. I had solidified on the following code to try to be as simple as possible for getting the last valid litter use date (as the PetKit sensor resets to “unknown” at midnight.

However when I do a quick reload, the logbook shows my invalid sensor itself going to unavailable then unknown.

I have no idea how to protect against that and actually get a valid date here. This is way way too complicated to protect against strange corner cases on reload to just get business logic working as it’s unknown to me as to what’s happening behind the scenes and under the covers on a HA restart. There are no errors in the log related to this code and just these two entires in the logbook when I reload in the last_valid_pet_litter_use_date sensor:

March 26, 2025
became unknown
10:15:01 AM - 4 minutes ago
became unavailable
10:15:01 AM - 4 minutes ago

My code never sets the value “invalid last and current” (a test value to see if that path was getting triggered), so somehow we’re getting set to unknown and unavailable despite me checking for those exact cases. This is excruciating.

 - trigger:
      - platform: state
        entity_id: 
          - sensor.pet_last_use_date
    sensor:
      - name: "Last Valid Pet Litter Use Date"
        unique_id: "last_valid_pet_litter_use_date"
        device_class: timestamp
        state: >
          {% set value = states('sensor.pet_last_use_date') %}
          {% set last_value = states('sensor.last_valid_pet_litter_use_date') %}

          {% if value in ['unknown', 'unavailable', 'none'] %}
            {% if last_value in ['unknown', 'unavailable', 'none'] %}
              invalid last and current
            {% else %}
              {{ last_value | as_datetime | as_local }}
            {% endif %}
          {% else %}
            {{ value | as_datetime | as_local }}
          {% endif %}

Read about “availabilty” in the sensor documentation.

Your code here checks for unavailable and unknown in other sensors but that’s not how you control the unavailable and unknown states of this sensor.

Essentially you are trying to override “availability” without using the built-in availability features.