[SOLVED] Help me condense this rule

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

I’ll do so tomorrow Greg. Send me a message to remind me in about 10 hours

Sitemap

Frame label="Irrigation" 
{

		
		Text item=gIrrigation
		{	
				
					Text item=Irrigation_Zone1 label="Zone 1 Front Grass [%s]" valuecolor=[Irrigation_Zone1!="Inactive"="green",Irrigation_Zone1=="Inactive"="grey"] icon="faucet"
					{
						Switch item=SprinklerSystemZone1	
						Slider item=onHours1 label="Zone 1 Timer?" icon="time" sendFrequency=1
						Setpoint item=onHours1 label=" [%d]" icon="none" step=1 minValue=1 maxValue=100
						Text item=Irrigation_StartTime1 valuecolor=["green"] icon="rain"
						Text item=FrontGrassMoistureTime valuecolor=["green"] icon="time"
						Text item=FrontGrassMoisture label="Moisture Level [%d %%]" valuecolor=[>=50="blue", >=30="green", 0="grey", <15="red"] icon="rain"
					}
					Text item=Irrigation_Zone2 label="Zone 2 Front Garden [%s]" valuecolor=[Irrigation_Zone2!="Inactive"="green",Irrigation_Zone2=="Inactive"="grey"] icon="faucet"
					{
						Switch item=SprinklerSystemZone2
						Slider item=onHours2 label="Zone 2 Timer?" icon="time" sendFrequency=1
						Setpoint item=onHours2 label=" [%d]" icon="none" step=1 minValue=1 maxValue=100
						Text item=Irrigation_StartTime2 valuecolor=["green"] icon="rain"
					}
					Text item=Irrigation_Zone3 label="Zone 3 Back Grass [%s]" valuecolor=[Irrigation_Zone3!="Inactive"="green",Irrigation_Zone3=="Inactive"="grey"] icon="faucet"
					{
						Switch item=SprinklerSystemZone3
						Slider item=onHours3 label="Zone 3 Timer?" icon="time" sendFrequency=1
						Setpoint item=onHours3 label=" [%d]" icon="none" step=1 minValue=1 maxValue=100
						Text item=Irrigation_StartTime3 valuecolor=["green"] icon="rain"
					}
					Text item=Irrigation_Zone4 label="Zone 4 Back Garden [%s]" valuecolor=[Irrigation_Zone4!="Inactive"="green",Irrigation_Zone4=="Inactive"="grey"] icon="faucet"
					{
						Switch item=SprinklerSystemZone4
						Slider item=onHours4 label="Zone 4 Timer?" icon="time" sendFrequency=1
						Setpoint item=onHours4 label=" [%d]" icon="none" step=1 minValue=1 maxValue=100
						Text item=Irrigation_StartTime4 valuecolor=["green"] icon="rain"
					}

	}

}	

Items


//*********************************
//Sprinkler System
//***********


String Irrigation_Zone1  	(gIrrigationTimes)
String Irrigation_Zone2 	(gIrrigationTimes)
String Irrigation_Zone3 	(gIrrigationTimes)
String Irrigation_Zone4 	(gIrrigationTimes)


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

Number Countdown_Zone1 		"Minutes Remaining [%d]"	(gIrrigationTimes)
Number Countdown_Zone2 		"Minutes Remaining [%d]"	(gIrrigationTimes)
Number Countdown_Zone3		"Minutes Remaining [%d]"	(gIrrigationTimes)
Number Countdown_Zone4 		"Minutes Remaining [%d]"    (gIrrigationTimes)
                         
Switch SprinklerSystemSchedule "Schedule Active"  	(gIrrigationTimes)


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



Number onHours1 "Front Grass Sprinkler Minutes" 	(gIrrigationTimes)
Number onHours2 "Front Garden Sprinkler Minutes"	(gIrrigationTimes)
Number onHours3 "Back Grass Sprinkler Minutes"		(gIrrigationTimes)
Number onHours4 "Back Garden Sprinkler Minutes"		(gIrrigationTimes)



String IFTTString "If this then that string" (All)

rules

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

then
Countdown_Zone1.postUpdate(0)
Irrigation_Zone1.postUpdate("Inactive")
if (timer1 !== null)
	{
	timer1.cancel
	timer1 = null
	}
end
//**************************************************************************************
rule "Zone 2 Inactive"
when
    Item SprinklerSystemZone2 received command OFF

then
Countdown_Zone2.postUpdate(0)
Irrigation_Zone2.postUpdate("Inactive")
if (timer2 !== null)
	{
	timer2.cancel
	timer2 = null
	}
end
//**************************************************************************************
rule "Zone 3 Inactive"
when
    Item SprinklerSystemZone3 received command OFF

then
Countdown_Zone3.postUpdate(0)
Irrigation_Zone3.postUpdate("Inactive")
if (timer3 !== null)
	{
	timer3.cancel
	timer3 = null
	}

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

then
Countdown_Zone4.postUpdate(0)
Irrigation_Zone4.postUpdate("Inactive")
if (timer4 !== null)
	{
	timer4.cancel
	timer4 = null
	}
end

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


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

if (onHours1.state !== NULL)
	{
	
	
		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)

		}
	}
else
	{
	SprinklerSystemZone1.sendCommand(OFF)
	onHours1.postUpdate(30)
	sendBroadcastNotification("Zone 1 does not have a timer set, this zone has been set to 30 minutes, please try again")

	}
end

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


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

if (onHours2.state !== NULL)
	{
	
		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)

		}
	}
else
	{
	SprinklerSystemZone2.sendCommand(OFF)
	onHours2.postUpdate(30)
	sendBroadcastNotification("Zone 2 does not have a timer set, this zone has been set to 30 minutes, please try again")

	}
end

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


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

if (onHours3.state !== NULL)
	{
		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")
		Countdown_Zone3.postUpdate(lawnTime)

		}
	}	
else
	{
	SprinklerSystemZone3.sendCommand(OFF)
	onHours3.postUpdate(30)
	sendBroadcastNotification("Zone 3 does not have a timer set, this zone has been set to 30 minutes, please try again")

	}
end

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


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

if (onHours4.state !== NULL)
	{
	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)
		}
	}
	else
	{
	SprinklerSystemZone4.sendCommand(OFF)
	onHours4.postUpdate(30)
	sendBroadcastNotification("Zone 4 does not have a timer set, this zone has been set to 30 minutes, please try again")
	}
end

//***************************************************************************

rule "Countdown Zone 1 Timer"
when
        Item Countdown_Zone1 received update
then	
if (Countdown_Zone1.state as DecimalType >= 1 && SprinklerSystemZone1.state == ON)
		{
			Irrigation_Zone1.postUpdate("Active " + (Countdown_Zone1.state as DecimalType) + " minutes remaining")
	    	timer1 = createTimer(now.plusMinutes(1)) [|
			Countdown_Zone1.postUpdate(Countdown_Zone1.state as DecimalType - 1)
			]
		}
			
if (Countdown_Zone1.state as DecimalType == 0 && SprinklerSystemZone1.state == ON)
				{
				SprinklerSystemZone1.sendCommand(OFF)
				sendBroadcastNotification("Finished Watering Zone 1")
				}
end
//***************************************************************************
rule "Countdown Zone 2 Timer"
when
        Item Countdown_Zone2 received update
then	
if (Countdown_Zone2.state as DecimalType >= 1 && SprinklerSystemZone2.state == ON)
		{
			Irrigation_Zone2.postUpdate("Active " + (Countdown_Zone2.state as DecimalType) + " minutes remaining")
	    	timer2 = createTimer(now.plusMinutes(1)) [|
			Countdown_Zone2.postUpdate(Countdown_Zone2.state as DecimalType - 1)
			]
		}
			
if (Countdown_Zone2.state as DecimalType == 0 && SprinklerSystemZone2.state == ON)
				{
				SprinklerSystemZone2.sendCommand(OFF)
				sendBroadcastNotification("Finished Watering Zone 2")
				}
end
//***************************************************************************

rule "Countdown Zone 3 Timer"
when
        Item Countdown_Zone3 received update
then	
if (Countdown_Zone3.state as DecimalType >= 1 && SprinklerSystemZone3.state == ON)
		{
			Irrigation_Zone3.postUpdate("Active " + (Countdown_Zone3.state as DecimalType) + " minutes remaining")
	    	timer3 = createTimer(now.plusMinutes(1)) [|
			Countdown_Zone3.postUpdate(Countdown_Zone3.state as DecimalType - 1)
			]
		}
			
if (Countdown_Zone3.state as DecimalType == 0 && SprinklerSystemZone3.state == ON)
				{
				SprinklerSystemZone3.sendCommand(OFF)
				sendBroadcastNotification("Finished Watering Zone 3")
				}
end
//***************************************************************************

rule "Countdown Zone 4 Timer"
when
        Item Countdown_Zone4 received update
then	
if (Countdown_Zone4.state as DecimalType >= 1 && SprinklerSystemZone4.state == ON)
		{
			Irrigation_Zone4.postUpdate("Active " + (Countdown_Zone4.state as DecimalType) + " minutes remaining")
	    	timer4 = createTimer(now.plusMinutes(1)) [|
			Countdown_Zone4.postUpdate(Countdown_Zone4.state as DecimalType - 1)
			]
		}
			
if (Countdown_Zone4.state as DecimalType == 0 && SprinklerSystemZone4.state == ON)
				{
				SprinklerSystemZone4.sendCommand(OFF)
				sendBroadcastNotification("Finished Watering Zone 4")
				}
end

//***************************************************************************


rule "IFTT rule"
when
    Item IFTTString received update
then
var item=IFTTString.state.toString.split(";").get(0)
val time = Integer::parseInt(IFTTString.state.toString.split(";").get(1))

if (item.contains("back garden"))
{
postUpdate(onHours4, time)
SprinklerSystemZone4.sendCommand(ON)
}

if (item.contains("back grass"))
{
postUpdate(onHours3, time)
SprinklerSystemZone3.sendCommand(ON)
}

if (item.contains("front garden"))
{
postUpdate(onHours2, time)
SprinklerSystemZone2.sendCommand(ON)
}

if (item.contains("front grass"))
{
postUpdate(onHours1, time)
SprinklerSystemZone1.sendCommand(ON)
}
end

My rules are not condensed, I haven’t gotten around to it. But they work with zero issues. so for now, i haven’t changed them

Also, disregard the groups in my items.
They are not needed for any rules.

1 Like