ZWave binding updates

The short answer is it’s no possible to access thing properties from rules.

The much longer answer is you could do it if you accessed the REST interface, but that’s not super simple.

1 Like

You can use REST, but this is entirely possible using scripted automation. Let me know if you’d like an example. I’ve also included a module for manipulating Things in the Jython helper libraries.

Just to point out that this is maybe a little “back door” and possibly might not work in future? I’m not sure how that works with the authentication that is now added to make this more secure? Have you tried it with the latest protected REST interfaces @5iver.

Scripted automation can access Things directly. I have not used the REST API in OH 3.0.

1 Like

Ah - cool - I didn’t know that :+1:

I’d be grateful if you can share one.

If not, I’ll go calling REST api from rules, as @chris suggested (thanks!)

I started a new topic for this…


@chris, after using scripted automation and having full access to zwave things configuration and properties I noticed two things which may be/are related to binding:

  1. zwave_lastwakeup property is present only on battery powered things. There is no such property on constantly powereed things with date of last communication. There is no any other property with ‘last communication’. Can you add something like that? This would be very helpful one. Either thing property or thing status?

  2. I found one battery operated thing, (which works correctly), but binding is showing zwave_lastwakeup property at all on this thing:

    2020-08-23 23:31:08.593 [WARN ] [jsr223.jython.TEST                      ] - 
    thing.label: zSensor Hackerspace
    thing.UID: zwave:device:zwave:node9
    thing.configuration: Configuration[{key=binding_cmdrepollperiod; type=BigDecimal; value=1500}, {key=config_41_3_00FFFF00; type=BigDecimal; value=0}, {key=config_100_1_wo; type=BigDecimal; value=0}, {key=config_201_2; type=BigDecimal; value=1}, {key=config_202_1; type=BigDe
    cimal; value=0}, {key=config_203_2; type=BigDecimal; value=0}, {key=config_204_1; type=BigDecimal; value=0}, {key=group_1; type=ArrayList; value=[controller]}, {key=action_reinit; type=Boolean; value=false}, {key=config_201_2_0000FF00; type=BigDecimal; value=0}, {key=confi
    g_112_4; type=BigDecimal; value=3600}, {key=wakeup_node; type=BigDecimal; value=1}, {key=config_39_1; type=BigDecimal; value=20}, {key=config_113_4; type=BigDecimal; value=3600}, {key=config_111_4; type=BigDecimal; value=3600}, {key=config_41_3_0000000F; type=BigDecimal; v
    alue=4}, {key=config_42_1; type=BigDecimal; value=10}, {key=config_201_2_000000FF; type=BigDecimal; value=1}, {key=config_43_2; type=BigDecimal; value=100}, {key=config_44_1; type=BigDecimal; value=10}, {key=config_41_3; type=BigDecimal; value=20}, {key=config_40_1; type=B
    igDecimal; value=0}, {key=config_252_1; type=BigDecimal; value=0}, {key=action_failed; type=Boolean; value=false}, {key=wakeup_interval; type=BigDecimal; value=3600}, {key=config_110_1_wo; type=BigDecimal; value=0}, {key=config_255_4_wo; type=BigDecimal; value=0}, {key=act
    ion_remove; type=Boolean; value=false}, {key=binding_pollperiod; type=BigDecimal; value=43200}, {key=action_heal; type=Boolean; value=false}, {key=config_2_1; type=BigDecimal; value=0}, {key=config_101_4; type=BigDecimal; value=241}, {key=config_102_4; type=BigDecimal; val
    ue=0}, {key=config_9_2; type=BigDecimal; value=0}, {key=config_46_1; type=BigDecimal; value=0}, {key=config_3_2; type=BigDecimal; value=240}, {key=config_4_1; type=BigDecimal; value=5}, {key=config_45_1; type=BigDecimal; value=2}, {key=config_5_1; type=BigDecimal; value=1}
    , {key=config_103_4; type=BigDecimal; value=0}, {key=node_id; type=BigDecimal; value=9}] {zwave_class_basic: BASIC_TYPE_ROUTING_SLAVE, zwave_class_generic: GENERIC_TYPE_SENSOR_MULTILEVEL, zwave_neighbours: 1,24, modelId: ZW100, zwave_version: 1.7, zwave_plus_devicetype: NODE_TYPE_ZWAVEPLUS_NODE, versionMin: 1.7, vendor: AEON Labs, defaultAsso
    ciations: 1, zwave_routing: true, zwave_beaming: true, commandClass:COMMAND_CLASS_SENSOR_MULTILEVEL: supportedGetSupported=false, zwave_secure: false, zwave_class_specific: SPECIFIC_TYPE_ROUTING_SENSOR_MULTILEVEL, zwave_devicetype: 2, zwave_frequent: false, versionMax: 1.7
    , zwave_listening: true, manufacturerId: 0086, manufacturerRef: 0002:0064,0102:0064,0202:0064,1A02:0064, dbReference: 355, zwave_deviceid: 100, zwave_nodeid: 9, zwave_lastheal: 2020-01-17T04:03:26Z, zwave_plus_roletype: ROLE_TYPE_SLAVE_SLEEPING_REPORTING, zwave_manufacture
    r: 134}

That is correct - only battery powered devices support wakeup. Mains powered devices are permanently awake.

Maybe it has not woken up? Is wakeup actually set.

Please note that wakeup is a specific function of battery devices - they can work perfectly normally (ie send data) without ever waking up. Wakeup is only required to configure the device.

1 Like

I got it, yet we don’t have information about last time thing was communicating with us, while we have multiple not so useful information (dbreference, zwave_version, etc). There is no way to get this information currently to detect missing/broken nodes, other than parsing zwave debug log AFAIK.

Would it be possible to add it? For example additional field on status, would be perfect (curl -X GET --header "Accept: application/json" "http://ohpi/rest/things/zwave%3Adevice%3Azwave%3Anode6/status"

Wakeup is set to 3600. Device is working ok (sending messages, it’s a multi sensor, so quite a lot of traffic in day). As far as I can see, do NOT containt zwave_lastwakeup parameter for this thing. it has plenty possibilities to send wakeup, however I can not exclude possibility that it does not as this is the only thing of this type in my network.

the only message in debug log grepped for ‘wake’ for this node:
d23 20:44:56.100 [.ZWaveCommandClass] - NODE 9: Command class COMMAND_CLASS_WAKE_UP, endpoint 0 created

This has been discussed in the past, and I think there are ways to record the time that a channel was updated already?

Anything is possible, but it’s not something that I’m likely to add myself - there’s simply too many other things to do - sorry.

I guess the device is not waking up then? It’s difficult for me to say a lot more, but if the last wakeup time is not set, and there are no wakeups in the log, that points to the device not waking for some reason. Maybe it didn’t get set during initialisation - you could try reinitialising this parameter and manually waking the device.

It would be possible, but needs much more spaghetti code to catch all channels on all device to detect this.

I would probably go for using zwave_lastheal if daily heal works correctly in last binding version, does it?

The other option, IIRC, I can use sendCommand(“REFRESH”) and try to check lastUpdate sometime later, on carefully selected items (one per thing).

I understand. Would you like to accept PR or simple wanted tester for this anytime in the future, just let me know.

Thanks, I will try it.

1 Like

Can’t you just use persistence for example? Won’t that give you the last time a channel was updated? If you configure persistence for record the update you should be able to get the time the channel was last updated?

I’m a little loath to add this sort of function to the binding as (IMHO) it’s a more generic thing that if OH wanted this sort of feature, then it should be in the core rather than being implemented in every binding.


@rdsiw, what are you trying to accomplish? I sounds like you might be trying to automate a notification when Z-Wave devices are not communicating. You could trigger a rule when the Thing status changes or a Channel triggers. You wouldn’t need to use rules for this though, if you used a timestamp-update or timestamp-change profile.

1 Like

@5iver has pointed to the documentation already, and I found this forum post very useful for implementing something very similar:

Design Pattern: Time of Last Update

Thanks Guys, your links helped me to verify all possible solutions and choose the best one.

My need was, in a relative modest installation of around hundred z-wave things, each having on average 6 channels, to be able to monitor all of things if they’re simply working (not channels, things).

I wanted to avoid doing it per every thing, as this would be too much hardcoded rules, plus (more importantly) much more to remember maintain in the future. This requirement excluded solutions:

  • timestamp-update/change -> requires additional item per channel
  • time of last status update -> reauires one additional item per thing
  • DSL rule “when Thing ‘NAME’ received update” -> requires one rule per every thing (there is no ‘memberof’ for things).

The only solution at the moment is to use NGRE and generic ThingStatusInfoChangedEvent as desribed here: Monitoring Thing Status which can be used with Jython rules.

If someone has small installation and is not afraid of having additional item per thing for communication monitoring here is a good start: Monitoring Things (Status).

You can currently monitor Z-Wave Thing status using this, which generates a rule with a trigger for each Z-Wave Thing…

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

def zwave_thing_trigger_generator():
    def generated_triggers(function):
        for thing_uid in [thing.UID.toString() for thing in things.all if "zwave" in thing.UID.toString()]:
            when("Thing {} changed".format(thing_uid))(function)
        return function
    return generated_triggers

@rule("Alert: Z-Wave Thing")
def zwave_thing_alert(event):
    if event.statusInfo.status.toString() in ["ONLINE", "OFFLINE"]:
        zwave_thing_alert.log.warn("{} is '{}'".format(event.thingUID, event.statusInfo.status))

The downside is that you will need to rerun the script to recreate the rule when you add more Things. One of the helper library features that I will be releasing soon are “Thing added” and “Thing removed” triggers for use in the core.triggers.when decorator. This will allow you to automate the recreation of the rule when Things are added/removed. These are implemented and testing out well. Once released, the script will look like this…

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

def zwave_thing_trigger_generator():
    def generated_triggers(function):
        for thing_uid in [thing.UID.toString() for thing in things.all if "zwave" in thing.UID.toString()]:
            when("Thing {} changed".format(thing_uid))(function)
        return function
    return generated_triggers

@rule("Z-Wave Thing added or removed")
@when("Thing added")
@when("Thing removed")
@when("System started")
def thing_added_or_removed(event):
    if event is not None:
        thing_added_or_removed.log.warn("thing: {}, topic: {}".format(event.thing.UID, event.topic.split("/")[-1]))
        for my_rule in [my_rule for my_rule in rules.all if == "Alert: Z-Wave Thing"]:

    @rule("Alert: Z-Wave Thing")
    def zwave_thing_alert(event):
        if event.statusInfo.status.toString() in ["ONLINE", "OFFLINE"]:
            zwave_thing_alert.log.warn("{} is '{}'".format(event.thingUID, event.statusInfo.status))
1 Like

Cool! That some nifty generator usage. Thank you kindly.

Feq questions:

  1. I understand I can change “Thing {} changed” to “Thing {} received update” (this may be needed due to how zwave works)
  2. How does logging (last line) works, specifcally how/when .log.warn is attached to function zwave_thing_alert
  3. I understand that I can just drop it to jsr223/python/personal/ ?
  4. what would be cosher way to access dictionary inside of zwaver_thing_alert function?

@chris Is using daily zwave heal safe/working as expected in 2.5.8 ?

You will be fine with changed. You’re also probably only concerned with when the Thing goes to OFFLINE, which would simplify things.

The core.rules.rule decorator creates the log attribute for you.

It would be found and executed anywhere under /automation/jsr223/. The directory you stated uses the directory structure that I have recommended for use with the helper libraries.

    zwave_thing_alert.log.warn("things.get(event.thingUID).properties: {}".format(things.get(event.thingUID).properties))

This returns a java.util.Collections.unmodifiableMap(), not a dict. So, you’ll need to use the Map methods.

    zwave_thing_alert.log.warn("things.get(event.thingUID).properties.get(\"modelId\"): {}".format(things.get(event.thingUID).properties.get("modelId")))

You are using Jython, so you can use and access Java classes, interfaces, etc. One of the methods for Map is get, which makes it very easy to think you are dealing with a dict. To be sure of what type of object you are dealing with, just use type

    zwave_thing_alert.log.warn("type(things.get(event.thingUID).properties): {}".format(type(things.get(event.thingUID).properties)))
2020-08-24 17:44:00.878 [WARN ] [jython.Alert: Z-Wave Thing] - type(things.get(event.thingUID).properties): <type 'java.util.Collections$UnmodifiableMap'>
1 Like