Debounce [3.2.0;3.4.9]

Thanks for sharing this Rule Template. I have quite a few anti-flapping timers that I will (over time as I make other changes) replace with this. I converted a couple of them to begin with, and it works great.

i implemented you code change, but i still get strange behaviour… i have a test setup like described in your post " Generic Presence Detection and i have this logs:

2022-04-03 22:08:20.833 [DEBUG] [el.script.Rules.rules_tools.Debounce] - Sending command OFF to Person1Presence
2022-04-03 22:08:20.857 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Person1Presence' received command OFF
2022-04-03 22:08:20.862 [INFO ] [hab.event.GroupItemStateChangedEvent] - Item 'Presence' changed from ON to OFF through Person1Presence
2022-04-03 22:08:20.865 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Person1Presence' changed from ON to OFF
2022-04-03 22:17:52.339 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'Person1Sensor1' received command ON
2022-04-03 22:17:52.349 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Person1Sensor1' changed from OFF to ON
2022-04-03 22:17:52.352 [INFO ] [hab.event.GroupItemStateChangedEvent] - Item 'Person1Presence_Raw' changed from OFF to ON through Person1Sensor1
2022-04-03 22:17:52.360 [DEBUG] [el.script.Rules.rules_tools.Debounce] - Debouncing Person1Presence_Raw with proxy = Person1Presence timeout = 5m and states =
2022-04-03 22:22:52.374 [DEBUG] [el.script.Rules.rules_tools.Debounce] - End debounce for Person1Presence, new state = ON, curr state = OFF, command = true
2022-04-03 22:22:52.391 [DEBUG] [el.script.Rules.rules_tools.Debounce] - Sending command ON to Person1Presence

in this log i do not understand why the line

Item 'Person1Presence_Raw' changed from OFF to ON

does not trigger a change of Person1Presence… if the item Person1Presence_Raw goes to ON, shouldn’t this immediatly trigger Person1Presence to ON??

…i just have seen that Person1Presence goes to ON, exactly 5 minutes later (my debounce time!)… is this a bug? …or do i have a wrong understandig what debounce should do…? or do a miss something??

If that’s how the metadata is configured. Assuming you’ve not changed the metadata from above where “states” only lists OFF, then it should immediately go to ON and the timer become cancelled.

That’s how it’s working for me with the change from above.

And the logs do indicate that the rule did send the ON command to Person1Presence. Those last two lines show that happening immediately in response to Person1Presence_Raw changing from OFF to ON.

You do not show any logs after that. You should see in events.log Person1Presence receiving that ON command and it changing to ON (assuming it wasn’t already ON). You shouldn’t see anything else in openhab.log after the “Sending command ON…” statement because the timer was in fact cancelled. The Debounce rule has nothing left to do for that Item change.

You’ve not posted enough logs to see what’s going on. From what’s posted above it’s working as expected.

i am not sure you noticed the 5 minutes delay, so i reformated the logs:

22:08:20.833 Sending command OFF to Person1Presence
22:08:20.857 Item ‘Person1Presence’ received command OFF
22:08:20.862 Item ‘Presence’ changed from ON to OFF through Person1Presence
22:08:20.865 Item ‘Person1Presence’ changed from ON to OFF
22:17:52.339 Item ‘Person1Sensor1’ received command ON
22:17:52.349 Item ‘Person1Sensor1’ changed from OFF to ON
22:17:52.352 Item ‘Person1Presence_Raw’ changed from OFF to ON through Person1Sensor1
22:17:52.360 Debouncing Person1Presence_Raw with proxy = Person1Presence timeout = 5m and states =
22:22:52.374 End debounce for Person1Presence, new state = ON, curr state = OFF, command = true
22:22:52.391 Sending command ON to Person1Presence

…but if i understand you correctly, the event should pass immediatly and not with 5 minutes (the debounce time) delay… but thx for the clarification, will try to to another test to reproduce it again…

PS: the debounce config of Person1Presence_Raw:

value: Person1Presence
config:
  command: "True"
  state: OFF
  timeout: 5m

I didn’t see the five minute delay. It’s hard to line thing up on a small screen.

The relevant code is as follows:

if(cfg["states"].length == 0 || 
  (cfg["states"].length > 0 && cfg["states"].indexOf(event.itemState.toString()) >= 0)) {
  logger.debug("Debouncing " + event.itemName + " with proxy = " + cfg["proxy"] 
               + " timeout = " + cfg["timeout"] + " and states = " + cfg["states"]);
  this.timers.check(event.itemName, cfg["timeout"], 
                    end_debounce_generator(event.itemState, cfg["proxy"], cfg["command"]));    
}
else {
  logger.debug(event.itemName + " changed to " + event.itemState + " which is not debouncing");
  this.timers.cancel(event.itemName); // Cancel the timer if it exists
  end_debounce_generator(event.itemState, cfg["proxy"], cfg["command"])();
}

In prose: If there is no “states” entry in the Item’s metadata or the item’s new state is among the comma separated list of states in the “states” entry in the Item’s metadata, create a Timer to call the function created by end_debounce_generator at the configured time.

If there is a “states” entry in the Item’s metadata but the Item’s new state is not among the comma separated list of states, immediately cancel the timer (if there was one) and immediately call the function created by end_debounce_generator.

That first part of the if ran, which means either there was no “states” entry or ON was among the list. And indeed, looking back at the metadata you don’t have a “states” entry. You have “state”.

yes, you found the error… big thanks, i did not see this… plz can you update your post because i think i copied the error from there :slight_smile:

Done. That post is very old and in an initial implementation it was called “state” instead of “states”.

Thx for the fast reply, works for me perfectly.

For my rule I added some debug logging:

else {
  logger.debug(event.itemName + " changed to " + event.itemState + " which is not debouncing");
  if (this.timers.hasTimer(event.itemName)) {
    logger.debug("cancelling timer for " + event.itemName);
    this.timers.cancel(event.itemName);
  }
  end_debounce_generator(event.itemState, cfg["proxy"], cfg["command"])();
}

Thx again!

Two minor suggestions:

  1. Step-by-step clarification

Should probably be

In the UI, navigate to the “raw” Item, click on “Add Metadata”, click “Enter Custom Namespace…” (under “Custom namespaces” section) and enter “debounce” as the namespace.


  1. Stylistic suggestion: separate the two config examples code blocks
Switch RawSensor (Debounce) { debounce="ProxySensor"[timeout="2m", states="OFF", command="False"] }
Switch ProxySensor

or in the UI

value: ProxySensor
config:
  states: OFF
  timeout: 2m
  command: "True"

Hmm I would like to several debounce rules configured, many of them operating on a single “raw” item.

I am thinking of I really need several “debounce group items” or could I simply slap all the raw items into single group?

I have actually currently pointed the rules towards the raw items (instead of a group). Getting some funky behavior…Now I realize that will not obviously work since “member of a group” trigger has been used in the rule.

I’m OK with that and will change it in the OP.

I agree with the suggestion but the code that imports it and shows it in MainUI wasn’t able to handle two code blocks so they are combined into one.

Not that there’s any problem with that approach but is there is a reason why?

You are freely able to modify the rule generated by the template in any way you need to. It’d only take a minor edit to the rule to change the trigger to an Item based trigger instead of Member of trigger.

1 Like

Simply…just trying to avoid having so many items, it makes it harder to understand the system later on (like 1y later when I have forgotten all this).

Similarly, I avoid modifying templated rules so I can update them without worrying what changes to make where.

For what it’s worth, having single debounce group seems to work fine based on quick testing. Quick code review suggests that rule uses member item name and member item state, and the actual group acts almost as filter mechanism for events only. The group name and group state (if any) does not seem play a rule, at least with the current version.

This is great as I also avoid having multiple debounce rules, making it easier to update the rule when needed.


Thanks for this, I find it very useful. I use this to debounce raw status that has been formed from item state (looking at undef) and thing status (online/offline) (Device status online monitor). With “expire” I can pick up nonupdating zwave items.

I had setup alerting first without debounce but that is suspect to some transient unnecessary alerts since my modbus device is sometimes unable to respond to requests (like, for 20 seconds). Debounce will resolve this kind of online/offline flakiness and alert only if the issue remains.

Correct. The Group name serves only configure the trigger for the rule. The event is used to get the Item’s name and state which will be the same whether the rule is triggered by Member of or Item received triggers.

As you’ve seen you really only need the one Group Item in addition to your already existing Items and that Group’s sole purpose is to trigger the rule.

Perhaps someday if a rule’s triggers are easier to modify at runtime I’ll change it so the rule automatically gets triggered by Items with the debounce metadata (that’s how my original Jython version worked) but there are weird limitations with rules creating rules in the UI that makes that challenging.

I use it for my persistence (have to be away for five minutes before I actually count it as away) and for a few wired reed sensors that go crazy periodically when the temp gets below 10 degrees F. Even just a half second debounce on those is enough to go for dozens of alerts to none (because the door never actually opened in the first place.

There was a PR and issue opened to create a Debounce profile but I don’t know that it’s ever been completed or merged. That wouldn’t completely replace this rule template since profiles only work with Links and not all Items one may want to debounce have a Link (e.g. the Item that is debounced for my presence detection is actually a Group Item with an aggregation function).

I’m glad you find it useful.

Thanks!

1 Like

I solved the debouncing pattern with the persistence service. Imagine this: you have a CO2 sensor that reports a value every minute. A rule processes this value and switches the speed of a fan at certain threshold values. Around this threshold, the fan is cycled up and down every minute. We need a debouncer. My implementation simply checks whether the value has changed for a certain period of time and if so, the fan speed is NOT updated/switched (for 30 minutes in my case).

var zdt = Java.type('java.time.ZonedDateTime');
var persistence = Java.type('org.openhab.core.persistence.extensions.PersistenceExtensions');

var co2 = itemRegistry.getItem('co2').getState().format('%f');
var co2_control = itemRegistry.getItem('co2_control').getState();

var step_changed_since_period = persistence.changedSince(itemRegistry.getItem('step'), zdt.now().plusMinutes(-30));

if (!step_changed_since_period) {
  var step = 2;
  if (co2_control === ON) {
    if (co2 >= 900) {
      step = 3;
    }
    if (co2 < 500) {
      step = 1;
    }
  }
  events.sendCommand('step', step);
}

Keep in mind that persistence is relatively slow. Anything faster than say 500 msec will run into timing problems as it takes time to save to the database and that happens in parallel with the rule execution.

Thanks for the feedback. True, depends on the use case. If the co2 sensor doesn’t report very often (everything above 5 secs is safe), this isn’t an issue. I’m using cron to trigger my script every 5 minutes, which is absolutely sufficent controlling air quality. I thought this approach might help solving simple use cases, since it is fairly easy to implement.

It might be worth posting as a separate Tutorials and Solutions thread. The discussion under a posting in the marketplace should be limited to the rule template posted in the first post. It’s not really appropriate for side discussions and alternative approaches.

Keep in mind that this thread represents a rule template that can be installed and configured through MainUI. The discussion should be related only to that.

I am experiencing initialization issues with this rule - I suspect it is a form of race condition.

Now on openHAB startup, I end up having NULL debounced item while the “input items” (items having debounce metadata) are OK/ON.

I suspect the “input items” get their state update/state change (from NULL to ON) before the rule is ready to process the update.

This is of course just a theory. Is this expected behaviour?

It would be great if the rule has on-startup behaviour to initalize the result item if we do not receive any state changes. Or perhaps there is some other way to mitigate this?

I think this would be expected. It’s pretty typical that restoreOnStartup happens before the rule engine is ready so there is no event to trigger the rule in that case.

I would expect the proxy Item to be the one with restoreOnStartup rather than or in addition to Item linked to the Channel.

However, if the Channel linked Item is indeed getting an update before the rule engine runs that one is a little tricky. IIRC the additions to event that include more information about how the rule was triggered hasn’t been merged yet making it not quite as straight forward to tell when the rule is triggered during a system start. I’ll have to think about it.

This whole rule template will be rewritten for OH 4, I’ll have to take this into account.

I do not use restoreOnStartup so it might be that rules actually take some time to initialize… more than my things that start pushing state updates

This is my workaround in Device status online monitor

const cronOrStarted = typeof event === 'undefined' || (event.statusInfo === undefined && event.itemState === undefined)

There is also the type property in UI events: JavaScript Scripting - Automation | openHAB (I have not tried this, not sure which version includes this)