State Trigger & Time Condition Question

I am trying to setup a trigger, condition and action for an automation and not sure how to go about doing it. Here is a list of the rules I am trying to implement.

  1. An entity’s state will change from off to on at some random time.
  2. I want to perform the action but only during the time period of 8:30AM to 5:30PM.
  3. If the entity’s state changes outside this time period, then I want it queued up and action performed at 8:30AM.
  4. If the entity’s state changes during this time period, then I want the action performed immediately.

Any ideas on how to implement? Here is sample code I tried and I know it doesn’t work because it’s only looking for the trigger to occur only during the hours of 8:30AM to 5:30PM.

alias: Automation Name
trigger:
  - platform: state
    entity_id: binary_sensor.pool
    to: 'on'
condition:
  - condition: time
    after: '08:30:00'
    before: '17:30:00'
action:
   "action will be placed here"

I appreciate any help or tips!!!

Thanks,

You could use a choose block.

If it is not in the time window, use a wait_for_trigger to wait until 08:30.

alias: Automation Name
trigger:
  - platform: state
    entity_id: binary_sensor.pool
    to: 'on'
action:
  - choose:
      - alias: "Waiting"
        conditions:
          - condition: time
            before: '08:30:00'
            after: '17:30:00'
        sequence:
          - wait_for_trigger:
              - platform: time
                at: '08:30:00'
  - "action will be placed here"

NB: I haven’t validated my indenting there, I’ve just quickly pulled that together here.

I would use a choose block for the actions and an input boolean helper to save the fact that the sensor turned ‘on’ overnight…

alias: Pool Automation
description: ''
trigger:
  - platform: time
    at: '08:30:00'
    id: Morning
  - platform: state
    entity_id: binary_sensor.pool
    to: 'on'
    id: Sensor
condition: []
action:
  - choose:
      - alias: "If sensor turns on overnight, turn on boolean"
        conditions:
          - condition: trigger
            id: Sensor
          - condition: time
            before: '08:30:00'
            after: '17:30:00'
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.pool_sensor
      - alias: "If sensor turns on during the day OR boolean is 'on' at 8:30am trigger, run actions and reset boolean for tonight"
        conditions:
          - condition: template
            value_template: >-
              {{ trigger.id == "Sensor" or is_state('input_boolean.pool_sensor', 'on') }}
        sequence:
          - service: ***your action here***
          - condition: state
            entity_id: input_boolean.pool_sensor
            state: 'on'
          - service: input_boolean.turn_off
            target:
              entity_id: input_boolean.pool_sensor
    default: []
mode: single

Thanks for both the suggestions. I wrote a test automation and it works great. Appreciate the input!!!

Thanks again

Yet another way to do the same thing. For this you need to create an input_datetime entity that stores both date and time (named input_datetime.delayed_pool in the following example).

alias: Pool Automation
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.pool
    to: 'on'
  - platform: time
    at: input_datetime.delayed_pool
condition: []
action:
  - choose:
      - conditions:
          - condition: time
            after: '08:30:00'
            before: '17:30:00'
        sequence:
          ... your actions go here ..
    default:
      - service: input_datetime.set_datetime
        target:
          entity_id: input_datetime.delayed_pool
        data:
          timestamp: "{{ (today_at('08:31') + timedelta(days=1 if now().hour >= 17 else 0))  | as_timestamp }}"
mode: single

How it works:

  • If the binary_sensor changes to on inside the specified time range, it executes your actions immediately.

  • If the binary_sensor changes to on outside the specified time range, it sets the input_datetime to tomorrow at 08:31. The next morning at 8:31 the Time Trigger will fire and your actions will be executed.

Easy-peasy.


EDIT

Correction. Adapt for triggers that occur before the time range and after it.

timedelta(days=1 if now().hour >= 17 else 0)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^

BTW, the drawback of using a wait_for_trigger (and several other things like delay, wait_template, for, etc) is that it is reset whenever you Reload Automations or restart Home Assistant.

In this case, the wait_for_trigger must wait many hours until the following morning so if a reload/restart occurs before then it’s cancelled and your actions won’t be executed.

FWIW, if you use the Automation Editor to create a new automation, or modify an existing one, it performs a Reload Automations the moment you save the automation.

1 Like

Won’t this set the input_datetime to the wrong day if the triggering sensor turns “on” after midnight?

You’re absolutely right; thanks for pointing it out.

This should handle the pre/post midnight issue:

{{ (today_at('08:31') + timedelta(days=1 if now().hour >= 17 else 0)) }}

Being new to Home Assistant, I have not used the time stamp features yet. In your code suggestion can you please explain exactly what the below line of code is doing. I understand all of it except the last part with the | as_timestamp. Not sure what that last part does but the rest I understand.

{{ (today_at('08:31') + timedelta(days=1 if now().hour >= 17 else 0)) | as_timestamp }}

Thanks, I like your suggested automation.

The input_datetime.set_datetime service call is explained in the documentation. The relevant portion is this:

Service data attribute Format String Description
datetime %Y-%m-%d %H:%M:%S This can be used to dynamically set both the date & time.
timestamp N/A This can be used to dynamically set both the date & time using a UNIX timestamp.

In the example I posted, I chose to use the service call’s timestamp option. It requires a floating-point number representing the number of seconds since the Unix Epoch (i.e. a Unix timestamp).

This portion of the template produces a datetime object.

today_at('08:31') + timedelta(days=1 if now().hour >= 17 else 0)

We need to convert the datetime object to a timestamp value and for that we use the as_timestamp filter which is described here.

as_timestamp(value, default) converts datetime object or string to UNIX timestamp. If that fails, returns the default value, or if omitted None. This function also be used as a filter.

Thanks, that really helped.

If it has answered your original question, please consider marking my post above with the Solution tag. It will automatically place a check-mark next to the topic’s title which signals to other users that this topic has been resolved. It will also place a link below your first post that leads to the Solution. All of this helps users find answers to similar questions. For more information refer to guideline 21 in the FAQ.