# Z-Wave

Z-Wave is built around a principle called *Command Classes*. A Command Class is a group of *Commands*, that each can make a device perform an action, or request data.

Commands usually belong to one of 3 categories:

* "set" commands update a value
* "report" commands to let devices know the current value
* "get" commands are a request the current value, the device will send a report in response

As an example lets take a look at `COMMAND_CLASS_BASIC`, this Command Class is supported by most controllable devices. It has a `BASIC_SET` command, which sets a boolean (usually on/off), a `BASIC_GET`command, and a `BASIC_REPORT` command. Sending a `BASIC_GET` to the device will make the device send a `BASIC_REPORT` to Homey. The `BASIC_REPORT` contains the boolean (on/off) value.

Sending a command to a device from Homey is simple:

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/drivers/\<driver\_id>/device.js" %}

```javascript
const Homey = require('homey');

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

    await node.CommandClass.COMMAND_CLASS_BASIC.BASIC_SET({ Value: true });
  }
}

module.exports = Device;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/drivers/\<driver\_id>/device.mts" %}

```mts
import Homey, { ZwaveCommandClass } from "homey";

interface CommandClassBasic extends ZwaveCommandClass {
  BASIC_SET: (args: { Value: unknown }) => Promise<void>;
}

export default class Device extends Homey.Device {
  async onInit(): Promise<void> {
    const node = await this.homey.zwave.getNode(this);

    await (node.CommandClass["COMMAND_CLASS_BASIC"] as CommandClassBasic).BASIC_SET({ Value: true });
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/drivers/\<driver\_id>/device.py" %}

```python
from homey import device


class Device(device.Device):
    async def on_init(self) -> None:
        node = await self.homey.zwave.get_node(self)

        await node.command_classes["COMMAND_CLASS_BASIC"].send_command(
            "BASIC_SET", {"Value": True}
        )


homey_export = Device

```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
You can view working examples of Homey Apps that use Z-Wave at: [https://github.com/athombv/com.fibaro-example ](https://github.com/athombv/com.fibaro-example)and <https://github.com/athombv/com.danalock-example>
{% endhint %}

## Homey Pro Z-Wave User Manual

The Homey Pro Z-Wave User Manual applies to Homey Pro (Early 2023) and newer, Homey Pro mini, Homey Self-Hosted Server and Homey Cloud.

{% file src="/files/eJt19rrA0DTJJPlz6cgZ" %}

## Pairing

Tell Homey your driver supports a Z-Wave device by adding a `zwave` object to your driver manifest. To create a driver for a Z-Wave device you need to know the following properties of the device:

* `manufacturerId`
* `productTypeId`
* `productId`

To find out your device's IDs, pair it as a Basic Z-Wave Device. After successfully pairing you can find these values in the device settings. All Z-Wave devices are paired using the built-in Z-Wave pair wizard. Upon pairing a Z-Wave device, an App will be selected if all three IDs match.

You can customize the built-in Z-Wave pair wizard by supplying more specific instructions and a custom image in the `learnmode` property.

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```javascript
{
  "name": { "en": "My Driver" },
  "class": "light",
  "capabilities": ["onoff"],
  "zwave": {
    "manufacturerId": 271,
    "productTypeId": [256],
    "productId": [260],
    "learnmode": {
      "image": "/drivers/<driver_id>/assets/learnmode.svg",
      "instruction": { "en": "Press the button on your device three times" }
    }
  }
}
```

{% endcode %}

{% hint style="info" %}
It is possible to add multiple `productTypeId` and `productId` values in order to support multiple devices with the same `Driver`.
{% endhint %}

Most Z-Wave devices are unpaired in the same way as they are paired but in case the unpair wizard benefits from custom instructions or a different image it is possible to provide `unlearnmode` options to your Z-Wave driver manifest. `unlearnmode` accepts the same properties as `learnmode`.

## Manifest

In addition to the essential driver `zwave` properties `manifacturerId`, `productTypeId` and `productId` there are properties allow you to configure the behaviour of your Z-Wave device.

{% hint style="warning" %}
Some of these properties configure the behaviour of your device while pairing, so changes to them will require you to remove and re-add your devices.
{% endhint %}

### Device settings

Z-Wave devices often have configuration parameters, in Homey these can be set in the device settings. The only thing you need to do is tell Homey which setting corresponds to which Z-Wave configuration parameter of the device. You can do this by adding a `zwave` property to the device setting that controls the configuration parameter. Read the [device setting guide](/the-basics/devices/settings.md) for more information.

{% code title="/drivers/\<driver\_id>/driver.settings.compose.json" %}

```javascript
[
  {
    "id": "minimum_brightness",
    "type": "number",
    "label": { "en": "Minimum brightness level" },
    "value": 1,
    "attr": { "min": 1,"max": 98 },
    "hint": { "en": "This parameter determines the minimal brightness." },
    "zwave": {
      "index": 13,
      "size": 1,
    }
  }
]
```

{% endcode %}

By default, the value is parsed as a **signed** integer. If your device requires the value to be interpreted as an **unsigned** integer, explicitly set the signed property to false in the setting's `zwave` configuration:

```javascript
  "zwave": {
    "index": 13,
    "size": 1,
    "signed": false,
  }
```

### Security

There are two types of Security in Z-Wave: Security 0 (legacy) and Security 2.

Most newer devices support Security 2 (S2). In S2 there are different keys available: Access, Authenticated, Unauthenticated (and S0). The actual encryption algorithm is the same, it just determines which devices know which keys.

Homey will always grant all S2 keys that a device requests.

{% hint style="info" %}
Homey Pro (2016-2019): Homey grants the highest requested key only.
{% endhint %}

#### Security 0 (S0)

Security 0 is not very secure and really inefficient, therefore it is not used by default. It is possible that devices only provide certain functionality when they are paired securely, for example a Z-Wave Doorlock can choose to only allow using `COMMAND_CLASS_LOCK` with secure communication. In this case your app should set the `requireSecure` property in your driver's `zwave` object to `true`. To find out what command classes require secure communication check your device's technical specifications.

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```javascript
{
  "name": { "en": "My Driver" },
  "class": "lock",
  "capabilities": ["locked"],
  "zwave": {
    "manufacturerId": 270,
    "productTypeId": [8],
    "productId": [2],
    "requireSecure": true
  }
}
```

{% endcode %}

{% hint style="info" %}
By default Homey will use S2 if a device supports it. Due to the additional communication overhead of S0 your app needs to opt-into S0 by setting `requireSecure` to `true`.
{% endhint %}

### Default device configuration

After pairing a device, Homey can write configuration parameters to ensure they are set to the correct value using the `defaultConfiguration` parameter of the devices `zwave` options.

Configuration parameters can be 1, 2 or 4 bytes long, use the `size` property to tell Homey how many bytes to write. The `value` is a signed integer and its range is determined by the size of the parameter:

* -128, 127 for size=1
* -32768, -32767 for size=2
* -2147483648, 2147483647 for size=4

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```javascript
{
  "name": { "en": "My Driver" },
  "class": "light",
  "capabilities": ["onoff"],
  "zwave": {
    "manufacturerId": 271,
    "productTypeId": [256],
    "productId": [260],
    "defaultConfiguration": [
      {
        "id": 3,
        "size": 1,
        "value": 123
      }
    ]
  }
}
```

{% endcode %}

### Association Groups

Association groups allow Z-Wave devices to be linked together (associated) so that they can communicate directly without needing a central controller, like Homey. For some devices, Homey needs to be added to their association group to be able to receive status updates. For example devices may choose to only send "central scene activation" commands to their "lifeline" (the node in association group 1).

Check the device documentation from the manufacturer for an overview of the available association groups. Information can also be found here: <https://products.z-wavealliance.org/>

Homey is always added to association group 1, the Lifeline group. This way Homey can receive a notification when a device is factory reset for example. In that case the node is removed from Homey and the device is marked Unavailable.

To add Homey to other association groups automatically after pairing, add the corresponding group number to the `associationGroups` array property in the drivers `zwave` object.

Homey automatically determines whether to add a regular or multi-channel association to the group.

*Note: the* `associationGroupsMultiChannel` *is handled the same way as* `associationGroups`  *so Homey is added to those groups as well using the correct association command class. It is used for backwards compatibility but it is recommended to use* `associationGroups` *and set the app compatibility to* `>=13.2.0`*.*

Associations between devices can be configured in the device settings. If a device supports `COMMAND_CLASS_ASSOCIATION_GRP_INFO` (most modern devices do), Homey uses this information to add a hint to the different groups explaining what they do. If this Command Class is not available or a more detailed hint is needed it can be provided in the `associationGroupsOptions` property.

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```javascript
{
  "name": { "en": "My Driver" },
  "class": "light",
  "capabilities": ["onoff"],
  "zwave": {
    "manufacturerId": 271,
    "productTypeId": [256],
    "productId": [260],
    "associationGroups": [1, 3],
    "associationGroupsOptions": {
      "3": {
        "hint": { "en": "On/off signals from input 3" }
      }
    }
  }
}
```

{% endcode %}

#### Association Groups before Homey v13.2.0

In addition to "regular" (single-channel) associations devices with multiple endpoints can support "multi-channel association". In Homey versions before 13.2.0, Homey did not choose the regular or multi-channel association automatically. If multi-channel associations were necessary (to get updates from endpoints), they were configured using the `associationGroupsMultiChannel` property in the devices `zwave` options.

*Note: in Homey before 13.2.0 it was  possible to opt-out of the Lifeline association (group 1) by providing an empty array. As of version 13.2.0 the Lifeline association is always added.*

### Battery Device Wake Up Interval

Z-Wave battery devices can have special behaviour to conserve power. These devices will send messages at any time but they are not always listening for messages. The interval with which it is possible to send messages to such a device is called the "wake up interval".&#x20;

By default the manufacturer has chosen an apropriate wake up interval. It may be nessecary to override this default. In those cases you can set the `wakeUpInterval` property in the device's `zwave` options. This property controls the desired wake up interval in seconds. The range of allowed values by Homey is: 30-16777215 (30 seconds to 194 days). Make sure to use a value that the device supports.

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```javascript
{
  "name": { "en": "My Driver" },
  "class": "light",
  "capabilities": ["onoff"],
  "zwave": {
    "manufacturerId": 271,
    "productTypeId": [256],
    "productId": [260],
    "wakeUpInterval": 900
  }
}
```

{% endcode %}

### Multi channel nodes

In Z-Wave a device can only implement a command class once. This means that technically a single Z-Wave device can only have a single switch, because it can only implement, for example, `COMMAND_CLASS_SWITCH_BINARY` once. In order to give devices the ability to implement a command class multiple times, Z-Wave has Multi Channel Nodes. A Multi Channel Node is a type of Z-Wave node that contains several "endpoints" which are each their own Z-Wave node. Each of these endpoints can individually implement, for example, `COMMAND_CLASS_SWITCH_BINARY`.

In Homey after pairing a Multi Channel node, several devices will be added to the devices overview if they are specified in the `multiChannelNodes` property in the `zwave` options.

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```javascript
{
  "name": { "en": "My Driver" },
  "class": "light",
  "capabilities": ["onoff"],
  "zwave": {
    "manufacturerId": 271,
    "productTypeId": [256],
    "productId": [260],
    "multiChannelNodes": {
      "1": {
        "name": { "en": "MultiChannel device 1" },
        "class": "socket",
        "capabilities": ["onoff", "measure_power", "meter_power"],
        "icon": "/drivers/<driver_id>/assets/icon-multichannelnode1.svg"
      }
    }
  }
}
```

{% endcode %}

It is possible to override the settings of the Multi Channel Node by providing the `settings` option. This option takes the same values as the regular [Device Settings](/the-basics/devices.md#settings):

{% code title="/drivers/\<driver\_id>/driver.compose.json" %}

```json
{
  "name": { "en": "My Driver" },
  "class": "light",
  "capabilities": ["onoff"],
  "zwave": {
    "manufacturerId": 271,
    "productTypeId": [256],
    "productId": [260],
    "multiChannelNodes": {
      "1": {
        "name": { "en": "MultiChannel device 1" },
        "class": "socket",
        "capabilities": ["onoff", "measure_power", "meter_power"],
        "icon": "/drivers/<driver_id>/assets/icon-multichannelnode1.svg",
        "settings": []
      }
    }
  }
}
```

{% endcode %}

## homey-zwavedriver

A library named [`homey-zwavedriver`](https://github.com/athombv/node-homey-zwavedriver) has been developed for Node.js to ease development of Z-Wave devices in Homey. It mainly maps Command Classes to Homey's capabilities. It is recommended for most app developers to use this library.

{% hint style="info" %}
[`homey-zwavedriver`](https://github.com/athombv/node-homey-zwavedriver) is only compatible with SDK version 3. Its predecessor [`homey-meshdriver`](https://github.com/athombv/node-homey-meshdriver) is available for SDK version 2.
{% endhint %}

```bash
npm install homey-zwavedriver
```

When you are using `homey-zwavedriver` your device should extend `ZwaveDevice` instead of `Homey.Device`. This class implements some useful helpers that you can use to integrate your Z-Wave device with Homey. When a new Device is initialized [`ZwaveDevice#onNodeInit()`](https://apps-sdk-v3.developer.homey.app/ZwaveDevice.html#onNodeInit) will be called. In this method you should register all the devices capabilities. `homey-zwavedriver` supplies a [`ZwaveDevice#registerCapability()`](https://apps-sdk-v3.developer.homey.app/ZwaveDevice.html#registerCapability) method that you only need to tell what capability your device has and which associated command class implements the capability.

{% code title="/drivers/\<driver\_id>/device.js" %}

```javascript
const { ZwaveDevice } = require('homey-zwavedriver');

class Device extends ZwaveDevice {
  async onNodeInit() {
    this.registerCapability('onoff', 'SWITCH_BINARY');
  }
}

module.exports = Device;
```

{% endcode %}

{% hint style="info" %}
If you are implementing a `light` device you can extend [`ZwaveLightDevice`](https://athombv.github.io/node-homey-zwavedriver/ZwaveLightDevice.html) instead. This class already implements all the expected behaviour for a Z-Wave light.
{% endhint %}

## Z-Wave API

{% hint style="warning" %}
If possible you should use [`homey-zwavedriver`](https://github.com/athombv/node-homey-zwavedriver) instead, only use the built-in Z-Wave API if you absolutely need to.
{% endhint %}

{% tabs %}
{% tab title="JavaScript" %}
{% code title="/drivers/\<driver\_id>/device.js" %}

```javascript
const Homey = require('homey');

class Device extends Homey.Device {
  async onInit() {
    // get the node by our Device's instance
    const node = await this.homey.zwave.getNode(this);

    // get the BASIC status
    node.CommandClass.COMMAND_CLASS_BASIC.BASIC_GET()
      .then((result) => {
        if (result["Value"]) {
          this.log("Device is turned on");
        } else {
          this.log("Device is turned off");
        }
      })
      .catch(this.error);

    // battery nodes can emit an 'online' event when they're available
    // you can send commands within 10s of this event, before the node goes to sleep again
    node.on("online", (online) => {
      if (online) {
        this.log("Device is online");
      } else {
        this.log("Device is offline");
      }
    });

    // register for 'report' events
    node.CommandClass.COMMAND_CLASS_BASIC.on("report", (command, report) => {
      this.log(command.name); // e.g. BASIC_REPORT
      this.log(report); // e.g. { Value: true }
    });
  }
}

module.exports = Device;
```

{% endcode %}
{% endtab %}

{% tab title="TypeScript" %}
{% code title="/drivers/\<driver\_id>/device.mts" %}

```mts
import Homey, { ZwaveCommandClass } from "homey";

interface CommandClassBasic extends ZwaveCommandClass {
  BASIC_GET: () => Promise<{ Value: boolean }>;
}

export default class Device extends Homey.Device {
  async onInit(): Promise<void> {
    // get the node by our Device's instance
    const node = await this.homey.zwave.getNode(this);

    // get the BASIC status
    await (node.CommandClass["COMMAND_CLASS_BASIC"] as CommandClassBasic)
      .BASIC_GET()
      .then(result => {
        if (result.Value) {
          this.log("Device is turned on");
        } else {
          this.log("Device is turned off");
        }
      })
      .catch(this.error);

    // battery nodes can emit an 'online' event when they're available
    // you can send commands within 10s of this event, before the node goes to sleep again
    node.on("online", (online: boolean) => {
      if (online) {
        this.log("Device is online");
      } else {
        this.log("Device is offline");
      }
    });

    // register for 'report' events
    node.CommandClass["COMMAND_CLASS_BASIC"].on("report", (command, report) => {
      this.log(command.name); // e.g. BASIC_REPORT
      this.log(report); // e.g. { Value: true }
    });
  }
}

```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code title="/drivers/\<driver\_id>/device.py" %}

```python
from homey import device


class Device(device.Device):
    async def on_init(self) -> None:
        # get the node by our Device's instance
        node = await self.homey.zwave.get_node(self)

        # get the BASIC status
        try:
            result = await node.command_classes["COMMAND_CLASS_BASIC"].send_command(
                "BASIC_GET"
            )
            if result.Value:
                self.log("Device is turned on")
            else:
                self.log("Device is turned off")
        except Exception as e:
            self.error(e)

        # battery nodes can emit an 'online' event when they're available
        # you can send commands within 10s of this event, before the node goes to sleep again
        def on_online(online: bool) -> None:
            if online:
                self.log("Device is online")
            else:
                self.log("Device is offline")

        node.on("online", on_online)

        # register for 'report' events
        def on_report(command, report) -> None:
            self.log(command.name)  # e.g. BASIC_REPORT
            self.log(report)  # e.g. { Value: true }

        node.command_classes["COMMAND_CLASS_BASIC"].on("report", on_report)


homey_export = Device

```

{% endcode %}
{% endtab %}
{% endtabs %}

## Command Class Reference

* <https://z-wavealliance.org/development-resources-overview/z-wave-command-classes/>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://apps.developer.homey.app/wireless/z-wave.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
