I have an automation that I need to make more robust. It fails following a restart and I need help figuring out how best to work around or remedy the failure.
BACKGROUND: I have hot water heat at home with old mercury switches on 5 different zones. I’ve integrated two Zooz ZEN16s so that the relays act as thermostats, wired in parallel with each of the mercury switches. The mercury switches are left at about 50-55 deg F so that if HA fails, then they will kick on if the temp falls to that point, as a fail-over. Other than control via HA, I also wanted to automate a manual run in each zone if the outside temp falls below freezing and the zone hasn’t turned on in at least 2 hours. This is to prevent pipes from freezing in zones that may get heat from lower floors and may not actually trigger the thermostat to turn that zone on. I create the following automation, which works, except when following a restart, a zone never comes on at least once due to its own thermostat. The problem is the trigger, because I’m using from: "on", but following a restart, it may never come on to begin with, and the trigger will never fire, even if it’s been 2 hours and it is below freezing.
I need help with a trigger. I have other automations that have difficulty with the state of things like this following a restart. I’m curious if anyone has found a creative solution. I thought about having an automation that just toggles on/off the relays upon a restart, but that seems clunky, especially in the Summer. Any advice would be appreciated.
CURRENT AUTOMATION:
# Cycle Heat Zones if Outside Temp Below Freezing and Zone Hasn't Been ON in a Set timeout
- id: cycle_heat_zones_atm
alias: Cycle Heat Zones (ATM)
mode: parallel
trigger:
- platform: state
entity_id:
- switch.zooz_zen16_r1s1_tstat_ds
- switch.zooz_zen16_r1s2_tstat_lr
- switch.zooz_zen16_r1s3_tstat_mbr
- switch.zooz_zen16_r2s1_tstat_us
- switch.zooz_zen16_r2s2_tstat_gar
from: "on"
for: "02:00:00"
condition:
- condition: template
value_template: "{{ (states('sensor.sensative_strips_comfort_temperature') | float < 32) or (states('sensor.zooz_zse40_multi_shed_temperature') | float < 32) }}"
action:
- service: homeassistant.turn_on
data:
entity_id: "{{ trigger.entity_id }}"
The challenge with that is it doesn’t start the 2-hour countdown until it first detects a state-change (from on to some other state such as off or even unavailable). As you explained, a restart wipes the slate clean and the trigger must first wait for a state-change before it starts the countdown.
The other bad news is that even just Reloading Automations will also wipe the slate clean. The trigger might be halfway through its countdown but if you use the Automation Editor to modify an automation, the moment you click Save it performs a Reload Automations.
One alternative is the concept of reporting “stale” entities (entity whose state value has remained unchanged for a given period of time).
For example, this template lists all switches whose state value hasn’t changed for more than 2 hours.
It could be used to create a Template Sensor and an automation can employ it in a State Trigger.
However, even this technique has a vulnerability. On startup, Home Assistant resets the last_changed property to the current time: any elapsed time is lost on startup. The advantage is that this technique isn’t affected by a Reload Automations and it will start calculating elapsed time immediately after a restart.
I think I also see another challenge: What will happen if after 2 hours of a switch being off, and the temp is 33, so nothing happens due to the condition. Now if the temp drops to 31, nothing will ever happen, until the zone is turned on by something else, or a restart.
I suggest an alternate way to do this is along these lines:
Make an automation that sets an input_datetime for each switch if its ‘on’, time trigger every minute or 5, or 10, whatever works for you. These will survive a restart if no intial: value is set, so now you know when each pump was last on.
Now call for service to turn on each switch, with 2 conditions: 1 using a value_template that compares the input_datetime for each switch to (now), if delta is greater than 2 hours, and a condition of outside temp less than 32 degrees(like you have), then turn on the switch. Add a delay for how long to keep the pump on, then turn it off. You probably want to incorporate a condition here to not turn it off if the zone is calling for heat, in the case that the automation started the pump, but now the room wants heat, before the delay is up. Just a possible way to go about it, and probably can be streamlined
These are indeed interesting ideas and provides me with details that will help now and into the future with other automations as well. Thank you for taking the time to respond in such detail with excellent examples. I may give all of part of this a try in a solution. Cheers.
Ah, you are right and I discovered that last night after having turned them all on briefly when it was below freezing. As long as it stayed below, the automation worked, but as soon as it went above freezing, it never worked again, even as the temp dropped again,. As you pointed out, a hole in the trigger.
I like your idea of a separate entity to keep track of information that HA normally won’t track accurately following a restart or reloads of the automations. I actually use something similar to this already in another instance where I use a Zooz Kit with a battery-powered door sensor as a ZWave device that is connected to a DC transformer plugged into an outlet. Whenever the sensor detects that the power is off, it sends a notification to HA, which lets me know the power is off. However, the state still showed OFF in HA because when the power came back on, the sensor would send an update, but HA missed it because it was booting up (when the outage was long enough to require powering down the hub - on short outages it worked fine). So I had to create an input boolean that tracked things appropriately across outages, using the initial_state customization.
I like this solution too and will give it a try. Maybe in the future there will be additional attributes for entities that survive a restart (like the previous state does now) to use for instances like this where we want to track things across restarts or changes to automations. That last bit throws a kink in my light automation that I’ve had to be creative to work around every time I reload automations. I added automation_reloaded to the trigger with mode: restart so that it restarts that particular automation upon reloads. Seems to work. Cheers.
where do you find the documdenation on how to use this filter. The jinja docs don’t explain it well, and I would have never guessed you could do what you did. I’ve also tried to do the test you have using states_attr(), but can’t get it to work. It’s thinks there is no value for ‘last_changed’ in that function.
The state_attr() function is for accessing the contents of a State Object’s attributes property. What everyone commonly refers to as an “attribute” is actually an item within the attributes property.
last_changed is a property of a State Object, not an item within the attributes property. That’s why the state_attr() function cannot use it.
For more information, refer to the documentation for State Objects. It explains each property of a State Object and makes it easier to understand how to get their values.
but the docs say to avoid that syntax, and I can’t figure out how to get it the recommended way: states(). I’m trying to build that sensor with the last_changed time.
The first is unique to Home Assistant and is for accessing Home Assistant’s entity attributes.
The second is native to Jinja2 and the attr in selectattr has nothing to do with Home Assistant’s entity attributes. It’s for accessing elements of the data structure received by selectattr. For example, this is not referring to an entity attribute called entity_id but an attribute/element of the data being passed to it.
| map(attribute='entity_id')
Yes, the data passed to it could be a list or a dict (or a generator object, but let’s skip that for now).
The documentation refers to getting an entity’s state value. The preference is to do this:
states('light.whatever')
as opposed to this:
states.light.whatever.state
because the states() function can gracefully handle a non-existent entity whereas the second form cannot.
However, there’s no function to get the value of an entity’s last_changed property so, in this case, you have to use the second form.