WTH aren't random delays built into the UI?

Delaying actions for a randomized amount of time is a pretty fundamental home automation feature and shouldn’t require YAML editing to use.

What is a use case for a random delay time over specified delay time?

Typically random(ish) times for lights going on and off when you’re not home.

3 Likes

I would use it to cycle spotlight colors. Instead of a script that randomizes each bulb every x seconds, I can randomize the wait between changes to add more randomness. Webcore had this built in. You just had to define your upper and lower limits.

What is your proposal for how this “randomized amount of time” should appear in Home Assistant?

When the “Wait for a time to pass (delay)” action type is selected, provide an additional time field that would be a +/- to add to the specified delay. Or maybe just a + and treat the delay as the lower limit.

Until your proposal is implemented (if ever), this is how it can be done now:

 - delay:
     minutes: '{{ range(-20, 21) | random }}'

The value of delay is randomly selected from a range of values from -20 to 20, inclusively (the range function requires the upper limit to be one more than the desired value so that’s why it appears as 21 in the example).

You should know that a drawback of using delay in an automation (or script) is that if Home Assistant is restarted, or Reload Automations is executed, while the delay is counting down, the automation is terminated. Therefore the delay never finishes counting down and whatever actions that come after it (like turning on/off a light) are never executed. The longer the delay, the more vulnerable it is to being terminated by a restart/reload.

The same is true for any scripting statement that causes the automation (or script) to “loiter” so that includes delay, wait_template, wait_for_trigger, repeat, etc. It also applies to the for option of a State Trigger (and Numeric State Trigger).

If you use the Automation Editor, when you click its Save button it automatically, and transparently, executes Reload Automations (thereby terminating all in-progress automations).

A more robust technique would be to employ a timer with a random duration. Another alternative is to use a Time Trigger triggered by a randomized time produced by a Template Sensor.

2 Likes

I like your last two suggestions. I’ll have to look up how to create a template sensor to provide a randomized time.

Someone else asked for the ability to trigger at sunrise and sunset plus or minus a random value (between -25 and +25 minutes). I supplied them with the following Trigger-based Template Sensors:

It would be easy to adapt it to suit your needs.

2 Likes

I like the random timer method!

alias: Automatic - Patio light off
description: ""
trigger:
  - platform: time
    at: "00:45:00"
    id: Start Timer
  - platform: event
    event_type: timer.finished
    event_data:
      event_data:
        entity_id: timer.patio_lights
    id: Timer Finished
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - Start Timer
        sequence:
          - service: timer.start
            data:
              duration:
                hours: "{{ range(0,2) | random }}"
                minutes: "{{ range(9,49) | random }}"
                seconds: "{{ range(15,45) | random }}"
            target:
              entity_id: timer.patio_lights
      - conditions:
          - condition: trigger
            id:
              - Timer Finished
          - condition: or
            conditions:
              - condition: state
                entity_id: person.daniel_brunt
                state: not_home
                enabled: true
              - condition: state
                entity_id: sensor.season
                state:
                  - autumn
                  - winter
                  - spring
        sequence:
          - service: light.turn_off
            data: {}
            target:
              entity_id: light.patio_light
mode: single

It seems like the approach from @123 doesn’t actually work anymore?

I used that exact code to create a delay action in an Automation:

- delay:
     minutes: '{{ range(-20, 21) | random }}'

but it returns an error on save now:

Message malformed: expected dictionary @ data['action'][0]

This is on:
Home Assistant 2023.10.3
Supervisor 2023.10.0
Operating System 11.0
Frontend 20231005.0 - latest

1 Like

Not sure what was on my mind when I posted that but you can’t specify negative minutes for delay. :man_facepalming:t3:

My previous example used negative minutes for timedelta (which is valid) and I probably made a careless leap of logic and suggested to use the same technique with delay (which is invalid).

I would like to offer the following “Random Delay” script based on the suggestion above. I was looking for a generalised way to provide a random delay, perhaps after a fixed time or wait for sunset.

alias: Random Delay
icon: mdi:clock-time-five
fields:
  minimum:
    name: Minimum
    description: The lower value of the range from which to choose a random number
    selector:
      number:
        min: 0
        max: 120
        step: 1
        unit_of_measurement: minutes
        mode: box
    required: true
  maximum:
    name: Maximum
    description: The upper value of the range from which to choose a random number
    selector:
      number:
        min: 0
        max: 120
        step: 1
        unit_of_measurement: minutes
        mode: box
    required: true
sequence:
  - delay: >-
      {{ (range(min(minimum, maximum), max(minimum, maximum)+1) | random) * 60 
      }}
mode: parallel

This would then allow the script to be added to a sequence of Actions in automations as shown below.

Care has been taken to avoid an erroneous range(-10, 10) which is empty and to make the range inclusive of the maximum value, hence the “+1” in the formula.

Being new to Home Assistant, I went round the houses with trials before settling on this neat and simple solution, with confirmation from the posts above that its probably about right.

4 Likes

Reminder:

Users of your script should call it directly and not via script.turn_on for the reason explained here.

1 Like

Indeed, as shown in the included graphic providing a simple example usage!