Please see Design Pattern: What is a Design Pattern and How Do I Use Them for a description of DPs.
Problem Statement
Frequently, when attempting to write generic Rules, especially when applying Design Pattern: Associated Items, one may need to set a separate Timer for each Item.
Concept
Store the Timers into a Map or dict using the Item name as the key.
Example
OH 3.x JavaScript
I’ve written a timerMgr class located at GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules. which can be downloaded and used in your rules.
Usage:
// Send an alert message when a door is left open for an hour
// Load TimerMgr
var OPENHAB_CONF = java.lang.System.getenv("OPENHAB_CONF");
load(OPENHAB_CONF+'/automation/lib/javascript/community/timerMgr.js');
this.tm = (this.tm === undefined) ? new TimerMgr() : this.tm;
if(event.itemState == OPEN){
tm.check(event.itemName,
"1h",
function(){ events.sendCommand("Alert", event.itemName + " is still open after an hour!");
}
else if(event.itemState == CLOSED){
tm.cancel(event.itemName);
}
tm.check
will check to see if a timer already exists for event.itemName
. If not it creates one to go off in one hour and will call the function to sendCommand to the Alert Item with the door is open message.
Be sure to read the docs for the library as there are arguments which let you control what happens when the timer already exists:
- reschedule the timer
- cancel the timer
- call a different function when the timer already exists
OH 2.5 Python
For Python I’ve written a timer_mgr library you can obtain from GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules.. This library implements all the management of timers like this for you. All you need do is instantiate the class and call the methods. See the README at that location for usage and other details.
For example, to use the library to set a Timer to send an alert when a door has been left open for 60 minutes:
from core.rules import rule
from core.triggers import when
from community.timer_mgr import TimerMgr
tm = TimerMgr()
@rule("Send an alert if a door has been left open for 60 minutes")
@when("Member of Doors changed")
def door_reminder(event):
if event.itemState == OPEN:
tm.check(event.itemName,
(60*60*1000),
function=lambda: events.sendCommand("Alert", event.itemName + " is still open!"))
elif event.itemState == CLOSED:
tm.cancel(event.itemName)
def scriptUnloaded():
tm.cancel_all()
The above rule will send a command to the Alert Item when the door remains open for more than 60 minutes. If the door closes before 60 minutes the Timer is cancelled. All the book keeping and checking whether the Timer exists and such is handled by the library. You can pass a reschedule=True
argument to reschedule the Timer if it already exists and pass in a flapping_function
that gets called if the Timer already exists when calling check
.
Rules DSL
The below code should work in either OH 2.5 or OH 3. See the “Group findFirst” example at Design Pattern: Associated Items for another full example. Pay attention to the lines where the variable timers
is used.
import java.util.Map
val Map<String, Timer> timers = newHashMap
rule "Send alert if a door has been open for more than 60 minutes"
when
Member of Doors changed
then
// Always cancel the existing Timer
val timer = timers.get(triggeringItem.name)
timer?.cancel
timers.put(triggeringItem.name, null)
// Create a Timer if the door opened
if(triggeringItem.state == OPEN) {
timer.put(timerItem.name, createTimer(now.plusHours(1), [ |
Alert.sendCommand(triggeringItem.name + " is still open!")
])
}
end
Advantages and Disadvantages
Managing Timers in this way makes it possible to write generic Rules that work with separate Timers for each Item. When using the Python or JavaScript library, it only takes a couple of lines of code.
Related Design Patterns
Design Pattern | How It’s Used |
---|---|
Design Pattern: Associated Items | Includes an example of this DP. |
Update: Added JavaScript implementation and updated for OH 3.