Avoid two consecutive item state changes or executions of rules

How can I implement a way to avoid two consecutive item state changes (i.e. 2 door bell button presses) or two executions of rules within a short period (e.g. 2 seconds).
At the moment I only can think of a more complex way like creating a cache variable which gets reset by a timer and the rule checks the existence of such a variable.

Just wanted to check if there are better ideas.

I do this like you say with a variable set and unset by timer…
I don’t know another way.
Greets
Sebastian

This is a typical debounce behavior. When the state changes you want to ignore all subsequent state changes for a given amount of time. Correct?

If so you don’t need to implement this yourself. Install the Debounce rule template from Settings → Automation → Rule Templates, and instantiate and configure a rule based on the template.

The template requires a debounce Group that all your “raw” Items belong to. Each raw Item has a proxy Item. Each raw Item also has metadata that points to the proxy Item and defines the behavior of the debounce (e.g. how long, whether to only debounce certain states, debounce changes and updates, etc.).

The rule template will only update the proxy Item for state updates or changes that occur the configured amount of time after the last update or change.

Everything else in OH uses the proxy Item.

It’s a complicated problem and it can be tough to get right so why code it if you don’t have to?

If you do want to code it yourself, you can at least look at how I did it for inspiration. Note that a little more than half of the code is error checking to generate meaningful error messages. Don’t be intimidated by it’s length.

What do you think about this?

var dingdonglock = 0
var Timer waittimer = null

rule "Türklingel" 
when 
Item Turklingel_klingeltaste changed to ON
then
if (dingdonglock == 1) return;

dingdonglock = 1
//Do things... 
waittimer = createTimer(now.plusSeconds(5), [ |
dingdonglock = 0
])
end

Over all that’s the general approach, though usually you’d want to reschedule the Timer if you receive a change while the Timer is active. You’d want to wait for the state to stabilize before accepting the last change.

Also, you’d have to write a separate rule for each Item and duplicate that code for every use of every Item you want to debounce. If you’ve only the one Item and the one rule then that’s definitely the way to go. If you have a few or more, you’ll want to centralize the logic so you don’t end up with the same code repeated all over the place.

You also probably don’t need the lock. You can use the existence of the Timer itself as the lock.

Another approach can be to use a script transform profile. Of course that would only work if you have a Link between a Channel and an Item involved. The following JS transform would prevent the Item from being updated too fast. It will not wait for the Item to stop rapidly updating though. It will just ignore the for a time (i.e. no reschedule).

(function(input, name, time) {
  var period = time.Duration.parse(time);
  var lastPub = cache.private.get(name, () => time.toZDT().minus(period).minusSeconds(1));
  var delta = time.Duration.between(lastPub, time.toZDT());
  if(delta.compareTo(period) >= 1) return input;
  return null;
})(input, name, time)

You can pass name and time to the transform using URL notation. For example:

JS:timeFilter.js?name="doorbell"&time="PT5S"

It looks to see if more time than what is specified in time has passed. If so we return the input. If not we throw the input away.

But again, this only samples the updates. It does not wait for the updates to stop before sending the value on.

1 Like

Thanks for the link, Rich. I was searching the forum for all various combinations for my problem but your post never appeared in the hit list - furthermore I am not aware of the term debounce in english. I’ll look into that.

In this particular use case it is not required. The use case is the following:
Several times per day the parcel delivery service is ringing the door bell and as they are in hurry they press the dorr bell button three times consecutively which is really annoying after a while.
So in this case I simply need the feature that the rule is not triggered one more time after the first run for e.g. 5 seconds and a reschedule of the delay is not required.

That’s why I like your second idea to even block the item being changed for a certain period in the channel link layer as it does not trigger the rule a second time at all.

It’s kind of a technical term. In electronics it refers to dealing with the fact that when someone physically presses a button, the spring causes the button to bounce when it’s released and making multiple contacts as a result, making look like the button was pressed more than once.

Meh. Something is going to trigger and process the event. Whether it’s the rule that processes the doorbell, a separate special purpose rule like Debounce, or the Script transform profile you will have a “script” running to process this event. The only difference is where it’s running.

Is your function updating the Item at first or last press of the doorbell?
greets
Sebastian

It updates the Item on the first press and then again on the first press after time, and then again on the first press after time and so on.

If time is two seconds (`PT2S1) it will update the Item no faster than once every two seconds, ignoring everything that happens inbetween.

I suppose I should promote my own library here as well. If you have openhab-rules-tools installed the script could be:

var RateLimit = require("openhab-rules-tools");

(function(input, name, time) {
  var rl = cache.private.get(name, () => RateLimit());
  var allowed = false;
  rl.run(() => allowed = true; time);
  if(allowed) return input;
  else return null;
})(input, name, time)

It doesn’t really save much in overall lines of code but it is simpler over all. I should update this so you can get a boolean back from run() if it ran the function so we don’t need this allowed hack. When/if I do that the function would become:

var RateLimit = require("openhab-rules-tools");

(function(input, name, time) {
  var rl = cache.private.get(name, () => RateLimit());
  if(rl.run(time)) return input;
  else return null;
})(input, name, time)

ok thank you

JRuby gives you three options to deal with this situation:

Guard Triggers Immediately Description
#debounce_for No Waits until there is a minimum interval between triggers.
#throttle_for No Rate-limits the executions to a minimum interval, regardless of the interval between triggers. Waits until the end of the period before executing, ignores any leading triggers.
#only_every Yes Rate-limits the executions to a minimum interval. Immediately executes the first trigger, then ignores subsequent triggers for the period.

To deal with a “Door bell” being pressed repeatedly, use only_every

Example rule:

# They can keep pressing the door bell as often as they like,
# but the bell will only ring at most once every minute
rule "Door Bell is pressed" do
  updated DoorBell_Button, to: "single"
  only_every 1.minute
  run { Audio.play_stream "doorbell.mp3" }
end

Or in a UI rule (just the rule body):

only_every 1.minute do
  Audio.play_stream "doorbell.mp3"
end

I store the datetime of current rule execution in a variable and before execution any action I compare last execution time with current time.

By this you do not need timers or additional proxy items.

Also you can easily reuse this, as you can have the variable name dynamic to include the item name

But you need to duplicate this same code in every rule where you need it. If you’ve only one or two that’s no big deal. If more than that it becomes untenable.

For me this argument does not really count, because for any possible solution you need to copy & paste something, if you need the same thing multiple times.

If you use the timer or datetime approach, then you need to copy & paste certain script block.
(If OH would support return values if you call other scripts from a rule, then it would be one line code per rule and you could have the real code in a separate script)

If you use the debounce rule template you always need to configure group, metadata and proxy item.

If there is one option where you can replicate this by magic, then I would agree, that copy & paste same code is unnecessary.

No you don’t. That’s the whole point. You should almost never copy and paste code like that. If you are doing that it’s a sign that either:

  1. you need to combine the logic into one rule
  2. you need to create a library function (global lambda for Rules DSL, personal library for other languages)

There’s no copy and paste for that at all. It’s just Item creation.

There’s no copy and paste for the script transformation above. You create it once and apply it where it’s needed.

Yes, you need to configure something for all the relevant Items. But configuration is a hell of a lot easier to maintain in the long run than the same lines of code over and over.

The whole point is when you need to make a change, you only have to change it in one place, not many. Add an Item, configure that one Item. Remove an Item, configure that one Item. Change how the logic works, change the one place where that logic is implemented.