This design pattern and all the code herein is deprecated. There is now a Debounce rule template in the Marketplace that can be installed and used so there is no longer a need for a Design Pattern. Simply install and configure Debounce according to the docs and you can get debounce behavior on any of your Items.
At some point I expect a Debounce Profile to become a part of core openHAB in which case that can be used for Items linked to Channels without the need for extra proxy Items.
Deprecated after this point. Kept for historic purposes only.
Please see Design Pattern: What is a Design Pattern and How Do I Use Them for a desciption of DPs.
Purpose
Debounce, sometimes called anti-flapping, is a process where either updates to an Item are ignored after a certain initial state is received, or more commonly, waiting for an Item to stay in a given state before accepting the new state. This is useful for situations like:
- a sensor may be faulty and occasionally flap back and forth rapidly (obviously you should fix the sensor but this can be a temporary fix)
- there is a period of rapid changes and one should wait until the changes stop before processing the most recent change
- cases like presence detection to avoid sending the house to away mode when only away for a very short period of time such as picking up the mail.
Concept
There are two Items, one linked to the sensor that needs to be debounced and another proxy Item. Only when the sensor Item has remained in it’s given state for long enough is that state transferred to the proxy Item. Note, that the sensor Item can be a Group.
Simple Example
We will be debouncing the Switch Person1PresenceSensor
so that Person1Presence
is not considered away until they have been away for more than 2 minutes and not considered present until they’ve been present for more than 2 minutes. When transferring the state to Person1Presence
, use an update.
OH 2.5 Python and OH 3.x JavaScript Implementations
A debounce rule written in Python for OH 2.5 and a MainUI rule with a JavaScript Script Action is available at GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules.. See the READMEs for installation and configuration details.
Both implementations work the same way and depend on Item metadata. Two Items must be defined for each value that is to be debounced. One gets updated with the raw sensor values and the other is a proxy that gets updated with the debounced values.
The raw Item must have debounce metadata defined.
Switch Person1PresenceSensor { debounce="Person1Presence"[timeout="2m"] } // Sensor for Person 1's presence
Switch Person1Presence // Proxy for Person1
The library has optional parameters where you can only debounce certain states and send a command to the proxy instead of an update. A more generic example for something like a button:
Switch ButtonSensor { debounce="Button"[timeout="0.25s" command="True"] }
Switch Button
With the above Items, the ButtonSensor must remain in a given state for 250 msec before the state is commanded to Button.
OH 2.5 and OH 3.x Rules DSL
var Timer timer = null
rule "Debounce Person1"
when
Item Person1PresenceSensor changed
then
timer?.cancel
timer = createTimer(now.plusMinutes(2), [ |
if(Person1Presence.state != Person1PresenceSensor.state)
Person1Presence.postUpdate(Person1PresenceSensor.state)
timer = null
])
end
Theory of Operation:
When Person1PresenceSensor
changes state, cancel any existing Timer
and then set a Timer
for two minutes. When Person1PresenceSensor has remained in the same state for two minutes, update Person1Presence with the current state of Person1PresenceSensor.
Complicated Example
This will be an implementation Generic Presence Detection where we have multiple people and multiple sensors per person. If any one sensor detects a person as present that person is considered immediately present. If all sensors for a person are OFF
for two minutes or more, that person is considered away. When all people are away, the main presence switch is turned OFF
with a command.
OH 2.5 Python and OH 3.x JavaScript Implementations
With one of the libraries discussed above installed everything is done through Item metadata. The configuration is the same for both.
Group:Switch:OR(ON, OFF) Present
Switch Person1_Present (PresenceSensors)
Group:Switch:OR(ON, OFF) Person1_PresenceSensors { debounce="Person1_Present" [timeout="2m", states="OFF", command="True"] }
Switch Person1_PresenceSensor1
Switch Person1_PresenceSensor2
Switch Person2_Present (PresenceSensors)
Group:Switch:OR(ON, OFF) Person2_PresenceSensors { debounce="Person2_Present"[timeout="2m", states="OFF", command="True"] }
Switch Person2_PresenceSensor1
Switch Person2_PresenceSensor2
That’s all there is to it. When Person1/2_PresenceSensors
changes to OFF
, the Debounce rule will wait two minutes and if it remains OFF
send OFF
to Person1/2_Present
as a command. If either Group changes to ON
, ON
is immediately sent as a command to Person1/2_Present
. Only the OFF
state is debounced.
Rules DSL
Items
Group:Switch:OR(ON, OFF) Present
Switch Person1_Present (PresenceSensors)
Group:Switch:OR(ON, OFF) Person1_PresenceSensors
Switch Person1_PresenceSensor1
Switch Person1_PresenceSensor2
Switch Person2_Present (PresenceSensors)
Group:Switch:OR(ON, OFF) Person2_PresenceSensors
Switch Person2_PresenceSensor1
Switch Person2_PresenceSensor2
OH 3.x Rule DSL
import java.util.Map
import org.openhab.core.model.script.ScriptServiceUtil
val Map<String, Timer> timers = newHashMap
rule "Presence detection debounce"
when
Item Person1PresenceSensors changed from OFF to ON or
Item Person1PresenceSensors changed from ON to OFF or
Item Person2PresenceSensors changed from OFF to ON or
Item Person2PresenceSensors changed from ON to OFF
then
// Cancel the timer if it exists
timers.get(triggeringItem.name)?.cancel
val delay = if(triggeringItem.state == ON) 0 else 2 // if ON update proxy immediately
timers.put(triggeringItem.name, createTimer(now.plusMinutes(delay), [ |
// get the proxy Item
val proxyName = triggeringItem.name.split("_").get(0) + "_Present"
val proxyItem = ScriptServiceUtil.getItemRegistry.getItem(proxyName)
// Command if different
if(proxyItem.state != triggeringItem.state) proxyItem.sendCommand(triggeringItem.state)
// clear the timer
timers.put(triggeringItem.name, null)
])
end
OH 2.5 Rules DSL
import java.util.Map
import org.eclipse.smarthome.model.script.ScriptServiceUtil
val Map<String, Timer> timers = newHashMap
rule "Presence detection debounce"
when
Item Person1PresenceSensors changed from OFF to ON or
Item Person1PresenceSensors changed from ON to OFF or
Item Person2PresenceSensors changed from OFF to ON or
Item Person2PresenceSensors changed from ON to OFF
then
// Cancel the timer if it exists
timers.get(triggeringItem.name)?.cancel
val delay = if(triggeringItem.state == ON) 0 else 2 // if ON update proxy immediately
timers.put(triggeringItem.name, createTimer(now.plusMinutes(delay), [ |
// get the proxy Item
val proxyName = triggeringItem.name.split("_").get(0) + "_Present"
val proxyItem = ScriptServiceUtil.getItemRegistry.getItem(proxyName)
// Command if different
if(proxyItem.state != triggeringItem.state) proxyItem.sendCommand(triggeringItem.state)
// clear the timer
timers.put(triggeringItem.name, null)
])
end
Theory of Operation
When all of the sensors for a person go to OFF
, the Person1PresenceSensors
Group
will go to OFF
through the Group
aggregation function. Any existing Timer is cancelled and a new one is created which will go off in two minutes. After two minutes without changing from OFF
, the OFF
command is commanded to the Proxy Item which is determined using Design Pattern: Associated Items if it’s different from the Proxy Item’s current state.
When an ON
command is received, the Timer for this Item is cancelled, if it exists, and a Timer is created to execute immediately which will command the Proxy Item to ON
if it isn’t ON
already.
Advantages and Disadvantages
When using the Python or JavaScript library implementations, there is no code to write or modify at all. All that is required is adding some Item metadata to the sensor Item and adding a proxy Item. The approach over all provides a generic way to delay processing of changes until it’s know n to be a good state in a general way.
Related Design Patterns
Design Pattern | How it’s used |
---|---|
Design Pattern: Associated Items | Used in the Rules DSL example to convert the sensor Item’s name to it’s proxy name |
Design Pattern: Motion Sensor Timer | A similar but subtly different approach that is more suitable to cases where updates drive the command to the proxy Item instead of changes. |