Simplifying Rule

I’ll recommend using Design Pattern: Expire Binding Based Timers and apply the Associated Items DP to access the Timers. It is amazing how much they simplify the code.

Note, I’m assuming you have a fixed timeOut of 30 seconds and that you do not want to dynamically calculate the timeOut at runtime.

Create a Group to hold the Motion Timer Switches and for each Motion Item create a Timer Switch.

Group:Switch MotionTimers
Switch Motion_02_Timer (MotionTimers) { expire="30s,command=OFF" }
...

In the Rule we will use Associated Items to get a reference to the Timer Item. We can cancel the Timer by postUpdate(OFF) on the Timer. We can start the Timer by sendCommand(ON). The “body” of the Timers will be put in a new Rule and we will use the same approach for the new Rule as we use for “Camera motion sensor trip.”

rule "Camera motion sensor trip"
when
    Item Motion_02 changed from CLOSED to OPEN or
    Item Motion_03 changed from CLOSED to OPEN or
    Item Motion_25 changed from CLOSED to OPEN or
    Item Motion_26 changed from CLOSED to OPEN or
    Item Motion_27 changed from CLOSED to OPEN
then
    val cameraNum = triggeringItem.name.split("_").get(1)
    logInfo("Motion", "Motion detected in zone " + cameraNum)

    val timer = MotionTimers.members.findFirst[t | t.name = "Motion_"+cameraNum+"_Timer"] as SwitchItem
    if(timer.state == OFF){
        logInfo("Motion", "Starting " + timer.name)
        timer.sendCommand(ON) // always use the sendCommand method ...
        sendCommand("Camera_"+cameraNum, "ON") // unless all you have is the name of the Item
    }
    else {
        logInfo("Motion", "Rescheduling " + timer.name)
        timer.sendCommand(ON)
    }
end

rule "Camera motion timer expired"
when
    Item Motion_02_Timer received command OFF or
    Item Motion_03_Timer received command OFF or
    Item Motion_25_Timer received command OFF or
    Item Motion_26_Timer received command OFF or
    Item Motion_27_Timer received command OFF
then
    val timerNum = triggeringItem.name.split("_").get(1)
    logInfo("Motion", "Motion timer " + timerNum + " expired.")
    seneCommand("Camera_"+timerNum, "OFF")
end

Now there are ways to make it work with Timers like the path you initially went down, but I think the above is far less complex. And again, once the GroupTrigger stuff gets merged in you can reduce all those triggers on the expired timer down to one.

If you have reasons to want to use Timers, I can present a solution that uses them instead.

One additional rule that you might want to add is a System started cleanup rule to either restart any running Timers or just turn off cameras that were running.

Assuming you have restoreOnStartup on your Timers you can reschedule the timers that were ON when OH went down:

rule "Reset camera timers"
when
    System started
then
    // schedule this to run a bit after system started so restoreOnStartup is done and the rest of the system has 
    // settled
    createTimer(now.plusSeconds(5), [ |
        MotionTimers.members.filter[timer | timer.state == ON].forEach[timer | timer.sendCommand(timer.state) ]
    ]
end

Or you can turn off the cameras that were ON when OH went down (add the Camera switches to a Group) and use restoreOnStartup on the Camera Items:

rule "Stop running cameras"
when
    System started
then
    createTimer(now.plusSeconds(5), [ |
        Cameras.members.filter[camera | camera.state == ON].forEach[camera | camera.sendCommand(OFF)]
        MotionTimers.members.filter[timer | timer.state == ON].forEach[timer | timer.postUpdate(OFF)]
    ]
end

Or you can just do a blanket reset of Cameras and Timers regardless of what state they were in before:

rule "Stop running cameras"
when
    System started
then
    createTimer(now.plusSeconds(5), [ |
        Cameras.members.forEach[camera | camera.sendCommand(OFF)]
        MotionTimers.members.forEach[timer | timer.postUpdate(OFF)]
    ]
end

All three approaches have potentials for side effects I can’t predict because I don’t know the specifics of your cameras. For example, what will a camera do if it receives multiple OFF commands? The five second delay may be too much. There is a potential for sensors to trigger in that five seconds and the cleanup rule will mess that up. You will need to experiment with the amount of time to be as small as possible but long enough that your rule doesn’t throw errors complaining about Items not existing or running before restoreOnStartup has finished.

And you may decide it isn’t worth the effort to clean up the cameras at all. In the worst case scenario you end up with cameras that remains ON after an OH reboot.

1 Like