Design Pattern: Event Limit

Please see Design Pattern: What is a Design Pattern and How Do I Use Them first.

Problem Statement

Often one will be in a situation where an event will occur many times but an action should only be taken on the event once over a certain time period and the rest ignored. This is best explained with an example.

For example, in my own rules I generate an alert when the temperature outside is a few degrees cooler than the inside (in the summer) to open the windows and another alert when it is warmer outside than inside to close the blinds.

Every time the temperature changes both inside and outside there is the potential to generate this alert. Given that some of the thermometers report every two minutes receiving an alert every time is unacceptable.

Concept

Create a flag which gets set to true when the first event occurs. Each subsequent event will cause check this flag and suppress the activity if the flag is set to true. At some point, either based on time, a Timer, or another event the flag gets set back to false so the activity can occur again the next time the event occurs.

Simple Example

OH 3.x JavaScript

A JavaScript rate_limit library implementation is available at GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules.. This is a class with a single run method that is called with a function to run and arguments to indicate how long to wait before subsequent calls to run will execute the function.

Usage:

var OPENHAB_CONF = java.lang.System.getenv("OPENHAB_CONF");
load(OPENHAB_CONF+'/automation/lib/javascript/community/rateLimit.js');

var runme = function() {
  // latched code goes here
}

this.rl = (rl === undefined) ? new RateLimit() : this.rl;

this.rl.run(runme, "24h"); // after the first run will wait 24 hours before it's allowed to run again

OH 2.5 Python

A Python rate_limit library implementation is available at GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules.. This is a class with a single run method that is called with a function to run and arguments to indicate how long to wait before subsequent calls to run will execute the function.

Usage:

from community.rate_limit import RateLimit

latch = RateLimit()
...

    # From a Rule, call the latch to limit how often the a command can be sent.
    # Let's say we don't want an alert more than once per hour. Any subsequent
    # calls to this latch will be dropped until an hour has passed.
    latch.run(lambda: events.sendCommand("Alert", "Somethings wrong!"), hours=1)

OH 2.5 and OH 3.x Rules DSL

In this example we only want one alert for a given event a day.

var boolean alerted = false

rule "Alerting event"
when
    // something happens that would generate an alert
then
    if(!alerted){
        // send alert
        alerted = true
    }
end

rule "Reset alerted"
when
    Time Midnight
then
    alerted = false
end

In the above example there is a flag set as a global. It starts out as false and the first time that an event occurs that generates an alert the flag is set to true. Each subsequent event will avoid sending the alert. At midnight the flag is reset to false.

Therefore, in effect, the alert will only go out once a day.

Complex Example

This design pattern is used in the Generic Is Alive design pattern, look for the code that uses the notification map. In that example, when OH first starts up it sets the notification flag to false for each Item. When an update occurs that sets that Item to ON an alert is generated. But these events can occur many many times so the notification flag keeps additional alerts from going out until the Item is set to OFF at which point an alert indicating the device is offline goes out. When it comes back an alert that it is online again goes out.

Edit: Small update for OH 3, added link to Python and JavaScript library implementations.

7 Likes