One script but different action each time?

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.

input_number:
  script_action:
    name: Last Script Action
    initial: 0
    min: 0
    max: 3
    step: 1

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.

jocnnor’s looks good.

You can also look at one of these two variable addons.

I don’t understand
Why are you hung up on 1 script?
You have 3 different actions, 1 press, 2 press, 3 press
Why not just have 3 automations?

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.

1 Like

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!

Your indent is incorrect here.

    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
  • service: input_number.set_vaule needs to go out one.
1 Like

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):

livingroom_scenes:
  - 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 %}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.script_livingroom
      value: "{{ (states('input_number.script_livingroom') | int + 1) % (state_attr('input_number.script_livingroom', 'max') | int) }}"
  - delay:
    seconds: 30
  - condition: template
    value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_livingroom.last_updated) < 25 }}'
  - service: input_number.set_value
    data:
       entity_id: input_number.script_livingroom
       value: 0

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 %}

Don’t you just love YAML spacing? I know I do!

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).

Code:

livingroom_scenes:
  - 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 %}
  - service: input_number.set_value
    data_template:
      entity_id: input_number.script_livingroom
      value: "{{ (states('input_number.script_livingroom') | int + 1) % (state_attr('input_number.script_livingroom', 'max') | int) }}"
  - delay:
    seconds: 30
  - condition: template
    value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_livingroom.last_updated) < 25 }}'
  - service: input_number.set_value
    data:
       entity_id: input_number.script_livingroom
       value: 0

Oh…if this is a script, these all have to be under a ‘sequence’ field.

livingroom_scenes:
  sequence:
    - 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 %}
    - service: input_number.set_value
      data_template:
        entity_id: input_number.script_livingroom
        value: "{{ (states('input_number.script_livingroom') | int + 1) % (state_attr('input_number.script_livingroom', 'max') | int) }}"
    - delay:
      seconds: 30
    - condition: template
      value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_livingroom.last_updated) < 25 }}'
    - service: input_number.set_value
      data:
         entity_id: input_number.script_livingroom
         value: 0

Scripts and actions (in automations) are the exact same syntax…I keep forgetting to add that, even in my own scripts.

1 Like

Thank you so much for your help!!

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

configuration.yaml:

input_number:
  script_livingroom:
    name: Last Script Livingroom
    initial: 0
    min: 0
    max: 3
    step: 1

scripts.yaml:

livingroom_scenes:
  sequence:
    - 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 %}
    - service: input_number.set_value
      data_template:
        entity_id: input_number.script_livingroom
        value: "{{ (states('input_number.script_livingroom') | int + 1) % (state_attr('input_number.script_livingroom', 'max') | int) }}"
    - delay: 00:00:30
    - condition: template
      value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_livingroom.last_updated) < 25 }}'
    - service: input_number.set_value
      data:
         entity_id: input_number.script_livingroom
         value: 0

If you change it to > 25, it should work. I must have wrote this code while sleeping or something, sorry!

We want this to continue if it’s been over 25 seconds since last modified!

    - condition: template
      value_template: '{{ as_timestamp(now()) - as_timestamp(states.input_number.script_livingroom.last_updated) > 25 }}'

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.

1 Like

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. :slight_smile:

1 Like