YAWS - Yet Another Watering Solution

This is an example of Yet Another Watering Solution I’ve developed for my garden.

Background:

The goal was to make a complete, fully configurable irrigation system, with the ability to easily expand the number of the zones, overwatering protection and correlation with the weather.

Assumptions:

  1. Use of the timers and cascading items pattern.
  2. Overwatering protection, after opening any valve, for a maximum of 2 hours of watering.
  3. Any number of watering zones, easy addition.
  4. Cascade valve opening.
  5. Manual watering lock.
  6. Taking into account temperature (min and max), wind, precipitation (last 24h and forecast for 24h)
  7. Correction factor which increases or decreases the valve opening time based on the temperature.
  8. Showing in the Basic UI how much watering time is left for the current zone.
  9. Watering on selected days of the week.
  10. Watering start based on the sunrise or the fixed time.

Prerequisites:

  1. Expire binding - https://www.openhab.org/addons/bindings/expire1/
  2. Astro binding - https://www.openhab.org/addons/bindings/astro/
  3. Configured persistence - https://www.openhab.org/docs/configuration/persistence.html
  4. OpenWeatherMap binding - https://www.openhab.org/addons/bindings/openweathermap/

I hope that the code is self-describing and easy to understand, as it is not overcomplicated. However, if something is not clear, feel free to ask.

Items

// settings group
Group Group_Irrigation_Settings

// irrigation lock
Switch IrrigationLock "Watering lock" (Group_Irrigation_Settings) { ga="Switch" }

// protection against too long watering, 2h default
Switch IrrigationTimerMax "Max irrigation time [MAP(irrigation.map):%s]" { expire = "2h,command=OFF" }

// all valves group
Group:Switch:OR(ON,OFF) GroupIrrigationValves "Irrigation valves [MAP(irrigation.map):%s]"
Group:Number:SUM GroupIrrigationTimes "Total irrigation time [%d min.]"

// cascading valves - current zone
String IrrigationCurrentValve "Current irrigation zone [MAP(irrigation.map):%s]"

// irrigation valves' switches
Switch IrrigationValveZone1 "Lawn - zone 1" (GroupIrrigationValves) { synonyms="Podlewanie w strefie pierwszej", ga="Switch" } 
Switch IrrigationValveZone2 "Lawn - zone 2" (GroupIrrigationValves) { synonyms="Podlewanie w strefie drugiej", ga="Switch" } 
Switch IrrigationValveZone3 "Lown - zone 3" (GroupIrrigationValves) { synonyms="Podlewanie w strefie trzeciej", ga="Switch" } 
Switch IrrigationValveZone4 "Garden flowers & plants - dripping line" (GroupIrrigationValves) { synonyms="Linia kropelkująca" } 

// irrigation times
Number IrrigationValveZone1Time "Lawn - zone 1 [%d min]" (GroupIrrigationTimes, Group_Irrigation_Settings)
Number IrrigationValveZone2Time "Lawn - zone 2 [%d min]" (GroupIrrigationTimes, Group_Irrigation_Settings)
Number IrrigationValveZone3Time "Lawn - zone 3 [%d min]" (GroupIrrigationTimes, Group_Irrigation_Settings)
Number IrrigationValveZone4Time "Flowers & plants - dripping line [%d min]" (GroupIrrigationTimes, Group_Irrigation_Settings)
Number IrrigationDurationCoefficientFactor "Correction factor"
Number IrrigationSectionRemainingTime "Remaining irrigation time [%d]"

// switch - whether to start at a particular hour or before the sunrise
Switch IrrigationStartAtSpecificHour "Start at a particular hour" (Group_Irrigation_Settings)
Number IrrigationStartTime "Irrigation start time" (Group_Irrigation_Settings)
Number IrrigationHoursBeforeSunrise "Start irrigation before sunrise [%d h]" (Group_Irrigation_Settings)

// irrigation week days
Switch IrrigationDay1 "Monday"
Switch IrrigationDay2 "Tuesday"
Switch IrrigationDay3 "Wednesday"
Switch IrrigationDay4 "Thursday"
Switch IrrigationDay5 "Friday"
Switch IrrigationDay6 "Saturday"
Switch IrrigationDay7 "Sunday"

Number:Length SumRainLast24h "Rainfall, last 24h [%.2f mm]"
Number:Length SumRainNext24h "Rainfall, forecast 24h [%.2f mm]"

Number:Speed MaxAllowedWindSpeed "Max allowed windspeed [%d km/h]" (Group_Irrigation_Settings)
Number:Length MaxAllowedRain "Max allowed rainfall sum [%.1f mm]" (Group_Irrigation_Settings)

Rules

/*
 * Irrigation rules
 */

import org.eclipse.smarthome.model.script.ScriptServiceUtil

val logName = "Irrigation"
var Timer irrigationTimer = null

// watering time correction factor - based on the average temperature
var coefficientFactor = 1

rule "Irrigation - system start"
when
	System started
then
	// initiall default settings

	if (SumRainLast24h.state == NULL) 
		SumRainLast24h.sendCommand(0)

	if (SumRainNext24h.state == NULL) 
		SumRainNext24h.sendCommand(0)

	if (MaxAllowedWindSpeed.state == NULL)
		MaxAllowedWindSpeed.sendCommand(35)

	if (MaxAllowedRain.state == NULL)
		MaxAllowedRain.sendCommand(3)

	if (IrrigationHoursBeforeSunrise.state == NULL)
		IrrigationHoursBeforeSunrise.sendCommand(1)

	if (IrrigationValveZone1Time.state == NULL)
		IrrigationValveZone1Time.sendCommand(1)

	if (IrrigationValveZone2Time.state == NULL)
		IrrigationValveZone2Time.sendCommand(1)

	if (IrrigationValveZone3Time.state == NULL)	
		IrrigationValveZone3Time.sendCommand(1)

	if (IrrigationValveZone4Time.state == NULL)	
		IrrigationValveZone4Time.sendCommand(1)

	if (IrrigationStartAtSpecificHour.state == NULL)
		IrrigationStartAtSpecificHour.sendCommand(OFF)

	if (IrrigationStartTime.state == NULL)	
		IrrigationStartTime.sendCommand(20)

	if (IrrigationHoursBeforeSunrise.state == NULL)
		IrrigationHoursBeforeSunrise.sendCommand(1)

	// close all valves
	GroupIrrigationValves.members.filter[valve | valve.state != OFF].forEach[valve | valve.sendCommand(OFF)]

	IrrigationCurrentValve.postUpdate(OFF)
end

rule "Irrigation - calculate whether to start watering"
when
    Time cron "0 * * ? * *" // every minute
then
	try {
		logInfo(logName, "Calculating whether to start irrigation")

		// calculate rainfall
		SumRainLast24h.sendCommand((WeatherAndForecastCurrentRain.sumSince(now.minusHours(24), "influxdb") as Number).doubleValue)
		SumRainNext24h.sendCommand(GroupForecastedRain24Sum.state as Number)

		// wait to propagate item states - not sure if necessary
		Thread.sleep(200)

		logDebug(logName, "Rain - last 24h (sum): {} mm", String::format("%.2f", (SumRainLast24h.state as Number).doubleValue))
		logDebug(logName, "Rain - forecast 24h (sum): {} mm", String::format("%.2f", (SumRainNext24h.state as Number).doubleValue))

		///////////////////		
		// start calculations, whether to start and for how long
		///////////////////

		// check for the manual lock
		if (IrrigationLock.state == ON) {
			logInfo(logName, "Irrigation lock is on")
			return
		}

		// check the week day
		val Number day = now.getDayOfWeek()
		val dayItem = ScriptServiceUtil.getItemRegistry.getItem("IrrigationDay" + day)
		
		if (dayItem === null || dayItem.state == OFF || dayItem.state == NULL) {
			logInfo(logName, "Inappropriate day to start irrigation", dayItem)
			return
		}

		// set the default irrigation hour to X hours before the sunrise
		val localSunRise = new DateTime(LocalSunRiseStart.state.toString).minusHours((IrrigationHoursBeforeSunrise.state as Number).intValue)
		var Number wateringHour = localSunRise.getHourOfDay()
		var Number wateringMinute = localSunRise.getMinuteOfHour()

		// if there is a specific hour in settings, then use it
		if (IrrigationStartAtSpecificHour.state == ON) {
			wateringHour = IrrigationStartTime.state as Number
			wateringMinute = 0
		}

		logInfo(logName, "Watering at: {}:{}", wateringHour, wateringMinute)

		// check if the current time is the watering time (full hour)
		if (now.getHourOfDay != wateringHour || now.getMinuteOfHour != wateringMinute) {
			// nope - good bye
			logInfo(logName, "Inappropriate time to start irrigation")
			return
		}
		logInfo(logName, "It is watering hour: {}:{}", wateringHour, wateringMinute)

		// check if the current wind speed is higher then the max allowed
		logInfo(logName, "Current wind speed: {} km/h", String::format("%.2f", (WeatherAndForecastCurrentWindSpeed.state as Number).doubleValue))
		if (WeatherAndForecastCurrentWindSpeed.state > MaxAllowedWindSpeed.state as Number) {
			logInfo(logName, "Wind speed too high to irrigate")
			return
		}

		// if the rainfall sum for the last 24h and the forecast for 24h is higher then set, then we are not going to irrigate
		val rainSum = (SumRainLast24h.state as Number).doubleValue + (SumRainNext24h.state as Number).doubleValue
		logInfo(logName, "Past and forcasted average rain: {} mm", String::format("%.2f", rainSum))
		if (rainSum > (MaxAllowedRain.state as Number).doubleValue) {
			logInfo(logName, "To heavy rain to irrigate (past and forcasted)")
			return
		}

		// check the wether, and based on that set the watering time coefficient factor
		// if the temperature is to low, don't start watering
		val avgTemperatureLast24h = (WeatherAndForecastCurrentTemperature.averageSince(now.minusHours(24), "influxdb") as Number).doubleValue

		logInfo(logName, "Average temperature for the last 24h: {}", avgTemperatureLast24h)

		if (avgTemperatureLast24h <= 10) {
			logInfo(logName, "Temperature too low to start irrigation")
			return
		} 
		else if (avgTemperatureLast24h > 30) {
			logInfo(logName, "Setting irrigation coefficient factor to 2")
		} 
		else {
			// coefficient factor should be between 1 and 2
			// this part could, and should be better
			coefficientFactor = avgTemperatureLast24h / 10 - 1;
			logInfo(logName, "Setting irrigation coefficient factor to {}", coefficientFactor)
		}

		///////////////////
		// ok, let's start watering, cascading all of the valves from the GroupIrrigationValves
		///////////////////

		// starting with the Zone 1, other zones will be turned on in sequence by the separate rule
		logInfo(logName, "Starting the irrigation sequence")
		IrrigationCurrentValve.sendCommand(IrrigationValveZone1.name)
	}
	catch (Exception e) {
        logError(logName, "Error calculating whether to start irrigation: " + e)
    }
end

rule "Irrigation - cascading"
when
    Item IrrigationCurrentValve received command
then
	try {
		// get the currently open valve
		val currValve = GroupIrrigationValves.members.findFirst[valve | valve.name == receivedCommand.toString]
		val currValveNum = Integer::parseInt(currValve.name.split("Zone").get(1))
		val currValveMins = GroupIrrigationTimes.members.findFirst[t | t.name == currValve.name+"Time" ].state as Number
		logDebug(logName, "Current valve {}, duration {}", currValve.name, currValveMins)

		// get the next valve in the sequence
		val nextValveNum = currValveNum + 1
		val nextValveName = "IrrigationValveZone" + nextValveNum
		val nextValve = GroupIrrigationValves.members.findFirst[valve | valve.name == nextValveName]
		
		// if there is no next valve in the sequence, then nextValve is null
		if (nextValve === null)
			logDebug(logName, "This is the last valve in the sequence")
		else
			logDebug(logName, "Next valve {}", nextValve.name)
		
		// open the current valve
		val valveOpenTime = currValveMins * coefficientFactor
		logInfo(logName, "Opening {} for {} mins", currValve.name, valveOpenTime)
		currValve.sendCommand(ON)

		IrrigationSectionRemainingTime.postUpdate(valveOpenTime.intValue)
		
		// set the timer, after expiring turn off the current valve and turn on the next one
		irrigationTimer = createTimer(now.plusMinutes(valveOpenTime.intValue), [ |
			if (nextValve !== null) {
				// this will invoke cascading valves, "Irrigation - cascading" rule
				IrrigationCurrentValve.sendCommand(nextValve.name)
			}
			else {
				logInfo(logName, "Irrigation is complete")
			}

			// let's wait for propagating item values
			Thread::sleep(500)

			// turn off current valve
			logInfo(logName, "Closing " + currValve.name)
			currValve.sendCommand(OFF)

			irrigationTimer = null
		])
	}
	catch (Exception e) {
        logError(logName, "Error controlling cascading valves: " + e)
    }
end

// for displaying remaining irrigation time purpose only
rule "Irrigation - update timer"
when
  Time cron "0 * * ? * *" // every minute
then
	if (IrrigationSectionRemainingTime.state as Number > 0)
		IrrigationSectionRemainingTime.postUpdate((IrrigationSectionRemainingTime.state as Number) - 1)
end

rule "Irrigation - all valves closed"
when
	Item GroupIrrigationValves changed to OFF
then
	// set the current valve to OFF
	logInfo(logName, "All irrigation valves closed")
	IrrigationCurrentValve.postUpdate(OFF)

	// reset the remaining time
	IrrigationSectionRemainingTime.postUpdate(0)
end

rule "Irrigation - valve updated, turn on the timer"
when
	Item GroupIrrigationValves changed 
then
	// protection against overwatering
	
	// log the state of all valves
	GroupIrrigationValves.members.forEach [valve | 
		logDebug(logName, "Irrigation valve: " + valve.name + " " + valve.state)
	]

	// a valve was turned on
	if (GroupIrrigationValves.state == ON) {
		if (IrrigationTimerMax.state == OFF) {
			// timer is not set yet, start the timer
			logDebug(logName, "Irrigation valve open, starting protection timer")
			IrrigationTimerMax.sendCommand(ON)
		}
		else {
			// the timer is already running
			logDebug(logName, "Irrigation valve open, timer already started, nothing to do")
		}
	}
	else {
		logDebug(logName, "All irrigation valves closed, stopping protection timer")
		IrrigationTimerMax.postUpdate(OFF)
	}
	triggeringItem.postUpdate(triggeringItem.state)
end

rule "Irrigation - protection timer off, close all valves"
when
	Item IrrigationTimerMax changed to OFF
then
	// protection timer expired - turn all valves off
	logWarn(logName, "Irrigation protection timer expired - close all valves")

	// close all valves from the group
	GroupIrrigationValves.members.forEach [valve | 
		logDebug(logName, "Closing valve: " + valve.name)
		valve.sendCommand(OFF)
	]
end

Sitemap

sitemap irrigation label="Irrigation"
{		
	Frame label="Status" {
		Switch item=IrrigationLock icon="lock"
		Text item=GroupIrrigationValves label="Irrigation" icon="water"
		Text item=IrrigationSectionRemainingTime visibility=[IrrigationSectionRemainingTime > 0] label="Section irrigation remaining time [%d min]" icon="time"
	}
	Frame label="Sections" {
		Switch item=IrrigationValveZone1 label="Lawn - zone 1" /*visibility=[IrrigationLock.state==OFF]*/ icon="faucet"
		Switch item=IrrigationValveZone2 label="Lawn - zone 2" icon="faucet"
		Switch item=IrrigationValveZone3 label="Lawn - zone 3" icon="faucet"
		Switch item=IrrigationValveZone4 label="Flowers & plants - dripping line" icon="faucet"
	}
	
	Frame label="Others" {
		// rainfall past 24h
		Text item=SumRainLast24h icon="rain"

		// rainfall forecast 24h
		Text item=SumRainNext24h icon="rain"

		Text label="Settings" icon="settings" {
			Frame label="Irrigation days" {
				Switch item=IrrigationDay1 mappings=[ON="Tak", OFF="Nie"]
				Switch item=IrrigationDay2 mappings=[ON="Tak", OFF="Nie"]
				Switch item=IrrigationDay3 mappings=[ON="Tak", OFF="Nie"]
				Switch item=IrrigationDay4 mappings=[ON="Tak", OFF="Nie"]
				Switch item=IrrigationDay5 mappings=[ON="Tak", OFF="Nie"]
				Switch item=IrrigationDay6 mappings=[ON="Tak", OFF="Nie"]
				Switch item=IrrigationDay7 mappings=[ON="Tak", OFF="Nie"]
			}

			Frame label="Irrigation time" {
				Selection item=IrrigationStartAtSpecificHour icon="sunrise" label="Start irrigation" mappings=["OFF"="Before sunrise", "ON"="At the specific hour"]
				
				Setpoint item=IrrigationHoursBeforeSunrise icon="time" minValue=0 maxValue=23 visibility=[IrrigationStartAtSpecificHour==OFF]

				Selection item=IrrigationStartTime visibility=[IrrigationStartAtSpecificHour==ON] mappings=[
														"0"="00:00", "1"="01:00", "2"="02:00",
														"3"="03:00", "4"="04:00", "5"="05:00", 
														"6"="06:00", "7"="07:00", "8"="08:00",
														"9"="09:00", "10"="10:00", "11"="11:00", 
														"12"="12:00", "13"="13:00", "14"="14:00",
														"15"="15:00", "16"="16:00", "17"="17:00",
														"18"="18:00", "19"="19:00", "20"="20:00", 
														"21"="21:00", "22"="22:00", "23"="23:00"
				]
			}

			Frame label="Irrigation duration" {
				Setpoint item=IrrigationValveZone1Time minValue=1 maxValue=60 step=5 icon="time"
				Setpoint item=IrrigationValveZone2Time minValue=1 maxValue=60 step=5 icon="time"
				Setpoint item=IrrigationValveZone3Time minValue=1 maxValue=60 step=5 icon="time"
				Setpoint item=IrrigationValveZone4Time minValue=1 maxValue=60 step=5 icon="time"
			}

			Frame label="Weather conditions" {
				Setpoint item=MaxAllowedRain label="Max allowed rainfall" minValue=0 maxValue=10 step=0.1 icon="rain"
				Setpoint item=MaxAllowedWindSpeed label="Max allowed wind speed" minValue=5 maxValue=150 step=5 icon="wind"
			}
		}
	}	
	Frame label="Rainfall (last 24h)" {
		Image refresh=60000 url="http://openhab:3000/render/d-solo/lPgA48Wgz/rain-current-and-forecast?tab=advanced&panelId=2&orgId=1&from=now-24h&to=now&theme=light&width=480&height=240&tz=Europe%2FWarsaw"
	}
 }

Final thoughts

Protection timer could be set in a physical device (relay) if possible - a better solution rather than software.
I’m not using any rain sensor, I just rely on the OpenWeatherMap. This is not a very accurate solution, however, it works well.
I don’t have enough knowledge yet on how to correlate the average temperature with the irrigation, so some additional research is required. I just wanted to have it already in the code.

Any ideas and comments are welcome.

5 Likes

What did you use for valves?

Hi @TheJetsetter,

thanks for sharing. Is this running on OH3?

Regards

I use Hunter valves, like this one:

It is based on OH 2.5

Thanks. Looks like a good choice. But I am searching for an alternative. A wireless, battery operated unit. I know, my first thought is, “it must take a power to keep the valve open. How long could a battery last.”
Well, I bought this house and the previous owners had installed MisterTimers.
MisterTimer. These things use just 2 AA cells that last all season and more. How do they do it? They just pulse the valve to open it and pulse to close it. Like a Flip/Flop. They would be great except the wireless part is for power, not control. So I can’t command it to do anything. I have done some searching for a Flip/Flop valve but no luck yet.
Thanks again.
Pete

Thanks for sharing your solution, Jacek.
I think that temperature correction done right, i`ve used similar settings for a long time.
But there are some vays to improve your logic. First - i have a covered greenhouse, so that specific valve needs no corrections to rain and windspeed. Second - cucumbers in the greenhouse are growing mostly at night, so they need a small amount of water before sundown.

@TheJetsetter , could you help me to improve watering widget, based on your YAWS?
Here is how he looks like:


And the code for him:

uid: Watering_v1
tags: []
props:
  parameters:
    - description: A text prop
      label: Prop 1
      name: prop1
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Aug 15, 2021, 2:38:08 AM
component: f7-card
config:
  style:
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.1)
    height: auto
    margin: 5px
    line-height: 1
    font-family: sans-serif
slots:
  default:
    - component: f7-card-header
      config:
        class:
          - justify-content-center
          - align-items-center
          - text-align-center
      slots:
        default:
          - component: Label
            config:
              text: Полив
              style:
                font-size: 250%
    - component: f7-card-content
      config:
        style:
          margin: 5px
          padding: 0px
      slots:
        default:
          - component: f7-row
            slots:
              default:
                - component: f7-segmented
                  config:
                    strong: true
                    color: green
                    class:
                      - segmented-strong
                    style:
                      --f7-segmented-strong-padding: 5px
                      --f7-segmented-strong-between-buttons: 5px
                      --f7-segmented-strong-button-font-weight: 300
                      --f7-segmented-strong-bg-color: transparent
                      --f7-segmented-strong-button-hover-bg-color: rgba(255, 255, 255, 0.15)
                      width: 100%
                      outline: true
                  slots:
                    default:
                      - component: oh-button
                        config:
                          text: Утром
                          action: variable
                          actionVariable: select
                          actionVariableValue: 1
                          active: =(vars.select == 1)
                          outline: =(vars.select == 1)
                          style:
                            font-weight: 400
                            font-size: 150%
                      - component: oh-button
                        config:
                          text: Вечером
                          action: variable
                          actionVariable: select
                          actionVariableValue: 2
                          active: =(vars.select == 2)
                          outline: =(vars.select == 2)
                          style:
                            font-weight: 400
                            font-size: 150%
                      - component: oh-button
                        config:
                          text: Днём
                          action: variable
                          actionVariable: select
                          actionVariableValue: 3
                          active: =(vars.select == 3)
                          outline: =(vars.select == 3)
                          style:
                            font-weight: 400
                            font-size: 150%
          - component: f7-tab
            config:
              visible: =(vars.select == 1 || vars.select == NULL)
              style:
                animation: f7-fade-in 300ms
                padding: 0px
                margin: 5px
                margin-right: 5px
              class:
                - display-flex
                - justify-content-space-between
                - flex-direction-column
            slots:
              default:
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      font-size: 120%
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Утренний полив
                      - component: oh-toggle
                        config:
                          item: IrrigationStartAtSunrise
                          color: green
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      font-size: 120%
                  slots:
                    default:
                      - component: f7-row
                        config: {}
                        slots:
                          default:
                            - component: Label
                              config:
                                text: "Рассвет:"
                                style:
                                  margin-right: 10px
                            - component: Label
                              config:
                                text: =items.Voshod.displayState
                      - component: f7-row
                        config: {}
                        slots:
                          default:
                            - component: Label
                              config:
                                text: Время после рассвета
                                style:
                                  margin-right: 10px
                            - component: oh-link
                              config:
                                color: green
                                text: =items.IrrigationHoursAfterSunrise.state
                                action: popover
                                popoverOpen: .timerpopover
                              slots:
                                default:
                                  - component: f7-popover
                                    config:
                                      class:
                                        - timerpopover
                                    slots:
                                      default:
                                        - component: oh-stepper-card
                                          config:
                                            color: green
                                            item: IrrigationHoursAfterSunrise
                                            title: Минут после рассвета
                                            min: 0
                                            max: 180
                                            step: 10
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      margin-bottom: 15px
                      font-size: 120%
                  slots:
                    default:
                      - component: f7-row
                        config: {}
                        slots:
                          default:
                            - component: Label
                              config:
                                text: "Время начала полива:"
                                style:
                                  margin-right: 10px
                            - component: Label
                              config:
                                text: =items.IrrigationStartTime.displayState
                - component: f7-row
                  config:
                    style:
                      padding: 5px
                      margin: 0px
                      font-size: 120%
                      border: 1px solid green
                      border-radius: var(--f7-card-expandable-border-radius)
                      display: inline
                  slots:
                    default:
                      - component: oh-repeater
                        config:
                          accordionList: true
                          fragment: false
                          for: item
                          sourceType: itemsInGroup
                          groupItem: GroupIrrigationValves
                        slots:
                          default:
                            - component: f7-row
                              config:
                                style:
                                  margin-top: 5px
                                  display: flex
                                  justify-content: flex-start
                                  align-items: center
                              slots:
                                default:
                                  - component: oh-link
                                    config:
                                      iconMaterial: opacity
                                      color: '=items[loop.item.name].state == "ON" ? "green" : "gray"'
                                      iconSize: 25px
                                      action: popover
                                      actionModal: widget:popup_manual_start
                                      actionModalConfig:
                                        item: =loop.item.name
                                  - component: oh-button
                                    config:
                                      text: =loop.item.label
                                      action: analyzer
                                      actionAnalyzerItems: =[loop.item.name]
                                      style:
                                        --f7-button-hover-bg-color: green
                                        color: white
                                        font-size: 20px
                                        margin-left: 10px
                                  - component: oh-stepper
                                    config:
                                      item: =(loop.item.name + 'Time1')
                                      title: Время полива
                                      min: 0
                                      max: 180
                                      step: 1
                                      round: true
                                      color: green
                                      autorepeat: true
                                      autorepeat-dynamic: true
                                      style:
                                        margin-left: auto
                                  - component: oh-button
                                    config:
                                      iconF7: gear_alt_fill
                                      color: '=vars[loop.item.name + "S"] === true ? "green" : "gray"'
                                      action: variable
                                      actionVariable: =(loop.item.name + 'S')
                                      actionVariableValue: =!(vars[loop.item.name + 'S'] === true)
                            - component: f7-segmented
                              config:
                                strong: true
                                small: true
                                outline: true
                                color: green
                                class:
                                  - segmented-round
                                style:
                                  --f7-segmented-strong-padding: 0px
                                  --f7-segmented-strong-between-buttons: 5px
                                  --f7-segmented-strong-button-font-weight: 300
                                  --f7-segmented-strong-bg-color: transparent
                                  --f7-segmented-strong-button-hover-bg-color: rgba(0, 255, 0, 0.1)
                                  --f7-segmented-strong-button-active-bg-color: transparent
                                  margin-top: 5px
                              slots:
                                default:
                                  - component: oh-repeater
                                    config:
                                      fragment: true
                                      for: day
                                      sourceType: array
                                      in:
                                        - name: 1
                                          label: Пн
                                        - name: 2
                                          label: Вт
                                        - name: 3
                                          label: Ср
                                        - name: 4
                                          label: Чт
                                        - name: 5
                                          label: Пт
                                        - name: 6
                                          label: Сб
                                        - name: 7
                                          label: Вс
                                    slots:
                                      default:
                                        - component: oh-button
                                          config:
                                            text: =loop.day.label
                                            action: toggle
                                            actionCommand: ON
                                            actionCommandAlt: OFF
                                            actionItem: =(loop.item.name + 'M' + loop.day.name)
                                            fill: "=(items[loop.item.name + 'M' + loop.day.name].state === 'ON' ? true : false)"
                                            style:
                                              font-weight: 400
                            - component: f7-row
                              config:
                                visible: =vars[loop.item.name + 'S'] === true
                                style:
                                  margin: 10px
                                  display: flex
                                  justify-content: space-around
                                  align-items: center
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      style:
                                        align-items: center
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: Коррекция
                                            style:
                                              font-size: 12px
                                  - component: f7-row
                                    config:
                                      style:
                                        align-items: center
                                        justify-content: space-between
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: по температуре
                                            style:
                                              font-size: 12px
                                              margin-right: 10px
                                        - component: oh-toggle
                                          config:
                                            color: green
                                            item: =(loop.item.name + 'TimeCorrection')
                                        - component: Label
                                          config:
                                            text: по осадкам
                                            style:
                                              font-size: 12px
                                              margin: 0px 10px
                                        - component: oh-toggle
                                          config:
                                            color: green
                                            item: =(loop.item.name + 'RainCorrection')
          - component: f7-tab
            config:
              visible: =(vars.select == 2)
              style:
                animation: f7-fade-in 300ms
                padding: 0px
                margin: 5px
                margin-right: 5px
              class:
                - display-flex
                - justify-content-space-between
                - flex-direction-column
            slots:
              default:
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      font-size: 120%
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Вечерний полив
                      - component: oh-toggle
                        config:
                          item: IrrigationStartAtSunrise
                          color: green
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      font-size: 120%
                  slots:
                    default:
                      - component: f7-row
                        config: {}
                        slots:
                          default:
                            - component: Label
                              config:
                                text: "Закат:"
                                style:
                                  margin-right: 10px
                            - component: Label
                              config:
                                text: =items.Zakat.displayState
                      - component: f7-row
                        config: {}
                        slots:
                          default:
                            - component: Label
                              config:
                                text: Время до заката
                                style:
                                  margin-right: 10px
                            - component: oh-link
                              config:
                                color: green
                                text: =items.IrrigationHoursBeforeSundown.state
                                action: popover
                                popoverOpen: .timerpopover
                              slots:
                                default:
                                  - component: f7-popover
                                    config:
                                      class:
                                        - timerpopover
                                    slots:
                                      default:
                                        - component: oh-stepper-card
                                          config:
                                            color: green
                                            item: IrrigationHoursBeforeSundown
                                            title: Минут после рассвета
                                            min: 0
                                            max: 180
                                            step: 10
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      margin-bottom: 15px
                      font-size: 120%
                  slots:
                    default:
                      - component: f7-row
                        config: {}
                        slots:
                          default:
                            - component: Label
                              config:
                                text: "Время начала полива:"
                                style:
                                  margin-right: 10px
                            - component: Label
                              config:
                                text: =items.IrrigationStartTime3.displayState
                - component: f7-row
                  config:
                    style:
                      padding: 5px
                      margin: 0px
                      font-size: 120%
                      border: 1px solid green
                      border-radius: var(--f7-card-expandable-border-radius)
                      display: inline
                  slots:
                    default:
                      - component: oh-repeater
                        config:
                          fragment: false
                          for: item
                          sourceType: itemsInGroup
                          groupItem: GroupIrrigationValves
                        slots:
                          default:
                            - component: f7-row
                              config:
                                style:
                                  margin-top: 5px
                                  display: flex
                                  justify-content: flex-start
                                  align-items: center
                              slots:
                                default:
                                  - component: oh-link
                                    config:
                                      item: =loop.item.name
                                      iconMaterial: opacity
                                      color: '=items[loop.item.name].state == "ON" ? "green" : "gray"'
                                      iconSize: 25px
                                      action: popover
                                      actionModal: widget:popup_manual_start
                                      actionModalConfig:
                                        item: =loop.item.name
                                  - component: oh-button
                                    config:
                                      text: =loop.item.label
                                      action: analyzer
                                      actionAnalyzerItems: =[loop.item.name]
                                      style:
                                        --f7-button-hover-bg-color: green
                                        color: white
                                        font-size: 20px
                                        margin-left: 10px
                                  - component: oh-stepper
                                    config:
                                      item: =(loop.item.name + 'Time2')
                                      title: Время полива
                                      min: 0
                                      max: 180
                                      step: 1
                                      round: true
                                      color: green
                                      autorepeat: true
                                      autorepeat-dynamic: true
                                      style:
                                        margin-left: auto
                                  - component: oh-button
                                    config:
                                      iconF7: gear_alt_fill
                                      color: '=vars[loop.item.name + "S"] === true ? "green" : "gray"'
                                      action: variable
                                      actionVariable: =(loop.item.name + 'S')
                                      actionVariableValue: =!(vars[loop.item.name + 'S'] === true)
                            - component: f7-segmented
                              config:
                                strong: true
                                small: true
                                outline: true
                                color: green
                                class:
                                  - segmented-round
                                style:
                                  --f7-segmented-strong-padding: 0px
                                  --f7-segmented-strong-between-buttons: 5px
                                  --f7-segmented-strong-button-font-weight: 300
                                  --f7-segmented-strong-bg-color: transparent
                                  --f7-segmented-strong-button-hover-bg-color: rgba(0, 255, 0, 0.1)
                                  --f7-segmented-strong-button-active-bg-color: transparent
                                  margin-top: 5px
                              slots:
                                default:
                                  - component: oh-repeater
                                    config:
                                      fragment: true
                                      for: day
                                      sourceType: array
                                      in:
                                        - name: 1
                                          label: Пн
                                        - name: 2
                                          label: Вт
                                        - name: 3
                                          label: Ср
                                        - name: 4
                                          label: Чт
                                        - name: 5
                                          label: Пт
                                        - name: 6
                                          label: Сб
                                        - name: 7
                                          label: Вс
                                    slots:
                                      default:
                                        - component: oh-button
                                          config:
                                            text: =loop.day.label
                                            action: toggle
                                            actionCommand: ON
                                            actionCommandAlt: OFF
                                            actionItem: =(loop.item.name + 'E' + loop.day.name)
                                            fill: "=(items[loop.item.name + 'E' + loop.day.name].state === 'ON' ? true : false)"
                                            style:
                                              font-weight: 400
                            - component: f7-row
                              config:
                                visible: =vars[loop.item.name + 'S'] === true
                                style:
                                  margin: 10px
                                  display: flex
                                  justify-content: space-around
                                  align-items: center
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      style:
                                        align-items: center
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: Коррекция
                                            style:
                                              font-size: 12px
                                  - component: f7-row
                                    config:
                                      style:
                                        align-items: center
                                        justify-content: space-around
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: по температуре
                                            style:
                                              font-size: 12px
                                              margin-right: 10px
                                        - component: oh-toggle
                                          config:
                                            color: green
                                            item: =(loop.item.name + 'TimeCorrection')
                                        - component: Label
                                          config:
                                            text: по осадкам
                                            style:
                                              font-size: 12px
                                              margin: 0px 10px
                                        - component: oh-toggle
                                          config:
                                            color: green
                                            item: =(loop.item.name + 'RainCorrection')
          - component: f7-block
            config:
              visible: =(vars.select == 3)
              style:
                animation: f7-fade-in 300ms
                width: 99%
                padding: 0px
                margin: 5px
                margin-right: 5px
              class:
                - display-flex
                - justify-content-space-between
                - flex-direction-column
            slots:
              default:
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                      width: 99%
                      font-size: 120%
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Дневной полив
                      - component: oh-toggle
                        config:
                          item: IrrigationStartAtSpecificHour
                          color: green
                - component: f7-row
                  config:
                    style:
                      margin: 5px
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Время начала полива
                      - component: oh-link
                        config:
                          color: green
                          text: =items.IrrigationStartTime2.displayState
                          action: popup
                          actionModal: widget:timepicker_1
                          actionModalConfig:
                            item: IrrigationStartTime2
                - component: f7-row
                  config:
                    style:
                      padding: 5px
                      margin: 0px
                      font-size: 120%
                      border: 1px solid green
                      border-radius: var(--f7-card-expandable-border-radius)
                      display: inline
                  slots:
                    default:
                      - component: oh-repeater
                        config:
                          fragment: false
                          for: item
                          sourceType: itemsInGroup
                          groupItem: GroupIrrigationValves
                        slots:
                          default:
                            - component: f7-row
                              config:
                                style:
                                  margin-top: 5px
                                  display: flex
                                  justify-content: flex-start
                                  align-items: center
                              slots:
                                default:
                                  - component: oh-link
                                    config:
                                      item: =loop.item.name
                                      iconMaterial: opacity
                                      color: '=items[loop.item.name].state == "ON" ? "green" : "gray"'
                                      iconSize: 25px
                                      action: popover
                                      actionModal: widget:popup_manual_start
                                      actionModalConfig:
                                        item: =loop.item.name
                                  - component: oh-button
                                    config:
                                      text: =loop.item.label
                                      action: analyzer
                                      actionAnalyzerItems: =[loop.item.name]
                                      style:
                                        --f7-button-hover-bg-color: green
                                        color: white
                                        font-size: 20px
                                        margin-left: 10px
                                  - component: oh-stepper
                                    config:
                                      item: =(loop.item.name + 'Time')
                                      title: Время полива
                                      min: 0
                                      max: 180
                                      step: 1
                                      round: true
                                      color: green
                                      autorepeat: true
                                      autorepeat-dynamic: true
                                      style:
                                        margin-left: auto
                                  - component: oh-button
                                    config:
                                      iconF7: gear_alt_fill
                                      color: '=vars[loop.item.name + "S"] === true ? "green" : "gray"'
                                      action: variable
                                      actionVariable: =(loop.item.name + 'S')
                                      actionVariableValue: =!(vars[loop.item.name + 'S'] === true)
                            - component: f7-segmented
                              config:
                                strong: true
                                small: true
                                outline: true
                                color: green
                                class:
                                  - segmented-round
                                style:
                                  --f7-segmented-strong-padding: 0px
                                  --f7-segmented-strong-between-buttons: 5px
                                  --f7-segmented-strong-button-font-weight: 300
                                  --f7-segmented-strong-bg-color: transparent
                                  --f7-segmented-strong-button-hover-bg-color: rgba(0, 255, 0, 0.1)
                                  --f7-segmented-strong-button-active-bg-color: transparent
                                  margin-top: 5px
                              slots:
                                default:
                                  - component: oh-repeater
                                    config:
                                      fragment: true
                                      for: day
                                      sourceType: array
                                      in:
                                        - name: 1
                                          label: Пн
                                        - name: 2
                                          label: Вт
                                        - name: 3
                                          label: Ср
                                        - name: 4
                                          label: Чт
                                        - name: 5
                                          label: Пт
                                        - name: 6
                                          label: Сб
                                        - name: 7
                                          label: Вс
                                    slots:
                                      default:
                                        - component: oh-button
                                          config:
                                            text: =loop.day.label
                                            action: toggle
                                            actionCommand: ON
                                            actionCommandAlt: OFF
                                            actionItem: =(loop.item.name + 'D' + loop.day.name)
                                            fill: "=(items[loop.item.name + 'D' + loop.day.name].state === 'ON' ? true : false)"
                                            style:
                                              font-weight: 400
                            - component: f7-row
                              config:
                                visible: =vars[loop.item.name + 'S'] === true
                                style:
                                  margin: 10px
                                  display: flex
                                  justify-content: space-around
                                  align-items: center
                              slots:
                                default:
                                  - component: f7-row
                                    config:
                                      style:
                                        align-items: center
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: Коррекция
                                            style:
                                              font-size: 12px
                                  - component: f7-row
                                    config:
                                      style:
                                        align-items: center
                                        justify-content: space-between
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: по температуре
                                            style:
                                              font-size: 12px
                                              margin-right: 10px
                                        - component: oh-toggle
                                          config:
                                            color: green
                                            item: =(loop.item.name + 'TimeCorrection')
                                        - component: Label
                                          config:
                                            text: по осадкам
                                            style:
                                              font-size: 12px
                                              margin: 0px 10px
                                        - component: oh-toggle
                                          config:
                                            color: green
                                            item: =(loop.item.name + 'RainCorrection')

uid: PolivSettings
tags: []
props:
  parameters: []
  parameterGroups: []
timestamp: Aug 15, 2021, 12:07:47 AM
component: f7-card
config:
  style:
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.1)
    height: auto
    margin: 5px
    line-height: 1
    font-family: sans-serif
slots:
  default:
    - component: f7-col
      config:
        style:
          width: 100%
      slots:
        default:
          - component: oh-chart
            config:
              chartType: ""
              period: D
              label: Температуры
            slots:
              title:
                - component: oh-chart-title
                  config:
                    text: Температуры
                    top: 0px
                    middle: 0px
              grid:
                - component: oh-chart-grid
                  config:
                    width: 85%
                    includeLabels: true
              xAxis:
                - component: oh-time-axis
                  config:
                    gridIndex: 0
              yAxis:
                - component: oh-value-axis
                  config:
                    gridIndex: 0
                    name: °C
              series:
                - component: oh-time-series
                  config:
                    name: Температура улица
                    gridIndex: 1
                    xAxisIndex: 0
                    yAxisIndex: 0
                    type: line
                    item: TemperatureUlica
                    smooth: true
                    markPoint:
                      data:
                        - type: max
                          name: Максимальная
                        - type: min
                          name: Минимальная
                        - type: average
                          name: Средняя
                - component: oh-time-series
                  config:
                    name: Температура теплица
                    gridIndex: 1
                    xAxisIndex: 0
                    yAxisIndex: 0
                    type: line
                    item: TemperatureTeplica
                    smooth: true
                    markPoint:
                      data:
                        - type: max
                          name: Максимальная
                        - type: min
                          name: Минимальная
                        - type: average
                          name: Средняя
                    areaStyle:
                      color:
                        type: linear
                        x: 0
                        y: 0
                        x2: 0
                        y2: 1
                        colorStops:
                          - offset: 0
                            color: rgb(70, 123, 168, 0.0)
                          - offset: 0.5
                            color: rgb(70, 123, 168, 0.5)
                          - offset: 1
                            color: rgb(70, 123, 168, 1.0)
              tooltip:
                - component: oh-chart-tooltip
                  config:
                    confine: true
                    smartFormatter: true
              visualMap:
                - component: oh-chart-visualmap
                  config:
                    show: false
                    presetPalette: greenred
                    orient: vertical
                    type: continuous
    - component: f7-row
      config:
        style:
          padding: 10px
          align-items: center
          justify-content: space-around
      slots:
        default:
          - component: f7-col
            slots:
              default:
                - component: f7-row
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Осадки сегодня
                      - component: Label
                        config:
                          text: =items.SumRainLast24h.state
          - component: f7-col
            slots:
              default:
                - component: f7-row
                  slots:
                    default:
                      - component: Label
                        config:
                          text: Осадки завтра
                      - component: Label
                        config:
                          text: =items.SumRainNext24h.state
    - component: f7-row
      config:
        style:
          margin: 10px
          align-items: center
          justify-content: space-around
      slots:
        default:
          - component: Label
            config:
              text: Сумма осадков
          - component: Label
            config:
              text: =items.irrRainSum.state
    - component: f7-row
      config:
        style:
          margin: 10px
          align-items: center
          justify-content: space-around
      slots:
        default:
          - component: Label
            config:
              text: Лимит осадков
          - component: oh-stepper
            config:
              item: MaxAllowedRain
              round: true
              color: green
              step: 0.1
              style:
                margin-left: auto
                margin-right: 15px
          - component: Label
            config:
              text: мм
    - component: f7-row
      config:
        style:
          margin: 10px
          align-items: center
          justify-content: space-around
      slots:
        default:
          - component: Label
            config:
              text: Температурный коэффициент
          - component: oh-stepper
            config:
              item: IrrigationDurationCoefficientFactor
              round: true
              color: green
              step: 0.1
              style:
                margin-left: auto
                margin-right: 36px
    - component: f7-row
      config:
        style:
          margin: 10px
          align-items: center
          justify-content: flex-start
      slots:
        default:
          - component: Label
            config:
              text: Максимальная скорость ветра
          - component: oh-stepper
            config:
              round: true
              color: green
              item: MaxAllowedWindSpeed
              style:
                margin-left: auto
                margin-right: 10px
          - component: Label
            config:
              text: км/ч

uid: popup_manual_start
tags: []
props:
  parameters:
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Aug 15, 2021, 2:27:04 AM
component: f7-card
config:
  title: Запуск вручную
  style:
    maxwidth: 100px
    height: auto
slots:
  default:
    - component: f7-block
      config:
        style:
          height: auto
          width: 80px
      slots:
        default:
          - component: oh-button
            config:
              text: =items[props.item].state
              color: green
              action: toggle
              actionCommand: ON
              actionCommandAlt: OFF
              actionItem: =props.item

you forgot to add irrigation.map :slight_smile:

@TheJetsetter thanks for the idea of using openweathermap’s rain measurement. My system is based on Tasmota and the schedule is stored in the device, that way it will still run independently even when openhab is down. Openhab’s role is to send a “rain delay” to tasmota to stop it from running its regular schedule for the next X hours.

Great! But it’s missing irrigation.map called in items …

Just remove irrigation.map from items, it’s just for display in UI. Or create manually irrigation.map with something like ON=Open OFF=Close NULL=Not Available.

Wauw, this was exactly what I needed to kick-start my irrigation program. Have been doubting a while to just buy an irrigation computer or program something myself. Stumbled upon YAWS and loved it.
I’ve extended it to 7 zones which are grouped in 3 groups. Created 3 individual times and incorporated a weather dependent option copied from the Zimmerman principle in OpenSprikler. Oh and I’ve made a link with Apple HomeKit so I can set the irrigation times using HomeKit on my iPhone/iPad etc.
Not completely satisfied with how the groups are related to the individual zones as the remaining time for the zones isn’t updated if a group is used for irrigation. Need to figure something out at some point, but for now it’s working.
Also I created a “every other day” function where the minimum time between 2 starts can be set (for each program separatey).
And last, I translated the user labels to Dutch as I’m from The Netherlands, but left all internals to English so should be readable for most.
Let me know if you have questions or find better ways to accomplish things.

ps. not all zones are linked yet to a KNX actuator as I don’t have more outputs available currently. Work in Progress…

// items for YAWS - Yet Another Watering Solution https://community.openhab.org/t/yaws-yet-another-watering-solution/101945

// settings group
Group Group_Irrigation_Settings

// program 1 group
Group Group_Program1_Settings

// program 2 group
Group Group_Program2_Settings

// program 3 group
Group Group_Program3_Settings

// active program
Number ActiveProgram "Actieve programma [%d]" (everyChange_Startup)

// irrigation lock
Switch IrrigationLock "Programma" (Group_Irrigation_Settings, everyChange_Startup)

// protection against too long watering, 2h default
Switch IrrigationTimerMax "Max irrigation time [%s]" (everyChange_Startup) { expire = "6h,command=OFF" }

// all valves group
Group:Switch:OR(ON,OFF) GroupIrrigationValves "Irrigation valves [%s]"
Group:Number:SUM GroupIrrigationTimes "Total irrigation time [%d s]"
Group:Number:SUM GroupRemainingTime "Total remaining irrigation time [%d s]"

// groups of valves to have multiple valves open simultaniously
//Group:Switch:AND(ON,OFF) IrrigationValveZone8 "Sproeiers achtertuin"
//Group:Switch:AND(ON,OFF) IrrigationValveZone9 "Sproeiers voortuin"
//Group:Switch:AND(ON,OFF) IrrigationValveZone10 "Alle druppelzones"

// cascading valves - current zone
String IrrigationCurrentValve "Current irrigation zone [%s]"

// integration HomeKit items//
// Group 1, dripline border backyard
Group                           gValve1                                 "Druppel border"                                                                                                                          {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone1                    "Zone 1 active"                                                     (gValve1, GroupIrrigationValves, IrrigationValveZone10)               {homekit = "Valve.ActiveStatus, Valve.InUseStatus"}
Number                          IrrigationValveZone1Time                "Zone 1: Druppel border achtertuin [%d s]"                          (gValve1, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone1                      "Valve 1 remaining duration"                                        (gValve1, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 2, sprinklers backside backyard
Group                           gValve2                                 "Sproeier 1 AT"                                                                                                                           {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone2                    "Zone 2 active"                                                     (gValve2, GroupIrrigationValves, IrrigationValveZone8)                {homekit = "Valve.ActiveStatus, Valve.InUseStatus", channel="knx:device:bridge:generic:Valve2"}
Number                          IrrigationValveZone2Time                "Zone 2: Sproeier 1 achtertuin [%d s]"                              (gValve2, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone2                      "Valve 2 remaining duration"                                        (gValve2, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 3, dripline trees backyard
Group                           gValve3                                 "Druppel bomen AT"                                                                                                                        {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone3                    "Zone 3 active"                                                     (gValve3, GroupIrrigationValves, IrrigationValveZone10)               {homekit = "Valve.ActiveStatus, Valve.InUseStatus"}
Number                          IrrigationValveZone3Time                "Zone 3: Druppel bomen achtertuin [%d s]"                           (gValve3, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone3                      "Valve 3 remaining duration"                                        (gValve3, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 4, sprinklers frontside backyard
Group                           gValve4                                 "Sproeier 2 AT"                                                                                                                           {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone4                    "Zone 4 active"                                                     (gValve4, GroupIrrigationValves, IrrigationValveZone8)                {homekit = "Valve.ActiveStatus, Valve.InUseStatus", channel="knx:device:bridge:generic:Valve4"}
Number                          IrrigationValveZone4Time                "Zone 4: Sproeier 2 achtertuin [%d s]"                              (gValve4, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone4                      "Valve 4 remaining duration"                                        (gValve4, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 5, sprinkler house side frontyard
Group                           gValve5                                 "Sproeier 1 VT"                                                                                                                           {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone5                    "Zone 5 active"                                                     (gValve5, GroupIrrigationValves, IrrigationValveZone9)                {homekit = "Valve.ActiveStatus, Valve.InUseStatus", channel="knx:device:bridge:generic:Valve5"}
Number                          IrrigationValveZone5Time                "Zone 5: Sproeier 1 voortuin [%d s]"                                (gValve5, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone5                      "Valve 5 remaining duration"                                        (gValve5, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 6, dripline trees frontyard
Group                           gValve6                                 "Druppel bomen VT"                                                                                                                        {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone6                    "Zone 6 active"                                                     (gValve6, GroupIrrigationValves, IrrigationValveZone10)               {homekit = "Valve.ActiveStatus, Valve.InUseStatus"}
Number                          IrrigationValveZone6Time                "Zone 6: Druppel bomen voortuin [%d s]"                             (gValve6, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone6                      "Valve 6 remaining duration"                                        (gValve6, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 7, sprinkler road side frontyard
Group                           gValve7                                 "Sproeier 2 VT"                                                                                                                           {homekit="Valve"  [homekitValveType="Irrigation"]}
Switch                          IrrigationValveZone7                    "Zone 7 active"                                                     (gValve7, GroupIrrigationValves, IrrigationValveZone9)                {homekit = "Valve.ActiveStatus, Valve.InUseStatus", channel="knx:device:bridge:generic:Valve7"}
Number                          IrrigationValveZone7Time                "Zone 7: Sproeier 2 voortuin [%d s]"                                (gValve7, GroupIrrigationTimes, Group_Irrigation_Settings)            {homekit = "Valve.Duration"}
Number                          RemainingTimeZone7                      "Valve 7 remaining duration"                                        (gValve7, GroupRemainingTime)                                         {homekit = "Valve.RemainingDuration"}

// Group 8, all sprinkler valves backyard
Group:Switch:AND(ON,OFF)        IrrigationValveZone8                    "Sproeiers achtertuin"                                              (GroupIrrigationValves)                                      
Number                          IrrigationValveZone8Time                "Sproeiers achtertuin [%d s]"                                       (GroupIrrigationTimes, Group_Irrigation_Settings)            
Number                          RemainingTimeZone8                      "Valve 8 remaining duration"                                        (GroupRemainingTime)                                         
/////

// Group 9, all sprinkler valves frontyard
Group:Switch:AND(ON,OFF)        IrrigationValveZone9                    "Sproeiers voortuin"                                                (GroupIrrigationValves)                                      
Number                          IrrigationValveZone9Time                "Sproeiers voortuin [%d s]"                                         (GroupIrrigationTimes, Group_Irrigation_Settings)            
Number                          RemainingTimeZone9                      "Valve 9 remaining duration"                                        (GroupRemainingTime)                                         
/////

// Group 10, all sprinkler valves backyard
Group:Switch:AND(ON,OFF)        IrrigationValveZone10                    "Druppelslangen"                                                   (GroupIrrigationValves)                                     
Number                          IrrigationValveZone10Time                "Druppelslangen [%d s]"                                            (GroupIrrigationTimes, Group_Irrigation_Settings)           
Number                          RemainingTimeZone10                      "Valve 10 remaining duration"                                      (GroupRemainingTime)                                        
/////

// irrigation variables
Number                          IrrigationDurationCoefficientFactor     "Correction factor"
Number:Time                     IrrigationSectionRemainingTime          "Resterende tijd huidige zone [%d s]"

// switch - whether to start at a particular hour or before the sunrise
Switch                          Program1IrrigationStartAtSpecificHour   "Start op vast tijdstip"                                            (Group_Program1_Settings, everyChange_Startup)
Number                          Program1IrrigationStartTime             "Starttijd beregening"                                              (Group_Program1_Settings, everyChange_Startup)
Number                          Program1IrrigationHoursBeforeSunrise    "Start beregening voor zonsopkomst [%d u]"                          (Group_Program1_Settings, everyChange_Startup)
Switch                          Program1WeatherAdjust                   "Weersafhankelijke aanpassing"                                      (Group_Program1_Settings, everyChange_Startup)

Switch                          Program2IrrigationStartAtSpecificHour   "Start op vast tijdstip"                                            (Group_Program2_Settings)
Number                          Program2IrrigationStartTime             "Starttijd beregening"                                              (Group_Program2_Settings)
Number                          Program2IrrigationHoursBeforeSunrise    "Start beregening voor zonsopkomst [%d u]"                          (Group_Program2_Settings)
Switch                          Program2WeatherAdjust                   "Weersafhankelijke aanpassing"                                      (Group_Program2_Settings)

Switch                          Program3IrrigationStartAtSpecificHour   "Start op vast tijdstip"                                            (Group_Program3_Settings)
Number                          Program3IrrigationStartTime             "Starttijd beregening"                                              (Group_Program3_Settings)
Number                          Program3IrrigationHoursBeforeSunrise    "Start beregening voor zonsopkomst [%d u]"                          (Group_Program3_Settings)
Switch                          Program3WeatherAdjust                   "Weersafhankelijke aanpassing"                                      (Group_Program3_Settings)

// irrigation week days
Group:Switch:OR(ON,OFF)         Group_Program1Days                      "Programma 1 [%s]"
Switch                          Program1SpecificDays                    "Specifieke dagen"                                                  (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay1                  "Maandag"                                                           (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay2                  "Dinsdag"                                                           (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay3                  "Woensdag"                                                          (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay4                  "Donderdag"                                                         (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay5                  "Vrijdag"                                                           (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay6                  "Zaterdag"                                                          (Group_Program1Days, everyChange_Startup)
Switch                          Program1IrrigationDay7                  "Zondag"                                                            (Group_Program1Days, everyChange_Startup)
DateTime                        Program1LastRun                         "Programma 1 laatste start [%1$td-%1$tm-%1$tY %1$tH:%1$tM:%1$tS]"   (everyChange_Startup)
Switch                          Program1EOD                             "Programma 1 laatste 24u actief"                                    (everyChange_Startup)
Number                          Program1HoursSinceLastRun               "Aantal uur sinds laatste start [%d u]"                             (everyChange_Startup)
Number                          Program1MinHoursBetween                 "Minimaal aantal uur tussen 2 starts [%d u]"                        (everyChange_Startup)

Group:Switch:OR(ON,OFF)         Group_Program2Days                      "Programma 2 [%s]"
Switch                          Program2SpecificDays                    "Specifieke dagen"                                                  (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay1                  "Maandag"                                                           (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay2                  "Dinsdag"                                                           (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay3                  "Woensdag"                                                          (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay4                  "Donderdag"                                                         (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay5                  "Vrijdag"                                                           (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay6                  "Zaterdag"                                                          (Group_Program2Days, everyChange_Startup)
Switch                          Program2IrrigationDay7                  "Zondag"                                                            (Group_Program2Days, everyChange_Startup)
DateTime                        Program2LastRun                         "Programma 2 laatste start [%1$td-%1$tm-%1$tY %1$tH:%1$tM:%1$tS]"   (everyChange_Startup)
Switch                          Program2EOD                             "Programma 2 laatste 24u  actief"                                   (everyChange_Startup)
Number                          Program2HoursSinceLastRun               "Aantal uur sinds laatste start [%d u]"                             (everyChange_Startup)
Number                          Program2MinHoursBetween                 "Minimaal aantal uur tussen 2 starts [%d u]"                        (everyChange_Startup)

Group:Switch:OR(ON,OFF)         Group_Program3Days                      "Programma 3 [%s]"
Switch                          Program3SpecificDays                    "Specifieke dagen"                                                  (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay1                  "Maandag"                                                           (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay2                  "Dinsdag"                                                           (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay3                  "Woensdag"                                                          (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay4                  "Donderdag"                                                         (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay5                  "Vrijdag"                                                           (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay6                  "Zaterdag"                                                          (Group_Program3Days, everyChange_Startup)
Switch                          Program3IrrigationDay7                  "Zondag"                                                            (Group_Program3Days, everyChange_Startup)
DateTime                        Program3LastRun                         "Programma 3 laatste start [%1$td-%1$tm-%1$tY %1$tH:%1$tM:%1$tS]"   (everyChange_Startup)
Switch                          Program3EOD                             "Programma 3 laatste 24u actief"                                    (everyChange_Startup)
Number                          Program3HoursSinceLastRun               "Aantal uur sinds laatste start [%d u]"                             (everyChange_Startup)
Number                          Program3MinHoursBetween                 "Minimaal aantal uur tussen 2 starts [%d u]"                        (everyChange_Startup)

Group:Switch:OR(ON,OFF)         Group_Program_Zones                     "Programma zones [%s]"
Switch                          Program1Zone1                           "Zone 1: Druppel border achtertuin"                                 (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone2                           "Zone 2: Sproeier 1 achtertuin"                                     (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone3                           "Zone 3: Druppel bomen achtertuin"                                  (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone4                           "Zone 4: Sproeier 2 achtertuin"                                     (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone5                           "Zone 5: Sproeier 1 voortuin"                                       (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone6                           "Zone 6: Druppel bomen voortuin"                                    (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone7                           "Zone 7: Sproeier 2 voortuin"                                       (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone8                           "Sproeiers achtertuin"                                              (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone9                           "Sproeiers voortuin"                                                (Group_Program_Zones, everyChange_Startup)
Switch                          Program1Zone10                          "Druppelslangen"                                                    (Group_Program_Zones, everyChange_Startup)

Switch                          Program2Zone1                           "Zone 1: Druppel border achtertuin"                                 (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone2                           "Zone 2: Sproeier 1 achtertuin"                                     (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone3                           "Zone 3: Druppel bomen achtertuin"                                  (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone4                           "Zone 4: Sproeier 2 achtertuin"                                     (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone5                           "Zone 5: Sproeier 1 voortuin"                                       (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone6                           "Zone 6: Druppel bomen voortuin"                                    (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone7                           "Zone 7: Sproeier 2 voortuin"                                       (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone8                           "Sproeiers achtertuin"                                              (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone9                           "Sproeiers voortuin"                                                (Group_Program_Zones, everyChange_Startup)
Switch                          Program2Zone10                          "Druppelslangen"                                                    (Group_Program_Zones, everyChange_Startup)

Switch                          Program3Zone1                           "Zone 1: Druppel border achtertuin"                                 (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone2                           "Zone 2: Sproeier 1 achtertuin"                                     (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone3                           "Zone 3: Druppel bomen achtertuin"                                  (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone4                           "Zone 4: Sproeier 2 achtertuin"                                     (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone5                           "Zone 5: Sproeier 1 voortuin"                                       (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone6                           "Zone 6: Druppel bomen voortuin"                                    (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone7                           "Zone 7: Sproeier 2 voortuin"                                       (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone8                           "Sproeiers achtertuin"                                              (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone9                           "Sproeiers voortuin"                                                (Group_Program_Zones, everyChange_Startup)
Switch                          Program3Zone10                          "Druppelslangen"                                                    (Group_Program_Zones, everyChange_Startup)

Number:Length                   SumRainLast24h                          "Neerslag, laatste 24u [%.2f mm]"                         
Number:Length                   SumRainNext24h                          "Neerslag, voorspelling 24u [%.2f mm]"                    

//Number:Speed                    avgWindLast2h                           "Windsnelheid afgelopen 2u [%d km/h]"
//Number:Speed                    avgWindNext3h                           "Windsnelheid voorspelling 3u [%d km/h]"

Number:Speed                    MaxAllowedWindSpeed                     "Max allowed windspeed [%d km/h]"                                   (Group_Irrigation_Settings, everyChange_Startup)
Number:Length                   MaxAllowedRain                          "Max allowed rainfall sum [%.1f mm]"                                (Group_Irrigation_Settings, everyChange_Startup)

Number                          TempBaseLine                            "Temperature base line"                                             (Group_Irrigation_Settings, everyChange_Startup)
Number                          TempAdjustment                          "Percentage change / 1 deg change"                                  (Group_Irrigation_Settings, everyChange_Startup)
Number                          HumidityBaseLine                        "Humidity base line"                                                (Group_Irrigation_Settings, everyChange_Startup)
Number                          RainAdjustment                          "Percentage change / 1mm rain"                                      (Group_Irrigation_Settings, everyChange_Startup)
Number                          CoefficientPercentage                   "Weersafhankelijke optimalisatie 0-200% [%.1f %%]"
Number:Temperature              AverageTemperature24h                   "Gemiddelde temperatuur afgelopen 24u [%.1f %unit%]"
Number                          AverageHumidity24h                      "Gemiddelde luchtvochtigheid afgelopen 24u [%.0f %%]"

DateTime                        LastStart                               "Laatste cyclus start [%1$td-%1$tm-%1$tY %1$tH:%1$tM:%1$tS]"
DateTime                        LastEnd                                 "Laatste cyclus einde [%1$td-%1$tm-%1$tY %1$tH:%1$tM:%1$tS]"
// rules for YAWS - Yet Another Watering Solution https://community.openhab.org/t/yaws-yet-another-watering-solution/101945

/*
 * Irrigation rules
 */

//import org.eclipse.smarthome.model.script.ScriptServiceUtil
import org.openhab.core.model.script.ScriptServiceUtil
import java.time.temporal.ChronoUnit

val logName = "Irrigation"
var Timer irrigationTimer = null

// Total rain fall over past 24h and forecasted 24h
var Number RainTotal = 0

// Wind speed averages to limit spray of lawn sprinklers
var Number avgWindLast2h = 0
var Number avgWindNext3h = 0

// Average temperature and humidity over past 24h
var Number avgTemperatureLast24h = 20
var Number avgHumidity24h = 60

// watering time correction factor - based on the average temperature, humidity and rain
var Number coefficientFactor = 1

rule "Irrigation - system start"
when
	System started
then
	// initiall default settings

	if (SumRainLast24h.state == NULL) 
		SumRainLast24h.sendCommand(0)

	if (SumRainNext24h.state == NULL) 
		SumRainNext24h.sendCommand(0)

	if (MaxAllowedWindSpeed.state == NULL)
		MaxAllowedWindSpeed.sendCommand(35)

	if (MaxAllowedRain.state == NULL)
		MaxAllowedRain.sendCommand(3)

	if (TempBaseLine.state == NULL)
		TempBaseLine.sendCommand(20)

	if (TempAdjustment.state == NULL)
		TempAdjustment.sendCommand(5)

	if (HumidityBaseLine.state == NULL)
		HumidityBaseLine.sendCommand(60)

	if (RainAdjustment.state == NULL)
		RainAdjustment.sendCommand(8)

	if (ActiveProgram.state == NULL)
		ActiveProgram.sendCommand(0)

	// Init program 1 settings
	if (Program1IrrigationHoursBeforeSunrise.state == NULL)
		Program1IrrigationHoursBeforeSunrise.sendCommand(1)

	if (Program1IrrigationStartAtSpecificHour.state == NULL)
		Program1IrrigationStartAtSpecificHour.sendCommand(OFF)

	if (Program1SpecificDays.state == NULL)
		Program1SpecificDays.sendCommand(OFF)

	if (Program1IrrigationStartTime.state == NULL)	
		Program1IrrigationStartTime.sendCommand(5)

	if (Program1WeatherAdjust.state == NULL)
		Program1WeatherAdjust.sendCommand(OFF)

	if (Program1EOD.state == NULL)
		Program1EOD.sendCommand(OFF)

	if (Program1HoursSinceLastRun.state == NULL)
		Program1HoursSinceLastRun.postUpdate(0)

	if (Program1MinHoursBetween.state == NULL)
		Program1MinHoursBetween.postUpdate(36)

	// Init all irrigation days to OFF if not set (null)
	Group_Program1Days.members.filter[day | day.state == NULL].forEach[day | day.sendCommand(OFF)]

	// Init program 2 settings
	if (Program2IrrigationHoursBeforeSunrise.state == NULL)
		Program2IrrigationHoursBeforeSunrise.sendCommand(1)

	if (Program2IrrigationStartAtSpecificHour.state == NULL)
		Program2IrrigationStartAtSpecificHour.sendCommand(OFF)

	if (Program2SpecificDays.state == NULL)
		Program2SpecificDays.sendCommand(OFF)

	if (Program2IrrigationStartTime.state == NULL)	
		Program2IrrigationStartTime.sendCommand(5)

	if (Program2WeatherAdjust.state == NULL)
		Program2WeatherAdjust.sendCommand(OFF)

	if (Program2EOD.state == NULL)
		Program2EOD.sendCommand(OFF)

	if (Program2HoursSinceLastRun.state == NULL)
		Program2HoursSinceLastRun.postUpdate(0)

	if (Program2MinHoursBetween.state == NULL)
		Program2MinHoursBetween.postUpdate(36)

	// Init all irrigation days to OFF if not set (null)
	Group_Program2Days.members.filter[day | day.state == NULL].forEach[day | day.sendCommand(OFF)]

	// Init program 3 settings
	if (Program3IrrigationHoursBeforeSunrise.state == NULL)
		Program3IrrigationHoursBeforeSunrise.sendCommand(1)

	if (Program3IrrigationStartAtSpecificHour.state == NULL)
		Program3IrrigationStartAtSpecificHour.sendCommand(OFF)

	if (Program3SpecificDays.state == NULL)
		Program3SpecificDays.sendCommand(OFF)

	if (Program3IrrigationStartTime.state == NULL)	
		Program3IrrigationStartTime.sendCommand(5)

	if (Program3WeatherAdjust.state == NULL)
		Program3WeatherAdjust.sendCommand(OFF)

	if (Program3EOD.state == NULL)
		Program3EOD.sendCommand(OFF)

	if (Program3HoursSinceLastRun.state == NULL)
		Program3HoursSinceLastRun.postUpdate(0)

	if (Program3MinHoursBetween.state == NULL)
		Program3MinHoursBetween.postUpdate(36)

	// Init all irrigation days to OFF if not set (null)
	Group_Program3Days.members.filter[day | day.state == NULL].forEach[day | day.sendCommand(OFF)]

	// Init all zones to OFF if not set (null)
	Group_Program_Zones.members.filter[zone | zone.state == NULL].forEach[zone | zone.sendCommand(OFF)]

	// Init default zone irrigation times
	GroupIrrigationTimes.members.filter[time | time.state == NULL].forEach[time | time.sendCommand(60)]

	// Init irrigation remaining times to 0
	GroupRemainingTime.members.filter[time | time.state == NULL].forEach[time | time.sendCommand(0)]

	// close all valves
	GroupIrrigationValves.members.filter[valve | valve.state != OFF].forEach[valve | valve.sendCommand(OFF)]

	IrrigationCurrentValve.postUpdate(OFF)

	logInfo(logName, "init all default settings")

end

rule "Irrigation - Generic calculations"
when
    Time cron "0 * * ? * *" // every minute
then
	try {
		logDebug(logName, "Calculating generic irrigation parameters")

		// calculate hours after last run
		Program1HoursSinceLastRun.postUpdate(ChronoUnit.HOURS.between((Program1LastRun.state as DateTimeType).zonedDateTime, (new DateTimeType()).zonedDateTime))
		Program2HoursSinceLastRun.postUpdate(ChronoUnit.HOURS.between((Program2LastRun.state as DateTimeType).zonedDateTime, (new DateTimeType()).zonedDateTime))
		Program3HoursSinceLastRun.postUpdate(ChronoUnit.HOURS.between((Program3LastRun.state as DateTimeType).zonedDateTime, (new DateTimeType()).zonedDateTime))

		// calculate wind speed
		avgWindLast2h = (WindSpeed.averageSince(now.minusHours(2), "influxdb") as Number).doubleValue * 3.6
		avgWindNext3h = GroupForecastedWind3Sum.state as Number
		logDebug(logName, "Windspeed last 2h: {}km/h, next 3h: {}km/h", avgWindLast2h, avgWindNext3h)

		// calculate rain fall history
		SumRainLast24h.sendCommand((localCurrentRainVolume.sumSince(now.minusHours(24), "influxdb") as Number).doubleValue)
		SumRainNext24h.sendCommand(GroupForecastedRain24Sum.state as Number)
		RainTotal = (SumRainLast24h.state as Number).doubleValue + (SumRainNext24h.state as Number).doubleValue
		logDebug(logName, "Rain last 24h: {}mm, next 24h: {}mm, total: {}mm", SumRainLast24h.state, SumRainNext24h.state, RainTotal)

		// if the temperature is to low, don't start watering
		avgTemperatureLast24h = (localCurrentTemperature.averageSince(now.minusHours(24), "influxdb") as Number).doubleValue
		AverageTemperature24h.sendCommand(avgTemperatureLast24h)
		logDebug(logName, "Average temperature last 24h: {}", avgTemperatureLast24h)

		avgHumidity24h = (localCurrentHumidity.averageSince(now.minusHours(24), "influxdb") as Number).doubleValue
		AverageHumidity24h.sendCommand(avgHumidity24h)
		logDebug(logName, "Average humidity last 24h: {}%", avgHumidity24h)

		// let's wait for propagating item values
		Thread::sleep(100)

		if (avgTemperatureLast24h <= 10) {
			CoefficientPercentage.postUpdate(0)
			logDebug(logName, "Temperature too low. Setting irrigation coefficient percentage to {}%", CoefficientPercentage)
			return
		} 
		else if (avgTemperatureLast24h > 30) {
			CoefficientPercentage.postUpdate(200)
			logDebug(logName, "Temperature very high. Setting irrigation coefficient percentage to {}%", CoefficientPercentage)
		} 
		else {
			// The software can be configured to use weather data to make adjustments to the irrigation duration automatically. The data is retrieved from openweathermap and uses the following parameters to adjust the duration:
			// Original by https://github.com/rszimm/sprinklers_pi/wiki/Weather-adjustments
			// Average humidity for the previous day.
			// Average temperature for the previous day.
			// Total precipitation for the previous day.
			// Total precipitation for the current day.
			// The weather adjustment formula uses a (configurable) baseline of 20deg C, 60% Humidity and no precipitation.

			// Humidity: Average humidity for the previous day above 60% is subtracted from the weather adjustment, below 60% is added to it. Example: 60% - 51% Avg Humidity = 9% Humidity Adjustment
			val HumidityPercentage = (((HumidityBaseLine.state as Number) - avgHumidity24h) * 1000).intValue
			// Temperature: +5% for each degree Celcius above 20, and -5% for each degree Celcius below 20.
			val TemperaturePercentage = (((avgTemperatureLast24h - (TempBaseLine.state as Number)) * (TempAdjustment.state as Number)) * 1000).intValue
			// Precipitation: -8% for each 1mm of precipitation from today and yesterday. Example: 3mm rain today + 1.27mm rain yesterday = 4.27 mm of rain * -8 = -34.16% Precipitation Adjustment
			val PrecipitationPercentage = ((RainTotal * (RainAdjustment.state as Number) * -1) * 1000).intValue // multiply with -1 to get to negative value as precipitation needs to be substracted
			val CoeffPct = (100 + (((HumidityPercentage as Number) + (TemperaturePercentage as Number) + (PrecipitationPercentage as Number))/1000)).intValue
			if (CoeffPct < 0) {
				CoefficientPercentage.postUpdate(0)
			}
			else {
				CoefficientPercentage.postUpdate(CoeffPct)
			}
			logDebug(logName, "Calculated coefficient percentage {}%", CoeffPct)
		}
	}
	catch (Exception e) {
        logError(logName, "Error calculating generic parameters: " + e)
    }
end

rule "Irrigation - calculate whether to start watering program 1"
when
    Time cron "1 * * ? * *" // every minute, 1 second after generic calculations
then
	try {
		logDebug(logName, "Calculating whether to start irrigation program 1")

		///////////////////		
		// start calculations, whether to start and for how long
		///////////////////

		// check for the manual lock
		if (IrrigationLock.state == OFF) {
			logInfo(logName, "Irrigation lock is on")
			return
		}

		// check if other program is active
		if (ActiveProgram.state != 0) {
			logInfo(logName, "Program {} already running", ActiveProgram.state)
			return
		}

		// check if schedule is set to every other day and if program ran yesterday
		if ((Program1HoursSinceLastRun.state as Number) < (Program1MinHoursBetween.state as Number) && Program1SpecificDays.state == OFF) {
			logInfo(logName, "Program 1 ran less than {}hrs ago, skip.", Program1MinHoursBetween.state)
//			Program1EOD.postUpdate(OFF)
			return
		}

		// set the default irrigation hour to X hours before the sunrise
		val localSunRise = new DateTimeType(Sunrise_Time.state.toString).getZonedDateTime.minusHours((Program1IrrigationHoursBeforeSunrise.state as Number).intValue)

		var Number wateringHour = localSunRise.getHour.intValue
		var Number wateringMinute = localSunRise.getMinute.intValue

		// if there is a specific hour in settings, then use it
		if (Program1IrrigationStartAtSpecificHour.state == ON) {
			wateringHour = Program1IrrigationStartTime.state as Number
			wateringMinute = 0
		}

		logDebug(logName, "Watering at: {}:{}", wateringHour, wateringMinute)

		// check if the current time is the watering time (full hour)
		if (now.getHour.intValue != wateringHour || now.getMinute.intValue != wateringMinute) {
			// nope - good bye
			logInfo(logName, "Inappropriate time to start irrigation. Program 1 watering at: {}:{}", wateringHour, wateringMinute)
			return
		}
		logInfo(logName, "It is watering hour: {}:{}", wateringHour, wateringMinute)

		// check the week day
		if (Program1SpecificDays.state == ON) {
			val Number day = now.getDayOfWeek.getValue
			val dayItem = ScriptServiceUtil.getItemRegistry.getItem("Program1IrrigationDay" + day)

			if (dayItem === null || dayItem.state == OFF || dayItem.state == NULL) {
				logInfo(logName, "Inappropriate day to start irrigation", dayItem)
				return
			}
		}

		// check if the current wind speed is higher then the max allowed
		logDebug(logName, "Current wind speed: {} km/h", String::format("%.2f", (localCurrentWindSpeed.state as Number).doubleValue))
		if (localCurrentWindSpeed.state > MaxAllowedWindSpeed.state as Number) {
			logInfo(logName, "Wind speed too high to irrigate")
			return
		}

		// if the rainfall sum for the last 24h and the forecast for 24h is higher then set, then we are not going to irrigate
		if (RainTotal > (MaxAllowedRain.state as Number).doubleValue) {
			logInfo(logName, "To heavy rain to irrigate (past and forcasted)")
			return
		}

		// check the weather, and based on that set the watering time coefficient factor
		// Rainbird R-VAN ~ 15mm/h (square precip)
		// Rainbird 3500 ~ 11mm/h (square precip)
		// Target lawn 10-15mm irrigation each time

		if (Program1WeatherAdjust.state == ON){
			if ((CoefficientPercentage.state as Number) <= 0) {
				logInfo(logName, "Weather conditions don't require irrigation")
				return
			}
			else {
				coefficientFactor = (CoefficientPercentage.state as Number) / 100
			}
		}
		else {
			coefficientFactor = 1

		}
		logInfo(logName, "Setting irrigation coefficient factor to {}", coefficientFactor)

		///////////////////
		// ok, let's start watering, cascading all of the valves from the GroupIrrigationValves
		///////////////////

		// Only if no other program is running (double check), start with the Zone 1, other zones will be turned on in sequence by the separate rule
		if (ActiveProgram.state == 0) {
			// if EveryOtherDay flag is off, turn it on. Warn and skip if flag is on and EOD is set. Shouldn't get to this point but skip irrigation.
			//if (Program1EOD.state == OFF) {
			//	Program1EOD.postUpdate(ON)
			//}
			//else if (Program1SpecificDays == OFF) {
			//	logWarn(logName, "About to start irrigation program 1, but only every other day required on and irrigation ran yesterday so skip")
			//	return
			//}
			logInfo(logName, "Starting the irrigation sequence from program 1")
			ActiveProgram.postUpdate(1)
			Program1LastRun.postUpdate(new DateTimeType())
			IrrigationCurrentValve.sendCommand(IrrigationValveZone1.name)
		}
		else {
			logWarn(logName, "About to start irrigation program 1, but program {} already running so skip", ActiveProgram.state) // shouldn't get to this point, but if so log warning. 
		}
	}
	catch (Exception e) {
        logError(logName, "Error calculating whether to start irrigation: " + e)
    }
end

rule "Irrigation - calculate whether to start watering program 2"
when
    Time cron "2 * * ? * *" // every minute, 1s after program 1
then
	try {
		logDebug(logName, "Calculating whether to start irrigation program 2")

		///////////////////		
		// start calculations, whether to start and for how long
		///////////////////

		// check for the manual lock
		if (IrrigationLock.state == OFF) {
			logInfo(logName, "Irrigation lock is on")
			return
		}

		// check if other program is active
		if (ActiveProgram.state != 0) {
			logInfo(logName, "Program {} already running", ActiveProgram.state)
			return
		}

		// check if schedule is set to every other day and if program ran yesterday
		if ((Program2HoursSinceLastRun.state as Number) < (Program2MinHoursBetween.state as Number) && Program2SpecificDays.state == OFF) {
			logInfo(logName, "Program 2 ran less than {}hrs ago, skip.", Program2MinHoursBetween.state)
			return
		}

		// check if program 2 is set
		if (Program2IrrigationStartAtSpecificHour.state == OFF) {
			logInfo(logName, "Program 2 not active")
			return
		}

		var Number wateringHour = Program2IrrigationStartTime.state as Number
		var Number wateringMinute = 0

		// check if the current time is the watering time (full hour)
		if (now.getHour.intValue != wateringHour || now.getMinute.intValue != wateringMinute) {
			// nope - good bye
			logInfo(logName, "Inappropriate time to start irrigation. Program 2 watering at: {}:{}", wateringHour, wateringMinute)
			return
		}
		logInfo(logName, "It is watering hour: {}:{}", wateringHour, wateringMinute)

		// check the week day
		if (Program2SpecificDays.state == ON) {
			val Number day = now.getDayOfWeek.getValue
			val dayItem = ScriptServiceUtil.getItemRegistry.getItem("Program2IrrigationDay" + day)

			if (dayItem === null || dayItem.state == OFF || dayItem.state == NULL) {
				logInfo(logName, "Inappropriate day to start irrigation", dayItem)
				return
			}
		}
		// check if the current wind speed is higher then the max allowed
		logDebug(logName, "Current wind speed: {} km/h", String::format("%.2f", (localCurrentWindSpeed.state as Number).doubleValue))
		if (localCurrentWindSpeed.state > MaxAllowedWindSpeed.state as Number) {
			logInfo(logName, "Wind speed too high to irrigate")
			return
		}

		// if the rainfall sum for the last 24h and the forecast for 24h is higher then set, then we are not going to irrigate
		if (RainTotal > (MaxAllowedRain.state as Number).doubleValue) {
			logInfo(logName, "To heavy rain to irrigate (past and forcasted)")
			return
		}

		// check the weather, and based on that set the watering time coefficient factor
		// Rainbird R-VAN ~ 15mm/h (square precip)
		// Rainbird 3500 ~ 11mm/h (square precip)
		// Target lawn 10-15mm irrigation each time

		if (Program2WeatherAdjust.state == ON){
			if ((CoefficientPercentage.state as Number) <= 0) {
				logInfo(logName, "Weather conditions don't require irrigation")
				return
			}
			else {
				coefficientFactor = (CoefficientPercentage.state as Number) / 100
			}
		}
		else {
			coefficientFactor = 1

		}
		logInfo(logName, "Setting irrigation coefficient factor to {}", coefficientFactor)

		///////////////////
		// ok, let's start watering, cascading all of the valves from the GroupIrrigationValves
		///////////////////

		// Only if no other program is running (double check), start with the Zone 1, other zones will be turned on in sequence by the separate rule
		if (ActiveProgram.state == 0) {
			logInfo(logName, "Starting the irrigation sequence from program 2")
			ActiveProgram.postUpdate(2)
			Program2LastRun.postUpdate(new DateTimeType())
			IrrigationCurrentValve.sendCommand(IrrigationValveZone1.name)
		}
		else {
			logWarn(logName, "About to start irrigation program 2, but program {} already running so skip", ActiveProgram.state) // shouldn't get to this point, but if so log warning. 
		}
	}
	catch (Exception e) {
        logError(logName, "Error calculating whether to start irrigation: " + e)
    }
end

rule "Irrigation - calculate whether to start watering program 3"
when
    Time cron "3 * * ? * *" // every minute, 2s after program 1, 1s after program 2
then
	try {
		logDebug(logName, "Calculating whether to start irrigation program 3")

		///////////////////		
		// start calculations, whether to start and for how long
		///////////////////

		// check for the manual lock
		if (IrrigationLock.state == OFF) {
			logInfo(logName, "Irrigation lock is on")
			return
		}

		// check if other program is active
		if (ActiveProgram.state != 0) {
			logInfo(logName, "Program {} already running", ActiveProgram.state)
			return
		}

		// check if schedule is set to every other day and if program ran yesterday
		if ((Program3HoursSinceLastRun.state as Number) < (Program3MinHoursBetween.state as Number) && Program3SpecificDays.state == OFF) {
			logInfo(logName, "Program 3 ran less than {}hrs ago, skip.", Program3MinHoursBetween.state)
			return
		}

		// check if program 3 is set
		if (Program3IrrigationStartAtSpecificHour.state == OFF) {
			logInfo(logName, "Program 3 not active")
			return
		}

		var Number wateringHour = Program3IrrigationStartTime.state as Number
		var Number wateringMinute = 0

		// check if the current time is the watering time (full hour)
		if (now.getHour.intValue != wateringHour || now.getMinute.intValue != wateringMinute) {
			// nope - good bye
			logInfo(logName, "Inappropriate time to start irrigation. Program 3 watering at: {}:{}", wateringHour, wateringMinute)
			return
		}
		logInfo(logName, "It is watering hour: {}:{}", wateringHour, wateringMinute)

		// check the week day
		if (Program3SpecificDays.state == ON) {
			val Number day = now.getDayOfWeek.getValue
			val dayItem = ScriptServiceUtil.getItemRegistry.getItem("Program3IrrigationDay" + day)

			if (dayItem === null || dayItem.state == OFF || dayItem.state == NULL) {
				logInfo(logName, "Inappropriate day to start irrigation", dayItem)
				return
			}
		}

		// check if the current wind speed is higher then the max allowed
		logDebug(logName, "Current wind speed: {} km/h", String::format("%.2f", (localCurrentWindSpeed.state as Number).doubleValue))
		if (localCurrentWindSpeed.state > MaxAllowedWindSpeed.state as Number) {
			logInfo(logName, "Wind speed too high to irrigate")
			return
		}

		// if the rainfall sum for the last 24h and the forecast for 24h is higher then set, then we are not going to irrigate
		if (RainTotal > (MaxAllowedRain.state as Number).doubleValue) {
			logInfo(logName, "To heavy rain to irrigate (past and forcasted)")
			return
		}

		// check the weather, and based on that set the watering time coefficient factor
		// Rainbird R-VAN ~ 15mm/h (square precip)
		// Rainbird 3500 ~ 11mm/h (square precip)
		// Target lawn 10-15mm irrigation each time

		if (Program3WeatherAdjust.state == ON){
			if ((CoefficientPercentage.state as Number) <= 0) {
				logInfo(logName, "Weather conditions don't require irrigation")
				return
			}
			else {
				coefficientFactor = (CoefficientPercentage.state as Number) / 100
			}
		}
		else {
			coefficientFactor = 1

		}
		logInfo(logName, "Setting irrigation coefficient factor to {}", coefficientFactor)

		///////////////////
		// ok, let's start watering, cascading all of the valves from the GroupIrrigationValves
		///////////////////

		// Only if no other program is running (double check), start with the Zone 1, other zones will be turned on in sequence by the separate rule
		if (ActiveProgram.state == 0) {
			logInfo(logName, "Starting the irrigation sequence from program 3")
			ActiveProgram.postUpdate(3)
			Program3LastRun.postUpdate(new DateTimeType())
			IrrigationCurrentValve.sendCommand(IrrigationValveZone1.name)
		}
		else {
			logWarn(logName, "About to start irrigation program 3, but program {} already running so skip", ActiveProgram.state) // shouldn't get to this point, but if so log warning. 
		}
	}
	catch (Exception e) {
        logError(logName, "Error calculating whether to start irrigation: " + e)
    }
end

rule "Irrigation - cascading"
when
    Item IrrigationCurrentValve received command
then
	try {
		// get the currently open valve
		var currValve = GroupIrrigationValves.members.findFirst[valve | valve.name == receivedCommand.toString]
		var currValveNum = Integer::parseInt(currValve.name.split("Zone").get(1))
		var currValveSecs = (GroupIrrigationTimes.members.findFirst[t | t.name == currValve.name+"Time" ].state as Number)

		var ProgZoneName = "Program" + ActiveProgram.state + "Zone" + currValveNum // name for item of current zone within current program for evaluation
		var ProgZoneItem = Group_Program_Zones.members.findFirst[zone | zone.name == ProgZoneName]

		//logInfo(logName, "Before while loop: Current valve {}, duration {}", currValve.name, currValveSecs) // changed to logInfo for debug

		// while loop to check if set time > 0. In case of 0, skip zone and select next one.
		var boolean exitFlag = false
		while (!exitFlag && (currValveSecs < 1 || ProgZoneItem.state == OFF)){
			currValveNum = currValveNum + 1
			var currValveName = "IrrigationValveZone" + currValveNum
			currValve = GroupIrrigationValves.members.findFirst[valve | valve.name == currValveName]
			ProgZoneName = "Program" + ActiveProgram.state + "Zone" + currValveNum // name for item of current zone within current program for evaluation
			ProgZoneItem = Group_Program_Zones.members.findFirst[zone | zone.name == ProgZoneName]

			if (currValve === null || ProgZoneItem === null){
				exitFlag = true
				logInfo(logName, "In while loop: No more valves")
			}
			else {

				currValveSecs = (GroupIrrigationTimes.members.findFirst[t | t.name == currValve.name+"Time" ].state as Number)
				logInfo(logName, "In while loop: currValve {}, currValveSecs {}", currValve.name, currValveSecs) // added for debug
			}
		}

		logInfo(logName, "After while loop: Current valve {}, duration {}", currValve, currValveSecs) // changed to logInfo for debug

		// get the next valve in the sequence
		val nextValveNum = currValveNum + 1
		val nextValveName = "IrrigationValveZone" + nextValveNum
		val nextValve = GroupIrrigationValves.members.findFirst[valve | valve.name == nextValveName]
		
		// if there is no next valve in the sequence, then nextValve is null
		if (nextValve === null) {
			logDebug(logName, "This is the last valve in the sequence")
		}
		else {
			logDebug(logName, "Next valve {}", nextValve.name)
		}


		if (currValve !== null){

			// open the current valve
			val valveOpenTime = (currValveSecs * coefficientFactor).intValue
			logInfo(logName, "Opening {} for {} seconds", currValve.name, valveOpenTime)

			currValve.sendCommand(ON)

			IrrigationSectionRemainingTime.postUpdate(valveOpenTime)

			// let's wait for propagating item values
			Thread::sleep(100)

			// update HomeKit remaining time
			val RemainingTimeCurrentZoneName = "RemainingTimeZone" + currValveNum
			val RemainingTimeCurrentZone = GroupRemainingTime.members.findFirst[valve | valve.name == RemainingTimeCurrentZoneName]
			RemainingTimeCurrentZone.postUpdate(IrrigationSectionRemainingTime.state)

			// set the timer, after expiring turn off the current valve and turn on the next one
			irrigationTimer = createTimer(now.plusSeconds(valveOpenTime), [ |
				if (nextValve !== null) {
					// this will invoke cascading valves, "Irrigation - cascading" rule
					IrrigationCurrentValve.sendCommand(nextValve.name)
				}
				else {
					logInfo(logName, "Irrigation is complete")
				}

				// let's wait for propagating item values
				Thread::sleep(500)

				// turn off current valve
				logInfo(logName, "Closing " + currValve.name)
				currValve.sendCommand(OFF)

				irrigationTimer = null
			])
		}
		else {
			logInfo(logName, "Irrigation is complete")
		}
	}
	catch (Exception e) {
        logError(logName, "Error controlling cascading valves: " + e)
    }
end

// for displaying remaining irrigation time purpose only
rule "Irrigation - update timer"
when
  Time cron "0 * * ? * *" // every minute
then
	if (IrrigationSectionRemainingTime.state as Number > 60) {
		IrrigationSectionRemainingTime.postUpdate((IrrigationSectionRemainingTime.state as Number) - 60)
	}

end

rule "Irrigation - update timer for HomeKit items"
when
  Time cron "/10 * * ? * *" // every 10s
then
	GroupRemainingTime.members.filter[valve | valve.state > 9].forEach[valve | 
		valve.postUpdate((valve.state as Number) - 10) 
		]
end

rule "Irrigation - all valves closed"
when
	Item GroupIrrigationValves changed to OFF
then
	// set the current valve to OFF
	logInfo(logName, "All irrigation valves closed")
	IrrigationCurrentValve.postUpdate(OFF)

	// reset the remaining time
	IrrigationSectionRemainingTime.postUpdate(0)
	GroupRemainingTime.members.filter[valve | valve.state != 0].forEach[valve | valve.postUpdate(0)]

	// reset active program
	ActiveProgram.postUpdate(0)

	// log cycle end
	LastEnd.sendCommand(new DateTimeType())

end

rule "Irrigation - valve updated, turn on the timer"
when
	Item GroupIrrigationValves changed 
then
	// protection against overwatering
	// log the state of all valves
	GroupIrrigationValves.members.forEach [valve | 
		logDebug(logName, "Irrigation valve: " + valve.name + " " + valve.state)
	]

	// a valve was turned on
	if (GroupIrrigationValves.state == ON) {
		if (IrrigationTimerMax.state == OFF) {
			// timer is not set yet, start the timer and log cycle start
			logDebug(logName, "Irrigation valve open, starting protection timer")
			IrrigationTimerMax.sendCommand(ON)
			LastStart.sendCommand(new DateTimeType())
		}
		else {
			// the timer is already running
			logDebug(logName, "Irrigation valve open, timer already started, nothing to do")
		}
	}
	else {
		logDebug(logName, "All irrigation valves closed, stopping protection timer")
		IrrigationTimerMax.postUpdate(OFF)
	}
end

rule "Irrigation - protection timer off, close all valves"
when
	Item IrrigationTimerMax changed to OFF
then
	// protection timer expired - turn all valves off
	logWarn(logName, "Irrigation protection timer expired - close all valves")

	// close all valves from the group
	GroupIrrigationValves.members.forEach [valve | 
		logDebug(logName, "Closing valve: " + valve.name)
		valve.sendCommand(OFF)
	]
end
sitemap irrigation label="Beregening"
{		
	Frame label="Status" {
		Switch item=IrrigationLock icon="lock" mappings=[ON="Automatisch", OFF="Uit"]
		Text item=GroupIrrigationValves label="Beregening" icon="water"
		// Last cycle start
		Text item=LastStart icon="time"
		// Last cycle end
		Text item=LastEnd icon="time"
		Text item=IrrigationSectionRemainingTime visibility=[IrrigationSectionRemainingTime > 0] label="Resterende tijd huidige zone [%d min]" icon="time"
		Text item=ActiveProgram visibility=[ActiveProgram > 0] label="Actieve programma [%d]" icon="text"
	}
	Frame label="Zones" {
		Switch item=IrrigationValveZone1 label="Zone 1: Druppel border achtertuin" /*visibility=[IrrigationLock.state==OFF]*/ icon="faucet"
		Switch item=IrrigationValveZone2 label="Zone 2: Sproeier 1 achtertuin" icon="faucet"
		Switch item=IrrigationValveZone3 label="Zone 3: Druppel bomen achtertuin" icon="faucet"
		Switch item=IrrigationValveZone4 label="Zone 4: Sproeier 2 achtertuin" icon="faucet"
		Switch item=IrrigationValveZone5 label="Zone 5: Sproeier 1 voortuin" icon="faucet"
		Switch item=IrrigationValveZone6 label="Zone 6: Druppel bomen voortuin" icon="faucet"
		Switch item=IrrigationValveZone7 label="Zone 7: Sproeier 2 voortuin" icon="faucet"
	}
	
	Frame label="Groepen" {
		Switch item=IrrigationValveZone8 icon="faucet"
		Switch item=IrrigationValveZone9 icon="faucet"
		Switch item=IrrigationValveZone10 icon="faucet"
	}
	
	Frame label="Metingen" {
		// rainfall past 24h
		Text item=SumRainLast24h icon="rain"
		// rainfall forecast 24h
		Text item=SumRainNext24h icon="rain"
		// Coefficient factor
		Text item=CoefficientPercentage icon="chart"
		// Average temperature past 24h
		Text item=AverageTemperature24h icon="temperature"
		// Average humidity past 24h
		Text item=AverageHumidity24h icon="humidity"
		// Current wind speed
		Text item=localCurrentWindSpeed label="Huidige windsnelheid" icon="wind"
	}

	Frame label="Instellingen" {
		Text label="Algemeen" icon="settings" {

			Frame label="Beregeningsduur" {
				Setpoint item=IrrigationValveZone1Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone2Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone3Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone4Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone5Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone6Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone7Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone8Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone9Time minValue=0 maxValue=3600 step=60 icon="time"
				Setpoint item=IrrigationValveZone10Time minValue=0 maxValue=3600 step=60 icon="time"
			}

			Frame label="Weersomstandigheden" {
				Setpoint item=MaxAllowedRain label="Max neerslag" minValue=0 maxValue=20 step=1 icon="rain"
				Setpoint item=MaxAllowedWindSpeed label="Max windsnelheid" minValue=5 maxValue=150 step=5 icon="wind"
			}

			Frame label="Weersafhankelijke parameters" {
				Setpoint item=TempBaseLine label="Temperatuur uitgangspunt (default 20°C) [%d °C]" minValue=15 maxValue=25 step=1 icon="temperature"
				Setpoint item=TempAdjustment label="Temperatuur aanpassingsfactor (default 5) [%d]" minValue=2 maxValue=10 step=1 icon="heating-on"
				Setpoint item=HumidityBaseLine label="Luchtvochtigheid uitgangspunt (default 60%) [%.0f %%]" minValue=40 maxValue=80 step=1 icon="humidity"
				Setpoint item=RainAdjustment label="Regen aanpassingsfactor (default 8) [%d]" minValue=1 maxValue=20 step=1 icon="water"
			}
		}

// split settings and program times - Program 1

		Text label="Programma 1" icon="time" {

			Frame label="Starttijd" {
				Selection item=Program1IrrigationStartAtSpecificHour icon="sunrise" label="Start beregening" mappings=["OFF"="Voor zonsopkomst", "ON"="Op vast tijdstip"]
				Setpoint item=Program1IrrigationHoursBeforeSunrise icon="time" minValue=0 maxValue=23 visibility=[Program1IrrigationStartAtSpecificHour==OFF]
				Selection item=Program1IrrigationStartTime visibility=[Program1IrrigationStartAtSpecificHour==ON] mappings=[
														"0"="00:00", "1"="01:00", "2"="02:00",
														"3"="03:00", "4"="04:00", "5"="05:00", 
														"6"="06:00", "7"="07:00", "8"="08:00",
														"9"="09:00", "10"="10:00", "11"="11:00", 
														"12"="12:00", "13"="13:00", "14"="14:00",
														"15"="15:00", "16"="16:00", "17"="17:00",
														"18"="18:00", "19"="19:00", "20"="20:00", 
														"21"="21:00", "22"="22:00", "23"="23:00"
				]
				//Switch item=Program1EOD label="Laatste 24u actief []" mappings=["ON"="Ja", "OFF"="Nee"]
				Text item=Program1HoursSinceLastRun icon="time"
				Text item=Program1LastRun icon="time"
			}

			Frame {
				Selection item=Program1SpecificDays icon="water" label="Beregeningsdagen" mappings=["OFF"="Om de dag", "ON"="Op vaste dagen"]
				Switch item=Program1WeatherAdjust icon="line" mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay1 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay2 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay3 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay4 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay5 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay6 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1IrrigationDay7 visibility=[Program1SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Setpoint item=Program1MinHoursBetween icon="time" visibility=[Program1SpecificDays==OFF]
			}

			Frame label="Actieve zones" {
				Switch item=Program1Zone1 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone2 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone3 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone4 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone5 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone6 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone7 mappings=[ON="Wel", OFF="Niet"]
			}

			Frame label="Actieve groepen" {
				Switch item=Program1Zone8 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone9 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program1Zone10 mappings=[ON="Wel", OFF="Niet"]
			}
		}

//

// split settings and program times - Program 2

		Text label="Programma 2" icon="time" {
			Frame label="Starttijd" {
				Selection item=Program2IrrigationStartAtSpecificHour icon="sunrise" label="Start beregening" mappings=["OFF"="Uit", "ON"="Op vast tijdstip"]
				Text label="" icon="" visibility=[Program2IrrigationStartAtSpecificHour==OFF]
				Selection item=Program2IrrigationStartTime visibility=[Program2IrrigationStartAtSpecificHour==ON] mappings=[
														"0"="00:00", "1"="01:00", "2"="02:00",
														"3"="03:00", "4"="04:00", "5"="05:00", 
														"6"="06:00", "7"="07:00", "8"="08:00",
														"9"="09:00", "10"="10:00", "11"="11:00", 
														"12"="12:00", "13"="13:00", "14"="14:00",
														"15"="15:00", "16"="16:00", "17"="17:00",
														"18"="18:00", "19"="19:00", "20"="20:00", 
														"21"="21:00", "22"="22:00", "23"="23:00"
				]
				Text item=Program2HoursSinceLastRun icon="time"
				Text item=Program2LastRun icon="time"
			}

			Frame visibility=[Program2IrrigationStartAtSpecificHour==ON] {
				Selection item=Program2SpecificDays icon="water" label="Beregeningsdagen" mappings=["OFF"="Om de dag", "ON"="Op vaste dagen"]
				Switch item=Program2WeatherAdjust icon="line" mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay1 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay2 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay3 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay4 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay5 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay6 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2IrrigationDay7 visibility=[Program2SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Setpoint item=Program2MinHoursBetween icon="time" visibility=[Program2SpecificDays==OFF]
			}

			Frame label="Actieve zones" visibility=[Program2IrrigationStartAtSpecificHour==ON] {
				Switch item=Program2Zone1 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone2 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone3 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone4 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone5 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone6 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone7 mappings=[ON="Wel", OFF="Niet"]
			}

			Frame label="Actieve groepen" visibility=[Program2IrrigationStartAtSpecificHour==ON] {
				Switch item=Program2Zone8 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone9 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program2Zone10 mappings=[ON="Wel", OFF="Niet"]
			}
		}

//

// split settings and program times - Program 3

		Text label="Programma 3" icon="time" {
			Frame label="Starttijd" {
				Selection item=Program3IrrigationStartAtSpecificHour icon="sunrise" label="Start beregening" mappings=["OFF"="Uit", "ON"="Op vast tijdstip"]
				Text label="" icon="" visibility=[Program3IrrigationStartAtSpecificHour==OFF]
				Selection item=Program3IrrigationStartTime visibility=[Program3IrrigationStartAtSpecificHour==ON] mappings=[
														"0"="00:00", "1"="01:00", "2"="02:00",
														"3"="03:00", "4"="04:00", "5"="05:00", 
														"6"="06:00", "7"="07:00", "8"="08:00",
														"9"="09:00", "10"="10:00", "11"="11:00", 
														"12"="12:00", "13"="13:00", "14"="14:00",
														"15"="15:00", "16"="16:00", "17"="17:00",
														"18"="18:00", "19"="19:00", "20"="20:00", 
														"21"="21:00", "22"="22:00", "23"="23:00"
				]
				Text item=Program3HoursSinceLastRun icon="time"
				Text item=Program3LastRun icon="time"
			}

			Frame visibility=[Program3IrrigationStartAtSpecificHour==ON] {
				Selection item=Program3SpecificDays icon="water" label="Beregeningsdagen" mappings=["OFF"="Om de dag", "ON"="Op vaste dagen"]
				Switch item=Program3WeatherAdjust icon="line" mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay1 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay2 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay3 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay4 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay5 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay6 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3IrrigationDay7 visibility=[Program3SpecificDays==ON] mappings=[ON="Wel", OFF="Niet"]
				Setpoint item=Program3MinHoursBetween icon="time" visibility=[Program3SpecificDays==OFF]
			}

			Frame label="Actieve zones" visibility=[Program3IrrigationStartAtSpecificHour==ON] {
				Switch item=Program3Zone1 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone2 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone3 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone4 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone5 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone6 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone7 mappings=[ON="Wel", OFF="Niet"]
			}

			Frame label="Actieve groepen" visibility=[Program3IrrigationStartAtSpecificHour==ON] {
				Switch item=Program3Zone8 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone9 mappings=[ON="Wel", OFF="Niet"]
				Switch item=Program3Zone10 mappings=[ON="Wel", OFF="Niet"]
			}
		}
	}	
 }

Hi all,

I’m looking into this solution to implement at home, is there anybody with a full implementation at OH3/4 with a working widget?