Cancelled timer gets still executed

  • Platform information:
    • Hardware: PI4
    • OS: Docker-Container on Raspbian
    • openHAB version: 2.5.12

I have a global timer in a rules file, which should run after 2 hours, but could also get cancelled before that time. The idea is basically to see if there was any movement in the house, by triggering this rule on any door-state or motion-state change. But still even if there is a movement in the 2h frame and the timer cancel code gets executed, the scheduled timer is still executed after the 2 hours. I I call cancel on the timer shouldnโ€™t this avoid the execution of the timer code?

var Timer noMovementTimer = null

rule "notice someone is at home"
       Item AllMotionStateItems changed or
       Item AllDoorStateItems changed or
       Time cron "0 0 8 1/1 * ? *"
       logInfo("GPS.rules", "movement detected")
       if (noMovementTimer !== null) {
               logInfo("GPS.rules", "cancel noMovementTimer")
               noMovementTimer = null
       if (now.getHourOfDay >= 8) {
               noMovementTimer = createTimer(now.plusMinutes(120), [ |
                       logInfo("GPS.rules", "Execute noMovementTimer")
  2021-07-07 15:42:27.488 [INFO ] [pse.smarthome.model.script.GPS.rules] - movement detected
  2021-07-07 15:42:27.507 [INFO ] [pse.smarthome.model.script.GPS.rules] - cancel noMovementTimer
  2021-07-07 15:56:38.507 [INFO ] [pse.smarthome.model.script.GPS.rules] - Execute noMovementTimer

With long running Timers like this, there is a special extra trap during rules development.

When you edit and re-save your rules file, it resets the global variable you are using for the handle. But any Timer already spawned is fully independent and continues to run, usually throwing an error at execution time if the code is non-trivial, but way work if simple.
Thereโ€™s not much you can do about Timers orphaned in this way, other than being aware of the circumstances when rule editing.

If youโ€™re relying on handle=null to manage your Timer, donโ€™t forget to have the Timer code set its own handle back to null if/when it executes.

Thanks, thatโ€™s indeed a trap to be aware of. Unfortunately, the log was created when I didnโ€™t edit the file for more than a day and this is the only rule editing the global variable. So it doesnโ€™t explain the issue I have.

Guessing your triggering Items might be Groups, beware multiple triggers in quick succession.

OH2 will happily run multiple starts of a rule in parallel. While one is cancelling Timer X before creating a new one, another can be creating Timer Y because none exists yet.
You might spot this by logging at Timer creation time and seeing if you have more creates than cancels.

Creating a new independent Timer using the same handle variable does not cancel or destroy the existing independent Timer, you just cannot talk to it anymore.

How are your Groups structured? You might reduce triggering frequency by using Member of style triggers.

It might help to restructure the rule to reschedule when required, less overhead than destroy/create every time.

Thanks this could really be the issue. Actually itโ€™s not groups but my posted code included only 2 example items, while in real I have around 30 different events. Will try to restructure it.

Maybe you should consider Groups and Member of triggering to make it easier to manage, but thatโ€™s an aside.

Considering throwing a door open will fire any PIR looking that way, it seems likely you can get repeat triggers within milliseconds. Streamlining the rule will help to minimize the time window for getting into trouble, but ultimately you may need to use a reentrant lock to queue the work.

There is an alternative approach that letโ€™s openHAB manage reentrancy, providing you only ever want a fixed time.
Set up a virtual Item switch โ€œOccupiedโ€, with expire binding set for x minutes to command OFF.
Make a rule that for every start/rescheduling event (with conditions if you wish) updates the virtual switch to ON.
A rule triggered from command OFF does whatever it is you want when time expires.
To cancel timing without action at 0800 or such, another rule to update (not command) virtual switch to OFF.