Re-run time-based automation if HA is down the first time

No clue how it’s stored. I suppose I could learn the database layout and go look.

But using various methods to compare or display it, I have confirmed that it sometimes comes out as a string. Here’s what I got just now:

boiler_burner:
2019-04-24 14:23:49.357804+00:00 False

burner_summary:
2019-04-24T12:18:00.032182+00:00 True

close_door_switch_off:
2019-04-24 14:21:48.491908+00:00 False

open_door_switch_on:
2019-04-24 14:21:47.285095+00:00 False

The two door switch automations are just residue from some earlier dabbling. But they do work.

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.

  extra_data_template: >
    if(!attributes.last_triggered) return null;
    var t,s=(new Date()-new Date(attributes.last_triggered))/1e3;
    return(
    (t=Math.floor(s/86400))?t+(t>1?" days":" day"):
    (t=Math.floor(s/3600))?t+(t>1?" hours":" hour"):
    (t=Math.floor(s/60))?t+(t>1?" minutes":" minute"):
    (t=Math.floor(s))!==1?t+" seconds":" second"
    )+" ago";

read more in this in Custom UI: Last time automation was triggered

Yup. I just restarted, and confirmed that:

boiler_burner:
2019-04-24T14:23:49.357804+00:00 True

burner_summary:
2019-04-24T12:18:00.032182+00:00 True

close_door_switch_off:
None False

open_door_switch_on:
None False

Also note that my door switches went to “None.” Proves your point about always checking for that!

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.

Restarting the software causes an attribute’s type to change.

I can’t imagine an application where this would be desirable behavior. Is this worthy of logging an issue?

Issue was already created and closed: https://github.com/home-assistant/home-assistant/issues/20110

Actually, I can’t find the issue. But I recall it being created.

EDIT2: I’m just remembering something unrelated. Ignore me.

Probably.

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.

That has nothing to do with the question of how datetimes are saved and restored.

Yah, I linked wrong one.

1 Like

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.

        {
            "last_seen": "2019-04-21T01:39:47.898162+00:00",
            "state": {
                "attributes": {
                    "friendly_name": "Input_Number Brightness Controller",
                    "last_triggered": "2019-04-21T01:20:22.541939+00:00"
                },
                "context": {
                    "id": "bc09015aeabe44d3a1afbd5ce3711620",
                    "parent_id": null,
                    "user_id": null
                },
                "entity_id": "automation.input_number_brightness_controller",
                "last_changed": "2019-04-21T01:19:37.381988+00:00",
                "last_updated": "2019-04-21T01:20:22.542120+00:00",
                "state": "on"
            }
        },

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.

1 Like

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.

1 Like

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.