Help debugging a rule / cleaning up a rule

Hey guys,

Looking for help to troubleshoot an error in a rule I made that is causing an error regularly, and I think is causing openhab to become very resource heavy. The rule still works fine, but the error tells me I made a bodge of the code…

Could someone show mehod I can troubleshoot a rule based on the error in the openhab logs, and or show me how to correctly write the rule.

I am running openhab 2.4
A little background on the rule and its super practical function is here

Thanks,

Here is the rule:

var Timer timer = null // global variable usually


rule "timer"
when
    Item KitchenEchoNextTimer changed
then


timer = null

timer = createTimer(now, [ |

        
        // Timer Date and time 2019-10-20T21:58:35.870-0600
        val timerString = KitchenEchoNextTimer.state.format("%1$tY-%1$tm-%1$tdT%1$tH:%1$tM:%1$tS")

        // parse String to Date Time
        var DateTime timestring = parse(timerString)

        // find out time left in minutes
        val timeleft = (((timestring.millis + 60000) - now.millis) /60000)

       // null the timer if time left is 0
        if(timeleft <= 0) timer = null 
    
        else {
            // update the timer Item with time remaining 
            CurrentTimer.sendCommand(timeleft)
            // Switch the Dashboard to the Timer Dashboard Holding the Timer Template Widget
            Pin_String.postUpdate("Timer")
            // reschedule the timer for 1 minute
            timer.reschedule(now.plusMillis(60000))
            
        }
    ])


   
end

rule "Update Display Timer Iterm"
when
   Item CurrentTimer received command
then
    // Give the item a half second to register the change in time left
    Thread::sleep(500)
    //Log the time left
    logInfo("alexatimer", CurrentTimer.state.toString + " time left on the Echo Timer")

    // create a second timer when the first timer has reached 1 minute left to switch the dashboard when the timmer reaches null
    if( receivedCommand == "1" ) {
                createTimer(now.plusMinutes(1),  [ |
                            // Switch the Dashboard to the Home Dashboard
                            Pin_String.postUpdate("Home")
                            ])
        
        
    }
    
end

And here is the error:

2020-03-03 21:28:41.187 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2020-03-03T21:28:41.156-07:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {
  val timerString
  var timestring
  val timeleft
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@7d49a515
} ] 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.oh240]
	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.oh240]
Caused by: java.util.IllegalFormatConversionException: Y != java.lang.String
	at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4302) ~[?:?]
	at java.util.Formatter$FormatSpecifier.printDateTime(Formatter.java:2835) ~[?:?]
	at java.util.Formatter$FormatSpecifier.print(Formatter.java:2740) ~[?:?]
	at java.util.Formatter.format(Formatter.java:2526) ~[?:?]
	at java.util.Formatter.format(Formatter.java:2455) ~[?:?]
	at java.lang.String.format(String.java:2940) ~[?:?]
	at org.eclipse.smarthome.core.types.UnDefType.format(UnDefType.java:34) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1086) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:1061) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1047) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:992) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:151) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:772) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:220) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:827) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:264) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:447) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:228) ~[?:?]
	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.doEvaluate(ScriptInterpreter.java:226) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:204) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:190) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.ClosureInvocationHandler.doInvoke(ClosureInvocationHandler.java:46) ~[?:?]
	at org.eclipse.xtext.xbase.interpreter.impl.AbstractClosureInvocationHandler.invoke(AbstractClosureInvocationHandler.java:29) ~[?:?]

The usual first step in debugging is to add logInfo() messages or similar, for two purposes.
You can see what the values are of variables, states, etc.
You can see what step in a rule you are getting to.

Not going to help in this case though.

From experience, this one -

The problem is something to do with a timer. It’s not going to tell us much more about which timer here.

But it does give a hint - that’s a kind of skeleton representation of the timer code block.
You’d look through your rules for any that generate a timer with lines of code something lke that.
The line that blew up in the timer was an if() statement.

In this case, yep the skeleton fits the rule you’ve shown us.
So let’s look at that.

Yes. All rules in this file can access and share a variable named timer. It starts out with null value.

Just as an aside - it’s a horrible name, because it’s commonplace. What happens when you want a second rule with a timer for a different purpose? I’d call it echoTimer or something, but that’s for later when it’s working.

Really? Whatever that Item is, whenever it changes you destroy the content of global variable named timer.

Then you make a Timer that runs immediately (now) and store a handle or pointer to it in the variable timer.

Looking in more depth, if your Timer is already running when this rule triggers, you will blindly set that handle to null. That doesn’t stop or cancel the real Timer at all, you just destroy your only means to access it.
That old Timer runs on until its due time, and then does its thing.

Meantime your rule makes another Timer and stores a handle.
You’ve got multiple Timers operating on the same things, it is going to end in trouble.

You need a big restructure here.
The usual set up in just about every timer tutorial here is to test that handle variable to see if it is null before creating new Timers.
If it is not null, then your Timer is already running, and you might reschedule or whatever.

What are you trying to do here, create a minute countdown timer?

Thanks for the tips, and explaining a bit more in detail about what nulling the timer does. The purpose is to create a countdown when I set a timer on my Amazon echo that shows in habpanel.

When I set a timer on the echo it updates that item with a time and date, so I need to create the countdown from a single date and time reference.

I am kinda a copy paste, edit a bit when it comes to coding so it took a lot for me to get this to work as I can’t really write complex rules from scratch…

Thanks again.