Hi again!
This example shows how we can correlate events from a single (or multiple) smart device(s) within a particular period of time.
This solution is not final or complete but can help new members to start with OpenHAB (at least that’s my objective
). It is based on all feedback and solutions shared by members of the community to my questions in this forum.
Automation #5: Boiler Failure Alert
Notify me if the boiler fires three Floor Heating Failures
and one Internal Failure
within one hour. Do not notify me more than once per hour.
Items
String BoilerAlarm { channel="mqtt:topic:MyBroker:Home:BoilerAlarm"}
Rules DSL Implementation
Note: Special thanks to @rlkoshak for providing this Rules DSL solution
import java.util.Map
// initialize the Map with zeros so we don't have to check for null in the Rule
val Map<String, Number> events = newHashMap("FHS_FAILURE" -> 0, "IS_FAILURE" -> 0)
var lastNotification = now.minusHours(1)
rule "(DSL) Boiler alert notification"
when
Item BoilerAlarm received update FHS_FAILURE or
Item BoilerAlarm received update IS_FAILURE
then
val eventStr = BoilerAlarm.state.toString
// Get current counter value for the event
val currCount = events.get(eventStr)
// Increment the count
events.put(eventStr, currCount+1)
// Create the Timer to decrement in an hour
// We don't ever need to cancel the Timer so we don't need to keep a handle on it
createTimer(now.now.plusHours(1), [ |
val count = events.get(eventStr)
events.put(eventStr, count-1)
])
// Check condition
if(events.get("FHS_FAILURE") >= 3 && events.get("IS_FAILURE") >= 1) {
if(lastNotification.isBefore(now.minusSeconds(5))) {
// send the notification
lastNotification = now
}
}
end
Jython Implementation
For the Jython counterpart, I will provide two versions: the first one using the OpenHab Timer + org.joda.time.DateTime
, and the second one using Jython threading.Timer + java.time.ZonedDateTime
.
Version 1
from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution
from org.joda.time import DateTime
import core
events = {"FHS_FAILURE": 0, "IS_FAILURE": 0}
lastNotification = DateTime.now().minusHours(1)
def update_counter(event):
global events
currCount = events[event]
events[event] = currCount-1
@rule("(Py) Boiler alert notification")
@when("Item BoilerAlarm received update FHS_FAILURE")
@when("Item BoilerAlarm received update IS_FAILURE")
def boiler_alert(event):
global events, lastNotification
eventStr = str(items.BoilerAlarm)
currCount = events[eventStr]
events[eventStr] = currCount+1
ScriptExecution.createTimer(DateTime.now().plusHours(1), lambda e=eventStr: update_counter(e))
if events["FHS_FAILURE"] >= 3 and events["IS_FAILURE"] >= 1:
if lastNotification.isBefore(DateTime.now().minusHours(1)):
// send the notification
lastNotification = DateTime.now()
Version 2
from core.rules import rule
from core.triggers import when
from threading import Timer
from java.time import ZonedDateTime as ZDateTime
import core
events = {"FHS_FAILURE": 0, "IS_FAILURE": 0}
lastNotification = ZDateTime.now().minusHours(5)
oneHour = 3.600 # seconds
def update_counter(event):
global events
currCount = events[event]
events[event] = currCount-1
@rule("(Py) Boiler alert notification")
@when("Item BoilerAlarm received update FHS_FAILURE")
@when("Item BoilerAlarm received update IS_FAILURE")
def boiler_alert(event):
global events, lastNotification
eventStr = str(items.BoilerAlarm)
currCount = events[eventStr]
events[eventStr] = currCount+1
Timer(oneHour, lambda e=eventStr: update_counter(e)).start()
if events["FHS_FAILURE"] >= 3 and events["IS_FAILURE"] >= 1:
if lastNotification.isBefore(ZDateTime.now().minusHours(1)):
// send the notification
lastNotification = ZDateTime.now()
Happy coding!
Humberto
Note :
- Suggestions or recommendations to improve the implementation are welcome!
- Do you have more complex automations that shares the same logic of this example? Please share it
Other automation examples