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 %}