Design Pattern(?): Event Debouncer for State Changes

Design Pattern: Event Debouncer for State Changes

Problem: Some sensors produce very fast, consecutive state changes, e.g., motion detectors or buttons.
Without filtering, these can trigger multiple unwanted actions.

Solution:

  • Use a timer-based “debounce” mechanism.

  • After the first event, start a timer (e.g., 2 seconds).

  • During this time, no new actions are executed.

  • Only after the timer expires are new state changes accepted.

Benefits:

  • Reduces unwanted duplicate actions.

  • Especially useful for unstable sensors or buttons.


1. Rule DSL Example

Items

Switch motionSensor "Motion Sensor"
Switch light "Light"

Rule

var Timer debounceTimer = null

rule "Debounced Motion Sensor"
when
    Item motionSensor changed
then
    // Check if timer is already running
    if(debounceTimer === null) {
        // Execute action
        logInfo("Debounce", "Motion detected – turning on light")
        light.sendCommand(ON)
        
        // Start timer (e.g., 2 seconds)
        debounceTimer = createTimer(now.plusSeconds(2), [|
            debounceTimer = null  // Reset timer to allow new events
            logInfo("Debounce", "Debounce finished – new events allowed")
        ])
    } else {
        logInfo("Debounce", "Event ignored – timer still running")
    }
end

Rule DSL Explanation:

  • debounceTimer prevents multiple actions from being triggered within the 2-second period.

  • After the timer expires, it is reset and new events are processed.

  • Especially useful for sensors that send signals in rapid succession.


2. JSR223 Python (Jython) Example

Items

from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution
from core.log import logging

log = logging.getLogger("org.openhab.rules.Debounce")

motionSensor = ir.getItem("motionSensor")
light = ir.getItem("light")

debounce_timer = None

Rule

@rule("Debounced Motion Sensor")
@when("Item motionSensor changed")
def debounced_motion_sensor(event):
    global debounce_timer
    
    if debounce_timer is None:
        # Execute action
        log.info("Motion detected – turning on light")
        light.sendCommand("ON")
        
        # Start timer (2 seconds)
        def reset_timer():
            global debounce_timer
            debounce_timer = None
            log.info("Debounce finished – new events allowed")
        
        debounce_timer = ScriptExecution.createTimer(now.plusSeconds(2), reset_timer)
    else:
        log.info("Event ignored – timer still running")

Jython Explanation:

  • debounce_timer ensures no new action is executed during the timer period.

  • After the timer expires, it is reset and new sensor events are processed.

  • Easily reusable for multiple sensors.


Benefits of the Debounce Pattern

  1. Prevents unwanted duplicate actions caused by unstable sensors or buttons.

  2. Reduces unnecessary log entries or commands sent to actuators.

  3. Easily extendable by adjusting the timer duration or using multiple sensors.


Further Reading

OpenHAB Community Thread on the Debounce Design Pattern:
https://community.openhab.org/t/design-pattern-debounce/101566

There is a profile in Basic Profiles - Transformation Services | openHAB also includes a couple of denounce profiles.

There is also an implementation in JS on the rule template marketplace that can be installed from the Add-on Store. Debounce [4.0.0.0;5.0.9.9].

1 Like

Jruby file based rule has a built in debouncer:

rule "Debounced Motion Sensor" do
  changed motionSensor
  debounce_for 2.seconds # just add this to debounce it
  run do
    light.on
  end
end

Or inside a UI based rule, simply enclose the code that needs to be debounced in a block, e.g.

debounce_for 2.seconds do
  light.on
end

Additionally, the JRuby debouncer has an optional “maximum wait time” at which, the event will be fired even though it’s still coming faster than the debounced interval.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.