[Deprecated] Design Patterns: Generic Is Alive

There is now a Rule Template on the marketplace to implement this for you. See below for instructions on how to set it up.

Please see Design Pattern: What is a Design Pattern and How Do I Use Them for a description of what Design Patterns are and how to use them.

Problem Statement

Many sensors report status to openHAB periodically (e.g. temperature sensor) or unpredictably (e.g. motion sensors) and a subset of these sensors do not have a built in way for openHAB to query whether or not they are alive. The only way for openHAB to detect that these devices are still alive is to assume they are dead if they have not communicated after a certain period of time.


Create Timers that get reset every time the sensor Item receives and update. If a certain amount of time passes (I recommend at least 2x the device’s normal reporting period) set the Item to UNDEF to indicate that it is no longer reporting. I recommend using the Expire binding for this.

Alternatively you can create a separate Switch Item that gets set to ON when the sensor updates and a Timer to set it to OFF.

If one is using MQTT, one can bind the previously mentioned Switch to the Last-Will-and-Testament topic for the device that communicates over MQTT to turn off.

Open Reminder Rule Template

Install Template and Instantiate and Configure the Rule

This rule template implements this design pattern. Configure it as follows:

  1. Create a Group and add all the Items that represent a sensor reading to that Group. We’ll call it “AllSensors”.

  2. Add expire metadata to all the Items added to the AllSensors Group so that they get set to UNDEF if they do not receive an update for too long.

  3. Create a rule or a script that will be called when the sensor is determined to be offline. This rule can do anything you want including sending an alert, changing the state of a Switch Item (tracks the online status of the sensor), take remedial action like restarting a binding or a service, etc. See below for an example.

  4. Install the Open Reminder Rule Template from MainUI → Settings → Automation.

  5. Create a new rule and choose “Open Reminder” as the template.

  6. Configure the rule as follows:

  • give the rule a meaningful ID, name and description

  • choose AllSensors as the Group

  • type in “UNDEF” for the Alert State

  • toggle Invert to ON meaning we want to alert when the Item goes to UNDEF, not changing away from UNDEF

  • Provide a default initial timeout, the amount of time before the alert is generated. Use ISO8601 format (e.g. "PT15m " means 15 minutes).

  • You can provide the namespace for Item metadata to use instead of the default initial timeout. This can be useful if you want to have a different timeout for each sensor.

  • If you want to get repeated alerts, set the duration between alerts, again using ISO8601 format. Leaving blank will alert only the one time.

  • Leave Reschedule toggled off. This option is useful for motion sensors where you might not want to alert until a certain amount of time after the last motion is detected.

  • Select the rule you created in step 3 as the “Alert Rule”

  • Finally, if desired, set a Do Not Disturb period. Alerts that occur during this period will be suppressed until the end of the period, if the Item is still in the alerting state. If the end time is before the start time, the period is assumed to span midnight.

  1. Click “Save” and you are done.

If you also want alerts when the sensor comes back online, create another rule with the same settings only leave Invert toggled OFF.

Processing Rule

The rule you created in step 3 above will be called by the rule instantiated from the rule template. The Item that caused the rule to be called will be stored in the context as alertItem and the state of the Item that caused the rule to be called will be stored in the context as currState. Access to the context depends on the Rules Language. In JS Scripting, they are just inserted as stand alone variables.

As previously mentioned, the called script can do anything desired. I’ve set mine up to keep track if we’ve already alerted on this piece of Equipment (based on the Semantic Model and with the help of a Switch Item) so I only get one alert for the whole equipment, even if more than one sensor went offline for that Equipment.

All code is in JS Scripting ECMAScript 11.

The script condition for that is:

var { itemRegistry } = require('@runtime');

var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item
var statusItem = items.getItem(equipment.name + '_Status');
statusItem.state == 'ON';

Notice the use of Design Pattern: Associated Items to obtain the helper Switch Item.

The script action is

var { itemRegistry } = require('@runtime');
var {alerting} = require('rlk_personal');
var logger = log('Sensor Offline');

var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item
var statusItem = items.getItem(equipment.name + '_Status').postUpdate('OFF');

alerting.sendAlert(equipment.label + ' has stopped reporting and is likely offline');

alerting is from my personal library and it sends broadcast alerts and/or email based on a bunch of conditions.

The whole rule is as follows:

configuration: {}
triggers: []
  - inputs: {}
    id: "2"
      type: application/javascript;version=ECMAScript-2021
      script: >
        var { itemRegistry } = require('@runtime');

        //console.log('Received an alert on ' + alertItem);

        var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item

        var statusItem = items.getItem(equipment.name + '_Status');

        //console.log('received an offline alert for equipment ' + equipment.name + ' and status Item ' + statusItem.name);

        // Just went offline

        statusItem.state == 'ON';
    type: script.ScriptCondition
  - inputs: {}
    id: "1"
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var { itemRegistry } = require('@runtime');

        var {alerting} = require('rlk_personal');

        var logger = log('Sensor Offline');

        var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item

        var statusItem = items.getItem(equipment.name + '_Status').postUpdate('OFF');

        alerting.sendAlert(equipment.label + ' has stopped reporting and is likely offline');
    type: script.ScriptAction

Related Design Patterns

Design Pattern How It’s Used
Design Pattern: Associated Items Building up the name of an Item to postUpdate or sendCommand based on the name of triggeringItem. Used in the Switch Status Item Example to update the status switch. Used in the complex working example to update the Alerted Switch.


The remaining examples are kept for historical purposes but they should be considered deprecated and no longer supported.

Expire Binding Example


Group:Switch DeviceSatuses // we must give the Group a type
Number Item1Sensor1 (DeviceStatuses) { expire="10m" }
Contact Item1Sensor2 (DeviceStatuses) { expire="10m" }
Switch Item1Sensor3 (DeviceStatuses) { expire="10m" }

Number Item2Sensor1 (DeviceStatuses) { expire="6m" }
Contact Item2Sensor2 (DeviceStatuses) { expire="6m" }
Switch Item2Sensor3 (DeviceStatuses) { expire="6m" }

The Group is optional and only required if you want to generate an Alert when one of these Items goes to UNDEF.



None. If you want to generate and Alert when a device goes to UNDEF use the following Rule.

from core.rules import rule
from core.triggers import when

@rule("A sensor stopped reporting")
@when("Member of DeviceStatuses changed to UNDEF")
def offline_alert(event):
    # report alert on event.itemName

Rules DSL

None. If you want to generate an Alert when a device goes to UNDEF use the following Rule.

rule "A sensor stopped reporting"
    Member of DeviceStatuses changed to UNDEF
    // report alert on triggeringItem

Theory of Operation

Every time the Item gets updated the Expire binding sets/resets a Timer. When the configured amount of time passes without an update, but default the Expire binding will set the Item to UNDEF. In your Rules, be sure to check for UNDEF before trying to use the Item.

The Rule above gets triggered any time a member of DeviceStatuses changes to UNDEF where you can generate an Alert or otherwise take remedial actions.

On the Sitemap or HABPanel, Items that are UNDEF will appear as -.

Switch Status Item Example


Group:Switch:AND(ON,OFF) DeviceStatuses "Device Status [%s]"

Group:Switch DeviceUpdates

Switch Device1_Status "Device 1 Status [%s]" <network> (DeviceStatuses)
Number Device1_Sensor1 (DeviceUpdates)
Contact Device1_Sensor2 (DeviceUpdates)
Switch Device1_Sensor3 (DeviceUpdates)

Switch Device2_Status "Device 2 Status [%s]" <network> (DeviceStatuses)
Number Device2_Sensor1 (DeviceUpdates)
Contact Device2_Sensor2 (DeviceUpdates)
Switch Device2_Sensor3 (DeviceUpdates)

Each device has more than one sensor. The online status of the device is rolled up into a status Switch. The Expire binding can and should be used here as well but I’ll show a Timer based solution for completeness. To use the Expire binding based approach use the following on the Status Switch Items:

{ expire="10m,command=OFF" }


from core.rules import rule
from core.triggers import when
from core.actions import ScriptExecution
from org.joda.time import DateTime

timers = {}

@rule("Process sensor update")
@when("Member of DeviceUpdates received update")
def sensor_update(event):
    if event.itemName not in timers:
            events.sendCommand("{}_Status".format(event.itemName.split("_")[0]), "OFF")

@rule("A device stopped reporting")
@when("Member of DeviceStatuses received command OFF")
def sensor_offline(event):
    # alert

Rules DSL

import java.util.Map

val Map<String, Timer> timers = newHashMap

rule "Process sensor update"
    Member of DeviceUpdates received update
    if(timers.get(triggeringItem.name) === null) {
        createTimer(now.plusMinutes(10), [ | 
            sendCommand(triggeringItem.name.split("_").get(0)+"_Status", "OFF")
    else timers.get(triggeringItem.name.reschedule(now.plusSeconds(10)))

rule "A device stopped reporting"
    Member of DeviceStatuses received command OFF
    // alert

Complex Working Example

This is based on old code that was written before the Expire binding was created. I plan on rewriting at some point to use Expire binding and UNDEF as shown in the first example.


Group:Switch:AND(ON, OFF) gSensorStatus "Sensor's Status [MAP(admin.map):%s]"  <network>

Group:Switch gOfflineAlerted

// Sonoffs
Switch vSonoff_3157_Online "Powercord 3157 [MAP(admin.map):%s]" <network> (gResetExpire) { mqtt="<[mosquitto:tele/sonoff-3157/LWT:state:MAP(sonoff.map)", epxire="24h,state=OFF" }
Switch vSonoff_3157_Online_Alerted (gOfflineAlerted)

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

// Network
Switch vNetwork_Cerberos "Cerberos Network [MAP(admin.map):%s]" <network> (gSensorStatus, gResetExpire) { channel="network:servicedevice:cerberos:online", expire="2m" }
Switch vNetwork_Cerberos_Alerted (gOfflineAlerted)

// Services
Switch vCerberos_SensorReporter_Online "Cerberos sensorReporter [MAP(admin.map):%s]" <network> (gSensorStatus, gResetExpire) { mqtt="<[mosquitto:status/sensor-reporters:command:OFF:.*cerberos sensorReporter is dead.*],<[mosquitto:status/cerberos/heartbeat/string:command:ON]", expire="11m,command=OFF" }
Switch vCerberos_SensorReporter_Online_Alerted (gOfflineAlerted)

// Zwave devices
Switch vMainFloorSmokeCOAlarm_Heartbeat "Main Floor Smoke/CO Alarm is [MAP(admin.map):%s]"    <network> (gAlarmStatus, gSensorStatus, gResetExpire) { channel="zwave:device:dongle:node5:alarm_general", expire="24h,command=OFF" }
Switch vMainFloorSmokeCOAlarm_Heartbeat_Alerted (gOfflineAlerted)

Examples of a variety of sensor types are shown above. Each has an associated Offline Alerted Item to prevent multiple alerts about the device over a given period of time.


Note: this version of the code differs slightly from the Rules version below. It also uses Timer Manager from the Helper Libraries to create and maintain the timers. The code supports antiflapping timers and uses Design Pattern: Using Item Metadata as an Alternative to Several DPs to keep track of whether we’ve alerted that a device is offline so we can alert again when it returns back online.

"""Rules to keep track of whether or not a device has gone offline or not and
generate an alert message when it goes offline or return online.

Author: Rich Koshak

    - alert_timer_expired: Called when a device changes state and stays that way
    for enough time that it's not flapping.
    - alert_timer_flapping: Called when a device changes state too rapidly
    indicating it's flapping.
    - status_alert: Rule called when a sensor changes state and sends an alert
    if necessary.
    - status_reminder: Rule triggered at 8am every morning to issue a report
    with all the known offline devices.
    - pm_online: Called when the Zwave power meter Thing changes state.
    - heartbeat: Called when a member of SensorEvents receives an update
    indicating the device is online.
from threading import Timer
from core.rules import rule
from core.triggers import when
from core.metadata import get_key_value, set_metadata
from core.actions import Transformation
from core.log import log_traceback
from personal.util import send_info, get_name
from personal.timer_mgr import TimerMgr

timers = TimerMgr()

def alert_timer_expired(itemName, name, origState, log):
    """Called when we determine that a sensor's online state is not flapping.

        - itemName: Name of the sensor's Item.
        - name: Human friendly name of the sensor.
        - origState: The state that originally triggered the Timer to check for
        - log: Logger from the triggering Rule.
    on_off_map = { ON: 'online', OFF: 'offline' }
    alerted = get_key_value(itemName, "Alert", "alerted") or "OFF"

    if items[itemName] != origState:
        log.warning("In alert_timer_expired and {}'s current state of {} is "
                    "different from it's original state of {}."
                    .format(name, items[itemName], origState))

    # If our alerted flag equals the Item's state we need to generate an alert
    if str(items[itemName]) == alerted:
        send_info("{} is now {}".format(name, on_off_map[items[itemName]]), log)
                     { "alerted": 'OFF' if alerted == 'ON'else 'ON' },
        log.warning("Alert timer expired but curr state doesn't match alert {} "
                    "!= {}".format(name, items[itemName], alerted))

def alert_timer_flapping(itemName, name, log):
    """Called when a sensor's online state appears to be flapping.
        - itemName: Name of the sensor Item.
        - name: Human friendly name of the sensor.
        - log: Logger from the triggering Rule.

    alerted = get_key_value(itemName, "Alert", "alerted") or "OFF"
    log.warning("{} is flapping! Alerted = {} and current state = {}"
                .format(name, alerted, items[itemName]))

@rule("Device online/offline",
      description="A device we track it's online/offline status changed state",
@when("Member of gSensorStatus changed")
def status_alert(event):
    """Triggered when a member of gSensorStatus changes. We don't care if the
    sensor changed from a UnDefType. Set a Timer to see if the device is

    name = get_name(event.itemName)

    if isinstance(event.oldItemState, UnDefType):
        status_alert.log.warning("{} is in an undef type, canceling any running "

                 lambda: alert_timer_expired(event.itemName,
                lambda: alert_timer_flapping(event.itemName,

@rule("System status reminder",
      description=("Send a message with a list of offline sensors at 08:00 and "
                   "System start"),
@when("Time cron 0 0 8 * * ?")
@when("System started")
def status_reminder(event):
    """Called at system start and at 8 AM and generates a report of the known
    offline sensors

    numNull = len([i for i in ir.getItem("gSensorStatus").members
                   if isinstance(i.state, UnDefType)])
    if numNull > 0:
        status_reminder.log.warning("There are {} sensors in an unknown state!"

    offline = [i for i in ir.getItem("gSensorStatus").members if i.state == OFF]
    if len(offline) == 0:
        status_reminder.log.info("All sensors are online")

    offline_str = ", ".join(["{}".format(get_name(s.name)) for s in offline ])
    offline_message = ("The following sensors are known to be offline: {}"

    for sensor in offline:
        set_metadata(sensor.name, "Alert", { "alerted" : "ON"}, overwrite=False)
    send_info(offline_message, status_reminder.log)

Theory of Operation

When a member of gSensorStatus changes we trigger the online/offline Rule.

If the previous state was NULL or UNDEF we cancel any running Timers and ignore the event.

timers.check() causes the Timer Manager to look to see if there is already a timer scheduled for event.itemName. If there is, it reschedules it for one minute into the future and the “alert_timer_flapping” lambda get’s called allowing us to do something in the case where the sensor is flapping (in this case we just log about it). If there is no timer, it creates one to go off in a minute.

After a minute without flapping, the Timer Manager will the call alert_timer_expired lambda. This function gets whether or not we’ve alerted on this sensor’s going offline from the Item Metadata. Then it checks to see if the Item’s current state differs from the state that caused the Timer to be created in the first place. If not we exit. If we alerted when the device went offline, we send an alert and set the Item metadata.

At system startup and once a day the “System status reminder” rule runs to generate a report listing all the devices that are currently offline. The friendly name for each sensor is pulled from the Item Metadata. send_info and get_name are both simple functions in my personal library and imported.

from core.actions import NotificationAction
from core.jsr223.scope import actions
from configuration import admin_email # automation/lib/python/configuration.py
from core.metadata import get_value

def send_info(message, logger):
    """Sends an info level message by sending an email and logging the message
    at the info level.

        - message: The String to deliver and log out at the info level.
        - logger: The logger used to log out the info level alert.
    out = str(message)
    logger.info("[INFO ALERT] {}".format(message))
    NotificationAction.sendNotification(admin_email, out)
    (actions.get("mail", "mail:smtp:gmail")
        .sendMail(admin_email,  "openHAB Info", out))

def get_name(itemName):
    """Returns the 'name' metadata value or the itemName if there isn't one.

        itemName: The name of the Item
        None if the item doesn't exist TODO: verify.
    return get_value(itemName, "name") or itemName

Rules DSL

import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.Map

val Map<String, Timer> timers = newHashMap

rule "A sensor changed its online state2"
    Member of gSensorStatus changed
    if(previousState == NULL) return;

    val alerted = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_Alerted") as SwitchItem
    if(alerted === null) {
        logError("admin", "Cannot find Item " + triggeringItem.name+"_Alerted")
        aInfo.sendCommand(triggeringItem.name + " doesn't have an alerted flag, it is now " + transform("MAP", "admin.map", triggeringItem.state.toString) + "!")

    var n = transform("MAP", "admin.map", triggeringItem.name)
    val name = if(n == "") triggeringItem.name else n

    // If we are flapping, reschedule the timer and exit
    if(timers.get(triggeringItem.name) !== null) {
        logWarn("admin", name + " is flapping!")

    if(alerted.state == triggeringItem.state) {
        val currState = triggeringItem.state
        // wait one minute before alerting to make sure it isn't flapping
        timers.put(triggeringItem.name, createTimer(now.plusMinutes(1), [ |
            // If the current state of the Item matches the saved state after 5 minutes send the alert
            if(triggeringItem.state == currState) {
                aInfo.sendCommand(name + " is now " + transform("MAP", "admin.map", triggeringItem.state.toString) + "!")
                alerted.postUpdate(if(currState == ON) OFF else ON)
            timers.put(triggeringItem.name, null)

rule "Reminder at 08:00 and system start"
          Time cron "0 0 8 * * ? *" or
          System started
    val numNull = gSensorStatus.members.filter[ sensor | sensor.state == NULL ].size
    if( numNull > 0) logWarn("admin", "There are " + numNull + " sensors in an unknown state")

    val offline = gSensorStatus.members.filter[ sensor | sensor.state == OFF ]
    if(offline.size == 0) return;

    val message = new StringBuilder
    message.append("The following sensors are known to be offline: ")
    offline.forEach[ sensor |
        var name = transform("MAP", "admin.map", sensor.name)
        if(name == "") name = sensor.name
        message.append(", ")
        gOfflineAlerted.members.filter[ a | a.name==sensor.name+"_Alerted" ].head.postUpdate(ON)
    message.delete(message.length-2, message.length)


Theory of Operation

When a member of gSensorStatus changes state if the previous state was NULL we ignore it.

Next we get the Alerted Switch to see if we have already alerted on this Item. We use Design Pattern: Human Readable Names in Messages to transform the Alerted Item’s name to something more meaningful for logs and alert messages.

If the sensors are flapping, we reschedule the timer and wait a bit for the flapping to stop, logging the fact that it is flapping of course.

If the alerted Switch matches the triggeringItem then that means that the triggeringItem changed state and we need to generate a new alert. Set a Timer and if the Item remains in the same state send an alert using Design Pattern: Separation of Behaviors.

The second Rule produces a digest listing all the offline sensors every morning at 08:00 and when OH restarts.


Provides a generic and expandable way to get alerted or execute logic when a periodically reporting device goes silent for a period of time. One need only create some Groups and add a simple rule to add monitoring for a new device. It works with any sort of device. Or if using the Expire binding, one doesn’t even need Rules.

Design Pattern: Human Readable Names in Messages
Design Pattern: Associated Items
Design Pattern: Expire Binding Based Timers
OpenHAB 2.0 Rules: Create list of HSBTypes
Design Pattern: Event Limit
Item/Thing error feedack
FGF-101 v2.1 FW3.4 suddenly offline
Need an idea for better "trigger only if unchanged for X minutes" script
Compare heartbeat time against current time to check if device is alive in jython
Error from forEach loop of group
Detecting Usage in HabPanel
How to setup Last Will & Testament to show device state (online/offline)?
Binding a status into an item: Is it possible?
Execute function in a time slot
Daily test for zwave nodes?
[SOLVED] Check if an Mqtt Item is Offline
Temperature alert
Design Pattern: Time of Last Update
Add-on to send events when battery is low or items offline
Sensor state (alive/unknown...)
My Central Heating Solution using Thermal Actuators
[SOLVED] How do I debounce or antiflap notifications for many devices in a group?
Qubino Smart Meters (ZMNHXD, ZMNHTD) endless problems
Programmatically check OH config files have loaded
Group averageing
How to show ip
OH 2.x Timer Things
Zigbee two way communication
Problem with LWT options
Problem with LWT options
A few questions on status indicators for switches
[SOLVED] How to check if state changed in the last day
[SOLVED] MQTT populating temperature values - no it is not!
Please test the new Expire Binding
[SOLVED] Watchdog Expire Alerting - Hope I Am On The Right Track
How can I round a value to 2 digits
Design Pattern: Working with Groups in Rules
Inactivity of items // no updates // automatically tracking
Action on DateTime item age
How to find out if binding stopped working?
Iterating over a group, want to check an alternate item, sometimes
Making Decisions Based on Time of Day
Detecting offline Things in a less stupid way
MQTT device initial state checking
Logic operators precedence
ASH 2200 with USB-WDE1-2 supported?
Notifications in group design pattern
No working trigger functions openHAB 2.1
Several button pressed in GUI on 1 line
Unable to get DateTime to work
Test if CloudMQTT broker is alive
sendCommand and itemName from variable
Z-Wave - Getting item status
Run a forEach once per object per hour
Problems with OneWire hang ups
Alert when item in a group has not been updated for x hours?
Trigger when an item is in state for certain time
[Deprecated] Design Pattern: Motion Sensor Timer
A rule to monitor and compare thing uptime?
Alert when item is not updated since x hours
Timer script Not Cancelling if state changes
OH3 script for time since last update
OH3 trigger rule when haven't received any updates for 5min
A couple of simple oh-repeater examples
Comparing Dates for a group of items (Zigbee devices)
Detecting Non- reporting Sensors
Using rules for updating item with last update time of several sensors
Simple sensor alive monitoring solution using rules

Some feedback…

“now.plusHours(timeoutMinutes )”

That looks like a bug. If you also get the reschedule and the schedule using the same units you should use the variable rather than hard coding…

timers.get(sw.name).reschedule(now.plusHours(2)) // Make sure this matches above, use an appropriate time

Yes, totally a bug. I discovered this last night when I was trying to figure out why I wasn’t getting an alert for something I knew was down.

That is also a bug. These things happen when you retype your code to be generic as opposed to just pasting in your working code. Grrr.

Added a new version that simplifies the code using the new Expire binding.

1 Like

Hi Rich,

I tried your “expire” version of “generic is alive”.

But I get two errors:

no viable alternative at input ‘Functions$Function3’


missing EOF at ‘if’


if(!lock.isLocked) { // skip this event if there is already a lock on this Switch

Maybe a typo I couldn’t find?

Thanks for your help

There is an unmatched {, (, or [ somewhere in your file I’m willing to bet.

I did a copy and paste from your code without changing anything. I’ll check again, maybe I find something…

Load the rule into Designer. If there is a typo of this sort or a syntax error it will highlight it.

I get these errors (according to the attached screenshot) in designer after copy and paste of your code block.

Maybe the first warning is a hint?:

The import ‘org.eclipse.xtext.xbase.lib.Functions’ is never used.

There were two typos and one error that I found in the code above. Here is a corrected version:

import org.eclipse.xtext.xbase.lib.Functions
import java.util.Map
import java.util.concurrent.locks.ReentrantLock

// Globals
val Map<String, Boolean> notified = newHashMap // Flag to avoid duplicate alerts
val Map<String, ReentrantLock> locks = newHashMap // locks to avoid InvalidState exceptions when processing multiple updates at the same time

val Functions$Function3<SwitchItem, Map<String, Boolean>, 
                        Map<String, ReentrantLock>, Boolean> processOn = 
[ sw, notified, locks |

    // Generate a lock if there isn't one for this Switch
    if(locks.get(sw.name) == null) locks.put(sw.name, new ReentrantLock)

    val lock = locks.get(sw.name)
    if(!lock.isLocked) { // skip this event if there is already a lock on this Switch
        try {

            sw.sendCommand(ON) // this will start the Expire timer

            // Alert if we have been previously been alerted the device was down
           if(notified.getOrDefault(sw.name, false)){
               // alert code goes here
           notified.put(sw.name, false)
        catch(Throwable t) {
            logError("isAlive", "Error in locked part of processOn: " + t.toString)
        true // return value

// We don't need the lock here because the rules that call it only get triggered once 
// unlike above which gets triggered multiple times per event
val Functions$Function2<SwitchItem, Map<String, Boolean>, Boolean> processOff = 
[sw, notified |
    if(!notified.getOrDefault(sw.name, false)){
        // alert code goes here
        notified.put(sw.name, true)

// Start timers for all devices
rule "System started, kick off initial timers"
    System started
    gDevicesStatus.members.forEach[SwitchItem sw |

// Device 1 is alive!
rule "gDevice1 received update"
    Item gDevice1 received update
    processOn.apply(Device1Status, notified, locks)

// Device 1 is dead!
rule "Device1Status is dead"
    Item Device1Status changed to OFF
    processOff.apply(Device1Status, notified)

For the curious they were:

  • I included the varaible name “sw” inside the < > part of the Functions$Function3 definition
  • I failed to close the < > in the Functions$Function2 definition
  • It didn’t like my true in the finally clause so I moved that to be the last line in the lambda (this is the error)

Cool, errors are gone :slight_smile:

Many thanks Rich!!!

Could you short explain why? Didn’t get it…

Because a Group never gets assigned a state if you don’t give it a type. If the Group never gets a state it never gets an update. If it never gets and update, there is no event that can be used to trigger the Rule.

1 Like

I am in the process of setting up a rule to check “alive” status, and in particular the zwave door contacts, some of these doors or windows are not opened often, even ever, yet I must be able to know if the sensor is “alive” i read this post and it helps me a lot, however i have some questions:
when a zwave module wakes up “sometimes only once a day”, does it send an “update” of the status? including battery level status? and if so can I use instead of “changeSince”, “updateSince” for windows that are rarely opened? and if i use influxdb can i use in strategy: everyUpdate, everyChange, “and” everyMinute, or the everyMinute can truncate the “updateSince”?

here are my rules :

rule “Detection d’anomalie batteries si pas de changement depuis 24h”
Time cron “0 03 19 1/1 * ? *”//demarre a 19h10
val ContactBatDevices = It_Group_ContactBat.members.filter[sensor|sensor.updatedSince(now.minusHours(24), “influxdb”) == false]

ContactBatDevices.forEach [ sensor |
    msg4 = msg4 + (transform("MAP", "ContactBatSensorAnomalie.map", sensor.name) + ': ' +  '\n')
    logInfo("Sensor Statut ","possible anomalie sur " + sensor.name + ": " + sensor.changedSince(now.minusHours(24), "influxdb").toString )
    VarNumberFailedSensor3 = VarNumberFailedSensor3 +1

 if (msg4 != "" )
    sendBroadcastNotification(msg4 + " .Présente une possible anomalie de fonctionnement")
    logInfo("Le satut du capteur : ",msg4 +".Présente une possible anomalie de fonctionnement")
   msg4 = ""


this actually work, but “inside” i can’t see change et update to be sure it work , for the z-wave and its awayking I will ask on a specific post, thank you in advance

Sometimes. You can find out but triggering a rule on received update on those Items and watch for that rule to trigger. It’s probably be a good idea to do that anyway so you can get an idea of how often they actually do wake up and report. Especially if it’s reporting a battery status I would expect the Item to be updated even if the received state is the same as the Item’s current state.

It may be less than once a day.

The database doesn’t know the difference between a value that was saved because of an update, a change, a command, or periodically because of a cron based strategy (e.g. everyMinute). There is no way it can implement an updateSince.

It can’t tell the difference between an entry caused by everyMinute and one caused by updateSince.

You could configure those Items to only save on updates and at no other times. Then you know that the only entries in the database were caused by updates. Then you can get lastUpdate's time and compare that to now.minusX to see if the Item has updated too long ago.

But it’ll probably be easier to implement the Expire Binding or a Timer based approach. Reschedule the timer every time the Item receives an update. If it takes too long, the timer goes off and sends the alert that the device is offline.

yes is Probably the best way .
I try to set strategy to “strategy = everyUpdate” and
I keep you informed

Very thanks again to all yours explanations and time!

with only set strategy to “strategy = everyUpdate” it work i see on grafana the “hit” point on the graph made with influxdb source

1 Like