Z-Wave Device Class

The st.zwave.Device class inherits from Device, extending behavior with Z-Wave-specific functionality. Device objects are instantiated by the framework and passed to drivers as parameters for device-related methods.

Sleepy devices

Sleepy devices present unique use cases for a Z-Wave controller since communication can only occur when the device is awake. One particular case is when a sleepy device’s preferences are updated and it is asleep, any configuration commands for the device cannot be sent until the device wakes up. To allow for sending device configuration commands when the device wakes up, a driver can set an update_preferences function on a device which will get called when the device wakes up. The function is provided args which are similar to the args on the infoChanged lifecycle event and contain the old_st_store.preferences with the preferences that were present the last time the device woke up. Note that there will still be an infoChanged event for sleepy devices, and this automatic preference update mechanism only works for devices that support the WakeUp command class. The following is an example of how this functionality should be handled in a driver that supports both listening and sleepy devices:

local capabilities = require "st.capabilities"
--- @type st.zwave.Driver
local ZwaveDriver = require "st.zwave"
--- @type st.zwave.defaults
local defaults = require "st.zwave.defaults"
local cc = require "st.zwave.CommandClass"

local function update_preferences(self, device, args)
  if args.old_st_store.prefrences["my_pref"] ~= device.preferences["my_pref"] then
    -- send commands if you need
  end
end

local function device_init(self, device)
  device:set_update_preferences_fn(update_preferences)
end

local function info_changed(self, device, event, args)
  -- only update preferences for devices we know are awake
  -- if this driver only supports sleepy devices, an infoChanged handler may not be needed at all.
  if ~device:is_cc_supported(cc.WAKE_UP) then
    update_preferences(self, device, args)
  end
end

local zwave_contact_driver = {
  supported_capabilities = {
    capabilities.contactSensor,
    capabilities.battery
  },
  lifecycle_handlers = {
    init = device_init,
    infoChanged = info_changed,
  },
}

defaults.register_for_default_handlers(zwave_contact_driver, zwave_contact_driver.supported_capabilities)
--- @type st.zwave.Driver
local contact_sensor = ZwaveDriver("zwave_contact_sensor", zwave_contact_driver)
contact_sensor:run()

Parent/Child Devices

There may be some situations where you prefer to model a single network device as multiple SmartThings device records. This can be done by creating “Child” devices to represent individual endpoints on the device. NOTE: If you aren’t modeling each child as an endpoint, you won’t be able to use the library abstractions, but you can build your own handling of messages for whatever model you desire.

You can define a find_child function using the set_find_child method on device objects. Then any use of emit_event_for_endpoint including the default capability handlers will use this to emit events on the appropriate children. The find_child function you define will be passed the source channel a message came from and should then return the device object representing the child device that is responsible for that source channel, or nil if there isn’t one.

When deleting devices in a parent/child relationship, deleting the parent will result in the removal of all of the children as well as all communication goes through the parent device. However, you can delete individual children without affecting the others. It should be noted though that typically the only way to create the children is on device join so if deleted getting the child devices back will often require deleting and re-onboarding the parent.

example

Profile:

name: outlet
components:
  - id: main
    capabilities:
      - id: switch
        version: 1
      - id: refresh
        version: 1
    categories:
      - name: Outlet

Driver:

local capabilities = require "st.capabilities"
local ZwaveDriver = require "st.zwave.driver"
local defaults = require "st.zwave.defaults"
local device_lib = require "st.device"
local constants = require "st.zwave.constants"
local cc  = require "st.zwave.CommandClass"
local SwitchBinary = (require "st.zwave.CommandClass.SwitchBinary")({version=2,strict=true})

-- These same handlers will work for both the parent and child devices
local function report_handler(driver, device, cmd)
  local event
  if cmd.args.value == SwitchBinary.value.OFF_DISABLE then
    event = capabilities.switch.switch.off()
  else
    event = capabilities.switch.switch.on()
  end

  -- This device will _always_ be the parent because the parent is the only one actually on the network sending
  -- messages but this call to `emit_event_for_endpoint` will use the `find_child` function set below to find the
  -- child and emit the event for that SmartThings device instead
  device:emit_event_for_endpoint(cmd.src_channel, event)
end

local function on_handler(driver, device, command)
  -- Since we will receive commands separately for the different children, the device object here could be a parent
  -- or child.  The message building will work the same as the child will defer to the parent for addressing
  -- information. And similarly `send_to_component` on the child device will ultimately call send on the parent as
  -- that is the actual network device.
  device:send_to_component(
    SwitchBinary:Set({ target_value = SwitchBinary.value.ON_ENABLE, duration = 0}),
    command.component
  )
  local query_device = function()
    device:send_to_component(SwitchBinary:Get({}), command.component)
  end
  device.thread:call_with_delay(constants.DEFAULT_GET_STATUS_DELAY, query_device)
end

local capability_handlers = {}


local function added(driver, device, event)
  -- Only create children for the actual Z-Wave device and not the children
  if device.network_type == device_lib.NETWORK_TYPE_ZWAVE then
    for i = 2, 5, 1 do
      local name = string.format("%s outlet %d", device.label, i)
      local metadata = {
        type = "EDGE_CHILD",
        label = name,
        profile = "outlet",
        parent_device_id = device.id,
        parent_assigned_child_key = string.format("%02X", i),
        vendor_provided_label = name,
      }
      driver:try_create_device(metadata)
    end
  end
end

local function find_child(parent, src_channel)
  return parent:get_child_by_parent_assigned_key(string.format("%02X", src_channel))
end

local function init(driver, device, event)
  if device.network_type == device_lib.NETWORK_TYPE_ZWAVE then
    device:set_find_child(find_child)
  end
end

local zwave_parent_child_switch_template = {
  supported_capabilities = {
    capabilities.switch,
  },
  zwave_handlers = {
    [cc.SWITCH_BINARY] = {
      [SwitchBinary.REPORT] = report_handler
    },
  },
  capability_handlers = {
    [capabilities.switch.ID] = {
      [capabilities.switch.commands.on.NAME] = on_handler,
      -- Off handler provided by defaults
    },
  },
  lifecycle_handlers = {
    added = added,
    init = init,
  },
}

-- The on_handler and on_off_attr_handler above would be covered by these defaults, but are included for example purposes
defaults.register_for_default_handlers(zwave_parent_child_switch_template, zwave_parent_child_switch_template.supported_capabilities)
local zwave_parent_child_switch = ZwaveDriver("child_device_example", zwave_parent_child_switch_template)
zwave_parent_child_switch:run()

Class Documentation

class st.zwave.Device: st.Device
zwave_endpoints: table

store Z-Wave endpoints

collect_default_refresh_commands(self)

Collect and return the list of refresh commands for self device as provided

by registered default modules, removing duplicates.

Parameters

self (st.zwave.Device) –

set_component_to_endpoint_fn(comp_ep_fn)

Set a function to map this devices SmartThings components to Zwave endpoints

Parameters

comp_ep_fn (CompToEp) – Component to Endpoint function to do the mapping for this device

set_endpoint_to_component_fn(ep_comp_fn)

Set a function to map this devices Zwave endpoints to SmartThings components

Parameters

ep_comp_fn (EpToComp) – function to do the mapping for this device

set_update_preferences_fn(update_pref_fn)

Set a function to be called with with previous preferences when this device wakes up. Should be used to update preferences on sleepy devices.

Parameters

update_pref_fn (UpdatePreference) – function to update preferences when a device wakes up.

default_refresh(self)

Collect device-specific refresh commands from registered default modules

and send these to the associated Z-Wave device.

Parameters

self (st.zwave.Device) –

refresh(self)

Use the capability dispatcher to execute refresh as appropriate for

the particular device instance.

Parameters

self (st.zwave.Device) –

default_configure(self)

Default device configure function. Execute refresh to bootstrap state

for all capability event listeners.

Parameters

self (st.zwave.Device) –

emit_event_for_endpoint(endpoint, event)

Emit event for Z-Wave endpoint(channel), mapped to component.

Parameters
  • endpoint (number) – the endpoint(Z-Wave channel) ID to find the component for

  • event (table) – the endpoint(Z-Wave channel) ID to find the component for

send(cmd)

Send a Z-Wave command to the associated Z-Wave device.

The command will be logged in the live logs when it is sent from the driver. There will also be logs to trace when the command is queued in the hub, and when the transmission has completed on the radio.

Parameters

cmd (st.zwave.Command) –

send_to_component(cmd, component_id)

Send a Z-Wave command to the specific component of associated Z-Wave device.

The command will be logged in the live logs when it is sent from the driver. There will also be logs to trace when the command is queued in the hub, and when the transmission has completed on the radio.

Parameters
component_to_endpoint(component_id)

Map component to end_points(channels)

e.g. {} - map component to un-encapsulated {2} - map to specific Z-Wave endpoint(channel) {1,2,3} - map to more then one Z-Wave endpoint (channels)

e.g. {2} for Z-Wave channel 2 or {} for unencapsulated

Parameters

component_id (str) – ID

Returns

dst_channels destination channels

Return type

table

endpoint_to_component(endpoint)

Map end_point(channel) to Z-Wave endpoint(channel)

Parameters

endpoint (number) – the endpoint(Z-Wave channel) ID to find the component for

Returns

the component ID the endpoint matches to

Return type

str

is_cc_supported(cc_value, endpoint)

Interrogate the device’s profile to determine whether a particular command class is supported.

Parameters
  • cc_value (number) – the command class id as defined in cc.lua, e.g cc.SWITCH_BINARY = 0x25

  • endpoint (number) – of the endpoint to check, if nil we check the first endpoint

Returns

true if the command class is supported, false if not

Return type

boolean

id_match(mfr_id, product_type, product_id)

Determine whether device self is a match to the passed manufacturer ID(s),

product ID(s) and product type(s). Filter arguments can be numerical literals or arrays of numerical literals.

In the case that a filter argument is an array, matching uses OR logic. Match against any single array item is considered a device match.

Parameters
  • mfr_id (number or table) – numerical manufacturer ID or array of IDs

  • product_type (number or table) – numerical product type ( aka product in DTH namespace) or array of types.

  • product_id (number or table) – numerical product ID ( aka model in DTH namespace) or array of IDs

Returns

true if device self matches the passed all filter arguments

Return type

boolean

static init(cls, driver, raw_device)

Initialize an st.zwave.Device instance

Parameters
debug_pretty_print()

Return a string representation of device model and supported command class information

Returns

string containing the device model and supported command class information

Return type

A

pretty_print()

Get a string with the ID, DNI and label of the device.

Returns

a short string representation of the device

Return type

str

class st.zwave.ChildDevice: st.Device
get_dst_channel()

Get the dst_channels array used to address a message to this child through the parent

The default implementation here assumes that this device has a parent assigned child key of the child endpoint

Returns

<number> the dst channels to address a message to this child device

Return type

table

send(cmd)

Send a Z-Wave command to the associated Z-Wave device.

The command will be logged in the live logs when it is sent from the driver. There will also be logs to trace when the command is queued in the hub, and when the transmission has completed on the radio.

Parameters

cmd (st.zwave.Command) –

component_to_endpoint(component_id)

Map component to end_points(channels)

e.g. {} - map component to un-encapsulated {2} - map to specific Z-Wave endpoint(channel) {1,2,3} - map to more then one Z-Wave endpoint (channels)

e.g. {2} for Z-Wave channel 2 or {} for unencapsulated

Parameters

component_id (str) – ID

Returns

dst_channels destination channels

Return type

table

is_cc_supported(cc_value, endpoint)

Interrogate the device’s profile to determine whether a particular command class is supported.

Parameters
  • cc_value (number) – the command class id as defined in cc.lua, e.g cc.SWITCH_BINARY = 0x25

  • endpoint (number) – of the endpoint to check, if nil we check the first endpoint

Returns

true if the command class is supported, false if not

Return type

boolean

id_match(mfr_id, product_type, product_id)

Determine whether device self is a match to the passed manufacturer ID(s),

product ID(s) and product type(s). Filter arguments can be numerical literals or arrays of numerical literals.

In the case that a filter argument is an array, matching uses OR logic. Match against any single array item is considered a device match.

Parameters
  • mfr_id (number or table) – numerical manufacturer ID or array of IDs

  • product_type (number or table) – numerical product type ( aka product in DTH namespace) or array of types.

  • product_id (number or table) – numerical product ID ( aka model in DTH namespace) or array of IDs

Returns

true if device self matches the passed all filter arguments

Return type

boolean

static init(cls, driver, raw_device)

Initialize an st.zwave.Device instance

Parameters
debug_pretty_print()

Return a string representation of device model and supported command class information

Returns

A string containing the device newtwork ID and the parent device ID of this child

Return type

str

pretty_print()

Get a string with the ID, DNI and label of the device.

Returns

a short string representation of the device

Return type

str