I’m using OH 3.3 in a RPi 4 with SSD, persistence in MySQL.
Everything is working flawlessly and I have multiple other rules just working fine. But now I want to create a more dynamic rule in order to have a better irrigation system, like so:
var Timer offTimer = null
rule "Irrigation system"
when
Time cron "0 0 0 ? * *"
then
// Set start and end times for humidity measurements
val ZonedDateTime istart = now.minusDays(1).withHour(8).withMinute(0).withSecond(0).withNano(0)
val ZonedDateTime iend = now.minusDays(1).withHour(20).withMinute(0).withSecond(0).withNano(0)
// Get all humidity readings between start and end times
val humidityReadings = OutsideHumidity.historicState(istart, iend).states
// Calculate average humidity level
val averageHumidity = humidityReadings.map[state | state as Number].reduce[sum, state | sum + state] / humidityReadings.size // Use reduce() to calculate the average
// Check if average humidity is above 90%
if (averageHumidity >= 90) {
logInfo("Irrigation system", "Average humidity is above 90%. Irrigation system will not activate.")
} else {
logInfo("Irrigation system", "Average humidity is below 90%. Irrigation system will activate.")
// Activate irrigation system
IrrigationOutput1.sendCommand(ON)
offTimer = createTimer(now.plusMinutes(5), [|
IrrigationOutput1.sendCommand(OFF)
IrrigationOutput2.sendCommand(ON)
offTimer = createTimer(now.plusMinutes(5), [|
IrrigationOutput2.sendCommand(OFF)
IrrigationOutput3.sendCommand(ON)
offTimer = createTimer(now.plusMinutes(5), [|
IrrigationOutput3.sendCommand(OFF)
IrrigationOutput4.sendCommand(ON)
offTimer = createTimer(now.plusMinutes(10), [|
IrrigationOutput4.sendCommand(OFF)
IrrigationOutput5.sendCommand(ON)
offTimer = createTimer(now.plusMinutes(10), [|
IrrigationOutput5.sendCommand(OFF)
])
])
])
])
])
}
end
Problem is, I am getting this error message in the log:
[ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'exterior-2' failed: An error occurred during the script execution: Could not invoke method: org.openhab.core.persistence.extensions.PersistenceExtensions.historicState(org.openhab.core.items.Item,java.time.ZonedDateTime,java.lang.String) on instance: null in exterior
What am I doing wrong here and how can I achieve this?
Thanks in advance. 
I think historic state needs just one date cause it gives one state from this date.
<item>.historicState(ZonedDateTime)
Whats about
<item>.averageBetween(ZonedDateTime, ZonedDateTime)
This should do what you want…
Greets
1 Like
I’m not aware of such a method as .state
s
for .historicState
.
Please be aware that .historicState()
will give you one value.
But you don’t need to calculate average yourself, there is a method…
val averageHumidity = OutsideHumidity.averageBetween(istart,iend)
Another thing is the timer for cycling through the vents. Maybe a somewhat cleaner solution:
var Timer tIrrigation = null
var Integer iIrrigation = 0
rule "Irrigation system"
when
Time cron "0 0 0 ? * *"
then
// Set start and end times for humidity measurements
val ZonedDateTime istart = now.minusDays(1).withHour(8).withMinute(0).withSecond(0).withNano(0)
val ZonedDateTime iend = now.minusDays(1).withHour(20).withMinute(0).withSecond(0).withNano(0)
// Get average humidity level
val averageHumidity = OutsideHumidity.averageBetween(istart, iend)
// Check if average humidity is above 90%
if (averageHumidity >= 90) {
logInfo("irrigation", "Average humidity is above 90%. Irrigation system will not activate.")
// stop the rule
return;
}
logInfo("irrigation", "Average humidity is below 90%. Irrigation system will activate.")
// initialize the counter
iIrrigation = 0
// create the timer to start in 10 Milliseconds
tIrrigation = createTimer(now.plusNanos(10000000), [|
// not first run?
if(iIrrigation > 0)
// then switch off the vent
sendCommand("IrrigationOutput"+iIrrigation.toString,"OFF")
// count up
iIrrigation += 1
// is there another vent to switch on?
if(iIrrigation > 5)
// if not, stop the timer code
return;
// switch the vent on
sendCommand("IrrigationOutput"+iIrrigation.toString,"ON")
// reschedule the timer (the last two vents need more time)
tIrrigation.reschedule(now.plusMinutes(if(iIrrigation < 4) 5 else 10))
])
end
Using a Group Item for the vents would be better (let’s call it gVents, just a Group)
var Timer tIrrigation = null
rule "Irrigation system"
when
Time cron "0 0 0 ? * *"
then
val ZonedDateTime zdtStart = now.minusDays(1).withHour( 8).withMinute(0).withSecond(0).withNano(0) // set start time
val ZonedDateTime zdtEnd = now.minusDays(1).withHour(20).withMinute(0).withSecond(0).withNano(0) // set end time
val averageHumidity = OutsideHumidity.averageBetween(zdtStart, zdtEnd) // get average
if (averageHumidity >= 90) { // Check if above 90%
logInfo("irrigation", "Average humidity is above 90%. Irrigation system will not activate.")
return; // stop the rule
}
logInfo("irrigation", "Average humidity is below 90%. Irrigation system will activate.")
tIrrigation = createTimer(now.plusNanos(10000000), [| // create the timer to start in 10 Milliseconds
var Integer iMarker = 0 // create local var
val active = gVents.members.sortBy[ name ].filter[ i | i.state == ON ].head // get active vent
if(active !== null) // check if there is any
iMarker = Integer.parseInt(active.name.substring(active.name.length()-1)) // get the digit
gVents.members.filter[i|i.state != OFF].forEach[v|v.sendCommand(OFF)] // switch off any vent which isn't
iMarker += 1 // count up
gVents.members.filter[i|i.name.endsWith(iMarker.toString)].head.sendCommand(ON) // open the next vent, if there is any
if(iMarker <= gVents.members.size) // out of bound?
tIrrigation.reschedule(now.plusMinutes(if(iMarker < 4) 5 else 10)) // reschedule timer
])
end
2 Likes
Yes, you are correct, thank you for that! 
Thank you SO much for your kind help!
Your code works flawlessly. Incredible.
Wish you a great day. 