maybe superfluous for this discussion, still, important to note:
the last_triggered attribute is only set when the action part of the automation has been activated. Not when the trigger takes place.
An important aspect to realize when analyzing automation behavior and derived logic in HA based on last_triggered.
recently added this customize_glob to my automations, to see how many seconds ago the trigger took place.
Showing empty on all automatons not yet triggered, and counting seconds on the one’s being activated.
thought id share.
Good reminder. In my example, I actually want this behavior; if the action didn’t happen yet today, I want it to happen now. But in other cases it would be the opposite.
My guess, BTW, is that the default JSON serializer being used to save data doesn’t support datetimes, so they’ve taken the easy route and converted it to a string when saving state, and on restoring, they just take the string instead of trying to convert it back to a datetime. But honestly I haven’t dug that deeply, and it’s been a while since I looked into this.
I looked up ‘JSON serialization datetime’ and discovered that the most common serialization function, json.dumps, doesn’t have an established way of serializing datetime.
The recommended solution is to have json.dumps call a custom function whenever it encounters a datetime object (or anything else it can’t serialize) and that function should convert datetime to a time string in ISO format.
When I look in core.restore_state, that’s exactly how all datetime objects are serialized (and not just last_triggered). The strings are UTC time in ISO format.
So Home Assistant’s serialization of datetime is as good as it gets. The culprit appears to be deserialization or rather the apparent lack of it. Instead of converting the time string into a datetime object it just passes it along verbatim.
So the question now is this how it ‘deserializes’ all stored time values? Although the anomaly was noticed for last_triggered it may also be true for last_changed and last_updated and all other datetime attributes.
If this is what’s happening (whether to just one or to all datetime attributes) is, to my mind, a significant error. Effectively, the deserialization process loses a data structure. It’s on par with handling a dictionary as a literal string.
I plan to experiment with this a bit more before I consider submitting it as an issue.
Again, haven’t followed through all the code that does the saving and restoring, but I think this code is where an object’s state is restored, and you can see there are special cases of converting the string representations of last_changed & last_updated back to datetimes, but there doesn’t appear to be the same for items in the attributes dictionary.
Thanks! Yup, it certainly looks like there’s no deserialization of attributes and they’re handled verbatim. Therefore a datetime attribute gets serialized into a nice UTC time string in ISO format but never deserialized back into a datetime object.
The only time the last_triggered attribute becomes a datetime object is when the automation is triggered.
This would explain why some last_triggered attributes appear to be a string for some automations and a datetime object for others. It all depends which automations have been triggered since the last restart.