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:
-
debounceTimerprevents 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_timerensures 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
-
Prevents unwanted duplicate actions caused by unstable sensors or buttons.
-
Reduces unnecessary log entries or commands sent to actuators.
-
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
