Detecting offline Things in a less stupid way

This is an interesting topic, and one that I have spent quite some time on pondering since I took my very first steps with OpenHAB several years ago…

To test the “internal monitoring” approach I have had the followig rule running for a while on one of my Z-Wave devices, and it seems to do the job:

rule "B01T010"
when
    Thing "zwave:device:e402b8c8:node26" changed
then
    var Status = ThingAction.getThingStatusInfo("zwave:device:e402b8c8:node26").getStatus()
		logInfo("b_zwave", "Node 26 " + Status)
		if (Status.toString() == "OFFLINE")	{
			B01T010_sOnline.postUpdate(OFF)
		}
		else {
			B01T010_sOnline.postUpdate(ON)
		}
end

I completetly agree that this is not a very elegant way to monitor the status of devices, at least if you have more than one, :slight_smile:

A more conceptual problem with this approach is that I am using the home automation system to monitor itself - something I believe goes against the basic philosphy of system monitoring.

For this reason I am currently playing around with using the REST API, but not from the inside by using OpenHAB rules (as proposed by @rlkoshak above). Instead I am using an external component (a Python script) to monitor this from the outside.

The longtime goal is to feed this into some other monitoring solution, like e.g. Icinga or Nagios.

2 Likes

Another interesting approach that fits into my current mental exercise on this subject, :slight_smile:

Granted, I am a bit biased since I am currently working with SCADA systems on the industrial automation scene, but I believe that to properly address this you first need to define your setup in terms of the process that your are monitoring and controlling (i.e. your house) and the system that you are using to monitor and controll the process (i.e. OpenHAB with various physical devices attached).

The control of the process (i.e. OpenHAB rules) should only care about whether the value (e.g. from a sensor) it needs to perform its task is valid or not. For this purpose the proposal from @spaceman_spiff is very well suited. If the value is different from NULL, then proceed. If the value is equal to NULL, then handle error condition.

Now, the next logical step is to monitor your system to make sure that it operates as intended (i.e. is able to control the process). For this purpose I think using the REST API is the best approach (at least with the current state of affairs). Again, in my view, and going with the philosophy of system monitoring in general, this should be done from the outside by a different system from the system that runs the process control.

2 Likes

I just want to clarify, because I am not sure everybody knows it.

This is the standard approach to monitor the status of a thing. Right?

rule "Onkyo offline"
when
    Thing 'onkyo:onkyoAVR:avr1' changed from ONLINE to OFFLINE
then
    logInfo("OnkyoOffline", "Onkyo is offline!")
    sendNotification("me@example.comt", "Onkyo is offline!")
end

rule "Onkyo online"
when
    Thing 'onkyo:onkyoAVR:avr1' changed from OFFLINE to ONLINE
then
    logInfo("OnkyoOnline", "Onkyo is online!")
    sendNotification("me@example.com", "Onkyo is online!")
end

But @dan12345 is right.
There should be an easier way instead of creating these two rules for every thing.
But then again, it is not that hard to copy&paste these rules for new things. :thinking:

4 Likes

On top of my head I think linking a thingStatus to an (switch) item would be a possible dev solution? Then we would have the possibility to iterate on groups etc.

Thank you all for your suggestions.

The Expire binding/NULL error checking is a great idea, and I’ll go ahead and do that. Must make sense to combine with an external script that checks across all Things - my crappy Expect script already does that, although I’d be very interested to see something more sophisticated, e.g. in Python.

Dan

The main reason I didn’t recommend that to OP in the first place is because anything based on the Expire binding is going to require that the Item linked to the channel to receive an update or command on a regular schedule (e.g. always posts update once per hour whether there is a change in state or not). I wasn’t sure if all of the Things op was interested in do that and I didn’t want to waste time if they don’t.

For completeness, here is a generic approach that will work if the Items are periodically updated whether or not there is a change.

Look at the bottom of the top post for the Expire based example.

When one is using the MQTT binding (subscribe to the topics the device reports on and set the online switch to ON for any message) or devices that have a heartbeat channel (some ZWave devices, e.g zcombo smoke/co alarms) or online status channel (e.g. Nest) you don’t need most of the code in that example as the Expire binding handles everything but the reporting.

But can you iterate over all the Things to get their status? That is the core of OP’s problem.

I agree with this statement and OH itself really isn’t built that well to support this type of monitoring. However, often in a Rule one will want to do something different if OH can know that something is down so whatever system you are using needs to report this status to OH which kind of puts you back in the same situation of OH monitoring itself.

But I think of it like this. OH really isn’t monitoring itself in this case, it is monitoring the status of the actuators and sensors that it interacts with and their online status can be very important. I suppose it all depends on where do you draw the boundaries between OH and Not OH.

I believe so.

1 Like

Just upgraded to 2.4, and ThingAction is no longer working.

This is my rule

rule "TV Binding State Changed"
when
    Thing "samsungtv:tv:familyroom" changed
then
    var status = ThingAction.getThingStatusInfo("samsungtv:tv:familyroom").getStatus()
    logInfo("Family Room TV", "TV Thing status changed to: " + status)
    postUpdate(TV_Status, status.toString)
end

This throws the error

23-Dec-2018 14:30:46.570 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'TV Binding State Changed': The name 'ThingAction' cannot be resolved to an item or type; line 3586, column 18, length 11
23-Dec-2018 14:30:46.570 [DEBUG]

Has anyone else seen this since upgrading to 2.4?

Thanks,

Just found that. Another breaking change.
Been fighting them for 4 days since the upgrade, quite a lot in 2.4.

Just tracking down the last few lurking breaking changes now though…

Thanks for the link, I was having a hard time finding a list of breaking changes in 2.4.

Thanks for inspiring me with this discussion! I added some Thing monitoring to my rules and optimized logging to suppress some unavailable or unhelpful strings from getThingStatusInfo():

rule "TV"
when
    Thing 'samsungtv:tv:14dc9380_XXX' changed
then
    val ThingStatusInfo = getThingStatusInfo("samsungtv:tv:14dc9380_XXX")
    if(ThingStatusInfo===null) return;
    var String thingStatusInfo_Msg = ""
    if(ThingStatusInfo.statusDetail.toString!="NONE")	thingStatusInfo_Msg = thingStatusInfo_Msg + ThingStatusInfo.statusDetail
    if(ThingStatusInfo.description!==null)				thingStatusInfo_Msg = thingStatusInfo_Msg + ThingStatusInfo.description
    if(thingStatusInfo_Msg!="")	logInfo("RULE","@R TV: {} [{}]", ThingStatusInfo.status, thingStatusInfo_Msg)
    else						logInfo("RULE","@R TV: {}", ThingStatusInfo.status)
end

I agree with @rlkoshak that adding a rule for each Thing is suboptimal.
It would be easier if something like triggeringItem exists for Things as you may find in this non-working example:

rule "TV"
when
    Thing 'samsungtv:tv:livingroom' changed or
    Thing 'samsungtv:tv:bedroom' changed or
    Thing 'samsungtv:tv:kitchen' changed
then
    val TriggeredThing = triggeringThing
    val ThingStatusInfo = getThingStatusInfo(TriggeredThing)

Would it make sense to open an issue for this?

Cheers,
Alex

The new rule engine handles this, so IMO it is doubtful much effort would go into adding features to the old rule engine, but you never know!

hmmm…I was warned to revive the topic after one year, but I think it might be quite of interest and want to contribute a solution that was not mentioned here before (I hope) and did do the trick for me.
I dont want to really monitor all my things but for sure also not create a rule for every single thing. But creating an item to hold the status for every thing I want to monitor seemed ok for me. So I use http binding with REST API and very simply created an item:

String OnlineAstroMoon "Online AstroMoon [%s]" (gOnline) { http="<[http://localhost:8080/rest/things/astro:moon:gg:30000:JSONPATH($.statusInfo.status)]"}

You can have than a rule for gOnline group changed if you want to alert getting the triggering item.
You could do some more transform in JS to use a switch item (having JS return ON/OFF)
You can also do more transform in JS to get more out of the Json returned by the REST api (thing label or thing UID…)
And through http binding you can obviously set the timing how often you want to check the thing.
And also nice in my experience: You can monitor via persistance (using Grafana or similar)

2 Likes

I can contribute something for all the HABApp users, too.
HABApp listens to the Thing events and it is really easy to trigger on a change of the status.

import HABApp
from HABApp import Rule
from HABApp.openhab.events import ThingStatusInfoChangedEvent
from HABApp.openhab.items import Thing


class CheckAllThings(Rule):
    def __init__(self):
        super().__init__()
        
        for thing in HABApp.core.Items.get_all_items():
            if isinstance(thing, Thing):
                thing.listen_event(self.thing_status_changed, ThingStatusInfoChangedEvent)
                print(f'{thing.name}: {thing.status}')
    
    def thing_status_changed(self, event: ThingStatusInfoChangedEvent):
        print(f'{event.name} changed from {event.old_status} to {event.status}')


CheckAllThings()

Sorry for my ignorance, but what is HABApp? Cannot find anything in the docs … :joy:

grafik

Python3 Rule engine which you can easily spin up next to openhab, normally I link it but I guess I forgot:

1 Like

Cool solution @haesslo many thinks!

I’m not an expert in JS, so can you please give some tips about how to do that? I assume you would add a script to the rest api url?

I tried and what should work is :
Define item as switch with http binding and transform JS:
Switch OnlineAstroMoon2 "Online AstroMoon [%s]" (gOnline) { http="<[http://localhost:8080/rest/things/astro:moon:gg:30000:JS(online.js)]"}

Then in your transform directory you create a fiel “online.js” which should contain:

(function(i) {
	var obj = JSON.parse(i); 
	var status	= obj.statusInfo.status;
	if (status == "ONLINE") {
			return "ON"
	} else {
			return "OFF"
	}		
})(input)
2 Likes

Hi @haesslo many thanks for the suggestion. I am not sure if it is really much simpler than creating a rule though. :slight_smile:

Maybe question of taste, but just to clarify:
The rules proposed here were one rule per Thing
My solution is one item per thing and one generic JS for all, which is only needed if you want to have the ON/OFF feature.
I feel managing items is much easier than managing rules…

Just for information, this is how I do it (in a rule)…

// lambda to update an (Alarm) Item to reflect a Thing's Offline state
val Functions$Function2<String, SwitchItem, Boolean> setOfflineAlarm = 
[ String thingId, SwitchItem alarmItem |
    val thingStatus = getThingStatusInfo(thingId)
    if ((thingStatus !== null) && (thingStatus.getStatus().toString() == "ONLINE")) {
        alarmItem.postUpdate(OFF)
    } else {
        alarmItem.postUpdate(ON)
    }
    true
]

rule "Hub Offline Status Checks (at *:*:5)"
when
    Time cron "5 1/1 * * * ?"
then
    setOfflineAlarm.apply("siemensrds:climatixic:cloud", Siemens_Cloud_Server_Offline_Alarm)
    setOfflineAlarm.apply("neohub:neohub:g24", Heatmiser_NeoHub_Offline_Alarm)
    setOfflineAlarm.apply("tado:home:g24", Tado_Home_Hub_Offline_Alarm)
    setOfflineAlarm.apply("hdpowerview:hub:g24", Luxaflex_Hub_Offline_Alarm)
    setOfflineAlarm.apply("hue:bridge:g24", Philips_Hue_Hub_Offline_Alarm)
    setOfflineAlarm.apply("harmonyhub:hub:g24", Logitech_Harmony_Hub_Offline_Alarm)
    setOfflineAlarm.apply("velux:klf200:g24", Velux_KLF200_Hub_Offline_Alarm)
    setOfflineAlarm.apply("zwave:serial_zstick:g24", ZWave_USB_Stick_Offline_Alarm)
    setOfflineAlarm.apply("astro:sun:g24", Astro_Offline_Alarm)
    setOfflineAlarm.apply("homeconnect:api_bridge:g24", Bosch_Home_Connect_Offline_Alarm)
    setOfflineAlarm.apply("darksky:weather-api:g24", DarkSky_Weather_API_Offline_Alarm)
end
1 Like