Looping lambda timers

Hi all,
i try to create a looping timer within a lambda. I already implemented a looping timer following the looping timer design pattern. But i have to created the timer in multiple rules, so i want to extract the logic into a global lambda which will run in every rule separately. Unfortunately i get a nullpointer while running the lambda.

This is my code:

var Timer nightFadingTimer = null

val createNightFadingTimer = [|
	nightFadingTimer = createTimer(now, [|
		//do stuff
		//loop timer
		nightFadingTimer.reschedule(now.plusSeconds(2))
	])
]

rule "Presence"
when
Item Motion_Any changed to ON
then
if(nightFadingTimer === null || nightFadingTimer.hasTerminated) {
	createNightFadingTimer.apply()
}
end

This is the exception:

2021-01-06 19:50:37.618 [WARN ] [ore.internal.scheduler.SchedulerImpl] - Scheduled job failed and stopped
java.lang.NullPointerException: cannot invoke method public abstract boolean org.openhab.core.model.script.actions.Timer.reschedule(java.time.ZonedDateTime) on null
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1161) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1151) ~[?:?]
	[...]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:47) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:30) ~[?:?]
	at com.sun.proxy.$Proxy640.apply(Unknown Source) ~[?:?]
	at org.openhab.core.model.script.actions.ScriptExecution.lambda$0(ScriptExecution.java:82) ~[?:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$12(SchedulerImpl.java:166) ~[bundleFile:?]
	at org.openhab.core.internal.scheduler.SchedulerImpl.lambda$1(SchedulerImpl.java:76) [bundleFile:?]
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515) [?:?]
	at java.util.concurrent.FutureTask.run(FutureTask.java:264) [?:?]
	at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) [?:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
	at java.lang.Thread.run(Thread.java:834) [?:?]

Its strange that it works if i replace the createNightFadingTimer.apply() call inside the rule by the code of the lambda itself.
Does anybody have an idea how to make it work?

Why is the createTimer in a lambda? There is no reason to do it this way and it’s the source of your problems. Global lambdas have no context. It has no idea that nightFadingTimer exists. You have to pass it as an argument to use it inside the global lambda.

Global lambdas like this have further problems that make me strongly recommend against their use, for example they are not thread safe.

In this case, if there is only the one rule here that is calling the lambda, why go through the extra effort to put it into a lambda? It doesn’t buy you anything and in fact makes things more complicated. It makes your code even longer and harder to understand.

And if you did have more than one rule calling it, you run the risk of the lambda being called from more than one rule at a time in which case the results will be unpredictable but will likely result in orphaned timers.

If you want to use functions, move to a supported language that has functions. In Rules DSL, only in very rare specific cases should you be using a global lambda.

i hoped there is a more simple solution than converting my rules into another language :frowning:
however i already wanted to have a closer look on python some time ago. so it seems this will be my next topic to check out :slight_smile: