[SOLVED] Help me condense this rule

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.

Seems to be an error with timers. I have been able to get around it by altering the order in which things run. Cheers

@Christopher_Hemmings
Hey Chris,
Could you please share your up-to-date sitemap,items and rules for this?

Cheers,
Greg