Zigbee Driver Structures

A ZigbeeDriver is an extension of the Driver and contains some more specific structures that are only applicable to a Zigbee device.

zigbee_handlers

The zigbee_handlers are the most common additional field you will be adding to the driver template used to build your driver. The zigbee_handlers structure is used to register message handlers for any message coming from the device that you may need to convert to a SmartThings capability event, or use as state to manage the continued execution of the device. Detailed information on the types of handlers as well as the structure of the table can be found on the RX message handlers page.

There are some additional caveats to take into account when setting these up in your driver. First is the interaction between zigbee_handlers you define in your driver template, and handlers defined in default Zigbee behavior that you pull in using the defaults.register_for_default_handlers call. The handlers you provide directly in your driver template will take precedence over any default behavior that would be present. More concretely, if you provide a handler for the OnOff attribute of the OnOff cluster in your driver template, but then call to register defaults for the switch capability (which also provides a handler for that cluster and attribute); your handler would be the one called with the message. In this way you can choose to only override the specific handlers in which your device strays from the standard behavior.

Another note is that each registered “handler” can be either a function, or a list of functions. If you provide a list EACH function in that list will be called with the same message.

Example

Below is an example creating a simple set of zigbee_handlers for a Zigbee switch that supports switch and level (cluster_configurations, default handlers, and capability_handlers omitted for example simplicity).

local capabilities = require "st.capabilities"
local ZigbeeDriver = require "st.zigbee"
local clusters = require "st.zigbee.zcl.clusters"
local OnOffCluster = clusters.OnOffCluster
local LevelControlCluster = clusters.LevelControlCluster

local function on_off_attr_handler(driver, device, value)
  local attr = capabilities.switch.switch
  device:emit_event(value.value and attr.on() or attr.off())
end

local function level_attr_handler(driver, device, value)
  device:emit_event(capabilities.switchLevel.level(math.floor((value.value / 254.0 * 100) + 0.5)))
end

local zigbee_switch_driver_template = {
  supported_capabilities = {
    capabilities.switch,
    capabilities.switchLevel,
  },
  zigbee_handlers = {
    global = {},
    cluster = {},
    attr = {
      [OnOffCluster.ID] = {
        [OnOffCluster.attributes.OnOff.ID] = on_off_attr_handler
      },
      [LevelControlCluster.ID] = {
        [LevelControlCluster.attributes.CurrentLevel.ID] = level_attr_handler
      }
    }
  }
}

local zigbee_bulb = ZigbeeDriver("zigbee-switch", zigbee_bulb_driver_template)
zigbee_bulb:run()

cluster_configurations

The cluster_configurations are another ZigbeeDriver template option that maps pretty closely to the ConfigureReporting Zigbee command. These configurations are used to drive how we configure a device newly joined to the hub to inform us of changes in state. See the class AttributeConfiguration for exactly what components go into each record. There are 2 use cases for these configurations and they can be controlled using the two optional fields configurable and monitored.

These configurations should be grouped into lists, under the key of the capability ID they are associated with. This is used in terms of registering defaults, if you define a set of configurations on your driver under a given capability, the corresponding defaults will not be included even if you call to register them.

Configured Attribute

An AttributeConfiguration that has configurable set to true (which is the default if it is not explicitly set), will result in a ConfigureReporting command being sent to the device when ZigbeeDevice:configure is called (by default hooked up to the capability command configuration.configure). This means that if the device supports reporting, it will send an ReportAttribute command to the hub when the corresponding attribute changes according the the other paramaters.

An AttributeConfiguration that has this field set to false will not send the commands to configure reporting. One common example of this would be a ZLL device as the ZLL profile does not support reporting.

Monitored Attribute

An AttributeConfiguration that has monitored set to true (which is the default if it is not explicitly set), will enroll that attribute to be tracked by the driver. What this means is that each time that attribute is reported, or we receive a read attribute response the driver will keep a timestamp. Then, periodically (on a 30 second interval) all monitored attributes will be checked, and if any of those attributes hasn’t reported in 1.5x the maximum_interval a ReadAttribute command will be sent to try to refresh the attribute value, and will continue to be sent every 1.5x the maximum_interval until it is heard from again. For most Zigbee devices behaving well, these read attribute commands will never need to be sent, but if network congestions causes a missed report, this can keep us from being out of date for too long. The most common use case for this is again ZLL devices which, if changed out of band (e.g. a physical switch is turned off then on), we will not know about until we poll.

Example

Below is an example creating a simple set of attribute_configurations for a Zigbee switch that supports switch and level (zigbee_handlers, default handlers, and capability_handlers omitted for example simplicity).

local capabilities = require "st.capabilities"
local ZigbeeDriver = require "st.zigbee"
local clusters = require "st.zigbee.zcl.clusters"
local OnOffCluster = clusters.OnOffCluster
local LevelControlCluster = clusters.LevelControlCluster
local data_types = require "st.zigbee.data_types"

local zigbee_switch_driver_template = {
  supported_capabilities = {
    capabilities.switch,
    capabilities.switchLevel,
  },
    cluster_configurations = {
      [capabilities.switch.ID] = {
        {
            cluster = zcl_clusters.OnOffCluster.ID,
            attribute = zcl_clusters.OnOffCluster.attributes.OnOff.ID,
            minimum_interval = 0,
            maximum_interval = 300,
            data_type = data_types.Boolean
        }
      },
      [capabilities.switchLevel.ID] = {
        {
          cluster = zcl_clusters.LevelControlCluster.ID,
          attribute = zcl_clusters.LevelControlCluster.attributes.CurrentLevel.ID,
          minimum_interval = 1,
          maximum_interval = 3600,
          data_type = data_types.Uint8,
          reportable_change = 1
        }
      }
    }
}

local zigbee_bulb = ZigbeeDriver("zigbee-switch", zigbee_bulb_driver_template)
zigbee_bulb:run()

ZigbeeDriver Class Documentation

class ZigbeeDriver: Driver
zigbee_channel: message_channel

the communication channel for Zigbee devices

cluster_configurations: list[AttributeConfiguration]

A list of configurations for reporting attributes

zigbee_handlers: table

A structure definining different ZigbeeHandlers mapped to what they handle (only used on creation)

zigbee_message_handler(self, message_channel)

Handler function for the raw zigbee channel message receive

This will be the default registered handler for the Zigbee message_channel receive callback. It will parse the raw serialized message into a ZigbeeMessageRx and then use the zigbee_message_dispatcher to find a handler that can deal with it.

Handlers have various levels of specificity. Global handlers are for global ZCL commands, and are specified with a cluster, then command ID. Cluster handlers are for cluster specific commands and are again defined by cluster, then command id. Attr handlers are used for an attribute report, or read response for a specific cluster, attribute ID. and finally zdo handlers are for ZDO commands and are defined by the “cluster” of the command.

Parameters
  • self (Driver) – the driver context

  • message_channel (message_channel) – the Zigbee message_channel with a message ready to be read

static populate_zigbee_dispatcher_from_sub_drivers(driver)

Add a number of child handlers that override the top level driver behavior

Each handler set can contain a handlers field that follow exactly the same pattern as the base driver format. It must also contain a zigbee_can_handle(driver, device, zb_rx) function that returns true if the corresponding handlers should be considered.

This will recursively follow the sub_drivers and build a structure that will correctly find and execute a handler that matches. It should be noted that a child handler will always be preferred over a handler at the same level, but that if multiple child handlers report that they can handle a message, it will be sent to each handler that reports it can handle the message.

Parameters

driver (Driver) – the executing zigbee driver (or sub handler set)

add_hub_to_zigbee_group(group_id)
Parameters

group_id (any) –

static init(cls, name, driver_template)

Build a Zigbee driver from the specified template

This can be used to, given a template, build a Zigbee driver that can be run to support devices. The name field is used for logging and other debugging purposes. The driver should also include a set of capability_handlers and zigbee_handlers to handle messages for the corresponding message types. It is recommended that you use the call syntax on the ZigbeeDriver to execute this (e.g. ZigbeeDriver(“my_driver”, {}) )

Parameters
  • cls (table) – the class to be instantiated (ZigbeeDriver)

  • name (str) – the name of this driver

  • driver_template (table) – a template providing information on the driver and it’s handlers

Returns

the constructed Zigbee driver

Return type

Driver

Attribute Configuration Documentation

class AttributeConfiguration
cluster: number

Cluster ID this attribute is a part of

attr: number

the attribute ID

minimum_interval: number

the minimum reporting interval for this configuration

maximum_interval: number

the maximum reporting interval for this configuration

data_type: st.zigbee.data_types.DataType

the data type class for this attribute

rep_change: st.zigbee.data_types.DataType

(optional) the amount of change needed to trigger a report. Only necessary for non-discrete attributes

configurable: boolean

(optional default = true) Should this result in a Configure Reporting command to the device

monitored: boolean

(optional default = true) Should this result in a expected report monitoring