Home AssistantESPHome

Binary-sensor short/long press

Turn any binary sensor — ESPHome GPIO, reed, dry contact, or a Papouch-class input — into a short tap versus long hold, with restart-mode spam protection so a bouncing contact does not fire both.

Difficulty · BeginnerSetup · 20–40 minutesHA · current stable (blueprint)ESPHome · current stable
Get the pack · from $19Have us set it up

Some hardware links on this page are affiliate links. As an Amazon Associate I earn from qualifying purchases. We may earn a commission if you buy through them, at no extra cost to you. See the affiliate disclosure.

A wall button that only exposes on / off is not a Hue remote. This workflow treats any binary_sensor as a press: a short tap runs one action list, a hold past a threshold runs another. It does not need device-specific short / hold events. Mash protection is mode: restart — a new press cancels the wait from the last one, so a bouncing reed does not enqueue both a short and a long.

Entity ids and action lists are blueprint inputs. Do not publish a live switch name.

This is the press primitive. Button scene cycling is the “walk a scene list” pack that sits on top of short versus long.

What problem it solves

You want two behaviours from one dry contact or GPIO — tap for one thing, hold for another — without firmware that already emits click events, and without a chatter of duplicate runs when the contact bounces.

How it works

  1. Trigger on press — the binary sensor goes on (button down, reed closed, input pulled). That is the only trigger. Release is handled inside the action.
  2. Wait for release or timeout — wait_for_trigger on to: "off" with timeout = the long-press threshold (pack default 1.0 s) and continue_on_timeout: true.
  3. Decide — if the wait timed out (wait.trigger is none), the contact is still held: run the long action list. If the wait caught the off, it was a tap: run the short action list. Long therefore fires at the threshold while still held, not on release. That is the Papouch-style timing.
  4. Spam protection — automation mode: restart, max_exceeded: silent. A second on before the wait finishes aborts the first run. You get at most one short or one long per press, not a queue.
  5. Actions are yours — each branch is an action selector (light.toggle, a scene, a script). The blueprint does not own lights; it only classifies the press.

A sensor that stays on for minutes (a door left open) will fire long once at the threshold. Use this pack for momentary contacts, not for “door has been open for a minute” alerts — that is nagging repeatable alert.

Hardware notes

  • ESPHome GPIO. Momentary to GND, pullup, inverted: true. Any spare pin on a node you already have. Add delayed_on_off: 20ms so mechanical bounce does not look like two presses.
  • Reed / dry contact. Same binary sensor. A latching reed used as a “button” works if you close-and-open it; a door you leave open will take the long path.
  • Papouch-class I/O. A Quido (or any module that exposes each input as a HA binary_sensor) is the original target. The blueprint does not care about the brand.
  • No extra hub. If the entity already flips in Developer Tools, this is YAML only.

Snippet

The pack ships a blueprint. Inputs replace real entity ids:

mode: restart
max_exceeded: silent

trigger:
  - platform: state
    entity_id: !input button_sensor
    to: "on"

action:
  - wait_for_trigger:
      - platform: state
        entity_id: !input button_sensor
        to: "off"
    timeout: !input long_press_time
    continue_on_timeout: true
  - choose:
      - conditions:
          - condition: template
            value_template: "{{ wait.trigger == none }}"
        sequence: !input long_press_action
      - conditions:
          - condition: template
            value_template: "{{ wait.trigger != none }}"
        sequence: !input short_press_action

Optional ESPHome button (credentials stay in secrets.yaml):

substitutions:
  friendly_name: Utility button
  button_pin: GPIO0

binary_sensor:
  - platform: gpio
    pin:
      number: ${button_pin}
      mode: INPUT_PULLUP
      inverted: true
    name: Press
    filters:
      - delayed_on_off: 20ms

Setup outline

  1. Confirm the binary sensor goes on while held and off when released (Developer Tools). Invert in ESPHome or a template helper if it is backwards.
  2. Import the blueprint. Pick the sensor, set the threshold (1.0 s is a good start), fill short and long actions.
  3. Test: tap under the threshold → short only; hold past it → long only, and long fires before you let go; mash twice quickly → one classified press, not a pile.

Troubleshooting

  • Short and long both fire. A second automation is also watching the same sensor, or the long action toggles something that retriggers this blueprint. One instance per contact.
  • Everything is long. Threshold is shorter than your fastest tap, or the contact never returns to off (stuck reed, inverted GPIO). Watch the state while you click.
  • Everything is short. Threshold is longer than you are willing to hold, or continue_on_timeout is missing so a hold stops the automation instead of taking the long branch.
  • GPIO double-taps. Bounce. Add delayed_on_off on the ESPHome sensor; do not fight it in the blueprint.
  • Door contact fires long every time you leave it open. Correct for this pattern. Use a different automation for “left open”; this pack is for momentary presses.

Credits

Based on the community blueprint Papouch Short/Long Press for Any Binary Sensor. This write-up is an original overview of the wait-for-release-or-timeout pattern with restart-mode spam protection.

Changelog

v1.0 · Oct 18, 2026: First publish.