Automation - state 'for' days/weeks?

I know I can make an automation for state being some value being in a state for some hh:mm:ss.

Is there a way to trigger on a state being set for days/weeks?

I have a few things I would like to do when a condition is true for multiple days – a couple use-cases I have so far:
-If I am not home for ~2 days, turn on a “vacation” mode (input_boolean)
-If I manually set a HVAC hold mode (input_boolean) and forget, I want to turn it off after ~1 week

The automation for each action is easy EXCEPT the trigger.

Example:

    trigger:
      - platform: state
        entity_id:
          - input_boolean.upstairs_hvac_severe_weather_mode
          - input_boolean.downstairs_hvac_severe_weather_mode
        to: 'on'
        for: ???????????????????

Two days would be 172,800 seconds (2 x 24 x 60 x 60). So,

    trigger:
      - platform: state
        entity_id:
          - input_boolean.upstairs_hvac_severe_weather_mode
          - input_boolean.downstairs_hvac_severe_weather_mode
        to: 'on'
        for: 172800

Home Assistant restarts will reset this.

2 Likes

A workaround would be to set input_datetimes when these actions happen, when you leave home and when you set the HVAC.
Then use the datetimes in the automation.

I jut did something similar for a battery charging bank to keep the batteries automatically charged at least every 7 days.

Here are the two automations I use to give you some inspiration:

  - alias: Garage Power Chargers Set Off Time
    trigger:
      - platform: state
        entity_id: switch.garage_power_strip_switch_1
        to: 'off'
      - platform: state
        entity_id: switch.garage_power_strip_switch_2
        to: 'off'
      - platform: state
        entity_id: switch.garage_power_strip_switch_3
        to: 'off'
    action:
      - service: input_datetime.set_datetime
        data:
          entity_id: >
            {% if trigger.entity_id == 'switch.garage_power_strip_switch_1' %}
              input_datetime.charger_1_off_time
            {% elif trigger.entity_id == 'switch.garage_power_strip_switch_2' %}
              input_datetime.charger_2_off_time
            {% elif trigger.entity_id == 'switch.garage_power_strip_switch_3' %}
              input_datetime.charger_3_off_time
            {% endif %}
          timestamp: "{{ now().timestamp() }}"

  - alias: Garage Power Chargers Refresh Battery Charge
    trigger:
      - platform: template
        value_template: "{{ as_timestamp(now()) - state_attr('input_datetime.charger_1_off_time', 'timestamp') | int > 86400 * 7 }}"
      - platform: template
        value_template: "{{ as_timestamp(now()) - state_attr('input_datetime.charger_2_off_time', 'timestamp') | int > 86400 * 7 }}"
      - platform: template
        value_template: "{{ as_timestamp(now()) - state_attr('input_datetime.charger_3_off_time', 'timestamp') | int > 86400 * 7 }}"
    action:
      - service: switch.turn_on
        data: 
          entity_id: >
            {% if  trigger.entity_id == 'input_datetime.charger_1_off_time' %}
              switch.garage_power_strip_switch_1
            {% elif trigger.entity_id == 'input_datetime.charger_2_off_time' %}
              switch.garage_power_strip_switch_2
            {% elif trigger.entity_id == 'input_datetime.charger_2_off_time' %}
              switch.garage_power_strip_switch_3
            {% endif %}

I haven’t fully tested it yet since I just wrote them a bit ago and it hasn’t been 7 days yet. but I’m pretty sure they will work.

you’ll also have to create the input_datetimes making sure they have both date and time.

Scrunch that trigger finity:

  - alias: Garage Power Chargers Set Off Time
    trigger:
      - platform: state
        entity_id:
          - switch.garage_power_strip_switch_1
          - switch.garage_power_strip_switch_2
          - switch.garage_power_strip_switch_3
        to: 'off'
2 Likes

Perhaps you’re already aware of it but if Home Assistant restarts (or automations are reloaded) while for is counting down, it gets reset.

So if you want to reliably use for for long durations, ensure your instance of Home Assistant isn’t restarted during the countdown. Otherwise, the for will reset and restart its countdown.

You’re right of course.

Thanks!

1 Like

Yup.

How do you solve it?

Input datetimes and automations?

MQTT retained msgs?

FWIW, you appear to be using a consistent naming scheme so you could reduce this:

    action:
      - service: input_datetime.set_datetime
        data:
          entity_id: >
            {% if trigger.entity_id == 'switch.garage_power_strip_switch_1' %}
              input_datetime.charger_1_off_time
            {% elif trigger.entity_id == 'switch.garage_power_strip_switch_2' %}
              input_datetime.charger_2_off_time
            {% elif trigger.entity_id == 'switch.garage_power_strip_switch_3' %}
              input_datetime.charger_3_off_time
            {% endif %}
          timestamp: "{{ now().timestamp() }}"

to this:

    action:
      - service: input_datetime.set_datetime
        data:
          entity_id: "input_datetime.charger_{{trigger.entity_id[-1:]}}_off_time"
          timestamp: "{{ now().timestamp() }}"
2 Likes

Maybe I clicked the wrong button but I was replying to the topic’s author (I know you know the drawbacks of for) but since you’re asking, my number one insurance policy to avoid resetting for is (drum roll) a UPS. :slight_smile:

That and I typically avoid assigning long duration values to for. At least with timers, I have concocted a means to restore active ones after a restart but there’s no such workaround for an interrupted for (or delay or wait_for_trigger or wait_template).

FWIW, to make some of my scheduled automations more “restart tolerant” I added a trigger to listen for the “homeassistant start” event. If an automation that ought to have triggered at, say, sunset missed its cue (because the system was restarting exactly when sunset occurred), on startup its conditions check what’s what and execute the action if need be. Of course, this is not exactly applicable to a State Trigger employing for.

1 Like

I’m nothing if not consistent. :laughing:

Thanks for the suggestion.

I usually tend to go for the brute force version of templates where sometimes a more finessed approach would work too.

After having a hell of a time getting a persistent counter in node red, this small function count has held for more than 2 months and several version upgrades. The version upgrades would always reset the count.

So you can use an input number to set the timer. The inject node hits every second reducing the count by 1. Then sent to a sensor in HA. Set the automation to run when and if that sensor hits zero.

Two things you will loose any seconds while HA is (hard)restarting/down. As long as the node red container is still running it shouldn’t stop the inject node from decreasing the count. You also need to enable context storage in nodered. It won’t be seconds precise but it will be pretty close.

[{"id":"ebfff19f.2b8d4","type":"function","z":"ec78a76b.a0a4d","name":"counter","func":"var count = context.get(\"count\");\n\nif(msg.topic == \"reset\" || msg.reset == true) {\n    count = 55000000;\n} else if (msg.topic == \"set\") {\n    count = msg.payload;\n} else if (msg.topic == \"increment\" || msg.topic == \"inc\") {\n    count += 1;\n} else {\n    //assume decrement\n    count -= 1;\n}\ncontext.set(\"count\", count); //store it\nnode.status({ text: count });\n\nmsg.count = count; //pass current count in msg.count\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":695,"y":700,"wires":[["69406e9a.ef3928"]],"icon":"node-red-dashboard/ui_numeric.png","l":false},{"id":"dd08640d.81c85","type":"server-state-changed","z":"ec78a76b.a0a4d","name":"input number","server":"7c85afe1.bbea8","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"str","halt_if_compare":"is","outputs":1,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":290,"y":700,"wires":[["53f1c705.6195d8"]]},{"id":"53f1c705.6195d8","type":"change","z":"ec78a76b.a0a4d","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"set","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":550,"y":700,"wires":[["ebfff19f.2b8d4"]]},{"id":"948d2d9d.23e738","type":"inject","z":"ec78a76b.a0a4d","name":"1 second inject","props":[],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":320,"y":620,"wires":[["53f1c705.6195d8"]]},{"id":"69406e9a.ef3928","type":"ha-entity","z":"ec78a76b.a0a4d","name":"","server":"","version":1,"debugenabled":false,"outputs":1,"entityType":"sensor","config":[{"property":"name","value":""},{"property":"device_class","value":""},{"property":"icon","value":""},{"property":"unit_of_measurement","value":""}],"state":"payload","stateType":"msg","attributes":[],"resend":true,"outputLocation":"payload","outputLocationType":"none","inputOverride":"allow","outputOnStateChange":false,"outputPayload":"$entity().state ? \"on\": \"off\"","outputPayloadType":"jsonata","x":890,"y":700,"wires":[[]]},{"id":"7c85afe1.bbea8","type":"server","name":"Home Assistant","legacy":false,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true}]

This flow will satisfy the first example

[{"id":"939764ab.2f4dc8","type":"inject","z":"d6a46901.ebee1","name":"Set timer value","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"not_home","payloadType":"str","x":120,"y":1300,"wires":[["7b41913f.999eb"]]},{"id":"67884340.a8a354","type":"inject","z":"d6a46901.ebee1","name":"inject every second","props":[{"p":"payload"}],"repeat":"1","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"dec","payloadType":"msg","x":140,"y":1500,"wires":[["9fcc8661.0c9908"]]},{"id":"6ad0c657.01c14","type":"api-call-service","z":"d6a46901.ebee1","name":"","server":"","version":1,"debugenabled":true,"service_domain":"input_boolean","service":"turn_on","entityId":"input_boolean.boo","data":"","dataType":"jsonata","mergecontext":"","output_location":"","output_location_type":"none","mustacheAltTags":false,"x":1120,"y":1400,"wires":[[]]},{"id":"6356c186.321d7","type":"switch","z":"d6a46901.ebee1","name":"","property":"count","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"},{"t":"eq","v":"count","vt":"msg"}],"checkall":"true","repair":false,"outputs":2,"x":910,"y":1280,"wires":[["20bca88.5d7f958"],[]]},{"id":"20bca88.5d7f958","type":"change","z":"d6a46901.ebee1","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"on","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1020,"y":1340,"wires":[["6ad0c657.01c14"]]},{"id":"5f293e4b.789cb","type":"group","z":"d6a46901.ebee1","name":"","style":{"stroke":"#0070c0","label":true},"nodes":["fcab385e.4e18a","7b41913f.999eb","ad753071.423ab"],"x":34,"y":1119,"w":572,"h":122},{"id":"fcab385e.4e18a","type":"server-state-changed","z":"d6a46901.ebee1","g":"5f293e4b.789cb","name":"","server":"","version":1,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"person.mike_fila","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"home","halt_if_type":"str","halt_if_compare":"is","outputs":2,"output_only_on_state_change":true,"for":0,"forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"x":190,"y":1200,"wires":[[],["7b41913f.999eb"]]},{"id":"7b41913f.999eb","type":"change","z":"d6a46901.ebee1","g":"5f293e4b.789cb","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"set","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"172800","tot":"num"}],"action":"","property":"","from":"","to":"","reg":false,"x":500,"y":1200,"wires":[["360705a9.6994ca"]]},{"id":"ad753071.423ab","type":"comment","z":"d6a46901.ebee1","g":"5f293e4b.789cb","name":"Resets timer when not home.  Set timer in change node.","info":"","x":340,"y":1160,"wires":[]},{"id":"a37b95af.a38e78","type":"group","z":"d6a46901.ebee1","name":"","style":{"stroke":"#0070c0","label":true},"nodes":["360705a9.6994ca","6a773bf1.56a704"],"x":734,"y":1119,"w":532,"h":122},{"id":"360705a9.6994ca","type":"function","z":"d6a46901.ebee1","g":"a37b95af.a38e78","name":"counter","func":"var count = context.get(\"timer\");\n\nif(msg.topic == \"reset\" || msg.reset == true) {\n    count = 55000000;\n} else if (msg.topic == \"set\") {\n    count = msg.payload;\n} else if (msg.topic == \"increment\" || msg.topic == \"inc\") {\n    count += 1;\n} else {\n    //assume decrement\n    count -= 1;\n}\ncontext.set(\"timer\", count); //store it\nnode.status({ text: count });\n\nmsg.count = count; //pass current count in msg.count\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":775,"y":1200,"wires":[["6356c186.321d7"]],"icon":"node-red-dashboard/ui_numeric.png","l":false},{"id":"6a773bf1.56a704","type":"comment","z":"d6a46901.ebee1","g":"a37b95af.a38e78","name":"For each timer create a different name. Replace \"timer\" in 2 locations","info":"","x":1000,"y":1160,"wires":[]},{"id":"dd4736f9.2429d8","type":"group","z":"d6a46901.ebee1","style":{"stroke":"#2b2b2b","stroke-opacity":"1","fill":"none","fill-opacity":"1","label":true,"label-position":"nw","color":"#a4a4a4"},"nodes":["9fcc8661.0c9908","473f53f4.f714b4","f6d9250c.ffb5e"],"x":174,"y":1339,"w":632,"h":122},{"id":"9fcc8661.0c9908","type":"api-current-state","z":"d6a46901.ebee1","g":"dd4736f9.2429d8","name":"","server":"","version":1,"outputs":2,"halt_if":"not_home","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"person.mike_fila","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":330,"y":1420,"wires":[["473f53f4.f714b4"],[]]},{"id":"473f53f4.f714b4","type":"api-current-state","z":"d6a46901.ebee1","g":"dd4736f9.2429d8","name":"","server":"","version":1,"outputs":2,"halt_if":"off","halt_if_type":"str","halt_if_compare":"is","override_topic":false,"entity_id":"input_boolean.boo","state_type":"str","state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","blockInputOverrides":false,"x":640,"y":1420,"wires":[["360705a9.6994ca"],[]]},{"id":"f6d9250c.ffb5e","type":"comment","z":"d6a46901.ebee1","g":"dd4736f9.2429d8","name":"Stops timer countdown if person home and/or input boolean turned on","info":"","x":470,"y":1380,"wires":[]}]

FWIW I solved restart state loss early on trying to code so everything will go to a known state.
In my case, the issue is usually everything else resetting with power blips (server is on a UPS for at least an hour) so I typically add in a on-start trigger that will initialize sane defaults. Ideally I shouldn’t be restarting it if everything is working properly.

The few things that don’t work right sort themselves out I assume are okay as-is until the next event (e.g. hvac temp setpoint if the server went down I probably manually set the temperature)

I also have “from unknown/unavailable to off/on” type triggers on most of my things so if we take a power-hit and the switch comes up in the wrong state it will re-sync my plugs/switches to the server’s state.

Ah, so if you put a number it will assume seconds? Is there an upper max limit to worry about?

Those are all excellent mitigation techniques but, as you apparently already know, unable to prevent for from surviving a restart. It and other constructs that perform a countdown (delay, etc) should be used judiciously because very long durations increase the odds of being reset due to a restart.

BTW, if you ever have the time, post some of your mitigation techniques in Community Guides so others can learn and benefit from them.

1 Like

If I can figure out where to post a community guide it I’d be happy to. (EDIT: I think I found the guide forum, I’ll have to polish and write something)

In a perfect world I’d probably try and figure out if “blueprints” can be used to simplify my stuff but I use my own style template for almost everything where I have a group of booleans for “X should be on”

Here’s one such example…my way does cause a “blink” when restarting the server as everything initializes to “off” then comes “on” but I’m okay with that because its rare I restart my server unless I have to fix something.

group:
  # This group is all the booleans that can turn the device "on"
  kitchen_lamp_on_conditions:
    entities:
      - input_boolean.kitchen_lamp_timers_on
      - input_boolean.kitchen_lamp_home_nothome_on
#      - input_boolean.kitchen_lamp_night_entering_on

  # This group is all the booleans that can turn the device off and block it from being turned on
  kitchen_lamp_blocker_conditions:
    entities:
      - input_boolean.kitchen_lamp_sunny_lockout_on

automation:

  # Turn the device on if any "on" condition and no "block" conditions are met
  - alias: "kitchen_lamp_turn_on"
    trigger:
      - platform: state
        entity_id: group.kitchen_lamp_on_conditions
        to: 'on'
      - platform: state
        entity_id: group.kitchen_lamp_blocker_conditions
        to: 'off'
      - platform: state
        entity_id: switch.s31_kitchen_light_relay
        from: 'unavailable'
        to: 'off'
      - platform: homeassistant
        event: start
    condition:
      - condition: state
        entity_id: group.kitchen_lamp_on_conditions
        state: 'on'
      - condition: state
        entity_id: group.kitchen_lamp_blocker_conditions
        state: 'off'
    action:
      service: switch.turn_on
      entity_id: switch.s31_kitchen_light_relay


  # Turn the device off if no "on" conditions are met, or if any "block" conditions are met
  - alias: "kitchen_lamp_turn_off"
    trigger:
      - platform: state
        entity_id: group.kitchen_lamp_on_conditions
        to: 'off'
      - platform: state
        entity_id: group.kitchen_lamp_blocker_conditions
        to: 'on'
      - platform: state
        entity_id: switch.s31_kitchen_light_relay
        from: 'unavailable'
        to: 'on'
      - platform: homeassistant
        event: start
    condition:
      - condition: or
        conditions:
          - condition: state
            entity_id: group.kitchen_lamp_on_conditions
            state: 'off'
          - condition: state
            entity_id: group.kitchen_lamp_blocker_conditions
            state: 'on'
    action:
      service: switch.turn_off
      entity_id: switch.s31_kitchen_light_relay

And then all my other automations which control the “inputs” to these also generally have a trigger on hassio-start to ensure they initialize to the state I want.

I’ve been told my “style” trying to apply encapsulatiton and “functions” would be bettter suited to using Python for automations but the only thing I hate more than YAML is Python…I’m a developer using C++/Java/Ada in real life but I’d like to think I do decently.

Here’s the personal rules I made to help keep myself sane without building myself into a corner

Designing modular flexable controls:
------------------------------------------------------------------------
Goal:
Allow independant unrelated logic chains to turn the light on for different reasons
Turn on the light if any of them are on, unless a blocker rule is set
Turn off thte light if all the rules are off, or if any blocker rules are set
Could allow you to "flash" a light without losing state by setting an "on" condition and cycling a "blocker" on and off (if no other blockers are set...need special flasher?)

Structure:

Packages:
- Folder for type of object (e.g. lamp)
        - Folder per object (lamp_livingroom)
                - File for overall on-off (lamp_livingroom.yaml)
                        - Groups
                                - Group for all the _on_conditions
                                - Group for all the _blocker_conditions
                        - Automation
                                - Turn on when _on_conditions goes on or _blocker_conditions goes off
                                  * conditions _on_conditions must be on
                                  * conditions _blocker_conditions must be off
                                  * trigger when plug unknown -> off (resync plug rebooted)
                                  * trigger when hassio start (resync when server rebooted)
                                - Turn off when _on_conditions goes off or _blocker_conditions goes on
                                  * conditions OR
                                                _on_conditions goes off
                                                _blocker_conditions goes on
                                  * trigger when plug unknown -> off (resync plug rebooted)
                                  * trigger when hassio start (resync when server rebooted)
                - File for each input_boolean by category (lamp_livingroom_basic_timer, lamp_livingroom_home_or_not_home, lamp_livingroom_phantom_guest, lamp_livingroom_entering_exiting)
                        - Input_Boolean
                                - Create the input_boolean
                                  * Set initial state false
                        - Groups
                                - Group for all automations that control a category (thing_timer_automations)
                        - Automations
                                - watchdog automation, which will toggle the input_boolean off if all the automations (the group) go "off"
                                  * name with filename_watchdog (e.g. lamp_livingroom_basic_timer_watchdog)
                                - Create all the automations that will control input_boolean on or off as needed
                                  * name with filename_descrip (e.g. lamp_livingroom_basic_timer_on_morning; lamp_livingroom_basic_timer_off_morning)
                                  * for automations that turn it on, when possible set conditions and also trigger by hassio-start to handle restarts

I also have a personal rule that anything I automate should be overridden by someone in person flipping the switch so I kinda accept thaht state may be mismatched under some cases, and I never automate any “primary” lighting/etc to avoid a mistake leaving someone in the dark. Had to break this rule in my garage (some nitwit electrician put only one light-switch, none by the exterior door) but it works well for almost everything.

The modular way also makes debugging easier, I don’t have to worry about interaction between stuff if something should be on for more than one complex reason. Or if things should be off for more than one reason.

If in doubt, I also generally shut everything off. One of my most hated thing with smart-bulbs (I use smart-switches or smart-plugs) is the bulbs default ON when the power blinks. IMO everything should (almost) always default-off when power is interrupted.

One thing I learned the hard way, NEVER have duplicate YAML names – HomeAssistant will only read the first it sees AND WILL NOT THROW ANY WARNINGS OR ERRORS. Its a PITA to find.

The issue with that is what happens if you are having an issue with the hub that would otherwise control the light?

If the light was default ‘off’ you are then locked in to having the hub to turn the light on. If the hub is down then you could never turn the light on.

the way it is with the default ‘on’ after a power cycle you still have total control of your lights even if the hub is down. To turn the light on manually just flip the power off to the bulb and turn it back on and the light comes on.

The problem with that though is every time the power blips all the lights turn on and then you have to go turn them all off again.

I gave up on smart-bulbs for this reason…too often they would get stuck on after any little blip or storm and not always re-connect without my manually turning them off for longer, then on, then command them off thru the hub.

During some outages (tree on line?) the auto-recloser trying to reset the lines would cycle on/off enough to even reset/unpair bulbs which takes even more work to fix. If you’ve ever had your power go off-on-off 3-5 times at a regular interval (say 1 second) during a storm at a regular interval, that’s a recloser trying to resolve a fault somewhere “upstream” of your house. At least in my area, its quite common.

Once in a while the switches get confused but they usually self-heal in about 10 minutes and worst case they work like an old fashion manual switch.

I’m actually considering putting a smart switch on my motion-light (part of the house) because it suffers a similar problem where brief power blips cause it to turn on and get stuck on instead of motion-sensing…has to be off for ~30 seconds to “reset” to normal.

I completely agree that smart switches are the way to go.

I use very few smart bulbs and only in areas that either the light stays on pretty much all the time or doesn’t have the availability/need to add a smart switch. I think I only have 6 smart bulbs total running right now.

I was just trying to explain why the bulbs almost have to function the way they do. Both situations suck. But having them default to off would suck way worse.

You can have that happen to some smart switches to depending on how the pairing sequence is configured. But yes it’s way more likely to happen on bulbs.

I even had a bulb un-pair on me once when I was unscrewing it from the socket. I didn’t realize (or care :wink:) that the power was on and the little bit of jiggle from the unscrewing process made enough on-off contact with the base it caused it to un-pair. It took a bit to figure out why it stopped working. :laughing: