Passing Timers in Lambda Procedure/Function

Hi,

I’m working on OpeHab 2.3 and above. Recently I tried to solve this scenario, let’s describe it theoretically:

  1. There is a switch with multiple states, that communicates with OpenHab
  2. According to the state a light relay should be activated
  3. Each relay should switch itself off after a given delay time
  4. There are many of relays and switch states that are bundled

My current solution:

  1. I would like to solve this in an elegant and efficient way according that there is the same procedure for each switch state but different relay and delay time
  2. I’m using procedures for repeated action
  3. I’m declaring a timer for each relay

My current code look like this:

var Timer ExampleTimer_1 = null
var Timer ExampleTimer_2 = null
var Timer ExampleTimer_3 = null
var Timer ExampleTimer_4 = null


val Delayed_Switch = [ SwitchItem Relay, Timer Tim, Number DelayTime |

	if(Relay.state == ON)
	{
		sendCommand(Relay, OFF)
		if( Tim !== null )	
		{
			Tim.cancel
			Tim=null
		}
	}
	else
	{
		sendCommand(Relay, ON)
		Tim=createTimer(now.plusSeconds(DelayTime))[|
		sendCommand(Relay, OFF)
		Tim=null 
		]
	}
]



rule "Example rule"
	when 
		Item MultiStateSwitch received update
	then
		switch(MultiStateSwitch.state.toString)
		{
			case "State Tex 1":
				Delayed_Switch.apply(Light_1_Relay, ExampleTimer_1, 120)

			case "State Tex 2":
				Delayed_Switch.apply(Light_1_Relay, ExampleTimer_2, 20)

			case "State Tex 3":
				Delayed_Switch.apply(Light_1_Relay, ExampleTimer_3, 80)
		}
	end

When I save this rule I get the following error:

[INFO ] [del.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'Example.rules', using it anyway:
Assignment to final parameter
Assignment to final parameter
Assignment to final parameter
[INFO ] [del.core.internal.ModelRepositoryImpl] - Refreshing model 'Example.rules'

After a switch send a state change the following happens:

[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'Light_1_Relay' received command ON
[ERROR] [untime.internal.engine.RuleEngineImpl] - Rule 'Example rule': Couldn't invoke 'assignValueTo' for feature param Tim
[ERROR] [org.quartz.core.JobRunShell          ] - Job DEFAULT.2020-04-12T17:13:53.606+02:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  sendCommand(<XFeatureCallImplCustom>,<XFeatureCallImplCustom>)
  <null>.Tim = <XNullLiteralImplCustom>
} ] threw an unhandled Exception:
java.lang.IllegalArgumentException: Couldn't invoke 'assignValueTo' for feature param Tim
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1224) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1212) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:215) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]
        at com.sun.proxy.$Proxy143.apply(Unknown Source) [?:?]
        at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) [137:org.eclipse.smarthome.model.script:0.10.0.oh230]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
[INFO ] [smarthome.event.ItemCommandEvent     ] - Item 'Light_1_Relay' received command OFF
[ERROR] [org.quartz.core.ErrorLogger          ] - Job (DEFAULT.2020-04-12T17:13:53.606+02:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  sendCommand(<XFeatureCallImplCustom>,<XFeatureCallImplCustom>)
  <null>.Tim = <XNullLiteralImplCustom>
} ] threw an exception.
org.quartz.SchedulerException: Job threw an unhandled exception.
        at org.quartz.core.JobRunShell.run(JobRunShell.java:213) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
        at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh230]
Caused by: java.lang.IllegalArgumentException: Couldn't invoke 'assignValueTo' for feature param Tim
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.assignValueTo(XbaseInterpreter.java:1224) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:1212) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:215) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:446) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:227) ~[?:?]
        at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:219) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:203) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:189) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
        at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]
        at com.sun.proxy.$Proxy143.apply(Unknown Source) ~[?:?]
        at org.eclipse.smarthome.model.script.internal.actions.TimerExecutionJob.execute(TimerExecutionJob.java:49) ~[?:?]
        at org.quartz.core.JobRunShell.run(JobRunShell.java:202) ~[?:?]
        ... 1 more

As far as I understand the error after triggering the procedure could be related to timer being null declared.

So I have some questions, how this cam be solved:

  1. How can I initialise a timer so I can safely pass it to a procedure?
  2. Maybe a better way would be to declare an array of timers, but then I need to somehow initialise the timers in this array and call them by the array index, or?
  3. What is the errors after saving the rule file meaning?
  4. Maybe there is some more elegant way?

Thank you in advance for some tips and tricks!

You’ll be interested in the way a Map (array) of timers is used here

Thank you rossko57, this seems to solve my problem in an elegant way. Will test it today.