There is now a rule template in the marketplace that implements this. See below for instructions on how to set it up for this DP.
Please see Design Pattern: What is a Design Pattern and How Do I Use Them for details on how to use DPs.
Problem Statement
Many motion detectors only report ON when they detect motion but do not send a corresponding OFF after no motion has been detected after a period of time. In addition, often one wants to control a device like a light for a certain period after the last motion is seen, not after the motion sensor goes to OFF. Therefore one needs to keep an occupancy Contact OPEN (or Switch ON) until the desired amount of time after the last motion is detected.
This approach works any time one wants something to happen a certain amount of time after the last time that an Item received a command or update. It can be easily adapted to report when a device stops reporting (see Generic Is Alive), to avoid flapping (e.g the switch constantly switching back and forth when something is on the edge of detection, see Debounce.
Concept
When the motion detector sends an ON command, create a timer. If subsequent ON commands are received reschedule the timer. When the Timer expires call a rule containing the code to execute when there hasn’t been motion events for long enough.
Rule Template Installation, Rule Instantiation and Configuration
Open Reminder Rule Template
Install Template and Instantiate and Configure the Rule
This rule template implements this design pattern. Configure it as follows:
-
Create a Group and add all the Items that represent a motion detection to that Group. We’ll call it “MotionSensors”.
-
Create a rule or a script that will be called when enough time has passed after the last motion. This rule can do anything you want including sending an alert, changing the state of a Switch Item (e.g. turning off a light, etc.) See below for an example.
-
Install the Open Reminder Rule Template from MainUI → Settings → Automation.
-
Create a new rule and choose “Open Reminder” as the template.
-
Configure the rule as follows:
-
give the rule a meaningful ID, name and description
-
choose MotionSensors as the Group
-
type in “ON” for the Alert State
-
toggle Invert to ON meaning we want to alert when the Item updates to
ON
(more on that in a moment), not changing away fromON
-
Provide a default initial timeout, the amount of time before the alert is generated. Use ISO8601 format (e.g. "PT15m " means 15 minutes).
-
You can provide the namespace for Item metadata to use instead of the default initial timeout. This can be useful if you want to have a different timeout for each motion sensor.
-
Leave the repeat duration blank.
-
Set Reschedule toggled to on.
-
Select the rule you created in step 2 as the “Alert Rule”
-
Finally, if desired, set a Do Not Disturb period. Alerts that occur during this period will be suppressed until the end of the period, if the Item is still in the alerting state. If the end time is before the start time, the period is assumed to span midnight. However, keep in mind that if your motion sensing Items do not go to OFF on their own, at the end of the DND period your script created in step 2 will be called at the end of the DND period.
-
Click “Save” and you are almost done.
-
Rule templates cannot change the type of the trigger to the rule. In this case, open the rule and change the trigger from “Member of a group changes” to “Member of the group receives an update”. If you’ve only the one Item, change the trigger to “Item receives an update”.
Processing Rule
The rule you created in step 2 above will be called by the rule instantiated from the rule template. The Item that caused the rule to be called will be stored in the context as alertItem
and the state of the Item that caused the rule to be called will be stored in the context as currState
. Access to the context depends on the Rules Language. In JS Scripting, they are just inserted as stand alone variables.
In my case I just use a simple log to alert. See [Deprecated] Design Patterns: Generic Is Alive for a more comprehensive idea of the sorts of things you can do in the called rule.
DEPRECATION WARNING
The code below is deprecated an no longer maintained. It’s left here for historical purposes only.
Simple Example: Expire Binding Version
Items
Switch Occupancy1 { expire="5m,command=OFF" }
Switch MotionDetector1 { expire="5m,command=OFF" }
Rules
Python
from core.rules import rule
from core.triggers import when
@rule("Motion sensor triggered")
@when("Item MotionDetector1 received update ON")
def motion(event):
events.sendCommand("Occupancy1", "ON")
Rules DSL
rule "Motion sensor triggered"
when
Item MotionDetector1 received update ON
then
Occupancy1.sendCommand(ON)
end
Theory of Operation
That is all there is to it. When the MotionDetector1 receives and ON update it sends an ON command to the occupancy switch. The Expire binding will set the occupancy Switch to OFF five minutes after the last ON command is received.
Simple Example: Timers Version
Items
Switch MotionDetector1
Rules
Python
from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution
from org.joda.time import DateTime
occupancyTimer = None
@rule("Motion sensor triggered")
@when("Item MotionDetector1 received update ON")
def motion(event):
global occupancyTimer
if occupancyTimer is None or occupancyTimer.hasTerminated():
occupancyTimer = ScriptExecution.createTimer(DateTime.now().plusMinutes(5), lambda: events.sendCommand("MotionDetector1", "OFF"))
else:
occupancyTimer.reschedule(DateTime.now().plusMinutes(5))
Rules DSL
var Timer occupancyTimer = null
rule "MotionDetector1 received ON"
when
Item MotionDetector1 received update ON
then
if(occupancyTimer === null || occupancyTimer.hasTerminated()) {
occupancyTimer = createTimer(now.plusMinutes(5), [|
MotionDetector1.sendCommand(OFF)
occupancyTimer = null
])
}
else {
occupancyTimer.reschedule(now.plusMinutes(timeoutMinutes ))
}
end
Theory of Operation
When MotionDetector1 receives and ON update create a Timer to turn it OFF in 5 minutes. If there already is a timer, reschedule the Timer.
Comprehensive Example
This example shows one way one can manage and consolidate the above logic for multiple Motion Detectors. The following rule depends upon persistence having been set up on the MotionDetector Items and assumes that detectors will not trigger really really close together.
Items
Group gMotionDetectors
Switch MotionDetector1 (gMotionDetectors)
Switch MotionDetector2 (gMotionDetectors)
Rules
Python
from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution
from org.joda.time import DateTime
timers = {}
timeoutMinutes = 5 # use an appropriate value
@rule("Motion sensor triggered")
@when("Member of gMotionDetectors received command ON")
def motion(event):
if event.itemName not in timers or timers[event.itemName].hasTerminated():
timers[event.itemName] = ScriptExecution.createTimer(DateTime.now().plusMinutes(timeoutMinutes), lambda: events.sendCommand(event.itemName, "OFF"))
else:
timers[event.itemName].reschedule(DateTime.now().plusMinutes(timeoutMinutes))
Rules DSL
import java.util.Map
val Map<String, Timer> timers = newHashMap
rule "A Motion Detector triggered"
when
Member of gMotionDetectors received command ON
then
val timeoutMinutes = 5 // use an appropriate value
if(timers.get(triggeringItem.name) === null or timers.get(triggeringItem.name).hasTerminated()){
timers.put(triggeringItem.name, createTimer(now.plusMinutes(timeOutMinutes), [|
triggeringItem.sendCommand(OFF)
timers.put(triggeringItem.name, null)
]))
}
else {
timers.get(triggeringItem.name).reschedule(now.plusMinutes(timeOutMinutes))
}
end
Theory of Operation
We store the Timers in a dict/Map using the name of the Motion Detector Item as the key. When one of the members of gMotionDetectors receive an ON command create a Timer for that detector to go off in 5 minutes. If one already exists reschedule it. Store the Timer in the Map.
Here is the version of the above code using the Expire binding:
Switch MotionDetector1 { expire="5m,command=OFF" }
Switch MotionDetector2 { expire="5m,command=OFF" }
Look ma, no code! The above does exactly the same thing as the complex example above with the Map and Timers.
Related Design Patterns
Design Pattern | How It’s Used |
---|---|
Design Pattern: Sensor Aggregation | The complex example of Sensor Aggregation uses this DP to implement an ant-flapping timer. |
[Deprecated] Design Pattern: Debounce | A similar DP that involves changes instead of updates. Eventually these two will be merged. |
Edit: Added Python examples. Minor cleanup of the Rules DSL code.