I am a fresh beginner of openhab. I already like it. It went quite smooth and I am starting to develop my own set of rules for a watering system.
Platform information:
openHAB version: docker image openhab/openhab:2.2.0 (docker run on ubuntu 16.04)
mqtt: mosquitto 1.4.12 eclipse-mosquitto:1.4.2
embedded system: sonoff 4ch pro with ESPURNA_802DF2 1.12.6
My goal is to open a set of valves, each one at a time, for a specified amount of time (user configurable) at a certain time (user configurable) and giving the option to manual open the valves.
Here is how I (partially) translated this (watering time is in seconds for testing):
Number arrpelsec
Number arrb
Number bambounordsec
Number arrstarth
Number arrstartm
Switch arrpelauto
Switch arrall
Switch arrpel
{ mqtt=">[agde:ESPURNA_802DF2/relay/0/set:command:*:default],
<[agde:ESPURNA_802DF2/relay/0:state:MAP(onoff.map)]" }
Switch arrbambounord
{ mqtt=">[agde:ESPURNA_802DF2/relay/1/set:command:*:default],
<[agde:ESPURNA_802DF2/relay/1:state:MAP(onoff.map)]" }
arr.rules
var Timer myTimer = null
//********************************************************************
rule "arrall water all"
when
Item arrall changed
then
if (arrall.state==OFF) {
arrbambounord.sendCommand(OFF)
arrpel.sendCommand(OFF)
}
else if (arrall.state==ON) {
arrbambounord.sendCommand(ON)
}
end
//********************************************************************
rule "arrbambounord timed"
when
Item arrbambounord changed
then
if (arrbambounord.state==OFF) {
if (myTimer!==null) {
myTimer.cancel
myTimer = null
}
}
else if (arrbambounord.state==ON) {
if (myTimer!==null) {
myTimer.cancel
myTimer = null
}
myTimer = createTimer(now.plusSeconds((arrbambounordsec.state as DecimalType).intValue)) [|
arrbambounord.sendCommand(OFF)
if (arrall.state==ON) {
arrpel.sendCommand(ON)}
]
}
end
//********************************************************************
rule "arrpel timed"
when
Item arrpel changed
then
if (arrpel.state==OFF) {
if (myTimer!==null) {
myTimer.cancel
myTimer = null
}
}
else if (arrpel.state==ON) {
if (myTimer!==null) {
myTimer.cancel
myTimer = null
}
myTimer = createTimer(now.plusSeconds((arrpelsec.state as DecimalType).intValue)) [| //use now.plusSeconds() for testing
arrpel.sendCommand(OFF)
if (arrall.state==ON) {
arrall.sendCommand(OFF)}
]
}
What is missing yet: scheduler for beginning of the watering session, persistance and the other 5 valves.
This work pretty well so far. But before progress I would like to:
to be sure I did not miss certain paradigm or better design pattern.
to know if there is a way to refactor the code for the timing rules (arrbambounord & arrpel) as the logic is the same and a lot of duplicate code will shows if I just copy past for every valve.
what is the best way to send arrall.sendCommand(ON) based on hours & minutes in arrstarth & arrstarts ?
yes, as I have not a single clue of how to create a cron rule based on the Number arrstarth and arrstartm items and condition arrpelauto.
thank you for your pointer. I will rewrite the rules based on this new information.
You can’t set up a cron rule based on variable
Have a cron rule to check every minute if the hour and the minutes match your item state and trigger your valves
some of the proposed things in the desing pattern can be done easier when using openHAB > 2.3.0 Build #1212
Using implicit variables and rule trigger MemberOf.
see the examples below, maybe the LED stripe example is of most interrest.
Just to name some things you could improve: one rule for all plants and group triggers, lock your timer to avoid seperate executed rules to cancle each other or better use multiple timer so they can execute in parallel…
Indeed the triggeringItem is a life saver. How safe is it to use the last snapshot in production environment ?
Thanks for the improvement ideas. It was not clear in my first post but it is by design those task are mutually exclusive. The exclusion is managed by ESPURNA
when
Item arrpel changed // here triggeringItem
Item arrpel received update // here receivedCommand
then
logInfo("triggeringItem", triggeringItem.toString )
Actually the .toString was needed. I do not understood the error displayed without the .toString: Rule 'arrpel timed': An error occurred during the script execution: Could not invoke method: org.eclipse.smarthome.model.script.actions.LogAction.logInfo(java.lang.String,java.lang.String,java.lang.Object[]) on instance: null
Was because of the .toString missing.
It seems I have trouble understanding the stacktrace.
With triggerItem I still have an issue. I do not know how to get the duration associated.
arrbambounord has duration stored in arrbambounordsec
arrpel has duration stored in arrpel.
I do not know how to solve this case. It is like a property of arrpel or arrbambounord but I cannot use this in rules right ? Or I though about maps but I do not know how to set value to key in rules or items. Maybe concatenate the triggeringItem.name with + “sec” but how to get the value ?
put arrpel and arrbambounordsec in a group called arrgroup
var itemName = triggeringItem.name
var state = 0
if (itemName == "arrbambounord") {
state = arrgroup.members.filter[ i | i.name == itemName + "sec" ].get(0).state as Number
} else if (itemName == "arrpel") {
state = arrgroup.members.filter[ i | i.name == itemName ].get(0).state as Number
logInfo("VALUE", state.toString)
}
Let`s put it simple, the compiler tries to gues what you want to do. but it is not always able to do so.
Writing
logInfo("triggeringItem", "Make compiler knows it is a string: " + triggeringItem)
schould work, as know there is some information what you want to do. Now the compiler will call the toString funktion for you, automatically, mostly.
Just putting a Object into a function call gives not enough information on what you want.
Yes, from what I remember the toString() was automatic in java. And for a moment I though I was still writing some. When you brought the toString solution I understood what you just wrote.
However, I am puzzled by the stacktrace which seem at first quite confusing. But now I know.
there seems to be a bug. As your label is [%d s] the value for the setpoint seems to be number s which is not only a number and this string can not be converted to a DecimalType. For now you can just remove the s and have only the number. i will make a bug report and ask if this is intended to work like this.