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
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.
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…
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…
# 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 }}"
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.
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.