Device Offline Rule

Problem Statement

Often we have one device that populates the states of multiple Items. When that device goes offline, it can be important in some cases to modify those Items to UNDEF so Rules and the user knows that there is something wrong and it may take alternative actions.

In some cases, the binding will handle this for you. For example, the MQTT 2.x binding will set all Items linked to one of it’s Thing’s Channels to UNDEF if the connection to the broker is lost. But other bindings may not support this.

This posting provides an approach to get the same behavior using Rules.

Concept

Create a Group of all the Items that represent the online/offline status of a device that has multiple Items that are populated by it. I called this Group DeviceControllers.

For each device’s online status Item, create another Group using Design Pattern: Associated Items which and add all the Items populated by that device to that Group.

Example Items

Group:Switch:AND(ON, OFF) DeviceControllers

Switch vNest_Online "Nest Status [MAP(hvac.map):%s]"
    <network> (DeviceControllers)
    { nest="<[thermostats(Entryway).is_online]" }

Group vNest_Items

DateTime vNest_LastConnection "Last Nest Connection [%1$tm/%1$td %1$tH:%1$tM]"
    <time>
    { channel="nest:thermostat:nest:blahblahblah:last_connection" }

Number:Dimensionless vNest_Humidity "Main Floor Humidity [%d%%]" 
    <humidity> (gChart, gIndoorHumidity, vNest_Items) 
    { channel="nest:thermostat:nest:blahblahblah:humidity",
      name="Main floor humidity" }

Number:Temperature vNest_Temp "Main Floor Ambient Temp [%d °F]" 
    <temperature> (gChart, gIndoorTemps, gMaxTemp, LowerFloorsTemps, UpperFloorsTemps, vNest_Items) 
    { channel="nest:thermostat:nest:blahblahblah:temperature" }

String vNest_State "Heating State [%s]" 
    <fire> (gChart, vNest_Items)
    { channel="nest:thermostat:nest:blahblahblah:state" }
    
String vNest_Heating "Heating Active [%s]"
	<fire> (gChart, vNest_Items)
     
Number:Temperature aNest_TargetTemp "Furnace Target Temp [%.0f °F]" 
    <temperature> (gChart)
    { channel="nest:thermostat:nest:blahblahblah:set_point" }

Switch aNest_Fan "Fan [%s]"                      
    <fan> (gChart, vNest_Items) ["Switchable"]
    { channel="nest:thermostat:nest:blahblahblah:fan_timer_active" }

Notice how there are some Items where it makes sense to not reset to UNDEF like last contact.

Rules

Python

"""Rule that sets all Items assocaited with a given device to UNDEF when that
device is determined to be offline.

Author: Rich Koshak

Functions:
    - device_offline: Rule triggered by a member of DeviceControllers changing.
"""
from core.rules import rule
from core.triggers import when

@rule("Reset Offline", 
      description=("When a device that controls the state of multiple Items "
                   "goes offline, set all of those Items to UNDEF."),
      tags=["admin"])
@when("Member of DeviceControllers changed")
def device_offline(event):
    """Called when a member of DeviceControllers goes offline, finds the Items
    that are populted by that device and sets them to UNDEF. This Rule uses the
    Associated Items design pattern to get the Group of Items populated by the 
    given device.
    """
    if event.itemState == ON: 
        return
    device_items = "{}_Items".format(event.itemName.split("_")[0])
    for sensor in ir.getItem(device_items).members:
        events.postUpdate(sensor, UNDEF)

NOTE: The above uses the Helper Libraries.

Rules DSL

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "Reset Offline"
when
    Member of DeviceControllers changed
than
    if(triggeringItem.state == ON) return
    val items = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name.split("_").get(0)+"_Items")
    items.members.forEach[ i | i.postUpdate(UNDEF) ]
end

Theory of Operation

When a device goes offline, meaning it is any state that is not ON, we acquire the Group that has all the Items controlled by that device using Associated Items DP. Then we loop through them all and update them to UNDEF.

1 Like

I have a lot of things in my test script that I need to get into the docs! Instead of using a group and associated Items, you could trigger on Thing change and then iterate through the Items linked to the Thing’s Channels…

from core.rules import rule
from core.triggers import when
from core import osgi
ItemChannelLinkRegistry = osgi.get_service("org.eclipse.smarthome.core.thing.link.ItemChannelLinkRegistry")

@rule("Reset offline")
@when("Thing kodi:kodi:familyroom changed to OFFLINE")
def device_offline(event):
    for channel in things.get(event.thingUID).channels:
        for item in list(ItemChannelLinkRegistry.getLinkedItemNames(channel.UID)):
            events.postUpdate(item, "UNDEF")
            device_offline.log.warn("item [{}], state [{}]".format(item, items[item]))

I thought that all bindings were to update the state of linked Items to UNDEF when the Thing status changed to OFFLINE. Which bindings are you aware of that do not do this? In testing, I actually found some of my Kodi Items are not marked as UNDEF…

2019-08-29 20:32:45.635 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Volume], state [100]
2019-08-29 20:32:45.636 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Mute], state [OFF]
2019-08-29 20:32:45.637 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Control], state [PAUSE]
2019-08-29 20:32:45.638 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Stop], state [ON]
2019-08-29 20:32:45.639 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Playuri], state [UNDEF]
2019-08-29 20:32:45.640 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Pvropentv], state [UNDEF]
2019-08-29 20:32:45.641 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Pvropenradio], state [UNDEF]
2019-08-29 20:32:45.642 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Pvrchannel], state [UNDEF]
2019-08-29 20:32:45.643 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Notification], state [UNDEF]
2019-08-29 20:32:45.644 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Input], state [UNDEF]
2019-08-29 20:32:45.645 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Inputtext], state [UNDEF]
2019-08-29 20:32:45.646 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Inputaction], state [UNDEF]
2019-08-29 20:32:45.647 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Systemcommand], state [UNDEF]
2019-08-29 20:32:45.648 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Title], state [UNDEF]
2019-08-29 20:32:45.649 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Showtitle], state [UNDEF]
2019-08-29 20:32:45.650 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Album], state [UNDEF]
2019-08-29 20:32:45.651 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Artist], state [UNDEF]
2019-08-29 20:32:45.652 [WARN ] [jsr223.jython.Update status of Items linked to Channels of OFFLINE Thing] - item [DS_FamilyRoom_Kodi_Mediatype], state [UNDEF]

@cweitkamp, am I correct in my assumption that all Items linked to a Channel of an OFFLINE Thing should be marked as UNDEF?

That’s not always appropriate. Channels may carry info like “last successful read timestamp” which is still valid, or be write-only which (until we attempt another write) is still valid, etc.

Certainly with the Modbus binding we chose not to update to UNDEF on read failure (although the v1 binding did) because we have retry mechanisms. The user can manage their own UNDEF flags with expire quite easily (because its a polling binding)

It’s always a bit difficult to say how this sort of thing should go on with sleeping battery devices too.

There’s a bit of a fine line between “stale data” and “its broken” which is dependent on the binding technology.

2 Likes

That isn’t always possible. For example, when my NUT server goes offline there are no Things because it’s a1.x binding. A MQTT Thing won’t go offline even if it’s LWT is send because the connection to the broker is still good.

Not all bindings have Things. The meaning of “offline” isn’t always what you think it is for some bindings (see MQTT above).

Indeed, and it’s one of your posts on this topic in another thread that inspired this approach. With an Item based approach you can, for example, use a Network Binding Linked Item to detect when a service goes offline (NUT, Nest, etc) or, in the case of MQTT, receipt of the LWT from the device. In neither case would there be a Thing event to tell you the device is offline.

OK… that makes more sense. I was confused by your example Items.

Something similar can be done with the bridge then, but I guess that point is moot!

No, you are not. Items keep their last known state until the next update, change or restart of the system.

1 Like

The answer is somewhat more ambiguous than that though. For example, as currently implemented, if the MQTT Broker Thing loses it’s connection to the MQTT Broker, all the Items linked to all the Channels for all Things under that MQTT Broker Bridge get set to UNDEF. IIRC, David’s reasoning is that OH requires bindings to always report the known state for a Channel and in that case the known state is properly UNDEF. There are other bindings that do the same (Scott sites Kodi).

And this is very useful information for the automation. I’d much rather know that I don’t/can’t know what state a device is in and take proper action instead of having the Item just remain in it’s last state forever.

A couple of practical examples from my configuration:

  • Issue an alert if the garage door controller is offline when someone tries to trigger it through openHAB.
  • I’ve a number of temp sensors scattered through the house which help control my HVAC. If one of them is NULL or UNDEF I can use a backup/fail over sensor to drive the system.