Detecting offline Things in a less stupid way

You can simplify the lambda definition a bit:

val setOfflineAlarm = [ String thingId, SwitchItem alarmItem |
    val thingStatus = getThingStatusInfo(thingId)
    if ((thingStatus !== null) && (thingStatus.getStatus().toString() == "ONLINE")) {
        alarmItem.postUpdate(OFF)
    } else {
        alarmItem.postUpdate(ON)
    }
]

It’s smart enough to figure out what type setOfflineAlarm needs to be on it’s own and since you don’t actually use the return value, it will generate a Procedure instead of a Function.

3 Likes

Had to implement Things monitoring as well, however in my case quite often the Thing only goes offline for a couple seconds, before the Binding reconnects (ChromeCasts). Here are the two rules I use for this:

// use a timer to avoid notifications for short outages

rule "ChromeCast Living Room offline - timer"
when
    Thing "chromecast:chromecast:xyz123" changed from ONLINE to OFFLINE
then
    logInfo("ChromeCast Status", "ChromeCast Living Room offline")
    if (timerLivingRoom === null) {
        timerLivingRoom = createTimer(now.plusMinutes(1), [ |
            // there was no cancel because the Thing never came online in the past minute
            // ... send notification here
            logInfo("ChromeCast Status", "ChromeCast Living Room is offline")
        ])
    }
end

rule "ChromeCast Living Room online - timer"
when
    Thing "chromecast:chromecast:xyz123" changed from OFFLINE to ONLINE
then
    logInfo("ChromeCast Status", "ChromeCast Living Room is online")
    if (timerLivingRoom === null) {
        // recovered from longer downtime, > 1 minute
        logInfo("ChromeCast Status", "Timer for chromecast:chromecast:xyz123 is already stopped")
    } else {
        logInfo("ChromeCast Status Debug", "Timer for chromecast:chromecast:xyz123 is running, stopping it")
        timerLivingRoom.cancel()
        timerLivingRoom = null
        // no further notification required, was a short downtime
    }
end

Need to pour that with more details into a blog post as well …

The same thing with HABApp:

from HABApp import Rule
from HABApp.core.events import ItemNoChangeEvent
from HABApp.openhab.items import Thing


class CheckThing(Rule):
    def __init__(self, name: str):
        super().__init__()
        
        self.thing = Thing.get_item(name)
        watcher = self.thing.watch_change(60)
        self.thing.listen_event(self.thing_no_change, watcher.EVENT)
    
    def thing_no_change(self, event: ItemNoChangeEvent):
        print(f'Thing {event.name} constant for {event.seconds}')
        print(f'Status: {self.thing.status}')


CheckThing('chromecast:chromecast:xyz123')
1 Like

I was looking for the same thing but I didn’t want to write a rule for every thing so I choose to use the logreader binding to trap ONLINE/OFFLINE events and an hashmap to associate things to items.

You might be able to put all ChromeCasts into a group, and then react on changes in the group.

In the end I decided to write them with a loop in the Ansible template. This way I change the template and when it is deployed, it created a rule for every CC.

I use this

I was looking for the same thing but I didn’t want to write a rule for every thing so I choose to use the logreader binding to trap ONLINE/OFFLINE events and an hashmap to associate things to items.

Rickytr, can you share your rule using Log Reader? I’m trying this but can’t get it working.

@fullmoonguru I used the trigger to catch a new custom event and added every received event to a linked queue to be processed (because usually you receive more than one event if a thing goes offline…)

rule “Event log management”
when
Channel “logreader:reader:b211c4e3:newCustomEvent” triggered
then
if (logEvents.peek === null && evento.state.toString == “ON”)
evento.postUpdate (OFF)
logEvents.add(receivedEvent.toString())
if(timer === null && evento.state.toString != “ON”) {
timer = createTimer(now.plusSeconds(5), [ |
if (evento.state.toString != “ON”) {
evento.sendCommand(ON)
timer = null
}
])
}
end

I put in patterns for the logreader thing “to ONLINE|to OFFLINE” and filtered out ItemStateChangedEvent to avoid the loop.

1 Like

@Rickytr I’m pretty new at this. When you say:

I put in patterns for the logreader thing “to ONLINE|to OFFLINE” and filtered out ItemStateChangedEvent to avoid the loop.

How did you do that?

Then in the script, I’m not clear what it’s doing. It looks like it’s posting an event (a virtual switch you created?) as OFF if something is offline and ON if nothing is offline. Not clear what teh timer is doing.

Sorry for my noob questions & thanks for your help.

You’ll want to read up on

@fullmoonguru I use the switch to start another piece of code that parse every event in the linked queue. I decided to use the timer to start parsing after some seconds because in this way I can manage better when I receive several offline errors together, for example when Hue hub goes offline with every connected light.

@rossko57 thanks for replying I DID read up on this before trying it. Here’s what I tried:

First I did:

Rule ID = Device_Offline_Alert

When > Thing Event > Log Reader > A Trigger Channel Fired > Channel > New Error Event > Event = COMMUNICATION_ERROR

Then > Execute a script (my email script)

That didn’t work so then I tried:

When > Thing Event > Log Reader > Trigger Channel Fires > Channel: newCustomEvent > Event = COMMUNICATION_ERROR

No luck there either.

I also tried Status change to OFFLINE and Status updated to OFFLINE, in the Logreader thing which didn’t work (I assume that’s because it’s referring to the Logreader thing itself).

I can see the alerts in the logfile so I know that’s working, and I know the email is working because I can get successful results by making the trigger an offline alert from a specific Thing.

@rossko57 do you have any ideas why this isn’t working for me?

is difficult to diagnose. Did the rule run, is it your email that doesn’t work? It’s up to you to find out. You could add a log at the start of your rule to say when it’s triggered.

Do you see that in your events.log ? May we see? Does it match your rule trigger? May we see that? ('code’if you are using UI rules)

Are there any new options in OH3 to support “monitoring a group of things”
a) thing status changed
b) certain channels are triggered

This should be based on groups. I just started to implement a rule monitoring my Shellys in an alarm is triggered. After the 15h “Chanel xxx triggered oir” I stopped and looking for other options. For me it doesn’t make sense that I need to list every channel and hard code the thing uid. Why is it not possible to build a group of channels?
or could I

  • build a group of things (possible with the OH3 model)
  • have a periodic rule (once a minute)
  • iterate on the group, get thing status and check for OFFLINE

That would be ok for checking the thing status, but even doesn’t cover the aspect that things raise an alarm by triggering a channel.

As with pretty much all “why” questions in openHAB, the answer is because no one has implemented it. Adding support for something like this will require changes to the core API and rule engine triggers since the ThingRegistry doesn’t have any way to get all Things or all Things with a given tag or anything like that.

Not possible, The model is just a way to organize your Items.

This is very much possible.

Given you can’t use the ThingRegistry to get the list of all Things and you can’t create a Group of Things about the only remaining thing you can do is to query the REST API for all the Things and parse through the returned JSON. You could put that into a once a minute triggered rule or the like.

@rossko57 I had a logfile running and could see the Thing drop. I just never saw the rule trigger. The email is the same script I use for other alerts and I also manually triggered the rule and got the email. I am working on some other issues right now but I’ll get back on this and try to give more detailed info. I appreciate the help.

@rossko57 OK, back on this. This is the log entry I get when a device drops.

2021-05-10 15:41:11.368 [INFO ] [ab.event.ThingStatusInfoChangedEvent] - Thing 'zwave:device:Z-Wave_Serial_Controller:node4' changed from ONLINE to OFFLINE (COMMUNICATION_ERROR): Node is not communicating with controller

I’m not sure if flagging an error alert doesn’t work because this is an info alert? How would you use Log Reader to trigger from something in this log?

No idea. How have you configured your log reader?

I wouldn’t. Personal choice, I would trigger a rule from Thing xxx:xxx changed

Thank you for this piece of code. I was looking for a way to check the status of things in HABApp because Homematic stuff needs some time after booting to initialize. HABApp is to fast. Is there also a way to come from an OpenHAB item to the linked thing to get the status?