Edit: Updated for OH 4, removed Nashorn JS and Python examples as those libraries are no longer maintained.
Please see Design Pattern: What is a Design Pattern and How Do I Use Them for a desciption of DPs.
Problem Statement
There are some technologies that are sensitive to receiving multiple commands too close together. When they occur too close together commands get dropped or other problems may occur. Insteon and 433MHz are two such technologies with this problem.
There are other situations where you may want to schedule a series of events to occur with a defined spacing between them. For example, to schedule the amount of time to irrigate a number of zones. This also applies to scheduling delays between calls to the say
command or scheduling the startup of a home entertainment system (e.g. turn on the stereo, wait five seconds, turn on the TV, etc.).
Concept
Centralize all the communication with these technologies into a single gatekeeper and add delays between each command sent to the devices so no two commands, regardless of their target device, ever get sent too close together or have the desired spacing between commands.
Example
We can take advantage of Design Pattern: Associated Items and Design Pattern: Encoding and Accessing Values in Rules to make a generic solution.
Note: The code below depends on the the WirelessDevice Items to be persisted.
Items
Group WirelessDevices
Switch WirelessDevice_xxxxx_1 (WirelessDevices)
Switch WirelessDevice_xxxxx_1_Proxy // linked to channel
Switch WirelessDevice_xxxxx_2 (WirelessDevices)
Switch WirelessDevice_xxxxx_2_Proxy // linked to channel
Rules
Create a rule triggered by “Member of WirelessDevices” and ensure that commands to the actual devices are at least one second apart.
Blockly
The OHRT Block Library includes a block that implements a gatekeeper.
JS Scripting
This implementation depends on openHAB Rules Tools Announcements
var {Gatekeeper} = require('openhab_rules_tools');
var gk = cache.private.get('gk', () => Gatekeeper());
gk.addCommand(() => items[event.itemName+'_Proxy'].sendCommand('ON'));
Rules DSL
import java.util.concurrent.ConcurrentLinkedQueue
import java.time.Duration
val Queue<Sring> items = new ConcurrentLinkedQueue()
val Queue<String> commands = new ConcurrentLinkedQueue()
var Timer timer = null
var lastCommand = now.minusSeconds(1).millis
rule "WirelessDevices gatekeeper"
when
Member of WirelessDevices received command
then
items.add(triggeringItemName)
commands.add(receivedCommand.toString)
if(timer === null) {
timer = createTimer(now, [ |
if(commands.peek !== null) {
val item = items.poll
val cmd = commands.poll
sendCommand(item+"_Proxy", cmd)
lastCommand = now
}
val deltaTime = Duration.between(now, lastCommand).toMillis();
timer.reschedule(now.plusMillis(if(deltaTime<1000) 1000-deltaTime else 0) // 0 will reschedule the timer to run immediately
])
}
end
Theory of Operation:
When a command comes in to a WirelessDevice Item, the name of the Item and the command is queued up. A looping timer is created which sends the command and waits the desired amount of time before sending the next command until the queue is empty. If the timer is running, the subsequent commands will be queued and worked off in turn.
Advantages and Disadvantages
Advantages
-
Does not tie up a Rule runtime thread sleeping
-
Can be used to schedule a series of events (particular the Python version), such as turning on a stereo receiver and waiting a couple seconds before starting the audio stream to it, giving it time to start up before playing the audio. The following is a Blockly example for an irrigation schedule.
-
Super easy to implement and use with the block library or OHRT library.
Disadvantages
- Requires creating a separate set of Rules for each Gatekeeper required (e.g. one for TTS say commands and other for Hue bulbs).
- The Rules DSL version has a hard coded delay between commands. The others let you specify a different delay after each command.
Related Design Patterns
Design Pattern | How It’s Used |
---|---|
Design Pattern: Separation of Behaviors | Gate Keeper is a specific implemenation of Separation of Behaviors |
Design Pattern: Proxy Item | The WirelessController and Device Items |
Design Pattern: Associated Items | Mentioned in the description but not used above |
Design Pattern: Encoding and Accessing Values in Rules | The naming for the device Items |
Design Pattern: Looping Timers | The implementation of the loop that works off the queue in the Rules DSL version |