Sleeping in a script?

I noticed this note from source.
Doesn’t look like this every really got anything to run async and I was hoping to create a loop that ran on precise intervals.
Is it still a problem to use time.sleep inside of python_scripts?

by script do you mean python script or HA script?
If ha script, the service is:
delay: 00:00:30
But you can also use a wait_template to wait until something happens:

    - wait_template: '{{ states("alarm_control_panel.alarm").split("_")[0] != "triggered"}}'
      timeout: '00:15:00'

could you explain why you need time.sleep?

I had an idea which I was looking to get started on but I can’t decide where it best fits. I want to make an intelligent light timer that updates an actionable notification in the ios app. Playing through the scenario for my garage:

  • If someone opens the door to the garage and it is dark out and the lights are off then turn on the lights for 5 minutes (i have an automation do this now)
  • Update an actionable notification on the ios app that shows the current coutdown and allows a user to:
    • sleep (add 5 minutes to the counter)
    • disable (don’t do anything else until the user shuts off the light)
  • Opening the door while the timer is active will set the timer count to max(count, 5)

I think this means that I need either a polling loop or some async event handlers tied to a counter.

This seems beyond one or two automations and scripts don’t really seem to help that. python_scripts look like maybe they could do this but there would be a lot of time.sleep() and that appears to be discouraged. I’m thinking its a custom_component?

I hadn’t read about the wait template yet, maybe:

  • automation 1:
    • trigger on door events and update counter to max(count, 5)
  • automation 2:
    • trigger on counter state and wait_template for counter value of -1 (timeout of 1 minute)
    • decrement counter and shut off on 0
  • automation 3:
    • handle ios events to update counter value

Really wanted this to be in a single automation but it doesn’t appear there is branching to support the different triggers.

I seem to have this working now as a script and three automations. Not quite as contained as I’d like as I plan to reuse for other lighting areas around the house.

The script was needed to prevent the automation from firing twice on the wait_interval

light_timer_delay:
  description: Timed delay to decrement count
  fields:
    counter_id:
      description: entity_id of counter to modify
    expected_state:
      description: present state of counter_id
  sequence:
    # wait one minute to decrement counter unless iOS user changes count
    - wait_template: "{{ states(counter_id) != expected_state }}"
      timeout: '00:01:00'
    - service: input_number.decrement
      data_template:
        entity_id: "{{ counter_id }}"

Then the automations

- id: garage_light_automation_1
  alias: Garage Lights Door Event Handler
  description: Update garage_timer_input on door events when it is night
  trigger: 
  - platform: state
    entity_id: binary_sensor.garage_door
    from: 'off'    
    to: 'on'
  condition:
  # only do this when it is dark out
  - condition: state
    entity_id: sun.sun
    state: below_horizon
  # don't do this when the timer is disabled (-1) and the garage light is on
  - condition: or
    conditions:
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: -1
    - condition: state
      entity_id: light.frst_garage_interior
      state: 'off'
  action:
    - service: input_number.set_value
      data_template:
        entity_id: input_number.garage_timer_input
        value: "{{ [states('input_number.garage_timer_input')|int, 5]|max }}"
- id: garage_light_automation_2
  alias: Garage Lights Counter State Handler
  description: Decrement garage_timer_input and take actions
  trigger: 
    - platform: state
      entity_id: input_number.garage_timer_input
  action:
    - delay: '00:00:01'
    # a value of '-1' indicates that the timer has been disabled by iOS app 
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: -1
    # a count of '0' means we turn off, otherwise stay on
    - service_template: >
        {% if states('input_number.garage_timer_input')|int == 0 %}
          light.turn_off
        {% else %}
          light.turn_on
        {% endif %}
      entity_id: light.frst_garage_interior
    # Nothing further to do if the count is 0
    - condition: numeric_state
      entity_id: input_number.garage_timer_input
      above: 0
    # update iOS app notification (apns-collapse-id: garge-interior-light)
    - service: notify.mobile_app_devans_iphone
      data_template:
        title: Garage Timer
        message: Garage lights will turn off in {{ trigger.to_state.state|int }} minutes.
        data:
          apns_headers:
            apns-collapse-id: garge-interior-light
          push:
            category: gargetimer
          action_data:
            entity_id: input_number.garage_timer_input
    # call script to wait for timer change (ie disabled) or decrement on interval
    - service: script.light_timer_delay
      data_template:
        counter_id: input_number.garage_timer_input
        expected_state: "{{ trigger.to_state.state }}"
- id: garage_light_automation_3
  alias: Garage Lights iOS App Event Handler
  description: Update garage_timer_input on app events
  trigger:
  - platform: event
    event_type: ios.notification_action_fired
    event_data:
      action_data:
        entity_id: input_number.garage_timer_input
  action:
    # set timer value based on button pressed in iOS notification
    service: input_number.set_value
    data_template:
      entity_id: input_number.garage_timer_input
      value: >
        {% if trigger.event.data.actionName == "SLEEP_TIMER" %}
          {{ states('input_number.garage_timer_input')|int + 6 }}
        {% else %}
          -1
        {% endif %}

Probably the last piece to share is the notification config:

ios:
  push:
    categories:
      - name: Garage Timer
        identifier: 'gargetimer'
        actions:
          - identifier: 'SLEEP_TIMER'
            title: 'Sleep'
            activationMode: 'background'
            authenticationRequired: false
            destructive: false
            behavior: 'default'
          - identifier: 'DISABLE_TIMER'
            title: 'Disable'
            activationMode: 'background'
            authenticationRequired: false
            destructive: false
            behavior: 'default'