ZigbeeDevice Class

This is a class that inherits from the Device class, but extends behavior with Zigbee-specific functionality. These functions are documented in the class documentation below.

Configuring Zigbee Devices

With Zigbee devices it is very common to want to configure attribute reporting for either when values change or on a schedule. There are a few different ways to do this. The first option is to define the doConfigure lifecycle event handler for your driver, manually building and sending configuration commands that are needed for your device. The Zigbee Device object also provides two options for adding attribute configurations either as a “configured attribute” or as a “monitored attribute”.

Configured Attributes

A configured attribute will result in sending a bind request ZDO command as well as a ZCL configure reporting command to the device when ZigbeeDevice:configure() is called. These can be added to a device using the st.zigbee.Device:add_configured_attribute. Typically you would add these as a part of the device object construction via the init lifecycle event, however, it can be done in whatever way makes sense for the device. These attributes will also automatically be read from the device when ZigbeeDevice:refresh() is called.

Monitored Attributes

Monitored attributes are those that will have their reports tracked by the platform and will automatically issue a read if the attribute hasn’t reported within the requested max reporting interval. These can be added using the ZigbeDevice:add_monitored_attribute(config). In order to avoid sending an immediate read if the max interval is exceeded slightly due to network issues, a grace period of half the max interval will be allowed before issuing the read. Additionally, the check if an attribute is out of date will happen every 30 seconds, so it could be up to 1.5 * max reporting interval + 30 seconds before a read is sent. In general this should not be needed if the device supports reporting configuration, however, some Zigbee devices (such as those using the ZLL profile) don’t and so it can be helpful to have periodic checks of attributes.

Zigbee device configuration from defaults

As described in the defaults section, quite a bit of default behavior is supported based on the capabilities a device supports defined in it’s profile. If your driver registers for defaults, there are a number of attribute configurations that will be added as configured and monitored attributes, which means calling ZigbeeDevice:configure() or ZigbeeDevice:refresh() will include these defaults.

Multi-Component devices

There are a number of situations where the SmartThings model of the device makes sense to be broken down into several “components”. A classic example of this would be a smart power strip, where it is a single device on the network, but each outlet can be controlled separately. This would best be modeled as a single device, with a component for each switch.

Within Zigbee these separate pieces of functionality are often modeled as “endpoints”, and messages to the devices can be addressed to the specific endpoint you want to control and interact with. In order to promote code reuse the ZigbeeDevice object provides a way for your driver to define how you want to map between components and endpoints that can then be used by the rest of the Zigbee Lua standard library to automatically generate events for the correct components or send messages to the correct endpoint. As an important note, as with most things in the standard library this is built to support the most common model, but it is likely that there will be individual devices that don’t adhere to this model and will need to override this behavior.

In order to opt in to this behavior you can use the following functions:

function ZigbeeDevice:set_component_to_endpoint_fn(comp_ep_fn)
function ZigbeeDevice:set_endpoint_to_component_fn(ep_comp_fn)

Here you can provide a function for each direction to map a Zigbee endpoint id (1 byte number) to a SmartThings component id (string), and vice versa. Once these are set the following functions on the device are used for event generation and message addressing:

function ZigbeeDevice:get_endpoint_for_component_id(comp_id)
function ZigbeeDevice:get_component_id_for_endpoint(ep)

If these functions are used without settign the mapping functions above, they will return the defaults (“main” for a component, and device.fingerprinted_endpoint_id for the endpoint). Further as a convenience method the following function is provided as well

function ZigbeeDevice:emit_event_for_endpoint(endpoint, event)

That will automatically generate the event for the correct endpoint.

example

Here is a simple example of a driver that supports multi switch Zigbee outlet where the profiles are defined as follows:

name: two-outlet
components:
- id: main
  capabilities:
  - id: switch
    version: 1
  categories:
  - name: Switch
- id: switch1
  capabilities:
  - id: switch
    version: 1
  categories:
  - name: Switch

name: three-outlet
components:
- id: main
  capabilities:
  - id: switch
    version: 1
  categories:
  - name: Switch
- id: switch1
  capabilities:
  - id: switch
    version: 1
  categories:
  - name: Switch
- id: switch2
  capabilities:
  - id: switch
    version: 1
  categories:
  - name: Switch

And uses Zigbee endpoints 0x00 for the first outlet and increments by one for each additional outlet. Then the following driver will be able to use the built-in behavior to correctly generate events and address commands.

local capabilities = require "st.capabilities"
local ZigbeeDriver = require "st.zigbee"
local defaults = require "st.zigbee.defaults"

local function component_to_endpoint(device, component_id)
  if component_id == "main" then
    return device.fingerprinted_endpoint_id
  else
    local ep_num = component_id:match("switch(%d)")
    return ep_num and tonumber(ep_num) or device.fingerprinted_endpoint_id
  end
end

local function endpoint_to_component(device, ep)
  if ep == device.fingerprinted_endpoint_id then
    return "main"
  else
    return string.format("switch%d", ep)
  end
end

local device_init = function(self, device)
  device:set_component_to_endpoint_fn(component_to_endpoint)
  device:set_endpoint_to_component_fn(endpoint_to_component)
end

local zigbee_outlet_driver_template = {
  supported_capabilities = {
    capabilities.switch,
  },
  lifecycle_handlers = {
    init = device_init,
  },
}

defaults.register_for_default_handlers(zigbee_outlet_driver_template, zigbee_outlet_driver_template.supported_capabilities)
local zigbee_outlet = ZigbeeDriver("zigbee_bulb", zigbee_outlet_driver_template)
zigbee_outlet:run()

Parent/Child Devices

Very similar to multi component 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 endpoint a message came from and should then return the device object representing the child device that is responsible for that endpoint, 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 ZigbeeDriver = require "st.zigbee"
local defaults = require "st.zigbee.defaults"
local device_lib = require "st.device"
local zcl_clusters = require "st.zigbee.zcl.clusters"

-- These same handlers will work for both the parent and child devices
local function on_off_attr_handler(driver, device, value, zb_rx)
  local attr = capabilities.switch.switch
  -- 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(zb_rx.address_header.src_endpoint.value, value.value and attr.on() or attr.off())
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(command.component, zcl_clusters.OnOff.server.commands.On(device))
end

local function added(driver, device, event)
  -- Only create children for the actual Zigbee device and not the children
  if device.network_type == device_lib.NETWORK_TYPE_ZIGBEE 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, ep_id)
  return parent:get_child_by_parent_assigned_key(string.format("%02X", ep_id))
end

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

local zigbee_parent_child_switch_template = {
  supported_capabilities = {
    capabilities.switch,
  },
  zigbee_handlers = {
    attr = {
      [zcl_clusters.OnOff] = {
        [zcl_clusters.OnOff.attributes.OnOff] = on_off_attr_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(zigbee_parent_child_switch_template, zigbee_parent_child_switch_template.supported_capabilities)
local zigbee_parent_child_switch = ZigbeeDriver("child_device_example", zigbee_parent_child_switch_template)
zigbee_parent_child_switch:run()

Class Documentation

class st.zigbee.Device: st.Device
get_endpoint(cluster)

Find the endpoint of the device that supports the given cluster

fingerprinted_endpoint_id if none include the cluster

Parameters

cluster (number) – The cluster ID to find the endpoint for on the device

Returns

the first endpoint with the cluster listed in its server clusters, or the

Return type

number

get_manufacturer()

Find the manufacturer of this device

Returns

The manufacturer of this device, nil if none present

Return type

str

get_model()

Find the model of this device

Returns

The model of this device, nil if none present

Return type

str

get_short_address()

Get the numeric zigbee short address for this device

Returns

The 2 byte Zigbee short address of this device

Return type

number

add_attributes_from_driver_template(attr_config_list)

Add a series of attribute configurations to this device

These devices will be added as configured and/or monitored attributes based on the config flags configurable and monitored. These flags are assumed to be true. That is, any value (or non value if they aren’t set) is treated as being set true, and only if it is explicitly set to false will a attribute in the list not be added.

A configured attribute will generate a configure reporting and bind request messages when configure is called on the device. A monitored attribute will monitor responses from the device for the corresponding attributes and send periodic reads if the value isn’t updated in too long.

configurations to add

Parameters

attr_config_list (list[st.zigbee.AttributeConfiguration]) – the list of attribute

set_ias_zone_config_method(ias_zone_config_type)

Set the configuration type for IAS Zone on this device

If unset it is assumed that this device does not need IAS Zone configuration

Parameters

ias_zone_config_type (IAS_ZONE_CONFIGURE_TYPE) – The type of configuration this device needs

supports_server_cluster(cluster_id, endpoint_id)

Check if this devices supports a specific cluster as a server

Parameters
  • cluster_id (number) – the cluster ID to check for

  • endpoint_id (number or nil) – the endpoint to check cluster support

Return type

boolean

add_configured_attribute(config)

Add a configured attribute for this device

A configured attribute will generate a configure reporting and bind request messages when configure is called on the device.

Parameters

config (st.zigbee.AttributeConfiguration) – the attribute configuration to add

add_monitored_attribute(config)

Add a monitored attribute for this device

A monitored attribute will monitor responses from the device for the corresponding attributes and send periodic reads if the value isn’t updated in too long. That length is determined by config.maximum_interval * 1.5

Parameters

config (st.zigbee.AttributeConfiguration) – the attribute configuration to add

check_monitored_attributes()

Check all monitored attributes for this device and send a read where necessary

This will look through all monitored attributes that have been added to this device and if we have not heard from or sent a read in above the expected interval, we will send a read attribute to update our status.

attribute_monitor(zb_rx)

Check a ZigbeeRx message against our monitored attributes and update the status

This message is expected to be called with all messages that are received from this device. If this is a read or report for a monitored attribute the timestamps are updated. If there is a status involving an unsupported attribute, or a read attribute failure, the offending attribute is removed from the monitored attributes for this device to avoid spurious monitor reads

Parameters

zb_rx (st.zigbee.ZigbeeMessageRx) – A received Zigbee message from this device

remove_monitored_attribute(cluster, attribute)

Remove a monitored attribute on this device

This will prevent future monitoring of this attribute.

Parameters
  • cluster (number) – The id of the cluster of the attribute to remove

  • attribute (number) – The id of the attribute to remove

remove_configured_attribute(cluster, attribute)

Remove a configured attribute on this device.

This will prevent configuration of this attribute. This must be done before device:configure() is called or it will have no effect.

Parameters
  • cluster (number) – The id of the cluster of the attribute to remove

  • attribute (number) – The id of the attribute to remove

configure()

Send the necessary bind requests and reporting configurations to this device

For each configured attribute on this device send the necessary configure reporting and bind requests to have the device send attribute updates. Attributes are configured for all zigbee endpoints that support the clusters.

set_component_to_endpoint_fn(comp_ep_fn)

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

Parameters

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

set_endpoint_to_component_fn(ep_comp_fn)

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

Parameters

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

get_endpoint_for_component_id(comp_id)

Given the component ID find the corresponding endpoint for this device

This will use the function set by st.zigbee.Device:set_component_to_endpoint_fn to return the appropriate endpoint given the component. If the function is unset it defaults to the devices “fingerprinted_endpoint_id”

Parameters

comp_id (str) – the component ID to find the endpoint for

Returns

the endpoint this component matches to

Return type

number

get_component_id_for_endpoint(ep)

Given the endpoint ID find the corresponding component for this device

This will use the function set by st.zigbee.Device:set_endpoint_to_component_fn to return the appropriate component given the endpoint. If the function is unset it defaults to “main”

Parameters

ep (number) – the endpoint ID to find the component for

Returns

the component ID the endpoint matches to

Return type

str

emit_event_for_endpoint(endpoint, event)

Emit a capability event for this device coming from the given endpoint

This uses st.zigbee.Device:get_component_id_for_endpoint to find the appropriate component and emit the event for that component

Parameters
  • endpoint (number) – the endpoint ID a message was received from

  • event (table) – the capability event to generate

refresh(endpoint)

Send a read attribute command for all configured attributes on this device

Parameters

endpoint (any) –

send(zb_tx)

Send a ZigbeeMessageTx to this device

Parameters

zb_tx (st.zigbee.ZigbeeMessageTx) – the message to send to this device

send_to_component(component_id, zb_tx)

Send a ZigbeeMessageTx to this device and component

Parameters
  • component_id (str) – the component id to send this message to

  • zb_tx (st.zigbee.ZigbeeMessageTx) – the message to send to this device

debug_pretty_print()

Return a string representation of device model and supported cluster information

Returns

A string containing the device model and supported cluster information

Return type

str

class st.zigbee.ChildDevice: st.Device
get_endpoint(cluster)

For a child device this only returns the endpoint that this child is modelling.

This assumes a specific parent assigned child key pattern of <endpoint> be used and anything other than that will require this function to be overridden, or manual address to be done for messages.

Parameters

cluster (number) – unused on child

Returns

the endpoint extracted from the device DNI

Return type

number

get_manufacturer()

Return the manufacturer of the parent device

Returns

The manufacturer of this device, nil if none present

Return type

str

get_model()

Return the model fo the parent device

Returns

The model of this device, nil if none present

Return type

str

get_short_address()

Return the short address of the parent device

Returns

The 2 byte Zigbee short address of this device

Return type

number

add_attributes_from_driver_template(attr_config_list)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

configurations to add

Parameters

attr_config_list (list[st.zigbee.AttributeConfiguration]) – the list of attribute

set_ias_zone_config_method(ias_zone_config_type)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

Parameters

ias_zone_config_type (IAS_ZONE_CONFIGURE_TYPE) – The type of configuration this device needs

supports_server_cluster(cluster_id, endpoint_id)

Check if this devices supports a specific cluster as a server

This just delegates to the parent of this child, so if an endpoint other than this devices endpoint the results could be misleading.

Parameters
  • cluster_id (number) – the cluster ID to check for

  • endpoint_id (number or nil) – the endpoint to check cluster support

Return type

boolean

add_configured_attribute(config)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

Parameters

config (st.zigbee.AttributeConfiguration) – the attribute configuration to add

add_monitored_attribute(config)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

Parameters

config (st.zigbee.AttributeConfiguration) – the attribute configuration to add

check_monitored_attributes()

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

attribute_monitor(zb_rx)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

Parameters

zb_rx (st.zigbee.ZigbeeMessageRx) – A received Zigbee message from this device

remove_monitored_attribute(cluster, attribute)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

Parameters
  • cluster (number) – The id of the cluster of the attribute to remove

  • attribute (number) – The id of the attribute to remove

remove_configured_attribute(cluster, attribute)

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

Parameters
  • cluster (number) – The id of the cluster of the attribute to remove

  • attribute (number) – The id of the attribute to remove

configure()

This is a noop on a child device, but is included to allow the children to be treated like a standard Zigbee device

get_endpoint_for_component_id(comp_id)

Given the component ID find the corresponding endpoint for this device

This will use the function set by st.zigbee.Device:set_component_to_endpoint_fn to return the appropriate endpoint given the component. If the function is unset it defaults to the value from :get_endpoint for the child which is derived from the DNI of this device

Parameters

comp_id (str) – the component ID to find the endpoint for

Returns

the endpoint this component matches to

Return type

number

refresh()

Calls refresh on the parent device

send(zb_tx)

Send a ZigbeeMessageTx to this device (delegates to parent:send())

Parameters

zb_tx (st.zigbee.ZigbeeMessageTx) – the message to send to this device

send_to_component(component_id, zb_tx)

Send a ZigbeeMessageTx to this device and component

If no function is defined to handle multi component for this child, it will call send on the parent with the message addressed to the endpoint returned from :get_endpoint() for this child

Parameters
  • component_id (str) – the component id to send this message to

  • zb_tx (st.zigbee.ZigbeeMessageTx) – the message to send to this device

debug_pretty_print()

Return a string representation of device model and supported cluster information

Returns

A string containing the device model and supported cluster information

Return type

str