ESPHome Button Held, Double Press

So I have a D1 Mini working as a button with ESPHome.
I’d love to enable a Button Held sensor, and Double Click sensor or something like that.
I see this page in the docs, but how can I make virtual switches for held and double press that turn on when the actions in this page are triggered?

Thanks

What do you mean virtual switch?

Off the top of my head I would think you need to create a template switch in the ESPhome code with a lambda relating to the held / double click. Not something I have ever tried so I can’t be of any more help unfortunately… but I’m keen to know the answer

1 Like

Here’s the snippet from my esphome config.

  - platform: esp32_touch
    name: "CS Buttons Touch 4"
    pin: GPIO13
    threshold: 1000
    on_press:
      then:
        - homeassistant.service:
            service: switch.toggle
            data:
              entity_id: switch.worklight
    on_double_click:
      then:
        - homeassistant.service:
            service: light.turn_off
            data:
              entity_id: group.all_lights

5 Likes

Thanks I’ll try this soon.

What, I’m trying to add a button to my relay on esp8266 with ESPHome. I wrote:

switch:
  - platform: gpio
    pin: D1
    name: "Living Room Dehumidifier"
    id: dehumidifier1

binary_sensor:
  - platform: gpio
    pin: D2
    name: "Living Room Dehumidifier Toggle Button"
    on_press:
      then:
        - switch.toggle: dehumidifier1

work only relay with switch software, but a button hardware don’t work. Can you help me?

I 'm assuming one end of your button is connected to D2 and the other end is connected to ground and your button is a normally open momentary. You need to amend your binary_sensor like so:

binary_sensor:
  - platform: gpio
    pin:
      number: D2
      name: "Living Room Dehumidifier Toggle Button"
      mode: input_pullup
      inverted: true
    filters:
      - delayed_on: 10ms
    on_press:
      then:
        - switch.toggle: dehumidifier1

You need to specify input_pullup because otherwise when your button isn’t activated the input to the 8266 will float causing spurious signals. With the pullup the input will be at a solid high level. Likewise you need the filter delayed_on to allow the button to settle after it is pushed because the switch contact will bounce for a while and cause spurious signals. Good luck.

Hi, I have created a cover and I would like to add a double press to the push-button so that when I press the push-button twice it automatically closes the relay for the time set in the cover.

- platform: gpio
  pin:
    number: GPIO16
    mode: INPUT_PULLUP
    inverted: True
  id: ph_button1
  on_press:
  - cover.open: my_cover
  on_release:
  - cover.stop: my_cover
  on_double_click:  
    min_length: 50ms
    max_length: 350ms
    then:
      - cover.open: my_cover

I discovered however that on press is not compatible with dual click. There will however be a lambda way of creating this with Lambda actions…
…delays in on_press/on_release and checks before action…

but I can’t use lambda. Can someone help me?

1 Like

If it’s any help here’s the code I ended up with.
This it toggling input_booleans in HA. It works.


binary_sensor:
  - platform: gpio
    pin:
      number: D4
      mode: INPUT_PULLUP
      inverted: True
    name: "Jackson D4 Button"
    on_multi_click:
    - timing:
      - ON for at most 1s
      - OFF for at most 1s
      - ON for at most 1s
      - OFF for at least 0.2s
      then:
        - homeassistant.service:
            service: input_boolean.turn_on
            data:
              entity_id: input_boolean.jackbuttondouble
    - timing:
      - ON for 1s to 3s
      - OFF for at least 0.5s
      then:
        - homeassistant.service:
            service: input_boolean.turn_on
            data:
              entity_id: input_boolean.jackbuttonheld
    - timing:
      - ON for at most 1s
      - OFF for at least 0.5s
      then:
        - homeassistant.service:
            service: input_boolean.turn_on
            data:
              entity_id: input_boolean.jackbuttonsingle
10 Likes

Thanks for sharing this, this is a great solution but it’s still quite cumbersome because you have to manually define each state and you end up with multiple input booleans. Is there a way to let EspHome pass the raw data (the ons and offs) and then have HA interpret them like it would for a “physical device”? On a physical device HA seems to recognize the patterns and offer from scratch the hold, single click and double click…

2 Likes

Keith I tried your code and it works fine, with one caveat : you don’t seem to turn the input boolean off in your code here. Are you doing it in HA after calling an automation or something?

Yeah I think my node red flow turns it off after the things it’s doing are done. Up to you where you reset it.

1 Like

This is what I did/ currently busy with. Also ESPHome powered wallswitch;

globals:
- id: counter
  type: int
  restore_value: no
  initial_value: '0'

binary_sensor:
  - platform: gpio
    id: btn
    name: "button test"
    pin:
      number: D1
      mode: INPUT
      inverted: false
    filters:
      - delayed_on: 10ms
      - delayed_off: 10ms
    internal: true
    discovery: false

    on_multi_click:
      #Double Click
      - timing:
          - ON for at most 0.5s
          - OFF for at most 0.3s
          - ON for at most 0.5s
          - OFF for at least 0.2s
        then:
          - lambda: |-
              id(${text_sensor_id}).publish_state("double");
          - delay: 0.5s
          - lambda: |-
              id(${text_sensor_id}).publish_state("");
      #Single Click
      - timing:
          - ON for at most 0.5s
          - OFF for at least 0.5s
        then:
          - lambda: |-
              id(${text_sensor_id}).publish_state("single");
          - delay: 0.5s
          - lambda: |-
              id(${text_sensor_id}).publish_state("");
      #Hold
      - timing:
          - ON for at least 0.75s
        then:
          - while:
              condition:
                binary_sensor.is_on: btn
              then:
                - lambda: |-
                    id(counter) += 1;
                    id(${text_sensor_id}).publish_state("hold");
                - delay: 0.5s
      #Release
      - timing:
          - ON for at least 1.5s
          - OFF for at least 0.5s
        then:
          - lambda: |-
              id(counter) = 0;
              id(${text_sensor_id}).publish_state("release");
          - delay: 0.5s
          - lambda: |-
              id(${text_sensor_id}).publish_state("");

#Button
# Notice;
# Home Assistant will handle JSON-payload as a String. Not pretty but it helps with handling incrementation of the count-key while holding button
# For automation I rely on Node-RED, not Home assistant. In Node-RED I've add a JSON-node to convert string into JSON. That works pretty well.
text_sensor:
  - platform: template
    name: ${text_sensor_name}
    icon: "mdi:toggle-switch"
    id: ${text_sensor_id}
    update_interval: 60s
    discovery: true
    internal: true #helps with hijacking publishing through mqtt.publish_json. Still sensor is discovered by HA
    on_value:
      then:
        #Hold
        - if:
            condition:
              # Test if state ends with 'hold'
              lambda: |-
                std::string mainStr = id(${text_sensor_id}).state;
                std::string toMatch = "hold";
                if(mainStr.size() >= toMatch.size() && mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0) {
                    return true;
                } else {
                    return false;
                }
            then:
              - mqtt.publish_json:
                  topic: ${mqtt_topic}/sensor/${text_sensor_name}/state
                  payload: |-
                    root["action"] = id(${text_sensor_id}).state;
                    root["count"] = id(counter);
            else:              
              #Release
              - if:
                  condition:
                    # Test if state ends with 'release'
                    lambda: |-
                      std::string mainStr = id(${text_sensor_id}).state;
                      std::string toMatch = "release";
                      if(mainStr.size() >= toMatch.size() && mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0) {
                          return true;
                      } else {
                          return false;
                      }
                  then:
                    - mqtt.publish_json:
                        topic: ${mqtt_topic}/sensor/${text_sensor_name}/state
                        payload: |-
                          root["action"] = id(${text_sensor_id}).state;
                          root["count"] = "";
                  else:
                    #Other states
                    - mqtt.publish_json:
                        topic: ${mqtt_topic}/sensor/${text_sensor_name}/state
                        payload: |-
                          root["action"] = id(${text_sensor_id}).state;

11 Likes

Thanks to your example I made something similar, but slightly simpler. I want HA to cycle through scenes when I hold the button, so it should be sufficient to send out the same MQTT message each time to trigger the same automation. Therefore, I don’t need the text_sensor:

binary_sensor:                         
  - platform: gpio                     
    pin:                               
      number: GPIO5                    
      #mode: INPUT_PULLUP              
      #inverted: True                  
    name: "Switch Shelly 1"             
    on_multi_click:
    - timing:
        - ON for at most 1s
        - OFF for at most 0.5s
        - ON for at most 1s
        - OFF for at least 0.1s
      then:
        - logger.log: "Double Clicked"
        - mqtt.publish:
            topic: shelly1_2/switch/shelly_1_switch/state
            payload: "double_click"
    - timing:
        - ON for at most 1s
        - OFF for at most 0.5s
        - ON for at most 1s
        - OFF for at most 0.5s
        - ON for at most 1s
        - OFF for at least 0.1s
      then:
        - logger.log: "Triple Clicked"
        - mqtt.publish:
            topic: shelly1_2/switch/shelly_1_switch/state
            payload: "triple_click"
    - timing:
        - ON for at least 1s
      then:
        - while:
            condition:
              binary_sensor.is_on: switchid
            then:
              - logger.log: "Single Long Clicked"
              - mqtt.publish:
                  topic: shelly1_2/switch/shelly_1_switch/state
                  payload: "single_long_click"
              - delay: 1s

    - timing:
        - ON for at most 1s
        - OFF for at least 0.5s
      then:
        - logger.log: "Single Short Clicked"
        - mqtt.publish:
            topic: shelly1_2/switch/shelly_1_switch/state
            payload: "single_click"
        - light.toggle: lightid 

    internal: true                     
    id: switchid         
4 Likes

I have taken @keithcroshaw approach and added turning off the input boolean.
“S20-5” is the name of the device, a Sonoff S20 and the 5th in my house now.
I removed the simple on press with the info from this thread.

binary_sensor:
  - platform: gpio
    pin:
      number: GPIO0
      mode: INPUT_PULLUP
      inverted: True
    name: "S20-5 Button"
    # on_press:
    #   - light.toggle: Sonoff_S20_light  #or  - switch.toggle: s20_relay
    on_multi_click:
    # double click
    - timing: 
      - ON for at most 1s
      - OFF for at most 1s
      - ON for at most 1s
      - OFF for at least 0.2s
      then:
        - homeassistant.service:
            service: input_boolean.turn_on
            data:
              entity_id: input_boolean.s20_5_button_double
        - delay: 0.2s
        - homeassistant.service:
            service: input_boolean.turn_off
            data:
              entity_id: input_boolean.s20_5_button_double
    # hold button
    - timing: 
      - ON for 1s to 5s
      - OFF for at least 0.5s
      then:
        - homeassistant.service:
            service: input_boolean.turn_on
            data:
              entity_id: input_boolean.s20_5_button_hold
        - delay: 0.2s
        - homeassistant.service:
            service: input_boolean.turn_off
            data:
              entity_id: input_boolean.s20_5_button_hold
    # single click
    - timing:
      - ON for at most 1s
      - OFF for at least 0.5s
      then:
      - light.toggle: Sonoff_S20_light

I have added the following to my configuration.yaml

input_boolean:
  s20_5_button_double:
    name: S20-5 double click
  s20_5_button_hold:
    name: S20-5 hold click
9 Likes

Home Assistant added the Button helper (aka input_button), which simplifies handling of double-clicks compared to the input_boolean solution shown above.

So instead of adding input_boolean entities in configuration.yaml as shown above, I’ve used the UI to add two helper Buttons to handle a click and a double-click: my_button_click and my_button_double_click

Note that to handle double-clicks on a remote_receiver button, like in the example below, you need to add a delayed_off filter.

binary_sensor:
  - platform: remote_receiver
    name: "My Button"
    icon: "mdi:button-pointer"
    rc_switch_raw:
      protocol: 6
      code: "111111111101101001001011"
    filters:
      - delayed_off: 200ms
    on_multi_click:
    - timing:
        - ON for at most 1s
        - OFF for at most 1s
        - ON for at most 1s
        - OFF for at least 0.2s
      then:
        - logger.log: Double Clicked.
        - homeassistant.service:
            service: input_button.press
            data:
              entity_id: input_button.my_button_double_click
    - timing:
        - ON for 1s to 2s
        - OFF for at least 0.5s
      then:
        - logger.log: Very long click
    - timing:
        - ON for at most 1s
        - OFF for at least 0.5s
      then:
        - logger.log: Clicked.
        - homeassistant.service:
            service: input_button.press
            data:
              entity_id: input_button.my_button_click

433MHz Button

1 Like