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)
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:
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)
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()
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)
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…
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.
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 …
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 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.
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.