How can I detect and notify press&hold on 2 buttons?

Hi,

I’m discovering HA and ESPHome actually, which seems powerfull, but I don’t find how I could handle what I need (that I previously did by manual coding in Arduino IDE and calling an URL from Jeedom, but now that I moved to HA, I think ESPHome would be the good choice !)

I have 2 push buttons, let’s call them B0 and B1. I have 4 lights, L0, L1, L2, L3. L0 is the ceiling light.

I want such scenario:

  • Single press B0 : turn L0 on, value 10%
  • Hold press B0 : turn L0 on, value 50%
  • Long hold press B0 : turn L0 on, value 100%
  • Single press B1 : turn L1 on, value 10%
  • Double press B1 : turn L1, L2, L3 on, value 10%
  • Hold press B1 : turn L1, L2, L3 on, value 50%
  • Long hold press B1 : turn L1, L2, L3 on, value 100%
  • Single press B0 if L0 is on : turn L0 off.
  • Single press B1 if L1 or L2 or L3 is on : turn L1, L2, L3 off

Until now, I can do all of this, using the following ESPHome configuration:

text_sensor:
  - platform: template
    name: 'Button 1'
    id: button1
    icon: 'mdi:toggle-switch'
    on_value: # When a state is set
      then:
        - if:
            condition:
              text_sensor.state:
                id: button1
                state: ''
            else: # If non-empty
              - delay: 20ms
              - text_sensor.template.publish:
                  id: button1
                  state: !lambda 'return "";' # Reset to empty
  - platform: template
    name: 'Button 2'
    id: button2
    icon: 'mdi:toggle-switch'
    on_value: # When a state is set
      then:
        - if:
            condition:
              text_sensor.state:
                id: button2
                state: ''
            else: # If non-empty
              - delay: 20ms
              - text_sensor.template.publish:
                  id: button2
                  state: !lambda 'return "";' # Reset to empty

binary_sensor:
  - platform: gpio
    pin:
      number: D2
      mode: INPUT_PULLUP
    id: raw_switch_state
    internal: true
    filters:
      - invert:
      - delayed_on: 30ms
      - delayed_off: 30ms
    on_multi_click:
      - timing:
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          - text_sensor.template.publish:
              id: button1
              state: !lambda 'return "single";'
      - timing:
          - ON for 40ms to 400ms
          - OFF for 40ms to 400ms
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          - text_sensor.template.publish:
              id: button1
              state: !lambda 'return "double";'
      - timing:
          - ON for 40ms to 400ms
          - OFF for 40ms to 400ms
          - ON for 40ms to 400ms
          - OFF for 40ms to 400ms
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          - text_sensor.template.publish:
              id: button1
              state: !lambda 'return "triple";'
      - timing:
          - ON for at least 1500ms
        then:
          - text_sensor.template.publish:
              id: button1
              state: !lambda 'return "hold";'
      - timing:
          - ON for at least 3000ms
        then:
          - text_sensor.template.publish:
              id: button1
              state: !lambda 'return "longhold";'
  - platform: gpio
    pin:
      number: D3
      mode: INPUT_PULLUP
    id: raw_switch_state2
    internal: true
    filters:
      - invert:
      - delayed_on: 30ms
      - delayed_off: 30ms
    on_multi_click:
      - timing:
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          - text_sensor.template.publish:
              id: button2
              state: !lambda 'return "single";'
      - timing:
          - ON for 40ms to 400ms
          - OFF for 40ms to 400ms
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          - text_sensor.template.publish:
              id: button2
              state: !lambda 'return "double";'
      - timing:
          - ON for 40ms to 400ms
          - OFF for 40ms to 400ms
          - ON for 40ms to 400ms
          - OFF for 40ms to 400ms
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          - text_sensor.template.publish:
              id: button2
              state: !lambda 'return "triple";'
      - timing:
          - ON for at least 1500ms
        then:
          - text_sensor.template.publish:
              id: button2
              state: !lambda 'return "hold";'
      - timing:
          - ON for at least 3000ms
        then:
          - text_sensor.template.publish:
              id: button2
              state: !lambda 'return "longhold";'

But now, I’d like to add:

  • Long press B0 and B1 : turn all house lights off

How can I achieve detection of both buttons pressed to trigger a text to HA on this behavior?

I know I could apply a scenario in HA, but the thing is, if B0 is pressed slightly before B1, HA will be first notified of B0 press before B1 press and will trigger B0 pressed scenario before “both buttons pressed” scenario.
I know in this case it would work, because “turn all lights off” would cancel “turn L0 on, value 50%”, but in other cases in my house, such coincidence may not appear, and I would like to avoid a slight flash of intensity on the bulb, as it is in the bedroom :slight_smile:

Thanks for your help!

Interesting problem because I am not sure myself but am interested in the answer.
I wonder what approach works best;

  • if the delayed_on filter helps you here with more conditions
  • or if delay after the press before conditions is your friend.

Secondly, I have had a 100% fail rate trying to train family members to ‘long-press’ any button. If it isn’t single-press then just don’t use it; lol. I wish you luck!

My thoughts are;

  • determine a suitable delay_on via experiments where all family members are asked to “press-both” while recording the logs/timestamps. I would not be surprised if the delay needs to be > 150ms.
  • add a condition to necessary sections of the config to prevent that flicker

eg. (un-verified yaml)

    on_multi_click:
      - timing:
          - ON for 40ms to 400ms
          - OFF for at least 500ms
        then:
          if:
          condition:
            - binary_sensor.is_off: raw_switch_state2
          then:
            - text_sensor.template.publish:
                id: button1
                state: !lambda 'return "single";'
          else:
            - do multipress things

I am not convinced that delayed_on helps the dual-button-press and suspect that a delay statement will be required. However I can see a delay potentially interfering with the multi-click config.

Nice guess, I’m not familiar with how to write conditions, I’m learning this ESPHome syntax for now :slight_smile:

I will give it a try once I get back to home and will let you know!

Keep us posted. I often have to experiment with different approaches in order to find what works, what doesn’t, and quirks…

Possibly 3 binary template sensors. Under each button press event, you will need a lambda to turn on a binary sensor.

id(switch_sensor1).publish_state(true);

Then a binary to read that the other two were on, under that entry you would add the action to turn off everything.

binary_sensor:
  - platform: template
    name: "Both long press"
    lambda: |-
      if (id(switch_sensor1).state && id(switch_sensor2).state) {
        // both pressed same time
        return true;
      } else {
        return false;
      }
    on_state 
      if:
        condition:
        binary_sensor.is_on: both_long_press

None of this is tested btw

1 Like