If then else statements with input_booleans working errratically

I use 3 input_booleans as follows. There are 3 tile cards positioned horizontally and when I click on any of the three, it toggles an input_boolean and if it is turning on, turns off the other two, if they are on. (if one is on, a conditional card appears, which is why I want only one on at the time.)

My code reliably toggles the clicked one, but the others are sometimes turned off, sometimes not. It is quite erratic. I can reproduce it by clicking rapidly and generally if I wait more than 10 seconds between clicks, the problem never arises. My system (HA green) doesn’t seem stressed: processor <6% and memory at 30% during these operations.

Is there something wrong with the way I’m doing this? Here is the code for input_boolean r1:

alias: SR1
sequence:
  - if:
      - condition: state
        entity_id: input_boolean.r1
        state: "on"
    then:
      - service: input_boolean.turn_off
        metadata: {}
        data: {}
        target:
          entity_id: input_boolean.r1
    else:
      - service: input_boolean.turn_off
        metadata: {}
        data: {}
        target:
          entity_id:
            - input_boolean.r2
            - input_boolean.rmb
      - service: input_boolean.turn_on
        metadata: {}
        data: {}
        target:
          entity_id: input_boolean.r1
mode: single
icon: mdi:home-floor-1

There are two other similar scripts, one for input_boolean.r2 and one for input_boolean.rmb. Again, they work some of the time as expected.

The way the script is accessed is through cards like

          - type: tile
            entity: input_boolean.r1
            name: Lights
            hide_state: true
            tap_action:
              action: call-service
              service: script.sr1

I do not fully understand how the inputs are used, but if you execute this scripts when r1 is on, you end up with all inputs off, which seems not intended. Also, execution mode is set to single, so it will ignore a call when the script is already running. That may explain weird fast clicking behaviour, and you ending up with all inputs off.

If I understand what you try to do I’d probably use an input select. and buttons to set it.

If you intend on using input booleans, I’d use automations to ensure only one is on, not scripts.

1 Like

Thank you for looking at my code. It definitely toggles r1 – if the condition r1 is off is not met, then r1 is off, and the else: segment turns r1 on. Moreover, that behavior is reliable no matter how fast I click on the buttons. It is the other clause in the else – turn off r2 and rmb – that sometimes happens and sometimes not.

You can think of my goal as “at most one of the three on” but all off is sometimes desired. (My use is I have a vertical stack of three conditional cards, and each input boolean shows one of them.)

I tried mode: queued instead of mode: single and it did not make a difference. I also tried reversing the items so that it triggered on off and did the longer part under then, with the simpler turn off first.

There are many ways to program this and part of what I wanted to do was learn if then else statements, because these are so useful in programming.

I do this for viewing specific rooms on a mobile dashboard.

On my dashboard, a button for each room toggles the input_boolean. (This lets me have all of the input_boolean toggles off if I want.)
Then, I have an automation that fires based on which input_boolean is turned on. I use trigger_id with the choose action instead of if/then action for this.

Here is an example with 3 rooms:

alias: Room Togglers
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.rooma_toggle
    from: "off"
    to: "on"
    id: rooma
  - platform: state
    entity_id:
      - input_boolean.roomb_toggle
    from: "off"
    to: "on"
    id: roomb
  - platform: state
    entity_id:
      - input_boolean.roomc_toggle
    from: "off"
    to: "on"
    id: roomc
condition: []
action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - rooma
        sequence:
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.roomb_toggle
                - input_boolean.roomc_toggle
            data: {}
      - conditions:
          - condition: trigger
            id:
              - roomb
        sequence:
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.rooma_toggle
                - input_boolean.roomc_toggle
            data: {}
      - conditions:
          - condition: trigger
            id:
              - roomc
        sequence:
          - service: input_boolean.turn_off
            target:
              entity_id:
                - input_boolean.rooma_toggle
                - input_boolean.roomb_toggle
            data: {}
mode: single

I use this technique for 14 rooms and it works without issue.

If you’re interested, you can reduce your automation to this:

alias: Room Togglers
description: ""
trigger:
  - platform: state
    entity_id:
      - input_boolean.rooma_toggle
      - input_boolean.roomb_toggle
      - input_boolean.roomc_toggle
    from: "off"
    to: "on"
condition: []
action:
  - variables:
      rooms:
        - input_boolean.rooma_toggle
        - input_boolean.roomb_toggle
        - input_boolean.roomc_toggle
  - service: input_boolean.turn_off
    target:
      entity_id: "{{ rooms | reject('eq', trigger.entity_id) | list }}"
mode: single
max_exceeded: silent

@PrestonMcAfee
You can use the same technique to ensure only one Input Boolean is on at a time. The following script uses a passed variable named room to determine which Input Booleans it should turn off (and which one to leave on).

alias: Room Toggler
sequence:
  - variables:
      rooms:
        - input_boolean.r1
        - input_boolean.r2
        - input_boolean.rmb
  - service: input_boolean.turn_off
    target:
      entity_id: "{{ rooms | reject('eq', room) | list }}"
mode: single
icon: mdi:home-floor-1

Modify your Tile cards to call the script and pass the Input Boolean’s entity_id via the room variable.

          - type: tile
            entity: input_boolean.r1
            name: Lights
            hide_state: true
            tap_action:
              action: call-service
              service: script.room_toggler
              service_data:
                room: input_boolean.r1
3 Likes

Thank you! You are a wealth of knowledge.

@iamjosh: Thanks! I managed to make my if then else work by replacing the tile card I was using to trigger it, but then 123 had a superior method of doing what I am doing.
@123: That is very clever and I will definitely use that! Thanks!

I’m not sure why my code was working erratically, but a simple fix was to replace the tile card, which has an entity, with a custom:button-card, which need not have an entity. Once I did that, the code worked without error no matter how fast I clicked. Then I styled it to light up when the relevant input_boolean was on.

FWIW, you marked iamjosh’s suggestion as the Solution but the example it provides can’t be used for your purposes because it’s an automation, not a script. Your Tile card isn’t doing anything that would trigger the automation. So it’s misleading to other readers that it represents the Solution to your problem.

It also suggests using conditional logic, similar to what your example already does, to determine which Input Booleans to turn off. The choose just makes it neater than multiple nested if then else but not necessarily better (or shorter). A simpler technique is to use a template for that purpose.

In addition, you have already stated you won’t be using what’s suggested in the Solution post so it doesn’t represent how you chose to solve the problem.

If you chose to fix the problem by simply changing the card, then mark your own post as the Solution.

You should definitely mark @123’s post as the solution since you used it.

His solution is cleaner and specific to your use case.

Done. Again, I appreciate the help. It is true that I got it working by replacing the card that calls the script but I have switched to 123’s solution, which is simpler and replaced 38 scripts with five scripts. I’m not sure why my earlier attempt was unreliable with tile cards but in retrospect I’m happier with custom: button-card for styling purposes anyway.

One more thing, once I switched to custom:button-card, the input_boolean no longer toggled, but a minor adjustment fixed that (this is a different grouping):

alias: TogR1
sequence:
  - variables:
      rooms:
        - input_boolean.rnest
        - input_boolean.rsc
        - input_boolean.rsns
        - input_boolean.rw
  - service: input_boolean.turn_off
    target:
      entity_id: "{{ rooms | reject('eq', room) | list }}"
  - service: input_boolean.toggle
    target:
      entity_id: "{{ room }}"
mode: single
icon: mdi:home-automation