Daily test for zwave nodes?

Yesterday, I’ve done some tests with a water sensor that’s been running for more then a year.

  • When I check PaperUI, this sensor was online. >>> GOOD
  • But when I check my logs, nothing was in it (like fe battery status, temperature…). >>> STRANGE
  • When I put some water on it, the sensor goes into alarm. But nothing arrives at the server. >>> BAD

After excluding/including the sensor, everything seems to be OK now? Everything is coming in on the server side. This brings me to the question, how does Openhab trigger the online/offline?

Should I put something else in place to check the availability of the sensors?
Fe a nightly rule that checks 24h-logs for all zwave nodes for inputs (or something simular).

See expire binding

1 Like

Based on this response from Chris, it looks like it’s based on missing n wakeups

And for battery devices a wakeup can be days apart so it can be weeks before you discover a battery device has gone offline.

Link an Item to all the Channels for the device and watch events.log for ho often each updates. Then, use the Expire binding to mark it as offline if it misses one or two of those updates by more than a certain amount of time. See Design Patterns: Generic Is Alive.

I use this to monitor my whole house power sensor and my smoke alarms. Though my smoke alarms have a nice and reliable heartbeat they post on a regular basis which makes it easy. You might need to just average the number of updates per hour(s) and double it for your Expire time.

1 Like

Comment; updates to same value e.g. a battery report of “78%, same as yesterday”, do not usually appear in events.log after OH2.3. Only actual changes.

For testing purposes it is easy to make a little rule listening for updates and logging them out.

True. Sometimes I wish we could put the eventbus into a debug or trace level and see the updates but it appears those events are just not logged.

The actual question we’ve really all avoided is about this “Thing offline” status.
We’ve offered workarounds to detect “device offline” because it is not the same as Thing on/offline.

Best to think of Thing status as representing the pathway to the device, not the device itself.
So, a Thing can be online if the real zwave battery device is fast asleep.

1 Like

I made a kind of working around/reporting. Or at least, I tried. Mostly to exercise a bit with rules and what’s possible, and use it in a fun way. I was very happy that suddenly, my rule worked. :wink:
At least, I’ve got somewhere a place to verify it.

Suggestions are more then welcome if I can improve it!
For example how to move from the minutes towards Days:Hours:Minute (or so).


Group   Date_zwave    "Date update ZWave"
Group   Last_zwave    "Last update ZWave"
Number  Zwave_LUDS    "How long away? [%s weeks]"

Number   Fire1_temp       "Atelier [%.1f C]"         (zwaveLUD)    { channel="zwave:device:2f4ef5d0:node83:sensor_temperature" }
DateTime Fire1_temp_LUD   "Last seen [%1$ta %1$tR]"  (Date_zwave)
String   Fire1_temp_LUDS  "Last seen[%s]"            (Last_zwave)


rule "Record Last Update"
   Member of zwaveLUD received update
   sendCommand( triggeringItem.name+"_LUD", now.toString)

rule "Calculate Last Update"
   Time cron "0 0/29 * * * ?"
   sendCommand(Zwave_LUDS, 0)
   Date_zwave.members.forEach[ LudItem |
      val CalculatedRestTime = ((now.millis - (LudItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) / 60000
      sendCommand( LudItem.name+"S", CalculatedRestTime.toString)
      if (( CalculatedRestTime > 10080 ) && ( CalculatedRestTime < 20160 )) {
         logInfo("LastUpdate", LudItem.name+"is longer then 1 week away...")
         sendCommand(Zwave_LUDS, 1)
      else if (( CalculatedRestTime > 20160 ) && ( CalculatedRestTime < 30240 )) {
         logInfo("LastUpdate", LudItem.name+"is longer then 2 weeks away...")
         sendCommand(Zwave_LUDS, 2)
      else if (( CalculatedRestTime > 23024 ) && ( CalculatedRestTime < 40320 )) {
         logInfo("LastUpdate", LudItem.name+"is longer then 3 weeks away...")
         sendCommand(Zwave_LUDS, 3)
      else if ( CalculatedRestTime > 40320 ) {
         logInfo("LastUpdate", LudItem.name+"is longer then 1 month away...")
         sendCommand(Zwave_LUDS, 4)

Give me as result in HABpanel.
The global status ‘circle’ in the middle is made by a knob (Zwave_Luds=O=green, 1=yellow, … 4=red) :stuck_out_tongue:

This rule can be replaced with the timestamp_update profile. Profiles are a way to do some simple stuff like this without Rules. You would just put the same channel from Fire1_temp on Fire1_temp_LUD with the profile and when ever Fire1_temp gets updated Fire1_temp_LUD will be updated to now.

Instead of doing the calculations on milliseconds you can use the methods built into now. For example, you could replace

      val CalculatedRestTime = ((now.millis - (LudItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)) / 60000
      sendCommand( LudItem.name+"S", CalculatedRestTime.toString)
     if (( CalculatedRestTime > 10080 ) && ( CalculatedRestTime < 20160 )) {


      val restTime = new DateTime(LudItem.state.toString)
      // sendCommand( LudItem.name+"S", CalculatedRestTime.toString) More on this line later
     if (resetTime.isBefore(now.minusWeeks(1) && resetTime.isAfter(now.minusWeeks(2)) {

In my mind this is way simpler to read and understand.

You can use a Period to do the math and get the time between two DateTimes.

    sendCommand(LudItem.name+"S", new Period(restTime, now).toString) // ISO 8601 format

There are all sorts of ways to pretty print the Period.

I’m not certain how it works but it also might automatically nicely format if you use a Number:time to store the Item instead of a String. In that case you would replace that one line with:

sendCommand(LudItem.name+"S", new Period(restTime, now).getSeconds + " s")

Then the label can be "Last seen [%d d]" which should format it nicely on the sitemap.

NOTE: I just typed in the above, there may be typos or something I’ve forgotten. Also Joda DateTime will go away in OH 3 and there is a way to do the above using ZonedDateTime but I don’t know that off that top of my head.

If the Unit of Measurement (i.e. Number:time) doesn’t work, you can leave it a Number and use the following JavaScript transformation which I’ve used for years before UoM existed.

(function(i) {
    if(isNaN(i)) return "NAN";
    var mins = i%60
    var totalhours = Math.floor(i/60)
    var hours = totalhours%24
    var days = Math.floor(totalhours/24)
    var pad = "00";
    return days+":"+(pad+hours).slice(-pad.length)+":"+(pad+mins).slice(-pad.length)+":00";

You’ll need to add one more line to do the math for seconds.

1 Like

Finally got the time to test your suggestions… :blush:

At first sight, I’ve got it all working.

Just the display of the items are a bit ‘odd’. They show just the last difference in seconds? I would like to have a ‘complete’ difference, fe 3 days, 2 hours, 4 minutes, 3 seconds). Today, I’ve got just the 3 seconds?
Can I use something else instead of getSeconds?


datetime    brand_inkom_lud   "lud [%1$ta %1$tr]"    (date_zwave) { channel="zwave:device:30038385:node90:sensor_temperature" [profile="timestamp-update"]  }
number:time brand_inkom_luds  "Last seen ago [%d d]" (last_zwave)

DateTime    Hobbykamer_LUD    "LUD [%1$ta %1$tR]"    (Date_zwave) { channel="zwave:device:2f4ef5d0:node8:sensor_temperature" [profile="timestamp-update"]  }
Number:Time Hobbykamer_LUDS   "Last seen ago [%d d]" (Last_zwave)



  Date_zwave.allMembers.forEach[ LudItem |
                if ( LudItem.state != NULL ) {
                        val restTime = new DateTime(LudItem.state.toString)
                        sendCommand( LudItem.name+"S", new Period(restTime, now).getSeconds + " s")

Seems I got it (partly) working with:


DateTime    SchTestLUD   "LUD [%1$ta %1$tR]" (Date_zwave)  { channel="zwave:device:2f4ef5d0:node73:battery-level" [profile="timestamp-update"]  }
Number:Time SchTestLUDS  "Last seen [%d s]"  (Last_zwave)


Date_zwave.allMembers.forEach[ LudItem |
   if ( LudItem.state != NULL ) {
      var DateTime restTime = new DateTime((LudItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli)
      var lastSeenSince = (now.millis - restTime.millis)
      sendCommand( LudItem.name+"S", lastSeenSince + " s")


Text item=SchTestLUDS label="Last seen (sec) [%d s]"
Text item=SchTestLUDS label="Last seen (min) [%d min]"
Text item=SchTestLUDS label="Last seen (hours) [%d h]"
Text item=SchTestLUDS label="Last seen (days) [%d d]"


But it seems that the habpanel doesn’t follow the format?
So almost there… :wink: