Automation #6: Boiler Failure Alert

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 :slightly_smiling_face:). 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 :slightly_smiling_face:

Other automation examples

3 Likes