Device

In order for a driver to do its job it will need to know information about the devices that it is responsible for. This information is held in a Device object that provides many utility functions and abstractions that will simplify many normal use cases. It will also provide relevant caching and updating on changes that originate outside the driver. In addition a driver can provide a infoChanged lifecycle handler that can do additional processing after the provided processing. The Device object is provided to event handlers handling events for a specific device. A full list of all of these objects for every device the driver is responsible for can be fetched with the Driver:get_devices method.

Below is an example of a Device object.

device = {
  driver = {
    id = "9fbc3680-b9a9-4bd8-a574-9b03517032b2", name = "smartthings/Lua Zigbee Water", type = "DATAMGMT" },
  components = {
    main = {
      capabilities = {
        { id = "temperatureMeasurement", version = 1 },
        { id = "battery", version = 1 },
        { id = "waterSensor", version = 1 },
        { id = "sensor", version = 1 },
      },
      emit_event = function(capability_event, event_metadata) end,
      id = "main"
    }
  },
  preferences = {},
  zigbee_endpoints = {
     1 = {
      client_clusters = { 25 },
      device_id = 1026,
      id = 1,
      manufacturer = "CentraLite",
      model = "3315-S",
      profile_id = 260,
      server_clusters = { 0, 1, 3, 32, 1026, 1280, 2821 }
    }
  },
  id = "02703834-4ab9-46e0-80ed-789fbd481f63",
  fingerprinted_endpoint_id = 1,
  zigbee_eui = "\x00\x0D\x6F\x00\x03\x26\x8A\x5C",
  network_type = "DEVICE_ZIGBEE",
  label = "Water Leak Sensor",
  data = {
    deviceEUI = "000D6F0003268A5C",
    firmwareChecked = "2020-01-15T00:41:46.213Z",
    initialJoinTime = "2019-12-05T15:54:31.563Z",
    initialJoinType = "Unsecure Join",
    lastJoinTime = "2020-01-08T18:33:57.480Z",
    lastJoinType = "Secure Rejoin",
    parent = "0000",
    targetFirmwareVersion = "520573712",
    zigbeeNodeType = "SLEEPY_END_DEVICE"
  },
  profile = {
    components = {
      {
        capabilities = {
          { id = "temperatureMeasurement", version = 1 },
          { id = "battery", version = 1 },
          { id = "waterSensor", version = 1 },
          { id = "configuration", version = 1 },
          { id = "sensor", version = 1 },
          { id = "healthCheck", version = 1 }
        },
        id = "main"
      }
    },
    id = "b648f97a-ab00-4a92-a1f5-2d10bf9a5c7d"
  },
  device_network_id = "D836"
}

Device Class

The device class is the wrapper on top of the raw data provided by the device_api. The device class table will store information in a variety of different locations, and these locations will be protected with metatables. Following are the main pieces to the device object.

st_store:

This is used to store the above shown representation of the SmartThings device model. It is read only, and will be updated automatically if there are changes in the model from an external source (e.g. a change in the cloud). The top level values of this table are also “mirrored” onto the device object itself. That is device.st_store.id will give you the device’s UUID to refer to the device in the SmartThings platform, but you can also access this value simply through device.id. This is done for simplicity of access.

transient_store:

The transient_store is used for storing driver/device specific information. This information however, will only exist as long as the driver is running, and will need to be re-populated where necessary on restart of the hub. A good example of this would be storing the timestamp of the last time you read a given attribute. It’s something that is only useful within the context of the specific driver, but is easily re-constructed after a restart. It is suggested that most of your data can/should be stored this way. It is also recommended that most user/driver stored data be accessed/set using the device:set_field and device:get_field methods documented below.

persistent_store:

Similar to the transient_store, this is for use for driver specific information, however, unlike the transient_store, information written here will be stored and persisted through restarts. This carries with it a cost in wear, as well as time delays associated with the writing and reading. This should also be accessed through the device:set_field and device:get_field methods. A good example of a target for the persistent store would be something like the number of lock codes a lock can support. This is something that only needs to be read once when the device is first joined, and won’t change for the lifetime of the device. This could be read every time the driver restarts, but it is reasonable to read once and store for the lifetime of the device. In order to protect the longevity of your hub device, we limit how frequently values are actually written to flash. This does, however, come with a potential loss of information. When the persistent store for your driver is “written” it is cached in memory and will actually be written to flash on a schedule and on graceful shutdown. This means there is potential information loss in the case of a power loss.

state_cache:

The state cache is a persistent data cache that will store for each component, capability, attribute, the most recently generated state by the driver. For example device.state_cache.main.switch would contain { value = "on" }.

The set_field and get_field functions can be used to write and read from the persistent and transient stores. It is enforced that top level keys cannot be repeated between the two stores, as such get_field will always return the single value with the matching key. Consequently set_field will delete the value in the other store if it is written to the other.

There are also a variety of other utility methods documented below.

Device Class Documentation

class st.Device

A device object contains all of the information we have about a given device that is set to be managed by this driver. It also provides a number of utility functions that make normal operations for dealing with devices simpler.

transient_store: table

Used to store driver specific data about a device that will not persist through driver restart.

persistent_store: table

Used to store driver specific data about a device that will be persisted through restart. The actual flash writes are on a schedule so some data loss is possible if the hub experiences a power loss

st_store: table

Contains the SmartThings device model. Read only and can be updated as a result of changes made elsewhere in the system.

state_cache: table

Caches the most recent event state generated for each component/capability/attribute this is per run session, and is persisted through restart.

thread: st.thread.Thread

The handle to the cosock thread that executes events for this device. This can also be used directly to schedule your own events or use its register_socket function to handle a device-specific socket.

emit_event(capability_event)

Emit a capability event for this devices main component. Will log a warning and do nothing if there is no “main” component on the device

Parameters

capability_event (table) – the capability event to emit

emit_component_event(component, capability_event)

Generate a capability event for this device and component

Usage: device:emit_component_event(device.profile.components.main, capabilities.switch.switch.on())

Parameters
  • component (table) – The component to generate this event for

  • capability_event (table) – The event table generated from a capability

Returns

The converted SmartThings event generated by this device

Return type

table

set_field(field, value, addtional_params)

Set a device specific value to be stored and retrieved when needed. The key names are unique across both persistent and transient stores.

Parameters
  • field (str) – The field name for this value

  • value (value) – The value to set the field to. If setting to persistent store it must be serializable

  • addtional_params (table) – Optional: contains additional description of the field. Currently only usage is the persist field which, if true, will store the field to the persistent store instead of transient

get_parent_device()

Get the st.Device object that is the parent of this device. This _can_ result in a blocking request for the

parents device data, and can be subject to race conditions based on order of data sync. This should _NOT_ be used within an added or init lifecycle event to avoid race conditions and deadlocks.

Returns

the parent device object of this device

Return type

st.Device

set_find_child(find_child_fn)

Set a function used to find a child given an “endpoint” input specific to each protocol

Parameters

find_child_fn (function) – A function that takes a protocol specific endpoint identifier to find a child from

get_child_list()

Get a list of all the children of this device

Returns

a list of the child devices of this device. The type will be specific to the protocol

Return type

list[st.Device]

get_child_by_parent_assigned_key(parent_assigned_key)

Find a child of this device by the parent assigned child key given at creation

Parameters

parent_assigned_key (str) – the key assigned by the parent to identify a child at creation

Returns

the child device with this key

Return type

st.Device or st.zigbee.ChildDevice or st.zwave.ChildDevice or st.matter.ChildDevice or nil

get_field(field)

Retrieve the value a previously set field. nil if non-existent

Parameters

field (str) – The field name for this value

Returns

value The value the field was set to.

Return type

value

supports_capability(capability, component)

Check if this device has a capability in its profile

Parameters
  • capability (Capability) – The capability to check for existence

  • component (str) – Optional: The component id to check for capability support. If nil, any component match will return true

Returns

true if the capability is present in this devices profile

Return type

boolean

component_exists(component_id)

Check if this device has a component_id in its profile

Parameters

component_id (str) –

Returns

true if the component is present in this devices profile

Return type

boolean

get_latest_state(component_id, capability_id, attribute_name, default_value, default_state_table)

Get the latest state of this device for a given component, capability, attribute

table (e.g. it would include both the value and unit keys if both are present)

Parameters
  • component_id (str) – the component ID to get the state for

  • capability_id (str) – the capability ID to get the state for

  • attribute_name (str) – the capability attribute name to get the state for

  • default_value (any) – Optional value to return if the state_cache for the lookup is nil

  • default_state_table (any) – Optional value to return if the state_cache for the lookup is nil

Returns

The first return value is the state.value present for the attribute, the second return is the state

Return type

any or any

component_count()
Returns

count of components in device profile

Return type

number

supports_capability_by_id(capability_id, component)

Check if this device has a capability_id in its profile

Parameters
  • capability_id (str) – The capability ID to check for existence

  • component (str) – Optional: The component id to check for capability support. If nil, any component match will return true

Returns

true if the capability is present in this devices profile

Return type

boolean

try_update_metadata(metadata)

Send a request to update the metadata of a device.

Example usage: device:try_update_metadata({profile = "bulb.rgb.v1", vendor_provided_label = "My RGB Bulb"})

All metadata fields are type string. Valid metadata fields are:

For all network types (LAN/ZIGBEE/ZWAVE/MATTER): profile - profile name defined in the profile .yaml file. provisioning_state - the provisioning state of the device (TYPED/PROVISIONED)

LAN specific: manufacturer - device manufacturer model - model name of the device vendor_provided_label - device label provided by the manufacturer/vendor

Parameters

metadata (table) – A table of device metadata

static build(cls, driver, raw_device)

Build a device object from a raw st_store of the SmartThings device model

helper event generation functions

Parameters
  • cls (table) – The Device class

  • driver (Driver) – The driver context this device will run under

  • raw_device (table) – The SmartThings device model representation, used to populate the st_store and generate

Returns

The created device

Return type

Device

load_updated_data(new_device_data)

Update the st_store data with newly provided data from the cloud.

Parameters

new_device_data (any) –

extend_device(func_name, func)

Add a function to this device object, or override an existing function

Parameters
  • func_name (str) – the name of the function to add/overwrite

  • func (function) – the function to add to the device object

deleted()

This will do any necessary cleanup if the device is removed. The device object will not

be functional after this call.

pretty_print()

Get a string with the ID and label of the device

Returns

a short string representation of the device

Return type

str

online()

Mark device as being online

Only useable on LAN type devices and children of LAN type devices. Calls to this API for ZIGBEE, ZWAVE, or MATTER type devices are ignored as their online/offline status are automatically determined at the radio level. When a parent device is marked online, the online state of its children will be determined independently (i.e. they can be either online or offline).

Returns

boolean Status of whether the call was successful or not

Return type

status

Returns

string The error that occured if status was falsey

Return type

error

offline()

Mark device as being offline and unavailable

Only useable on LAN type devices and children of LAN type devices. Calls to this API for ZIGBEE, ZWAVE, or MATTER type devices are ignored as their online/offline status are automatically determined at the radio level. When a parent device is marked offline, its children will also be marked offline.

Returns

boolean Status of whether the call was successful or not

Return type

status

Returns

string The error that occured if status was falsey

Return type

error

register_native_capability_cmd_handler(capability_id, capability_cmd_id)

Register a capability command to be handled natively by the hub.

Starting with API version 11, the hub can run the Zigbee/Z-Wave/Matter default handler implementations for some capability commands outside of the driver’s Lua environment. A driver can opt-in to using these handlers for capability commands which will provide substantial latency improvements over the handlers defined in the driver.

When handled natively, a command will not be forwarded to the driver; however, all protocol messages received from the device will still be handled by the driver.

If a particular command cannot be supported for a device, this API will return an error message. Only single component Matter, Z-Wave, and Zigbee devices currently support having commands handled natively. And only some capability commands (i.e. switch on/off and switchLevel setLevel) are available for those devices.

It is expected that even if a driver is opting into this functionality, that the driver provides a handler for all the capability commands it supports. This functionality is a latency optimization and there are no guarantees that it will always be available to a driver on all hub platforms.

Parameters
  • capability_id (str) – The ID of the capability to be handled natively

  • capability_cmd_id (str) – The ID of the command to be handled natively

Returns

userdata if success, or nil if there was an error

Return type

status or nil

Returns

string error message if an any

Return type

nil or error