[Solved] Timer Cancel Not Working Right

My rule using the timer below was working a week or so back, but after upgrading to the last few snapshots it seems to have quit working (or I made a change I’m not recognizing that broke it). I’m running yesterday’s Unstable Snapshot.

Here is my rule:

// Global Variables
var Timer tNest = null
val int nestTimeout = 30

rule "Nest Occupancy Switch"
when 
	Item Sensor_Nest_Mode changed
then
	logInfo("Nest Occupancy","Nest Status: " + Sensor_Nest_Mode.state)
	if(Sensor_Nest_Mode.state == "auto-away") {
		logInfo("Nest Occupancy","Start to set Away Mode.  Timer: " + tNest)
		if(tNest==null) {
			logInfo("Nest Occupancy","Status Timer Set")
			tNest = createTimer(now.plusMinutes(nestTimeout)) [|
				logInfo("Nest Occupancy","Nest occupancy set to Off")	
				Sensor_Nest_Motion.sendCommand(OFF)
				tNest = null
			]
		} 
		else {
			logInfo("Nest Occupancy","Timer rescheduled")
			tNest.reschedule(now.plusMinutes(nestTimeout))
		}
	} 
	else if(Sensor_Nest_Mode.state == "home") {
		if(Sensor_Nest_Motion.state!= ON) {
			Sensor_Nest_Motion.sendCommand(ON)
			logInfo("Nest Occupancy","Nest occupancy set to On")
		}
		if(tNest!=null) {
			logInfo("Nest Occupancy","Timer cancelled")
			tNest.cancel()
		}
	}
end

So what I’m seeing in my log files is after the timer records as cancelled, it’s still showing in my logInfo statement and going into the reschedule mode. Once rescheduled it never executes. Here are the relevant log entries:

2017-02-07 07:06:45.986 [INFO ] [marthome.model.script.Nest Occupancy] - Nest Status: home
2017-02-07 07:06:46.010 [INFO ] [marthome.model.script.Nest Occupancy] - Timer cancelled
2017-02-07 07:34:08.719 [INFO ] [marthome.model.script.Nest Occupancy] - Nest Status: auto-away
2017-02-07 07:34:08.734 [INFO ] [marthome.model.script.Nest Occupancy] - Start to set Away Mode.  Timer: org.eclipse.smarthome.model.script.internal.actions.TimerImpl@19002cd
2017-02-07 07:34:08.743 [INFO ] [marthome.model.script.Nest Occupancy] - Timer rescheduled
2017-02-07 07:37:11.597 [INFO ] [marthome.model.script.Nest Occupancy] - Nest Status: home
2017-02-07 07:37:11.615 [INFO ] [marthome.model.script.Nest Occupancy] - Timer cancelled
2017-02-07 08:01:31.696 [INFO ] [marthome.model.script.Nest Occupancy] - Nest Status: auto-away
2017-02-07 08:01:31.708 [INFO ] [marthome.model.script.Nest Occupancy] - Start to set Away Mode.  Timer: org.eclipse.smarthome.model.script.internal.actions.TimerImpl@19002cd
2017-02-07 08:01:31.715 [INFO ] [marthome.model.script.Nest Occupancy] - Timer rescheduled
2017-02-07 10:45:53.939 [INFO ] [marthome.model.script.Nest Occupancy] - Nest Status: home
2017-02-07 10:45:53.962 [INFO ] [marthome.model.script.Nest Occupancy] - Timer cancelled

Any thoughts of what I might be doing wrong would be appreciated or if it can be confirmed as a bug I’ll enter in the issues system.

Shouldn’t you null your timer global after cancelling? (as you test for null when considering reschedule)

That’s what I was wondering. I had it in but had commented it out because it threw errors last week. Shouldn’t cancelling the timer also null it out?

I don’t think so; the timer that is cancelled does not I think ‘know’ about any variable you are using to reference it?

Re “errors last week”. Don’t be surprised if you get timer related errors when reloading a rules file - if the timer was running at reload it runs on but gets ‘orphaned’ out of context.

(edit - corrected link)

@rossko57 Thanks, that was the problem. Once I uncommented the set to null after cancel it started working right again. Do you know of any other logic checks other then != null for a timer to verify if it’s active or not?

Thanks for the link to the other thread as well.

Nope. null testing (and null setting in timer expiry) works for me.

Thanks

@rossko57 Now that I added back in the tMotion = null line:

  var Timer tMotion = null
  val int motionTimeout = 30
   //Security Motion Occupancy
    rule "Security Motion Occupancy"
    when
    	Item Sensor_Security_Motion_Tripped changed to OPEN
    then
    	if(tMotion == null) {
    		Sensor_Security_Motion.sendCommand(ON)
    		tMotion = createTimer(now.plusMinutes(motionTimeout)) [|
    			Sensor_Security_Motion.sendCommand(OFF)
    			tMotion = null
    		]
    	} else {
    		tMotion.reschedule(now.plusMinutes(motionTimeout))
    	}
    end

I’m seeing the following errors pop up. These are the same ones that led me to comment it out last week.

2017-02-08 20:58:54.151 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.2017-02-08T20:58:24.987-07:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  <XFeatureCallImplCustom>.sendCommand(<XFeatureCallImplCustom>)
  <null>.tMotion = <XNullLiteralImplCustom>
} ] threw an unhandled Exception: 
java.lang.RuntimeException: The name '<unkown>' cannot be resolved to an item or type.
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:112)[129:org.eclipse.smarthome.model.script:0.9.0.201702010824]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluateArgumentExpressions(XbaseInterpreter.java:1115)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:114)[129:org.eclipse.smarthome.model.script:0.9.0.201702010824]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:763)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:219)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29)[145:org.eclipse.xtext.xbase:2.9.2.v20160428-1452]
	at com.sun.proxy.$Proxy100.apply(Unknown Source)[:]
	at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:38)[129:org.eclipse.smarthome.model.script:0.9.0.201702010824]
	at org.quartz.core.JobRunShell.run(JobRunShell.java:202)[104:org.eclipse.smarthome.core.scheduler:0.9.0.201702010824]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)[104:org.eclipse.smarthome.core.scheduler:0.9.0.201702010824]

When I commented out the tMotion = null line before the error messages went away. Any thoughts or suggestions? Thanks.

Edit: Here are the two related item definitions:

Contact  Sensor_Security_Motion_Tripped "Security Motion Sensor [MAP(en.map):%s]" <contact> (Mios) {mios="unit:home,device:320/service/SecuritySensor1/Tripped"}
Switch   Sensor_Security_Motion "Security Motion Sensor"	<motion>	(Motion)

Actually on further testing, I think it’s the same issue you flagged before of an orphan timer running when a rule is reloaded. Going to let them set for a day w/o reloading the rules file and see if they quit happening.

I should perhaps have been more clear, in that you can get an error (mine look just like yours) some time after a rules reload, when the orphan timer expires. In your case, up to half an hour later.

I don’t think there is any fix, not sure there can be. I suspect the root cause is using a global variable which (quite rightly) gets destroyed/rebuilt upon rules reload. A fix would be to reference the Timer via a super-global that survives rules reload - say, like an Openhab Item. Don’t know any way to do that.

In the end it is not a problem in a live system, only for tinkering.

Edit - have you looked into Expire binding? It is not a replacement for every Timer need by a long shot, but can often be used for simple “is it alive?” checks

Thanks. I haven’t looked into the Expire binding but may if I end up with more issues. I let it settle out over night and it seemed to run w/o errors so I do think it was the orphaning of the timer between rule changes. Surprising that setting the timer to null when the rule reloads didn’t clear it.

When you went through this last time, did anyone ever check with @Kai i to see if this is an issue they would want submitted to dig into?