Rules/Whatever to detect "offline" zwave nodes

I would like to detect offline nodes and set some actions afterwards…send email/set value to specific number/string…

Is that possible / how…does openhab have a simple way or do I have to check for “connectivity” every minute or so?

Thanks a lot for help,
Norbert

Not that I can tell based on a reading of the wiki page. You can set a refresh_interval which will poll the device for changes, set a polling rule and if the item doesn’t receive an update after the polling period you will know it isn’t responding.

Switch light {zwave="3:command=switch_binary,refresh_interval=10"}
rule "Is light responding"
when
    Time cron "0/10 * * * ? *"
then
    if(light.lastupdate < now.minusSeconds(10)) {
        // it has been more than 10 seconds since light was updated
    }
end

This requires persistence to work. I don’t know if you can compare light.lastUpdate and now like that, you may need to play around with it to get the right way (e.g. light.lastUpdate.calendar < now.minusSeconds(10).calendar). You will probably also want to add some logic to avoid it sending you an email every 10 seconds when a node does go offline.

I have used the following concept for a while and it seems to be working OK:

Items

DateTime	S01D001_tLastUpdate		{ zwave="5:command=info, item=last_update" }
Contact		S01D001_cAlive

Rules

var Timer S01D001_tAlive = null

rule "S01D001 Alive"
when
    Item S01D001_tLastUpdate changed
then
    S01D001_cAlive.postUpdate(OPEN) // OPEN = TRUE
    S01D001_tAlive?.cancel()
    S01D001_tAlive = createTimer(now.plusHours(1)) [|
        logWarn("S01", "S01D001 appears to be dead")
        S01D001_cAlive.postUpdate(CLOSED) // CLOSED = FALSE
    ]
end

sitemap

sitemap S01 label="System 01 - Z-Wave" {
	Frame label="Devices" {
		Text	item=S01D001_tLastUpdate	label="S01D001 - Motion sensor [%1$td.%1$tm.%1$tY %1$tT]"	valuecolor=[S01D001_cAlive==OPEN="GREEN", S01D001_cAlive==CLOSED="RED"]	icon="my_clock" {
			Frame label="Internal" {
				Text	item=S01D001_cMotion		label="Motion [%s]"								icon="contact"
				Text	item=S01D001_nTemperature	label="Temperature [%.1f °C]"					icon="temperature"
				Text	item=S01D001_nIlluminance	label="Illuminance [%.2f lux]"					icon="light-on"
				Text	item=S01D001_nBattery		label="Battery [%d %%]"							icon="my_battery"
				Text	item=S01D001_cAlarm			label="Alarm [%s]"								icon="contact"
			}
			Frame label="Utility" {
				Text	item=S01D001_tLastUpdate	label="Last update [%1$td.%1$tm.%1$tY %1$tT]"	icon="my_clock"
				Text	item=S01D001_cAlive			label="Alive [%s]"								icon="contact"
			}
		}
        }
}

Some notes:

  • The prefix ‘S01D001’ is just my own naming convention for devices. ‘S01’ denotes System 01 (which is Z-Wave) and D001 device number 1 within this system.
  • For brevity I have only shown the setup for one device above. Obviously I am using the same for all the other devices as well (i.e. S01D002, S01D003, etc.).
  • The value of the timer that is used to detect offline nodes (1 hour in the example above) must be tuned for each device, according to their internal settings for publish/wakeup, etc. I have found 1 hour to work well for all my devices, except a garage door tilt sensor that I needed to set to 1,5 hours.

Hope this helps!

Ps! I am sure someone could find a clever way of collapsing this system a bit (in the case of several devices) by using groups and/or lambda functions. Myself I have not taken the step into this jungle just yet…

Thanks KjetilA,

Here’s my version for visibility!

// Node Health checks

rule "Node2Status"
when
    Item Node2 changed
then
    logInfo("Status", "NODE CHECK: Checking Node2 Status.")
    Node2_tAlive?.cancel()
    Node2_tAlive = createTimer(now.plusHours(1)) [|
        logWarn("N2", "Node2 appears to be dead")
        sendMail("me@here.com", "Security System Alert!", "Node2 Appears dead. Please Investigate.")
    ]
end

Straight and to the point :wink:

This timer based approach is pretty good in the generic case as well. It’s kind of a dead man’s switch. If you don’t hear from the item after a certain amount of time mark it dead. It works best for items that report periodically.

Agreed.

I made a group called gNodes now and trying to migrate it over like we did with the door alerts and alert.apply but it dont seem to like it.

rule "DeadNodeCheck"
when
    Item gNodes received update
then
    logInfo("Status", "NODE GROUP CHECK: Checking Node Status.")
    gNodes_tAlive?.cancel()
    gNodes_tAlive = createTimer(now.plusHours(1)) [|
    alert.apply(gNodes, "- Dead Node")
    ]

end

and…

val Functions$Function2 alert = [GroupItem g, String type |
    val latest = g.members.sortBy[lastUpdate].last
    val name = latest.name.split("_")
    Notification_Proxy.postUpdate(name.get(1) + " " + name.get(2) + " " + name.get(3) + " " + type + " Alert!. " + latest.state.toString)
]


rule "Dispatch Notification"
when
    Item Notification_Proxy received update
then
    logWarn("Notification", Notification_Proxy.state.toString)
    sendMail("alerts@cidcomm.com", "Security System Alert!", Notification_Proxy.state.toString)
end

but throwing…

2016-05-25 13:55:48.338 [WARN ] [m.s.internal.actions.TimerImpl] - An error occured while cancelling the job 'DEFAULT.2016-05-25T14:55:48.316-04:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: org.eclipse.xtext.xbase.impl.XClosureImplCustom@f00553 (explicitSyntax: true)': Unable to unschedule trigger [DEFAULT.6da64b5bd2ee-d2926540-d86c-43cb-b862-ce961a521ee3] while deleting job [DEFAULT.2016-05-25T14:55:48.316-04:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: org.eclipse.xtext.xbase.impl.XClosureImplCustom@f00553 (explicitSyntax: true)]

and…

2016-05-25 13:57:48.454 [ERROR] [script.actions.ScriptExecution] - Failed to schedule code for execution.
org.quartz.ObjectAlreadyExistsException: Unable to store Job : 'DEFAULT.2016-05-25T14:57:48.383-04:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Procedure0: org.eclipse.xtext.xbase.impl.XClosureImplCustom@16a9b00 (explicitSyntax: true)', because one already exists with this identification.
        at org.quartz.simpl.RAMJobStore.storeJob(RAMJobStore.java:277) ~[na:na]
        at org.quartz.simpl.RAMJobStore.storeJobAndTrigger(RAMJobStore.java:249) ~[na:na]
        at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:840) ~[na:na]
        at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:250) ~[quartz-all-2.1.7.jar:na]
        at org.openhab.model.script.actions.ScriptExecution.makeTimer(ScriptExecution.java:130) [org.openhab.model.script_1.8.2.jar:na]
        at org.openhab.model.script.actions.ScriptExecution.createTimer(ScriptExecution.java:92) [org.openhab.model.script_1.8.2.jar:na]
        at sun.reflect.GeneratedMethodAccessor296.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_101]
        at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_101]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.invokeOperation(XbaseInterpreter.java:729) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._featureCallOperation(XbaseInterpreter.java:713) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at sun.reflect.GeneratedMethodAccessor283.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_101]
        at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_101]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) [org.eclipse.xtext.util_2.3.0.v201206120633.jar:na]
        at org.openhab.model.script.interpreter.ScriptInterpreter.internalFeatureCallDispatch(ScriptInterpreter.java:69) [org.openhab.model.script_1.8.2.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(XbaseInterpreter.java:658) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at sun.reflect.GeneratedMethodAccessor284.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_101]
        at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_101]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) [org.eclipse.xtext.util_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAssignment(XbaseInterpreter.java:843) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at sun.reflect.GeneratedMethodAccessor292.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_101]
        at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_101]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) [org.eclipse.xtext.util_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateBlockExpression(XbaseInterpreter.java:321) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at sun.reflect.GeneratedMethodAccessor287.invoke(Unknown Source) ~[na:na]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_101]
        at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_101]
        at org.eclipse.xtext.util.PolymorphicDispatcher.invoke(PolymorphicDispatcher.java:291) [org.eclipse.xtext.util_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.internalEvaluate(XbaseInterpreter.java:218) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter.evaluate(XbaseInterpreter.java:204) [org.eclipse.xtext.xbase_2.3.0.v201206120633.jar:na]
        at org.openhab.model.script.internal.engine.ScriptImpl.execute(ScriptImpl.java:59) [org.openhab.model.script_1.8.2.jar:na]
        at org.openhab.core.scriptengine.ScriptExecutionThread.run(ScriptExecutionThread.java:44) [org.openhab.core.scriptengine_1.8.2.jar:na]

So i believe the issue is that due to the multiple timers being created,

gNodes_tAlive

needs to be something dynamic like the name.tAlive as the timers are getting confused. So have to figure that out… Something like,

name.get(1) +_tAlive...