Hello,
I wonder if anyone can assist me create a rule that would send an alarm message by publishing to an MQTT topic when the humidity reaches a certain threashold and then repeat the alarm every hour as long as humidity remains in that specific range/threashold
For example,
Send an alarm when humidity reaches 60+% and continue to send the alarm every 60 minutes as long as the humidity is within 60-70%. If it goes above 70% then publish to a different MQTT topic but this time send the alarm once every 15 minutes.
Given a number item myHumidity and string Items myHumWarn and myHumCrit, the rule would be something like that:
var Timer tHum = null
var Boolean tHumCrit = false
rule "humidity alarm"
when
Item myHumidity changed //humidity changed
then
if (myHumidity.state instanceof DecimalType) { //is a number?
if((myHumidity.state as DecimalType).intValue > 59 && myHumidity.state as DecimalType).intValue < 70 ) { //warning level
if (tHumCrit) { //was critical?
tHumCrit = false //not any longer
tHum.cancel
tHum = null
}
if(tHum == null) { //timer started?
tHum = createTimer(now.plusSeconds(1),[|
myHumWarn.sendCommand("Humidity Alert!")
tHum.reSchedule(now.plusHours(1))
])
}
}
else
if((myHumidity.state as DecimalType).intValue > 69 ) { //critical level
if(tHumCrit != true) { //was not critical?
tHumCrit = true
tHum.cancel //then set new timer...
tHum = createTimer(now.plusSeconds(1),[|
myHumCrit.sendCommand("Humidity Alert Critical!")
tHum.reSchedule(now.plusMinutes(15))
])
}
}
else
if(tHum != null) { //timer running?
tHum.cancel // cancel timer
tHum = null
}
}
else
logInfo("hum_alarm","myHumidity state error!") //value was not of type number
end
The idea is, to trigger the rule, if the item changed, then get the range, catch, if timer is still running, otherwise start timer with a short delay, when timer expires, send the message and reschedule the timer with 1 hour or rather 15 Minutes.
If not in range, cancel timer, so the messages will nor reappear.
This code is not tested, so there may be issuesā¦
A slightly simpler solution using the Expire Binding:
// same Items as Udo plus
Switch myHumWarn_Timer { expire="60m,command=OFF" }
Switch myHumCrit_Timer { expire="15m,command=OFF" }
import org.eclipse.xtext.xbase.lib.Functions
val Functions$Function<Boolean> setTimers = [|
val hum = myHumidity.state as Number
var zone = "SAFE"
if(hum > 70) zone = "CRITICAL"
else if(hum >=60 && hum < 70) zone = "WARN"
switch zone{
case "SAFE": {
myHumCrit_Timer.postUpdate(OFF)
myHumWarn_Timer.postUpdate(OFF)
}
case "WARN": {
myHumCrit_Timer.postUpdate(OFF)
if(myHumWarn_Timer.state != ON) myHumWarn_Timer.sendCommand(ON)
}
case "CRITICAL": {
if(myHumCrit_Timer.state != ON) myHumCrit_Timer.sendCommand(ON)
myHumWarn_Timer.postUpdate(OFF)
}
}
true
]
rule "humidity changed"
when
Item myHumidity changed
then
setTimers.apply()
end
rule "myHumCrit_Timer"
when
Item myHumCrit_Timer received command
then
if(receivedCommand == ON) myHumCrit.sendCommand("Humidity Alert Critical!")
else setTimers.apply()
end
rule "myHumWarn_Timer"
when
Item myHumWarn_Timer received command
then
if(receivedCommand == ON) myHumWarn.sendCommand("Humidity Alert!")
else setTimers.apply()
end
Theory of operation.
When the humidity gets into an alerting zone, the appropriate Timer Item is sent the ON command. If the humidity falls out of a danger zone, the appropriate Timer Item is post updated the OFF state. Using postUpdate will cancel the Expire Timer without triggering the received command rules.
The received command rules check to see if the command was ON, in which case it sends the alert message. If the command was OFF it means the Expire binding was the source so it is time to send the alert again but only if we are still in the danger zone.
OK, I admit this really isnāt any fewer lines of code but I maintain it is a little simpler because there isnāt as much Timer book keeping code. But it did introduce the lambda to keep from repeating code so maybe its a tossup.
Thank you both very much indeed for your prompt reply and apologies for not replying sooner. I have to admit that so far I have implemented @Udo_Hartmann rule and havenāt had a chance to try @rlkoshak though Iām grateful to both of you for your time.
It took me longer to reply because it wasnāt working and I was keen to debug the issue myself which was quite useful as I learnt a lot more doing so than I would have if I had simply replied to say that it wasnāt working. Anyway, I managed to narrow it down to the tHum.reSchedule having a capital S. Changing it to tHum.reschedule fixed the issue (if anyone wants to use this rule be mindful of this)
Hi guys,
Just playing with the code to trigger a critical event and Iām getting āRule āhumidity alarmā: cannot invoke method public abstract boolean org.eclipse.smarthome.model.script.actions.Timer.cancel() on nullā when the second part of the code is executed
else
if((myHumidity.state as DecimalType).intValue > 69 ) { //critical level
if(tHumCrit != true) { //was not critical?
tHumCrit = true
**tHum.cancel** //then set new timer...
tHum = createTimer(now.plusSeconds(1),[|
myHumCrit.sendCommand("Humidity Alert Critical!")
tHum.reSchedule(now.plusMinutes(15))
])
}
}
In my code, I did not take care of a humidity level over 69% AND no timer started, because the timer should have been start when humidity rose over 59%. But of course, if testing with values set by hand, this would be a possible conditionā¦
I tried to implement your rule, but it does not work. is it coded for OH2.2? Iām on 2.3.
the errormsg:
2018-08-22 22:00:20.226 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'humidity.rules', using it anyway:
The import 'org.eclipse.xtext.xbase.lib.Functions' is never used.
The field Tmp_humidityRules.setTimers refers to the missing type Object
The field Tmp_humidityRules.setTimers refers to the missing type Object
The field Tmp_humidityRules.setTimers refers to the missing type Object
somewhere I read that OH2.3. does not need any imports anymore?
however, when the humidity rule triggers I get this messages:
2018-08-22 21:57:58.669 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'humidity changed': 'apply' is not a member of 'Object'; line 30, column 5, length 17
humidity.rules
import org.eclipse.xtext.xbase.lib.Functions
val Functions$Function<Boolean> setTimers = [|
val hum = LED_Bad_hum.state as Number
var zone = "SAFE"
if(hum > 60) zone = "CRITICAL"
else if(hum >=50 && hum < 60) zone = "WARN"
switch zone{
case "SAFE": {
LED_Bad_humWarn_Timer.postUpdate(OFF)
LED_Bad_humCrit_Timer.postUpdate(OFF)
}
case "WARN": {
LED_Bad_humCrit_Timer.postUpdate(OFF)
if(LED_Bad_humWarn_Timer.state != ON) LED_Bad_humWarn_Timer.sendCommand(ON)
}
case "CRITICAL": {
if(LED_Bad_humCrit_Timer.state != ON) LED_Bad_humCrit_Timer.sendCommand(ON)
LED_Bad_humWarn_Timer.postUpdate(OFF)
}
}
true
]
rule "humidity changed"
when
Item LED_Bad_hum changed
then
setTimers.apply()
end
rule "LED_Bad_humCrit_Timer"
when
Item LED_Bad_humCrit_Timer received command
then
if(receivedCommand == ON) LED_Bad_humCrit.sendCommand("Humidity Critical Alert!")
else setTimers.apply()
end
rule "LED_Bad_humWarn_Timer"
when
Item LED_Bad_humWarn_Timer received command
then
if(receivedCommand == ON) LED_Bad_humWarn.sendCommand("Humidity Warning Alert!")
else setTimers.apply()
end
If you define the lambda like this you do need the import. The fact that it is saying that the import isnāt being used implies there is a typo or something in the definition. But you can define the lambda without needing to refer to Functions at all:
val setTimers = [ |
// blah blah blah
// don't put true as the last line of the lambda, it isn't needed
]
I donāt know what is wrong but that should at least address the warning about the import since you no longer need the import when you use this syntax.
I tried to expand the rule for a group of humidity items. Finally I had to avoid lambdas because I ran into troubles to handle triggeringItems status inside the lambda codeā¦
this is what I have coded now:
val hum = 0
rule "humidity changed"
when
Member of gHum changed
then
val hum = triggeringItem.state as Number //get humidity level from Item
val timerWarning = triggeringItem.name +"_humWarn_Timer" //append the triggering Item name with "_humWarn_Timer" so I can select the right timer. unfortunately this val is a string
val timerWarn = gHumTimer.members.findFirst[ i | i.name == timerWarning] //find the above string in the group. as the members of the group are switches, I can use sendCommand to shoot the timer
val timerCritical = triggeringItem.name +"_humCrit_Timer" //same procedure as for warning alerts
val timerCrit = gHumTimer.members.findFirst[ i | i.name == timerCritical]
var zone = "SAFE"
if(hum > 80) zone = "CRITICAL"
else if(hum >=70 && hum < 80) zone = "WARN"
switch zone{
case "SAFE": {
timerWarn.postUpdate(OFF)
timerCrit.postUpdate(OFF)
}
case "WARN": {
timerCrit.postUpdate(OFF)
if(timerWarn.state != ON) timerWarn.sendCommand(ON)
}
case "CRITICAL": {
if(timerCrit.state != ON) timerCrit.sendCommand(ON)
timerWarn.postUpdate(OFF)
}
}
Thread::sleep(250)
logInfo("settimer", timerWarning +": " + timerWarn.state.toString + ", " +timerCritical +": " + timerCrit.state.toString +", " + hum + "%")
end
rule "humidity Alert notification"
when
Member of gHumTimer received command
then
if(receivedCommand == ON) {
val alert = transform("MAP", "humidity.map", triggeringItem.name.toString) //just to transform the timer item into a more human readable form
val item = transform("MAP", "humidity.map", alert) //to find which humidity item is related to the Timer that got ON
val hum = gHum.members.findFirst[ i | i.name == item] //to transform the above string "item" into a openhab item and get the humidity level
var String humalert = alert + ": " + hum.state + "%"
humidityAlert.sendCommand(humalert)
sendNotification("m.xxxxxxx@gmx.at", "Luftfeuchtigkeit: " + alert + ": " + hum.state + "%")
}
end
first tests were successfull.
one difference to the lambda code from the previous posts is that as soon as the timers expire the rule immediately restarts the setTimers rule, that said as long as the humidity values are too high the warning or critical alert is ON. my code requires to receive an item update to re-activate the alerts, much less elegant I would say.
do you see any improvements I can do in that rules?