How to refresh zwave values and monitor for device health

I have an automation that periodically polls the state of the switch to make sure that the value is up-to-date and that the device is responding. The automation works, zwavejs read the value, but the “last_updated” attribute of the entity is never updated, and hence it’s not possible to know if the value is current or if the device responded.

- id: "switch_2 poll switch state"
  alias: switch_2 poll switch state
  description: "Automation to periodically poll switches"
  trigger:
    - platform: time_pattern
      minutes: "/10"
    - platform: homeassistant
      event: start
  action:
    - service: zwave_js.refresh_value
      data:
        entity_id: switch.switch_2_switch

When the script runs I see in home_assistant.log (it’s node 14)

2022-04-04 15:50:00 INFO (MainThread) [homeassistant.components.zwave_js.entity] Refreshing primary value Value(value_id='14-37-0-currentValue') for switch.switch_2_switch, state update may be delayed for devices on battery

And then in the zwavejs2mqtt log

2022-04-04 15:50:00.243 INFO ZWAVE: Node 14: value updated: 37-0-currentValue false => false

But the last_updated value for the entity does not update

{{            
   states.switch.switch_2_switch.last_updated 
}}

2022-04-04 19:26:26.569554+00:00


See the docs for the explanation of why this isn’t working the way you are expecting. The description of state.last_updated property is:

Time the state was written to the state machine in UTC time. Note that writing the exact same state including attributes will not result in this field being updated.

When you refreshed, the state didn’t change (false => false), so there’s no update in HA.

I think if a mains-powered device doesn’t respond to a command it will be marked dead. You could monitor the “node status” sensor for a change there instead.

Another alternative is to use the value update device trigger. That isn’t affected by non-state changes.

Thanks, I’ll look to that device update trigger.

As far as I can tell these events never fire and i am getting updates from the temperature sensor.

template:
  - trigger:
      - platform: event
        event_type: zwave_js_value_updated
        event_data:
          entity_id: sensor.sensor_2_temperature
    sensor:
      - name: "sensor_2_last_updated"
        state: "{{ now() }} "

I also see no evidence of zwave events when I listen for all events or this specific event from the developer panel. Is there something that needs to be done to enable them in the zwave_js integration?

What I linked to and suggested using is the device trigger zwave_js.value_updated, not the event zwave_js_value_updated. That event is for very specific devices that are hard-coded into the integration:

This event is enabled on a per device and per entity domain basis, and the entities will have assumed_state set to true

You can create device triggers from the UI (despite what those docs say, they are out of date there).

Thank you and apologies for not understanding the difference between the two different triggers.

I do have it working when I specify the device_id (this is basically the trigger the UI generated for me - thanks for points me to that!!)

template:
  - trigger:
      - platform: device
        device_id: 0c7e5ca861ea58b4f27ec29f84d412c3
        domain: zwave_js
        type: zwave_js.value_updated.value
        command_class: 49
        property: Air temperature
    sensor:
      - name: "sensor_2_last_updated"
        state: "{{ now() }} "

But it does not work when I specify the entity_id instead

template:
  - trigger:
      - platform: device
        entity_id: 
          - sensor.sensor_2_temperature
        domain: zwave_js
        type: zwave_js.value_updated.value
        command_class: 49
        property: Air temperature
    sensor:
      - name: "sensor_2_last_updated"
        state: "{{ now() }} "

I get an error

2022-04-06 10:11:50 ERROR (MainThread) [homeassistant.config] Invalid config for [template]: [entity_id] is an invalid option for [template]. Check: template->entity_id. (See ?, line ?). 

However, this looks consistent with the example you referenced:

Z-Wave JS - Home Assistant (home-assistant.io)

trigger:
  platform: zwave_js.value_updated
  # At least one `device_id` or `entity_id` must be provided
  device_id: 45d7d3230dbb7441473ec883dab294d4  # Garage Door Lock device ID
  entity_id:
    - lock.front_lock
    - lock.back_door
  # `property` and `command_class` are required
  command_class: 98 # Door Lock CC
  property: "latchStatus"
  # `property_key` and `endpoint` are optional
  property_key: null
  endpoint: 0

I’m on 2022_3_6. Are the docs wrong? My config bad? Is there a way to extract the device_id at run_time from the entity_id?

Minor correction to myself. The trigger created by the UI and the one documented are slightly different, but they do the same thing. The UI created one is for devices only (hence Device trigger). The YAML version accepts entity IDs.

If you want to use an entity ID, I think you just need to update yours to use the correct platform.

template:
  - trigger:
      - platform: zwave_js.value_updated
        entity_id: 
          - sensor.sensor_2_temperature
        command_class: 49
        property: Air temperature
    sensor:
      - name: "sensor_2_last_updated"
        state: "{{ now() }} 

Thanks that was the ticket! Would be nice to have a table of those CC_IDs :-).

Here’s the full solution:

# sensor_2
# sensor.sensor_2_temperature to monitor
# 480

input_boolean:
  zwave_sensor_2_alarm_enable:

template:
  - trigger:
      - platform: zwave_js.value_updated
        entity_id:
          - sensor.sensor_2_temperature
        command_class: 49
        property: Air temperature
    sensor:
      - name: "sensor_2_last_updated"
        state: "{{ now() }} "

sensor:
  - platform: template
    sensors:
      zwave_sensor_2_latency:
        unit_of_measurement: secs
        value_template: "{{ ((as_timestamp(now(), 0) | int(0)) - as_timestamp(states('sensor.sensor_2_last_updated'),0) | int(0)) }}"
        availability_template: "{{ as_timestamp(states.sensor.sensor_2_temperature.last_updated,0) | int(0) > 0 }}"

binary_sensor:
  - platform: template
    sensors:
      zwave_sensor_2_online:
        value_template: >-
          {{ ( 480 - (states('sensor.zwave_sensor_2_latency') | int(0))) > 0  and
               states('sensor.sensor_2_node_status') != 'dead' and
               states('sensor.sensor_2_node_status') != 'unavailable' and
               states('sensor.sensor_2_node_status') != 'unknown'
          }}
      zwave_sensor_2_alert:
        value_template: '{{ is_state("binary_sensor.zwave_sensor_2_online", "on") and is_state("input_boolean.zwave_sensor_2_alarm_enable", "on") }}'

alert:
  zwave_sensor_2:
    name: zwave sensor_2 alert
    message: '{{ state_attr("zone.home","friendly_name") }} zwave sensor_2 offline - Latency: {{ states("sensor.zwave_sensor_2_latency") }} Node {{ states("sensor.zwave_sensor_2_node_status") }}'
    done_message: '{{ state_attr("zone.home","friendly_name") }} zwave sensor_2 online'
    entity_id: binary_sensor.zwave_sensor_2_alert
    state: "on"
    repeat: 240
    can_acknowledge: true
    skip_first: false
    notifiers:
      - sms_notifiers_all

recorder:
  include:
    entities:
      - binary_sensor.zwave_sensor_2_online
      - binary_sensor.zwave_sensor_2_alert
      - zwave.sensor_2_node_status
      - input_boolean.zwave_sensor_2_alarm_enable
      - alert.zwave_sensor_2

Since I have more than one device I do this for, I created a parameretized package

# multisensor_1
# ENTITY_ID to monitor
# TIMEOUT

input_boolean:
  zwave_multisensor_1_alarm_enable:

template:
  - trigger:
      - platform: zwave_js.value_updated
        entity_id:
          - ENTITY_ID
        command_class: COMMAND_CLASS
        property: PROPERTY_NAME
    sensor:
      - name: "multisensor_1_last_updated"
        state: "{{ now() }} "

sensor:
  - platform: template
    sensors:
      zwave_multisensor_1_latency:
        unit_of_measurement: secs
        value_template: "{{ ((as_timestamp(now(), 0) | int(0)) - as_timestamp(states('sensor.multisensor_1_last_updated'),0) | int(0)) }}"
        availability_template: "{{ as_timestamp(states.ENTITY_ID.last_updated,0) | int(0) > 0 }}"

binary_sensor:
  - platform: template
    sensors:
      zwave_multisensor_1_online:
        value_template: >-
          {{ ( TIMEOUT - (states('sensor.zwave_multisensor_1_latency') | int(0))) > 0  and
               states('sensor.multisensor_1_node_status') != 'dead' and
               states('sensor.multisensor_1_node_status') != 'unavailable' and
               states('sensor.multisensor_1_node_status') != 'unknown'
          }}
      zwave_multisensor_1_alert:
        value_template: '{{ is_state("binary_sensor.zwave_multisensor_1_online", "on") and is_state("input_boolean.zwave_multisensor_1_alarm_enable", "on") }}'

alert:
  zwave_multisensor_1:
    name: zwave multisensor_1 alert
    message: '{{ state_attr("zone.home","friendly_name") }} zwave multisensor_1 offline - Latency: {{ states("sensor.zwave_multisensor_1_latency") }} Node {{ states("sensor.zwave_multisensor_1_node_status") }}'
    done_message: '{{ state_attr("zone.home","friendly_name") }} zwave multisensor_1 online'
    entity_id: binary_sensor.zwave_multisensor_1_alert
    state: "on"
    repeat: 240
    can_acknowledge: true
    skip_first: false
    notifiers:
      - sms_notifiers_all

recorder:
  include:
    entities:
      - binary_sensor.zwave_multisensor_1_online
      - binary_sensor.zwave_multisensor_1_alert
      - zwave.multisensor_1_node_status
      - input_boolean.zwave_multisensor_1_alarm_enable
      - alert.zwave_multisensor_1

Then blast out the configuration

TARGET="/mnt/c/github/ha_house2"

zwave_js_health_template()
# ZWAVE Entity
# ENTITY_ID to monitor 
# TIMEOUT
# COMMAND_CLASS
# PROPERTY_NAME
# Output file
{
   sed "s/multisensor_1/$1/g" zwave_js_health_template_multisensor_1_package.yaml | 
   sed "s/ENTITY_ID/$2/g" | sed "s/TIMEOUT/$3/g" | sed "s/COMMAND_CLASS/$4/g" | sed "s/PROPERTY_NAME/$5/g"> $TARGET/packages/$6
}

zwave_js_health_template sensor_2 sensor.sensor_2_temperature 480 49 "Air temperature" zwave_js_health_template_sensor_2_package.yaml
zwave_js_health_template entrance sensor.entrance_temperature 600 49 "Air temperature" zwave_js_health_template_entrance_package.yaml
zwave_js_health_template basement sensor.basement_temperature 480 49 "Air temperature" zwave_js_health_template_basement_package.yaml
zwave_js_health_template garage_sensor sensor.garage_temperature 600 49 "Air temperature" zwave_js_health_template_garage_sensor_package.yaml
zwave_js_health_template upstairs_thermostat climate.upstairs_thermostat 1200 49 "Air temperature" zwave_js_health_template_upstairs_thermostat_package.yaml

zwave_js_health_template switch_2 switch.switch_2_switch 1200 37 currentValue zwave_js_health_template_switch_2_package.yaml
zwave_js_health_template outdoor_switch switch.outdoor_switch 1200 37 currentValue zwave_js_health_template_outdoor_switch_package.yaml
zwave_js_health_template hotwater_heater switch.hotwater_heater_switch 1200 37 currentValue zwave_js_health_template_hotwater_heater_switch_package.yaml

zwave_js_health_template loft_fan_1 fan.loft_fan_1_level 1200 38 currentValue zwave_js_health_template_loft_fan_1_package.yaml
zwave_js_health_template loft_fan_2 fan.loft_fan_2_level 1200 38 currentValue zwave_js_health_template_loft_fan_2_package.yaml


I still have some other devices that I need to discover the command_class and property_names for.

For devices that don’t report periodically (like switch states) I run automatons like this

- id: "loft_fan_2 poll switch state"
  alias: loft_fan_2 poll switch state
  description: "Automation to periodically poll switches"
  trigger:
    - platform: time_pattern
      minutes: "/10"
    - platform: homeassistant
      event: start
  action:
    - service: zwave_js.refresh_value
      data:
        entity_id: fan.loft_fan_2_level

So I poll it every 10 minute and generate an alert if no switch state is received in 20 minutes.

If you use the automation UI and the device trigger, the list of CCs supported by that specific device are listed in the CC menu. Or, you can download the Diagnostic data from the device page and see what’s supported. Lastly, there’s a list of command classes in the driver docs.

1 Like

Yep, UI was taking time. Driver Docs are what I wanted, so I can go straight from the zwave_js log output to the CCs.