"Loop Until" script error - syntax?

Hi guys,

I have a Shelly 1pm controlling the charger for my plug-in hybrid vehicle. Sometimes I want it to start charging right away, and sometimes I want it to only start charging 2 hours after sunrise tomorrow to take maximum advantage of solar power generation.

I’ve wired a momentary action button to the switch input on the 1pm module, to be used to trigger the automation either in immediate or delayed mode.

The problem is that if I set the Shelly input to momentary action mode (so I can get single click or long click events), then the button also directly switches the Shelly relay on and off, which starts or interrupts a charge cycle right now irrespective of what I actually want.

If I set the Shelly input to detached switch, it leaves the charge cycle alone, but I can only get an On or Off state to use in my script.

To solve this, I am wanting to run a separate script to “read” how many times the 1pm switch input is triggered in a time period, and set a boolean helper to indicate what type of click event that should represent.

An automation will trigger two scripts when the button is pressed for the first time, then time delay long enough so it doesn’t interfere with the remainder of the “read” process.

Script 1 increments a number helper each time it sees the button being pressed and then loops around to look for another button press.

Script 2 times out over an “acceptable” time period (perhaps 3 seconds), then terminates script 1 and sets a boolean helper on the basis of the current value of the number helper, then resets the number helper and ends.

The Car Charge script will then be triggered by the change of state in the boolean helper - which is effectively simulating the click event that I can’t get out of the Shelly 1pm.

Trying to get the first script to do the read and loop cycle is what is holding me up right now - as I can’t save it due to errors being thrown around the repeat instruction.

Message malformed: extra keys not allowed @ data['sequence'][0]['target']['repeat']

The script starts with a counter increment to account for the button press that triggered the automation in the first place, then waits for the next “off to on” transition to increment again… etc

I picked 5 as a likely maximum count, as I can’t see any purpose for more than a quintuple press event :wink:

alias: car charge button
sequence:
  - service: input_number.increment
    target:
      entity_id: input_number.car_charge_button
  repeat:
    - wait_for_trigger:
        - platform: state
          entity_id: binary_sensor.car_charger_input
          to: 'on'
          from: 'off'
    - service: input_number.increment
      target:
        entity_id: input_number.car_charge_button
  until:
    - condition: state
      entity_id: input_number.car_charge_button
      state: 5
mode: single

I’ve tried to follow the example in the documentation - but clearly have gone wrong somewhere.

Where have I gone wrong?

Many thanks in anticiaption

Every step in a sequence is a list item (preceded by a “-”).

alias: car charge button
sequence:
  - service: input_number.increment
    target:
      entity_id: input_number.car_charge_button
  - repeat:
      - wait_for_trigger:
          - platform: state
            entity_id: binary_sensor.car_charger_input
            to: 'on'
            from: 'off'
      - service: input_number.increment
        target:
          entity_id: input_number.car_charge_button
    until:
      - condition: state
        entity_id: input_number.car_charge_button
        state: 5
mode: single

Thanks @finity

The example I was working from here didn’t show a “-” before the repeat command.

In any case, even with it I am still getting the same error when I try to save the script…

That example has an “alias:” line added to the sequence step. it has the “-” at the beginning. If you remove that line you have the same syntax I posted.

However, I did miss one thing:

alias: car charge button
sequence:
  - service: input_number.increment
    target:
      entity_id: input_number.car_charge_button
  - repeat:
      sequence:
        - wait_for_trigger:
            - platform: state
              entity_id: binary_sensor.car_charger_input
              to: 'on'
              from: 'off'
        - service: input_number.increment
          target:
            entity_id: input_number.car_charge_button
      until:
        - condition: state
          entity_id: input_number.car_charge_button
          state: 5
mode: single

Thanks again @finity

It certainly saves now - but still does not run as expected.

It seems to get stuck on the first instance of the loop and never goes anywhere, So the helper value increments once for each run, but never picks up any additional key presses.

I’ll show all the other parts of the system as well…

The triggering Automation is

alias: read car charge button
description: ''
trigger:
  - platform: state
    entity_id: binary_sensor.car_charger_input
    to: 'on'
    from: 'off'
condition: []
action:
  - service: script.car_charge_button_timeout
  - service: script.car_charge_button
  - delay:
      hours: 0
      minutes: 0
      seconds: 5
      milliseconds: 0
mode: restart

The two scripts that trigger from the automation are…

alias: car charge button
sequence:
  - service: input_number.increment
    target:
      entity_id: input_number.car_charge_button
  - repeat:
      sequence:
        - wait_for_trigger:
            - platform: state
              entity_id: binary_sensor.car_charger_input
              to: 'on'
              from: 'off'
        - service: input_number.increment
          target:
            entity_id: input_number.car_charge_button
      until:
        - condition: state
          entity_id: input_number.car_charge_button
          state: '5'
mode: single

(I had to add single quotes around the state value in the until condition to get it to save, but as that part of the code has never executed still not sure if it’s right.)

alias: car charge button timeout
sequence:
  - delay:
      hours: 0
      minutes: 0
      seconds: 3
      milliseconds: 0
  - service: script.turn_off
    target:
      entity_id: script.car_charge_button
  - choose:
      - conditions:
          - condition: numeric_state
            entity_id: input_number.car_charge_button
            above: '0'
            below: '2'
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.car_charge_x1
      - conditions:
          - condition: numeric_state
            entity_id: input_number.car_charge_button
            above: '1'
            below: '3'
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.car_charge_x2
      - conditions:
          - condition: numeric_state
            entity_id: input_number.car_charge_button
            above: '2'
            below: '4'
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.car_charge_x3
      - conditions:
          - condition: numeric_state
            entity_id: input_number.car_charge_button
            above: '3'
            below: '5'
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.car_charge_x4
      - conditions:
          - condition: numeric_state
            entity_id: input_number.car_charge_button
            above: '4'
            below: '6'
        sequence:
          - service: input_boolean.turn_on
            target:
              entity_id: input_boolean.car_charge_x5
    default: []
  - delay:
      hours: 0
      minutes: 0
      seconds: 2
      milliseconds: 0
  - service: input_number.set_value
    target:
      entity_id: input_number.car_charge_button
    data:
      value: 0
  - service: input_boolean.turn_off
    target:
      entity_id:
        - input_boolean.car_charge_x1
        - input_boolean.car_charge_x2
        - input_boolean.car_charge_x3
        - input_boolean.car_charge_x4
        - input_boolean.car_charge_x5
mode: single

It seems as if the reset to zero at the end of the timeout script is almost instantly incremented to one again, when the incrementing script gets stuck looking for a state change on the input button.

Could the button state be getting hijacked by the automation so it is not available to to be read by the script?

I don’t understand how the script is still running, when the timeout script has already terminated it???

It might be a bit hard to follow the logic but I think that’s what it’s supposed to do according to the code.

  1. the automation gets triggered by the binary sensor going to ‘on’
  2. ‘script.car_charge_button_timeout’ begins.
  3. it waits 3 seconds and then checks the value of ‘input_number.car_charge_button’ and takers actions accordingly.
  4. waits 2 more seconds and sets ‘input_number.car_charge_button’ to 0 and turns off all the input booleans.
  5. it then jumps back to the original calling automation and runs the next step which is the ‘script.car_charge_button’.
  6. the first step of that script is to increment ‘input_number.car_charge_button’. so it immediately goes to 1.
  7. so ‘input_number.car_charge_button’ will go from whatever value it was to 0 then immediately back to 1 again while it waits for the trigger of ‘binary_sensor.car_charger_input’ to go to off.
  8. When the trigger comes it increments the input number and immediately jumps back to waiting for the trigger again.
  9. It does that until the input number is 5
  10. and then returns to the automation next step which is a 5 second delay (for some reason…?) and then everything ends.

I was given to understand that the automation would trigger the script and then just move on, not wait for it to finish first.
I previously had issues with script calls interfering with each other because they were not all queued up one at a time.

The logic was that the counter and the timeout would be running simultaneously so the first script would count as many button inputs as it could before the second script killed it 3 seconds later. The 5 second dely on the automation was so that the two scripts could do their job, and trigger the pseudo-button click event while the automation was still busy timing out so 2nd to Xth button clicks would not trigger the automation to run again.

Clearly a deficiency in my understanding of the operating principle…

I recommend you check the docs here.

if you call it by name it waits for the script to complete then moves to the next step in the sequence.

if you use “script.turn_on” then it doesn’t wait and runs in parallel.

Thanks @finity - suddenly it all works. Have to tweak the timing a little bit to take account of the latency on the wifi (not quite as responsive as the firmware in the module for counting clicks), but will do the job.

I have a piezo tweeter coming in the post that I plan to attach to the spare output on a ShellyUni in the door control on the other side of the garage that I can use as audio feedback for confirming button counting has been done successfully.

The problem with the on-line documentation is that most of us noobs don’t know how much we don’t know. If you google the right question, I’m sure its all there. But if you don’t know what the right question is… you end up going round in circles for hours pulling snippets from all over in the hope they will play nice together - at least until a White Knight steps up and points in the right direction.

Thank you. :slight_smile:

1 Like

You’re welcome.

It’s always hard to know what you don’t know.