It sounds so simple but how do you:
-
deal with the events that took place 61+ seconds ago? How do those time out? You can’t just keep a running count because the events have to time out over time
-
trigger the alarm immediately after the three events occur rather than having to wait up to a minute after the three events occur before triggering the alarm?
You need a timer for each ON event, not a Timer for when to turn off the siren.
Now that I’m at a computer, here is the code. It is as simple as it can be made.
import java.util.List
var List<Timer> timers = createArrayList
var Timer shutoffTimer = null
var lastRun = now
rule "SonoffMotion changed from OFF to ON 3 times in a minute"
when
Item Sonoff1 changed from OFF to ON or
Item Sonoff3 changed from OFF to ON
then
// ignore the event if we are playing the siren already or the Alarm is OFF or it has been less
// then five minutes since the last time the siren was played
if(shutoffTimer !== null || Alarm.state == OFF || lastRun.isAfter(now.minusMinutes(5))) return;
// if timers.size < 2 we have not yet had three ON events in a minute
if(timers.size < 2) {
val t = createTimer(now.plusMinutes(1), [ | timers.remove(t) ])
timers.add(t)
}
// if timers.size == 2 then this event is the thrid ON event in a minute
if(timers.size == 2) {
sendMail("xxx@gmail.com", "Sonoff1", "Sonoff1 motion")
Sonoff2.sendCommand(ON)
lastRun = now
logInfo("RuleInfo", "Siren START")
shutoffTimer = createTimer(now.plusSeconds(240)) [|
Sonoff2.sendCommand(OFF)
logInfo("RuleInfo", "Siren STOP")
shutoffTimer = null
]
}
end
It is the exact same concept but slightly “cleaner” to use Design Pattern: Expire Binding Based Timers.
Group:Number:SUM MotionEventTimers
Switch MotionEvent1Timer (MotionEventTimers) { expire="1m,command=OFF" }
Switch MotionEvent2Timer (MotionEventTimers) { expire="1m,command=OFF" }
// Add the expire to the Siren
Switch Sonoff2 ... { mqtt=... , expire="4m,command=OFF" }
var lastRun = now
rule "SonoffMotion changed from OFF to ON 3 times in a minute"
when
Item Sonoff1 changed from OFF to ON or
Item Sonoff3 changed from OFF to ON
then
// ignore the event if we are playing the siren already or the Alarm is OFF or it has been less
// then five minutes since the last time the siren was played
if(shutoffTimer !== null || Alarm.state == OFF || lastRun.isAfter(now.minusMinutes(5))) return;
// if there are less than 2 ON timers, find the one that is OFF and set it to ON
if(MotionEventTimers.state < 2) {
MotionEventTimers.members.filter[ t | t.state == OFF ].head.sendCommand(ON)
}
// if timers.size == 2 then this event is the thrid ON event in a minute
if(MotionEventTimers.state == 2) {
sendMail("xxx@gmail.com", "Sonoff1", "Sonoff1 motion")
Sonoff2.sendCommand(ON)
lastRun = now
logInfo("RuleInfo", "Siren START")
}
// The Expire binding will automatically turn off the siren
end
One could try to use persitence for this but because both ON and OFF events gets stored in the database I don’t think it will work. Persistence also doesn’t record NULL so we can’t know if the event was changing from OFF to ON.