Interacting with Zigbee devices

There are three Zigbee API levels which all build on top of each other with increasing complexity to interact with Zigbee devices on Homey:

zigbee-api-overview

In general it is likely you will only need node-homey-zigbeedriver, and possibly node-zigbee-clusters if you want to do some more advanced things. The Zigbee-API is what node-zigbee-clusters is built on. It is not advised to use this API directly, but it is there in case you need it.

1. node-homey-zigbeedriver

This is a library developed by Athom which makes it easier to create Drivers for Zigbee devices.

It exposes classes which can be extended in your app and other useful functionality. The most important class is ZigBeeDevice. This class handles getting a ZigBeeNode and uses node-zigbee-clusters to extend the ZigBeeNode with Zigbee Cluster Library (ZCL) functionality. Doing this enables a developer to directly communicate with the ZigBeeNode using ZCL very easily. The basic usage is demonstrated below, for more in-depth information take a look at the node-homey-zigbeedriver documentation or the source code on GitHub.

/device.js

const { ZigBeeDevice } = require('homey-zigbeedriver');

class MyZigBeeDevice extends ZigBeeDevice {
  async onNodeInit({ zclNode }) {
    await zclNode.endpoints[1].clusters.onOff.toggle();
  }
}

Note: for Zigbee light devices (e.g. bulbs and spots) we created ZigBeeLightDevice, this class can be extended in your driver and will by default handle all light related functionality.

For inspiration check out some of the Zigbee apps already created with node-homey-zigbeedriver:

2. node-zigbee-clusters

This is the library used by node-homey-zigbeedriver to expose the Zigbee Cluster Library (ZCL) functionality. It implements all the clusters that are accessible through zclNode in lib/clusters. It is very easy to add new clusters, or add attributes or commands to an existing cluster, merely by changing the definition in one of the cluster files (e.g. the onOff cluster). Take a look at the Zigbee Cluster Specification (PDF) if you are interested in how this library works. The basic usage of this library is demonstrated below, note that it is usually better to use ZigBeeDevice exported by node-homey-zigbeedriver instead.

/device.js

const Homey = require('homey');
const { ZCLNode, CLUSTER } = require('zigbee-clusters');

class MyDevice extends Homey.Device {
  async onInit() {
    // Get ZigBeeNode instance from ManagerZigBee
    const node = await this.homey.zigbee.getNode(this);

    // Create ZCLNode instance
    const zclNode = new ZCLNode(node);

    // Interact with the node
    await zclNode.endpoints[1].clusters.onOff.toggle();
  }
}

There are a few cases where you need to interact with node-zigbee-clusters in your app.

1. Interacting with node-homey-zigbeedriver

In order to inform node-homey-zigbeedriver about which cluster we are targeting we need to import the cluster specification from node-zigbee-clusters as demonstrated below.

const { CLUSTER } = require('zigbee-clusters');
const { ZigBeeDevice } = require('homey-zigbeedriver');

class MyZigBeeDevice extends ZigBeeDevice {
  onNodeInit() {
    // Register onoff capability
    this.registerCapability('onoff', CLUSTER.ON_OFF);
  }
}

This ensures the cluster to be registered is actually available in node-zigbee-clusters.

2. Implementing a new cluster, or improving an existing cluster

It is very easy to add or improve clusters in node-zigbee-clusters by changing the cluster definition in lib/clusters. For more information on how to do this, check out Implementing a cluster.

3. Implementing a bound cluster

As mentioned in the introduction, in order to receive commands from a node a binding must be made to the respective cluster. This can be done by listing the cluster id in the bindings array in the driver's manifest (read Creating a Zigbee Driver for more information on the driver's manifest). Please refer to Implementing a bound cluster for more information on how to create a BoundCluster in your driver.

4. Implementing a custom cluster

Zigbee device manufacturers are allowed to implement custom clusters for their devices. In order to use such a custom cluster in your driver you need to extend an existing cluster with the custom behaviour. Check out Implementing a custom cluster on how to do this exactly.

3. Zigbee-API

The Zigbee-API can be used for directly communicating with a ZigBeeNode if needed. In general it is advised not to use this API and take a look at node-zigbee-clusters and node-homey-zigbeedriver which are libraries built on top of the Zigbee-API which do most of the heavy lifting and make it even easier to develop Zigbee apps for Homey.

In the unexpected case that you do want to access the Zigbee-API take a look below for a basic example.

First, the ZigBeeNode must be retrieved from ManagerZigBee.

/device.js

class MyDevice extends Homey.Device {
  async onInit() {
    const node = await this.homey.zigbee.getNode(this);
  }
}

Next, we can use the Zigbee-API to directly communicate with the ZigBeeNode using ZigBeeNode#sendFrame and ZigBeeNode#handleFrame. Important: override the handleFrame method on ZigBeeNode, this method is called when a frame is received and if it is not overridden it will throw.

/device.js

const node = await this.homey.zigbee.getNode(this);
node.handleFrame = (endpointId, clusterId, frame, meta) => {
  if (endpointId === 1 && clusterId === 6) {
    // The node sent a frame to Homey from endpoint 1 and cluster 'onOff'
  }
};

// Send a frame to endpoint 1, cluster 6 ('onOff') which turns the node on
await node.sendFrame(
  1, // endpoint id
  6, // cluster id
  Buffer.from([
    1, // frame control
    0, // transaction sequence number
    1, // command id ('on')
  ]),
);

// Send a frame to endpoint 1, cluster 6 ('onOff') which turns the node off
await node.sendFrame(
  1, // endpoint id
  6, // cluster id
  Buffer.from([
    1, // frame control
    1, // transaction sequence number
    0, // command id ('off')
  ]),
);