Hey there,
is there a way to write a script that, when executed, is a different one each time, but goes back to the first action after the third time it ran?
I never was good at programming but it may work with a “while” or “for” loop, I guess?
My idea is to have three different scenes inside one script, but if I want to go to scene 2 I press twice, scene 3, three times etc
I also would love it if there’s a timer bound to that: If I haven’t run that script again after 30 seconds, that it resets itself (so that it thinks I press it the first time and not the second time after 30 seconds).
I hope you understand what I mean. Otherwise let me know!
You’d have to store the state somewhere. I’d recommend creating an input_number. We just increment this number each time you call the script, then use this number to do certain things.
Now in your script, check the number and call something based on it, the increment the number.
script:
my_scene_script:
# Depending on what the input_number is, call a specific scene
- service: scene.turn_on
data_template:
entity_id: >-
{% set num = states('input_number.script_action') | int %}
{% if num == 0 %}
scene.scene_1
{% elif num == 1 %}
scene.something_else
{% else %}
scene.another_one
{% endif %}
- service: input_number.set_value
data_template:
entity_id: input_number.script_action
# Add 1 to the current number. Wrap around when we get to the end with the modulo operator.
value: "{{ (states('input_number.script_action') | int + 1) % (state_attr('input_number.script_action', 'max') | int) }}"
# Now wait 30 seconds. If no other inputs, reset the value.
- delay:
seconds: 30
# Delays will exit early if this script gets called again. So check
# to see when the last changed time of input_number was.
# In this case, the entire script will still execute to completion, so we'll check the modified time
# to see if it was a full delay or early exit.
- condition: template
# If this number was changed in the last few seconds, this script was exited early. Nothing to do.
# Note - subtracting a few seconds as I'm not sure how exact delays are.
value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_action.last_updated) < 25 }}'
- service: input_number.set_value
data:
entity_id: input_number.script_action
value: 0
The comments should hopefully explain my thought process. Maybe. Sometimes I can’t even follow my own thought process.
Thank you so much for helping out! I changed the script just a tiny bit (instead of turning on scenes, I use turning on other scripts, because in those scripts I have my Philips Hue scenes). However, there always seems to be a “bad indentation” at
service: input_number.set_value
which I can’t get rid of no matter how much I indent it or not. I put the script in my scrips.yaml and looks like this so far:
my_scene_script:
# Depending on what the input_number is, call a specific scene
- service: script.turn_on
data_template:
entity_id: >-
{% set num = states('input_number.script_action') | int %}
{% if num == 0 %}
script.livingroom_white
{% elif num == 1 %}
script.livingroom_muckeln
{% else %}
script.livingroom_muckeln_nobali
{% endif %}
- service: input_number.set_value
data_template:
entity_id: input_number.script_action
# Add 1 to the current number. Wrap around when we get to the end with the modulo operator.
value: "{{ (states('input_number.script_action') | int + 1) % (state_attr('input_number.script_action', 'max') | int) }}"
# Now wait 30 seconds. If no other inputs, reset the value.
- delay:
seconds: 30
# Delays will exit early if this script gets called again. So check
# to see when the last changed time of input_number was.
# In this case, the entire script will still execute to completion, so we'll check the modified time
# to see if it was a full delay or early exit.
- condition: template
# If this number was changed in the last few seconds, this script was exited early. Nothing to do.
# Note - subtracting a few seconds as I'm not sure how exact delays are.
value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_action.last_updated) < 25 }}'
- service: input_number.set_value
data:
entity_id: input_number.script_action
value: 0
Any idea?
…
…
.
I use this workaround to program my Philips Hue Dimmer switches through Apple Homekit, as well as other Homekit apps for my Watch. This way I can use one button for different actions each time I press it.
So what you are saying is that press1 = press2 = press and that you iterate through 3 (in this case, but could be (say) 10) possible scripts. And any pause longer than (say) 20 secs resets the sequence to 1 ?
Okay I think I see where you are coming from. I used to do something similar to set individual lights to minimum or maximum.
The second input_number.set_value needs to line up exactly with all of the other - service, - condition, -delay lines. It looks like it’s indented by 1 extra space.
I didn’t test any of that, so if you copied it directly from me, I’m sure I put that in by accident lol. Sorry!
Oh right, that was the tiny space that was too much! I got the green checkmark then, but Home Assistant showed me a config error after saving:
Invalid config for [script]: expected a dictionary for dictionary value @ data[‘script’][‘livingroom_scenes’]. Got [OrderedDict([(‘service’, ‘script.turn_on’), (‘data_template’, OrderedDict([(‘entity_id’, “{% set num = states(‘input_number.script_livingroom’) | int %} {% if num == 0 %}\n script.livingroom_white\n{% elif num == 1 %}\n script.livingroom_muckeln\n{% else %}\n script.livingroom_muckeln_nobali\n{% endif %}”)]))]), OrderedDict([(‘service’, ‘input_number.set_value’), (‘data_template’, OrderedDict([(‘entity_id’, ‘input_number.script_livingroom’), (‘value’, "{{ (states('input_number.script_livi… (See /config/configuration.yaml, line 154).
This is the code (I only changed the input number name after our last update):
It looks like the spacing in that template needs to be moved over by 1 to have the same 2 spaces as everything else. At least, that’s my only guess. Everything else looks good…
- service: script.turn_on
data_template:
entity_id: >-
{% set num = states('input_number.script_livingroom') | int %}
{% if num == 0 %}
script.livingroom_white
{% elif num == 1 %}
script.livingroom_muckeln
{% else %}
script.livingroom_muckeln_nobali
{% endif %}
Thanks man!
Yeah that spacing drives me crazy.
I followed your last post and still get an error message:
Invalid config for [script]: expected a dictionary for dictionary value @ data[‘script’][‘livingroom_scenes’]. Got [OrderedDict([(‘service’, ‘script.turn_on’), (‘data_template’, OrderedDict([(‘entity_id’, “{% set num = states(‘input_number.script_livingroom’) | int %} {% if num == 0 %}\n script.livingroom_white\n{% elif num == 1 %}\n script.livingroom_muckeln\n{% else %}\n script.livingroom_muckeln_nobali\n{% endif %}”)]))]), OrderedDict([(‘service’, ‘input_number.set_value’), (‘data_template’, OrderedDict([(‘entity_id’, ‘input_number.script_livingroom’), (‘value’, "{{ (states('input_number.script_livi… (See /config/configuration.yaml, line 154).
I had to rewrite the delay, because it must be written differently in scripts but now everything works except the timer for the 30 seconds reset. I have created the input number in configuration.yaml and the script in scripts.yaml
I’m not sure I love this solution. There is a chance that this will instead fire a new script in parallel everytime you press the button. It’s not a big deal really, you’ll just have X scripts waiting for 30 seconds, then each one will finally finish and one of them will reset the counter (the rest will just quit because the condition will be false).
I’m not 100% sure with scripts. I know with automations, it will instead force finish the automation and restart it which is how I wrote this sequence. But in the long run, it shouldn’t be that big of a deal I don’t think to have a few scripts doing nothing in the background.
Thank you so much for your help in the past couple of days!! I really do appreciate it. The > 25 fixed it and now all three scenes work and it resets itself after 30 seconds. Nailed it.