Random for cover and lights

Hello all,

how should I do my automation to use my script? Or any other solution to do this? The entity_id should come from a group like group.light_floor_1 or group.cover_floor_1

  light_random_on_test:
    sequence:
      - delay: "00:00:{{ range(3, 15) | random }}"
      - service: light.turn_on
        data_template:
          entity_id: "{{ eid }}"
          
  cover_random_down_test:
    sequence:
      - delay: "00:00:{{ range(5, 25) | random }}"
      - service: cover.set_cover_position
        data_template:
          entity_id: "{{ eid }}"
          position: "{{ range(10, 23) | random }}"
action:
  - service: script.light_random_on_test
    data_template:
      eid: "{{ states.group.light_floor_1 .attributes.entity_id | random }}"
  - servcie: script.cover_random_down_test
    data_template:
      eid: "{{ states.group.cover_floor_1.attributes.entity_id | random }}"

Thanks! It works for one entity_id of the group.

How can I do it for all entity_id of the group?

best case: turn_on one of the entity_id (light) again delay from the script, next light goes on

Oh, ha. Thought you just wanted to turn on one light in the group.

input_number:
  light_index:
    name: Light Index
    initial: 0
    min: 0
    max: 35
    step: 1
script:
  # Since a script can't call itself recursively, using 2 scripts to call each other. 
  light_scheduler:
    sequence:
      # Turn on the current index
      - service: script.light_random_on_test
        data_template: 
          eid: "{{ state_attr(group, 'entity_id')[states('input_number.light_index') | int] }}"
      - service: input_number.set_value
        data_template:
          entity_id: input_number.light_index
          # Add 1 and wrap around. 
          value: "{{ (states('input_number.light_index') | int + 1 ) %  expand(group) | list | count }}"


  light_random_on_test:
    sequence:
      # Waits a few seconds, turns on a light, then checks the light_index
      # to see if it should call again. 
      - delay: "00:00:{{ range(3, 15) | random }}"
      - service: light.turn_on
        data_template:
          entity_id: "{{ eid }}"
      # Light index is incremented by the schedule script. If it's 0, it means it has wrapped around
      # and there is nothing left to do.....so don't schedule the light_scheduler again.
      - condition: template
        value_template: "{{ not is_state('input_number.light_index', '0') }}"
      - service: script.light_scheduler

Now just call light_scheduler and pass in a group.

service: light_scheduler
data: 
  group: group.light_floor_1

The cover would do the exact same thing. Create a new input_number for the cover.

OR

Write a python script to do this. It would probably be 10x easier!

Hey, thanks a lot for your help!

Still only one light turns on after call:

service: script.light_scheduler
data: 
  group: group.light_floor_1

Whoops, I forgot to pass the group in the recursive script part in the 2nd script

Sorry, could you please tell me how?

pretty sure he edited his response, just try what he posted again. edit:maybe not

Yeah! I thought you knew how to pass variables since your scripts took them as inputs.

script:
  # Since a script can't call itself recursively, using 2 scripts to call each other. 
  light_scheduler:
    sequence:
      # Turn on the current index
      - service: script.light_random_on_test
        data_template: 
          eid: "{{ state_attr(group, 'entity_id')[states('input_number.light_index') | int] }}"
          # Pass the group to the next script so it can call this one back with the same group id.
          group: "{{ group }}"
      - service: input_number.set_value
        data_template:
          entity_id: input_number.light_index
          # Add 1 and wrap around. 
          value: "{{ (states('input_number.light_index') | int + 1 ) %  expand(group) | list | count }}"


  light_random_on_test:
    sequence:
      # Waits a few seconds, turns on a light, then checks the light_index
      # to see if it should call again. 
      - delay: "00:00:{{ range(3, 15) | random }}"
      - service: light.turn_on
        data_template:
          entity_id: "{{ eid }}"
      # Light index is incremented by the schedule script. If it's 0, it means it has wrapped around
      # and there is nothing left to do.....so don't schedule the light_scheduler again.
      # Keep in mind, this is a race condition. It works here because this script starts with
      # a delay. It's not good code, that's for sure! We are relying on that delay so the first
      # script had time to increment the index before this script evaluates it. And I'm sure there
      # exists hardware in which even a 3 second delay would fail. 
      - condition: template
        value_template: "{{ not is_state('input_number.light_index', '0') }}"
      - service: script.light_scheduler
        data_template:
          group: "{{ group }}"

To explain, in a script, you’ll often see variables used as “{{ variable }}”. Those get passed in with the data (or data_template) fields by whoever called the script.

You can name them whatever you want. Here’s an example…

script:
  useless_script: 
    sequence: 
      service_template: "{{ the_service_to_call }}"
      data_template:
        entity_id: "{{ the_entity_id }}"

This is just a wrapper script that calls some service on some entity id. There would be very few reasons to abstract a service call like this.

To use it, you now just call “script.useless_script” like so

service: script.useless_script
data_template: 
  the_service_to_call: "light.turn_on"
  the_entity_id: "light.my_light_id"

As you can see, we’re passing light.turn_on as “the_service_to_call” and “light.my_light_id” as the_entity_id.

Thank you very much again! Special for your explanations! :+1: :grinning:

Still, only 1 light is turning on after running the automation.

Ha. Yeah, I see what you mean now. Just loaded them all here and it complains that script.light_random_on_test is already running. Looking at the states, I can’t seem to stop that script either. It’s just stuck on forever…

Ok, this worked for me.

# Since a script can't call itself recursively, using 2 scripts to call each other. 
light_scheduler:
  sequence:
    # Cancel the other script if it's still running. 
    - service: script.turn_off
      entity_id: script.light_random_on_test
    - service: script.light_random_on_test
      data_template: 
        eid: "{{ state_attr(group, 'entity_id')[states('input_number.light_index') | int] }}"
        # Pass the group to the next script so it can call this one back with the same group id.
        group: "{{ group }}"
    - service: input_number.set_value
      data_template:
        entity_id: input_number.light_index
        # Add 1 and wrap around. 
        value: "{{ (states('input_number.light_index') | int + 1 ) %  expand(group) | list | count | int }}"

light_random_on_test:
  sequence:
    # Waits a few seconds, turns on a light, then checks the light_index
    # to see if it should call again. 
    - delay: "00:00:{{ range(2, 5) | random }}"
    - service: light.turn_on
      data_template:
        entity_id: "{{ eid }}"
    # Light index is incremented by the schedule script. If it's 0, it means it has wrapped around
    # and there is nothing left to do.....so don't schedule the light_scheduler again.
    # Keep in mind, this is a race condition. It works here because this script starts with
    # a delay. It's not good code, that's for sure! We are relying on that delay so the first
    # script had time to increment the index before this script evaluates it. And I'm sure there
    # exists hardware in which even a 3 second delay would fail. 
    - condition: template
      value_template: "{{ states('input_number.light_index') | int != 0 }}"
    - service: script.light_scheduler
      data_template:
        group: "{{ group }}"

I just cancel the other script. I did have to manually set the state of that other script to ‘off’ one time (I must have got it stuck with all of my syntax errors).

Now you have a script that turns lights on…but not one that turns them off. Think about passing the servcie into light_random_on_test so you can randomly turn them off too!

- service_template: "{{ service }}"
  data_template:
    entity_id: "{{ eid }}"

Sorry for my late replay. rrrrrrr* still, only one light turns on :frowning:

here are my config:

  light_index:
    name: Light Index
    initial: 0
    min: 0
    max: 35
    step: 1
    
    
  light_scheduler:
    sequence:
      - service: script.light_random_on_test
        data_template:
          eid: "{{ state_attr(group, 'entity_id')[states('input_number.light_index') | int] }}"
          group: "{{ group }}"
      - service: input_number.set_value
        data_template:
          entity_id: input_number.light_index
          value: "{{ (states('input_number.light_index') | int + 1 ) %  expand(group) | list | count }}"


  light_random_on_test:
    sequence:
      - delay: "00:00:{{ range(3, 15) | random }}"
      - service: light.turn_on
        data_template:
          entity_id: "{{ eid }}"
      - condition: template
        value_template: "{{ not is_state('input_number.light_index', '0') }}"
      - service: script.light_scheduler
        data_template:
          group: "{{ group }}"
          
  - alias: "test_random"
    trigger:
      - platform: state
        to: "on"
        entity_id: input_boolean.test
    action:
      - service: script.light_scheduler
        data:
          group: group.light_floor_1

Looks like you didn’t copy my light_random_on_test I had above.

There was an error in my original one I found while testing. Specifically

      - condition: template
        value_template: "{{ not is_state('input_number.light_index', '0') }}"

This will always be false. I changed it to:

      value_template: "{{ states('input_number.light_index') | int != 0 }}"

some some… only one light turns on.

The “input_number.light_index” got once a new number, but never changed after. Could this my problem?

Yeah, it only goes up once for each light. If only one light turns on, it will only go up by one. If it didn’t start at 0, it will stop early. It should stop at 0 if it ever manages to run completely through.

Are there any errors in the logs?

For some reason, it looks like I pasted an old version…you’re missing the part where I killed the script. No idea how that happened.

This is the one I just tested again and works. Give that a shot…

# Since a script can't call itself recursively, using 2 scripts to call each other. 
light_scheduler:
  sequence:
    # Turn on the current index
    - service: script.turn_off
      entity_id: script.light_random_on_test
    - service: script.light_random_on_test
      data_template: 
        eid: "{{ state_attr(group, 'entity_id')[states('input_number.light_index') | int] }}"
        # Pass the group to the next script so it can call this one back with the same group id.
        group: "{{ group }}"
    - service: input_number.set_value
      data_template:
        entity_id: input_number.light_index
        # Add 1 and wrap around. 
        value: "{{ (states('input_number.light_index') | int + 1 ) %  expand(group) | list | count | int }}"
        #value: "{{ states('input_number.light_index') | int + 1 }}"


light_random_on_test:
  sequence:
    # Waits a few seconds, turns on a light, then checks the light_index
    # to see if it should call again. 
    - delay: "00:00:{{ range(2, 5) | random }}"
    - service: light.turn_on
      data_template:
        entity_id: "{{ eid }}"
    # Light index is incremented by the schedule script. If it's 0, it means it has wrapped around
    # and there is nothing left to do.....so don't schedule the light_scheduler again.
    # Keep in mind, this is a race condition. It works here because this script starts with
    # a delay. It's not good code, that's for sure! We are relying on that delay so the first
    # script had time to increment the index before this script evaluates it. And I'm sure there
    # exists hardware in which even a 3 second delay would fail. 
    - condition: template
      value_template: "{{ states('input_number.light_index') | int != 0 }}"
    - service: script.light_scheduler
      data_template:
        group: "{{ group }}"

Hi,
I went down this road too to schedule lights with and without random elements.
However there is an alternative if you are willing to add node red to your installation.
Have a look at the “presence faker” module where you can easily schedule lights with a random component and of course the “light scheduler” where you can edit the weekly schedule with a graphic UI.

After I tried that I never went back to do this stuff with HA automation scripts.

presence faker

light scheduler

perfect, lights are turning on! :+1: :grinning: step by step

The order is always the same. How do I get a different order? Random?

Without a complete rewrite of everything, create a few groups with the entities in a different order, then pick a group at random.

groups: 
  group1:
    - A
    - B
    - C
  group2:
    - B
    - A
    - C
  group3:
    - C
    - A
    - B
  - alias: "test_random"
    trigger:
      - platform: state
        to: "on"
        entity_id: input_boolean.test
    action:
      - service: script.light_scheduler
        data_template:
          group: "{{ ['group1', 'group2', 'group3'] | random }}"

At this point, I’d almost recommend learning how to use AppDaemon to do what you want! Or follow @chris_ka recommendation and look at presense faker in Node Red.

Thank you very much for your help!!!