[SOLVED] Help me condense this rule

I have an irrigation setup with 4 zones
Each zone is a switch item called SprinklerZone1 to SprinklerZone4

My rules file is long, as i have each seperate rule, written seperately for each zone.
For instance, i have a rule that runs when the zone is turned on, One that runs when its turned off, and one that runs when the zone starts and shows a countdown timer.

But I then have each of those 4 times, for each zone.

Is there a way to get the number from the switch or number item, and use that in a variable. So i can condense my rules file smaller.

For instance
when SprinklerSystem1 changed to ON
then
var Number Zone = item.Switch.numbercode (i know this isn’t correct, but it shows what im trying to do.

Here is my rules and items file for reference.

String Irrigation_Zone1 "Zone 1 is [%s]"
String Irrigation_Zone2 "Zone 2 is [%s]"
String Irrigation_Zone3 "Zone 3 is [%s]"
String Irrigation_Zone4 "Zone 4 is [%s]"
Switch FrontDownlights "Front Downlights" [ "Switchable" ]{mqtt=">[broker:SmartHouse/Utilities/SprinklerSystem:command:ON:TESTON],>[broker:SmartHouse/Utilities/SprinklerSystem:command:OFF:TESTOFF]"}
Number Countdown_Zone1 "Minutes Remaining [%d]"
Number Countdown_Zone2 "Minutes Remaining [%d]"
Number Countdown_Zone3 "Minutes Remaining [%d]"
Number Countdown_Zone4 "Minutes Remaining [%d]"                               
Switch SprinklerCycle "30 Minute Cycle" 
Switch SprinklerSystemSchedule1 "Front Grass Schedule"  
Switch SprinklerSystemSchedule2 "Front Garden Schedule" 			
Switch SprinklerSystemSchedule3 "Back Grass Schedule" 			
Switch SprinklerSystemSchedule4 "Back Garden Schedule"

Switch SprinklerSystemZone1 "Front Grass Sprinklers" [ "Switchable" ] {mqtt=">[broker:SmartHouse/Utilities/SprinklerSystem:command:ON:Z1ON],>[broker:SmartHouse/Utilities/SprinklerSystem:command:OFF:Z1OFF]"}
Switch SprinklerSystemZone2 "Front Garden Sprinklers"[ "Switchable" ] {mqtt=">[broker:SmartHouse/Utilities/SprinklerSystem:command:ON:Z2ON],>[broker:SmartHouse/Utilities/SprinklerSystem:command:OFF:Z2OFF]"}
Switch SprinklerSystemZone3 "Back Grass Sprinklers" [ "Switchable" ] {mqtt=">[broker:SmartHouse/Utilities/SprinklerSystem:command:ON:Z3ON],>[broker:SmartHouse/Utilities/SprinklerSystem:command:OFF:Z3OFF]"}
Switch SprinklerSystemZone4 "Back Garden Sprinklers" [ "Switchable" ] {mqtt=">[broker:SmartHouse/Utilities/SprinklerSystem:command:ON:Z4ON],>[broker:SmartHouse/Utilities/SprinklerSystem:command:OFF:Z4OFF]"} 


DateTime Irrigation_StartTime1     "Zone 1 Last Watered [%1$ta %1$tR]" 
DateTime Irrigation_StartTime2     "Zone 2 Last Watered [%1$ta %1$tR]" 
DateTime Irrigation_StartTime3     "Zone 3 Last Watered [%1$ta %1$tR]" 
DateTime Irrigation_StartTime4     "Zone 4 Last Watered [%1$ta %1$tR]" 

and my Rules File

//This is the rules page



    import org.openhab.core.library.types.*
    import org.openhab.core.persistence.*
    import org.openhab.model.script.actions.*
    import org.joda.time.*

rule "Reset Irrigation at OH Start"
when
    System started
then

	SprinklerySystemZone1.sendCommand(OFF)
	SprinklerySystemZone2.sendCommand(OFF)
	SprinklerySystemZone3.sendCommand(OFF)	
	SprinklerySystemZone4.sendCommand(OFF)
	Irrigation_Zone1.postUpdate("Inactive")
	Irrigation_Zone2.postUpdate("Inactive")
	Irrigation_Zone3.postUpdate("Inactive")
	Irrigation_Zone4.postUpdate("Inactive")
	Countdown_Zone1.postUpdate(0)
	Countdown_Zone2.postUpdate(0)
	Countdown_Zone3.postUpdate(0)
	Countdown_Zone4.postUpdate(0)

end

//**************************************************************************************
rule "Zone 1 Inactive"
when
    Item SprinklerSystemZone1 received command OFF

then
Countdown_Zone1.postUpdate(0)
Irrigation_Zone1.postUpdate("Inactive")

end
//**************************************************************************************
rule "Zone 2 Inactive"
when
    Item SprinklerSystemZone2 received command OFF

then
Countdown_Zone2.postUpdate(0)
Irrigation_Zone2.postUpdate("Inactive")
end
//**************************************************************************************
rule "Zone 3 Inactive"
when
    Item SprinklerSystemZone3 received command OFF

then
Countdown_Zone3.postUpdate(0)
Irrigation_Zone3.postUpdate("Inactive")

end
//**************************************************************************************
rule "Zone 4 Inactive"
when
    Item SprinklerSystemZone4 received command OFF

then
Countdown_Zone4.postUpdate(0)
Irrigation_Zone4.postUpdate("Inactive")
end

//**************************************************************************************
//Zone 1 Watering	


rule "Zone 1 Watering"
when
    Item SprinklerSystemZone1 changed to ON
then

var Number timeout = onHours1.state as DecimalType
var int lawnTime = (timeout).intValue
postUpdate(Irrigation_StartTime1, new DateTimeType())
Irrigation_Zone1.postUpdate("Active")

if (lawnTime > 0)
{
sendBroadcastNotification("Watering Zone 1 for " +  timeout.toString + " minutes")
Irrigation_Zone1.postUpdate("Active " + timeout.toString + " minutes remaining")
Countdown_Zone1.postUpdate(lawnTime)

	while (SprinklerSystemZone1.state == ON)
        {
                if (now.minusMinutes(lawnTime).isAfter((Irrigation_StartTime1.state as DateTimeType).calendar.timeInMillis))
                {
                sendCommand(SprinklerSystemZone1, OFF)
		sendBroadcastNotification("Zone 1 has finished watering")
                lawnTime = 0
                }
                if ((onHours1.state as DecimalType).intValue != lawnTime)
                {
                lawnTime = (onHours1.state as DecimalType).intValue
                }

        }

}
else {
	sendBroadcastNotification("Watering Zone 1")
}
end

//**************************************************************************************
//Zone 2 Watering


rule "Zone 2 Watering"
when
    Item SprinklerSystemZone2 changed to ON
then

var Number timeout = onHours2.state as DecimalType



var int lawnTime = (timeout).intValue
        postUpdate(Irrigation_StartTime2, new DateTimeType())
Irrigation_Zone2.postUpdate("Active")
if (lawnTime > 0)
{
sendBroadcastNotification("Watering Zone 2 for " +  timeout.toString + " minutes")
Irrigation_Zone2.postUpdate("Active " + timeout.toString + " minutes remaining")
Countdown_Zone2.postUpdate(lawnTime)

        while (SprinklerSystemZone2.state == ON)
        {
                if (now.minusMinutes(lawnTime).isAfter((Irrigation_StartTime2.state as DateTimeType).calendar.timeInMillis))
                {
                sendCommand(SprinklerSystemZone2, OFF)
		sendBroadcastNotification("Zone 2 has finished watering")
                lawnTime = 0
                }
                if ((onHours2.state as DecimalType).intValue != lawnTime)
                {
                lawnTime = (onHours2.state as DecimalType).intValue
                }

        }


}
else {
	sendBroadcastNotification("Watering Zone 2")
}
end

//**************************************************************************************
//Zone 3 Watering


rule "Zone 3 Watering"
when
    Item SprinklerSystemZone3 changed to ON
then

var Number timeout = onHours3.state as DecimalType
var int lawnTime = (timeout).intValue
        postUpdate(Irrigation_StartTime3, new DateTimeType())
Irrigation_Zone3.postUpdate("Active")
if (lawnTime > 0)
{
sendBroadcastNotification("Watering Zone 3 for " +  timeout.toString + " minutes")
Irrigation_Zone3.postUpdate("Active " + timeout.toString + " minutes remaining")
Countdown_Zone3.postUpdate(lawnTime)

        while (SprinklerSystemZone3.state == ON)
        {
                if (now.minusMinutes(lawnTime).isAfter((Irrigation_StartTime3.state as DateTimeType).calendar.timeInMillis))
                {
                sendCommand(SprinklerSystemZone3, OFF)
		sendBroadcastNotification("Zone 3 has finished watering")
                lawnTime = 0
                }
                if ((onHours3.state as DecimalType).intValue != lawnTime)
                {
                lawnTime = (onHours3.state as DecimalType).intValue
                }

        }


}
else {
	sendBroadcastNotification("Watering Zone 3")
}
end

//**************************************************************************************
//Zone 4 Watering


rule "Zone 4 Watering"
when
    Item SprinklerSystemZone4 changed to ON
then

var Number timeout = onHours4.state as DecimalType
var int lawnTime = (timeout).intValue
        postUpdate(Irrigation_StartTime4, new DateTimeType())
Irrigation_Zone4.postUpdate("Active")
if (lawnTime > 0)
{
sendBroadcastNotification("Watering Zone 4 for " +  timeout.toString + " minutes")
Irrigation_Zone4.postUpdate("Active " + timeout.toString + " minutes remaining")
Countdown_Zone4.postUpdate(lawnTime)

        while (SprinklerSystemZone4.state == ON)
        {
                if (now.minusMinutes(lawnTime).isAfter((Irrigation_StartTime4.state as DateTimeType).calendar.timeInMillis))
                {
                sendCommand(SprinklerSystemZone4, OFF)
		sendBroadcastNotification("Zone 4 has finished watering")
                lawnTime = 0
                }
                if ((onHours4.state as DecimalType).intValue != lawnTime)
                {
                lawnTime = (onHours4.state as DecimalType).intValue
                }

        }


}
else {
	sendBroadcastNotification("Watering Zone 4")
}
end
//***************************************************************************
rule "Countdown Zone 1 Timer"
when
	Item Countdown_Zone1 changed
then
	
        createTimer(now.plusMinutes(1)) [|
	if (Countdown_Zone1.state as DecimalType != 0)
	{
	Irrigation_Zone1.postUpdate("Active " + (Countdown_Zone1.state as DecimalType - 1) + " minutes remaining")
	Countdown_Zone1.postUpdate(Countdown_Zone1.state as DecimalType - 1)
	}
	
]
end
//***************************************************************************
rule "Countdown Zone 2 Timer"
when
        Item Countdown_Zone2 changed
then
        
	createTimer(now.plusMinutes(1)) [|
        if (Countdown_Zone2.state as DecimalType != 0)
        {
	Irrigation_Zone2.postUpdate("Active " + (Countdown_Zone2.state as DecimalType - 1) + " minutes remaining")
        Countdown_Zone2.postUpdate(Countdown_Zone2.state as DecimalType - 1)
        }

]
end
//***************************************************************************
rule "Countdown Zone 3 Timer"
when
        Item Countdown_Zone3 changed
then	
	 
   	 createTimer(now.plusMinutes(1)) [|
        if (Countdown_Zone3.state as DecimalType != 0)
        {
	Irrigation_Zone3.postUpdate("Active " + (Countdown_Zone3.state as DecimalType - 1) + " minutes remaining")
  	Countdown_Zone3.postUpdate(Countdown_Zone3.state as DecimalType - 1)
        }

]
end
//***************************************************************************
rule "Countdown Zone 4 Timer"
when
        Item Countdown_Zone4 changed
then
	createTimer(now.plusMinutes(1)) [|
        if (Countdown_Zone4.state as DecimalType != 0)
        {
	Irrigation_Zone4.postUpdate("Active " + (Countdown_Zone4.state as DecimalType - 1) + " minutes remaining")
        Countdown_Zone4.postUpdate(Countdown_Zone4.state as DecimalType - 1)
        }

]
end

I’ll give you an example for the OFF rules

rule "Zone X Inactive" 
when Item SprinklerSystemZone1 received command OFF  or 
Item SprinklerSystemZone2 received command OFF  or
Item SprinklerSystemZone3 received command OFF or
Item SprinklerSystemZone4 received command OFF  
then 
var String ZoneNumber=triggeringItem.name.substring(18,19) // should give the trailing number character
postUpdate("Countdown_Zone" + ZoneNumber,0)
postUpdate("Irrigation_Zone" + ZoneNumber,"Inactive")
end

Legend. triggeringitem.name.substring(x,x) helps me with all of them.

can i change that to
triggeringItem.name.substring(len(triggeringItem.name.substring)-1,len(triggeringItem.name.substring))

basically taking the second last and last substrings? or is there a better way.
That way it just looks for the very last number as a string?

Can’t say/test at the Moment.
If you use LogInfo Statements, you will see the result in the logs!
Like.
logInfo(“MyRuleName”, ZoneNumber)

BTW: I had a typo in the posted code “ZoneNumer”

Looking into your ON rule, I don’t think the “While” part would be working. Using such in a rule would Keep that rule running as Long the Irrigation is on! I would use a timer or even easier the expire binding (using the expire, you have to “hardcode” the lawnTime) .

Perhaps it would be an idea to use lambda functions to control the zones? You should read the design patterns from @rlkoshak.

I suggest this design pattern to start with:

yes, the while part runs while irrigation is turned on, it continuously looks for if the current time is after start time plus lawn time. then turns off the zone.

It works perfect, and i wanted this rather than a timer as with this, i can update the lawn time by changing the onHours while irrigation is running and it updates. Which means if i change the onhours to be less than the current time run, it will stop.
I didn’t want to hardcode everything, as i have setpoint numbers which allow me to select the run time.

I read that yesterday, but i have no idea what he is talking about. I can only really work with examples.

The Point that the rule is running as Long as you are irrigating is a Major disadvantage. If I’m not mistaken the number of threads for rules is very limited, having a single rule running for an Extended time will result hamper other rules to be run!
Keep in mind that you are using a second rule that runs a times already to update the time! The code in the “While” could be performed in the timer as well!

@opus is correct.
Although the rule works, use a while loop is a bad idea. It locks the thread for that rule while it is running. You have 4 zone, so if 4 of them are running then you have 4 thread occupied and Openhab only have 5! That leaves you with only 1 thread to run all the rest of your system. Not good.

Try changing it to a timer:

var Timer Zone1Timer = null //At the top of the rules file

rule "Zone 1 Watering"
when
    Item SprinklerSystemZone1 changed to ON
then
    var Number timeout = onHours1.state as DecimalType
    var int lawnTime = (timeout).intValue
    postUpdate(Irrigation_StartTime1, new DateTimeType())
    Irrigation_Zone1.postUpdate("Active")

    if (lawnTime > 0) {
        sendBroadcastNotification("Watering Zone 1 for " +  timeout.toString + " minutes")
        Irrigation_Zone1.postUpdate("Active " + timeout.toString + " minutes remaining")
        Countdown_Zone1.postUpdate(lawnTime)

        if (timer === null) {
            Zone1Timer = createTimer(now.plusMinutes(lawnTime), [ |
                sendCommand(SprinklerSystemZone1, OFF)
		sendBroadcastNotification("Zone 1 has finished watering")
                lawnTime = 0
                Zone1Time = null
            ] )
        }
....        
  • Design Pattern: Associated Items : Use a naming scheme that is easier to parse and put your Items into Groups to more easily access them

  • If you are coding OH 2.x rules you do not need and should not have any of those imports.

  • Design Pattern: Cascading Timers Here is a concrete example for you

  • As Jürgen and Vincent points out, using a while loop in this way is a really bad idea. You are consuming 4 of your 5 total available threads for running Rules. Furthermore, there is no sleep in your while loops so you are probably maxing out your CPU sitting there running through this loop doing nothing which can possibly cause heating and other performance issues. I want to emphasize, this is a really bad implementation approach. You need to generate an event when it is time to start the irrigation system. Not sit spinning in an infinite loop forever consuming tons of CPU doing nothing. You need to use the CalDav binding, Astro Binding, one of the Alarm Clock examples, or something external to trigger the irrigation to start. Otherwise your OH install will only be good for just running these four zones of irrigation and you will probably burn up your machine with overheating. If you must poll like this, use a Time cron triggered rule that checks once a minute or so to see if it is time to turn on or off the zone at a minimum.

  • There is no need to use a primitive int for lawnTime. Unlike with many other languages, the Rules DSL does better when you avoid the use of primitives unless necessary.

1 Like

Thanks for the Tips.

My question is this, If i use a timer
lets say

 if (timer === null) {
            Zone1Timer = createTimer(now.plusMinutes(lawnTime), [ |
                sendCommand(SprinklerSystemZone1, OFF)
		sendBroadcastNotification("Zone 1 has finished watering")
                lawnTime = 0
                Zone1Time = null
            ] )

How do i change lawnTime while that is running. I currently use

if ((onHours1.state as DecimalType).intValue != lawnTime)
                {
                lawnTime = (onHours1.state as DecimalType).intValue
                }

This looks to see if onHours1.state has changed from when it was initially set at the beginning of the rule. Is there a way to do this, and then update the TImer.
What if it was initially set to 15, and I then change it to 5, but the zone has been running for 7 minutes, will the timer turn off the system. This is how i want it.

Then I think you need a completely different approach with a rule running every minute a compare the start time with the current time to start the watering and calculate the end time with your lawntime + starttime and compare with current time to stop the watering.

Lots of ways are possible. You can implement a while loop in a Timer.

sprinklerTimer = createTimer(now.plusSeconds(0), [ |
    if(<condition to continue looping>){
        <loop body>
        sprinklerTimer.reschedule(now.plusMinutes(1))
    }
    else sprinklerTimer = null
])

Or when the time changes check to see if the timer exists for that zone and reschedule it accordingly.

Or use the con triggered rule that runs every minute like Vincent suggests.

All things are possible and there are multiple approaches to get you there. But the looping method you chose and even the loo[ping using the timers I included above is probably not the best approach.

There are tons and tons of edge cases here. We can simplify a couple of them if we can make some assumptions:

  • can more than one zone run at the same time?
  • can you generate an event when the irrigation is supposed to start (e.g. CalDav binding, Alarm clock examples)?

so basically using a while loop is not suggested. Could I have a section inside the timer to check each minute if the time has been updated.
something like

sprinklerTimer = createTimer(now.plusMinutes(1), [ |
if ((onHours1.state as DecimalType).intValue != lawnTime)
                {
                lawnTime = (onHours1.state as DecimalType).intValue
                }
       
if (now.minusMinutes(lawnTime).isAfter((Irrigation_StartTime1.state as DateTimeType).calendar.timeInMillis))
                {
                sendCommand(SprinklerSystemZone1, OFF)
		sendBroadcastNotification("Zone 1 has finished watering")
                lawnTime = 0
          else sprinklerTimer.reschedule(now.plusMinutes(1))
                }

sprinklerTimer = null       
])

This would inevitibly start a timer for 1 minute, after that minute, it will check if onHours1 has been updated, if so it will update lawnTime.
It will then check to see if current time is after start time plus lawnTime, if so it will stop, otherwise it will reschedule the timer to add another minute.

Will it then start the timer again from the top? is that how this works, like will that reloop back. or what will happen?
Is there a way to tell it to loop back to the start of the timer if that zone is still on?

That would work and it is essentially what I was thinking about when I suggested implementing the while loop in the timer.

It will wait a minute then execute the whole timer body again from the top. Everything between the [ | and the ] will be executed.

That is what the reschedule does.

All a timer is is a way to schedule a lambda to run sometime in the future. The lambda is defined by the square brackets. So when you reschedule the timer, your are rescheduling everything in the square brackets to run again.

THanks for that, Im getting random errors in my log file. but cant figure it out. For now i have it running properly without being able to update the timer during the run time.
Any idea what these logs could mean?

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) [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.201805241348]

	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.evaluateArgumentExpressions(XbaseInterpreter.java:1115) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) [137:org.eclipse.smarthome.model.script:0.10.0.201805241348]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) [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.201805241348]

	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:457) [156:org.eclipse.xtext.xbase:2.12.0.v20170519-0752]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:243) [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.201805241348]

	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.201805241348]

	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.$Proxy182.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.201805241348]

	at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [107:org.eclipse.smarthome.core.scheduler:0.10.0.201805241348]

	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.201805241348]

2018-06-01 12:52:37.388 [ERROR] [org.quartz.core.ErrorLogger         ] - Job (DEFAULT.2018-06-01T12:52:37.204+10:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: [ | {

  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@7f485

} ] 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.201805241348]

	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573) [107:org.eclipse.smarthome.core.scheduler:0.10.0.201805241348]

Caused by: java.lang.NullPointerException

	at org.eclipse.smarthome.model.script.engine.ScriptError.<init>(ScriptError.java:66) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:132) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:864) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:223) ~[?:?]

	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:759) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:219) ~[?:?]

	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:507) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter._doEvaluate(ScriptInterpreter.java:236) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:233) ~[?:?]

	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.evaluateArgumentExpressions(XbaseInterpreter.java:1115) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) ~[?:?]

	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.evaluateArgumentExpressions(XbaseInterpreter.java:1115) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._invokeFeature(XbaseInterpreter.java:1045) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeFeature(XbaseInterpreter.java:991) ~[?:?]

	at org.eclipse.smarthome.model.script.interpreter.ScriptInterpreter.invokeFeature(ScriptInterpreter.java:143) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._doEvaluate(XbaseInterpreter.java:901) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:225) ~[?:?]

	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:457) ~[?:?]

	at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.doEvaluate(XbaseInterpreter.java:243) ~[?:?]

	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.$Proxy182.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

What is the first line of that stack trace. All those lines are the same error but we need the ffirst couple of lines before the "at"s to know what the error actually is.