Skip to content

# ZWaveJSController

ZWaveJSController is the interface to Z-Wave JS. Currently, this Controller accesses Z-Wave JS through a websocket interface available when running either Z-Wave JS UI (formerly called zwavejs2mqtt) or zwave-js-server.

## Prerequisites

You will need to be running Z-Wave JS as either zwave-js-ui (preferred) or zwave-js-server. These subsystems are available on Github and DockerHub with complete instructions, so their installation will not be covered here.

If you don't have either installed, choose zwave-js-ui. It has some nice management tools that you will find handy. I also recommend running it in a docker container, using docker-compose for the easiest management and upgrades of that container.

You will need to enable the Z-Wave JS websocket interface, which can be found in its UI under Settings > Home Assistant (whether you use Home Assistant or not, this is the name of the tab under which the WS Server (websocket) configuration lives.

Z-Wave JS' websocket uses port 3000, to which ZWaveJSController must connect. You need to make sure that port is accessible from the MSR host.

- If you are running Z-Wave JS in a docker container controlled by docker-compose, you will need to add `ports` configuration;
- If you are running it in a docker container natively (i.e. you are not using docker-compose), your container run command (`docker run`) must include the option to publish port 3000 (`-p 3000:3000`);
- If you are running Z-Wave JS as an add-on under HassOS (Home Assistant OS), please see this post for information. Restart HassOS after making configuration changes.

Also port 8091

You will likely also want to publish port 8091 in the same way as 3000, above — this allows you to access the Z-Wave JS UI web interface from a host other than the one on which it is installed.

## Supported Devices

Z-Wave JS has excellent device support, but like all Z-Wave systems, it doesn't perfectly support every possible thing that has ever been (or is currently) manufacturered. Z-Wave JS provides a searchable database where you can see if your device(s) is (are) supported. It should go without saying (but will be said anyway) that devices not supported by Z-Wave JS will not be supported by ZWaveJSController.

## Installation

ZWaveJSController is a separately-packaged add-on; it is not included in Reactor install packages or docker images, so you need to add it to use it.

1. Download the latest ZWaveJSController build archive (tarball recommended for Linux users, but ZIP also works; ZIP only is recommended for Windows).

2. If you are on a bare-metal install of Reactor, create an `ext` directory in your Reactor install directory (at the same level as `config`, `logs`, etc.

   If you are using a Reactor docker container, go to your Reactor data directory (where your `config` and `logs` directories live), and create a directory called `ext`.

3. Go into your `ext` directory.

4. Unpack the archive file you downloaded in step 1.

       tar xzf ZWaveJSController-nnnnn.tar.gz  # for Linux with tarball
       unzip ZWaveJSController-nnnnn.zip       # for Linux with ZIP archive

   If you are on Windows, just open the ZIP archive and copy/paste the contents to your `ext` directory.

5. Run the install file. For Linux users, this is `install.sh` (e.g. `/bin/sh install.sh`); for Windows users, run `install.bat`.

Later, to upgrade to a newer release of ZWaveJSController, you will simply repeat step 1 and then steps 3 through 5 (skip step 2).

## Configuration

To configure an instance of ZWaveJSController:

1. Make sure your Z-Wave JS UI or server instance is running. If you are using Z-Wave JS UI, you should be able to connect to its web interface on port 8091 (usually).
2. Open your `reactor.yaml` configuration file.

3. In the `controllers` section, add the following template, substituting your site's IP address:

       controllers:
         - id: zwavejs
           implementation: ZWaveJSController
           enabled: true
           config:
             source: ws://192.168.0.10:3000  # modify the IP address as needed

   Remember that properly indenting using spaces (only) is vital to the integrity of YAML files. If you format your file incorrectly, Reactor will not start.

4. Restart Reactor.

5. Connect to the Reactor UI using a browser.

You should be able to go to the Entities list in the Reactor UI and see at least the system entity for the controller instance. If your existing Z-Wave JS installation has configured devices, they should be visible as entities. Be aware that if Z-Wave JS is still interviewing a node when ZWaveJSController sees it for the first time, the respective entity in Reactor will have incomplete capabilities and attributes until Z-Wave JS has completed its interview. You can look at the `zwave_device.status_text` attribute on the entity for the node to see its current status.

## Entity Structure

Z-Wave devices are called nodes. A node can have one or more endpoints, which are effectively sub-units of the device.

Reactor maps at least one entity per endpoint, so a node with one endpoint (the most common scenario), will be represented by a single Reactor entity. If a node has additional endpoints, each endpoint will be represented by an additional entity.

Some command classes also require additional entities to represent properly. For example, the Central Scene command class, used by newer scene controllers, will typically represent scene notification value per button on the device. In this case, Reactor will create additional entities to represent each of the buttons. Currently, however, this is the only command class for which that is necessary.

As with other controllers, all values known for the node/endpoint will be published on the related entity. For recognized Z-Wave command classes and values, Reactor will use its native capabilities (e.g. power_switch, dimming, etc.); the rest are published in extended attributes. It is highly recommended you avoid direct use of the extended attributes in rule conditions. If you find you need something that Reactor is not providing in a native capability, open a post on the forums, describing the device in detail.

## Setting Configuration Values

Some Z-Wave devices have configuration values that you can set. These can be set in one of two ways:

1. Using the `zwave_device.set_config` action (preferred);
2. Using the `zwave_device.set_value` action, specifying command class 112 (or the word "Configuration", the name of the command class), the configuration parameter number in the `property` field, empty `propertyKey`, and the value to be set (you can see why `set_config` is preferred).

You can set configuration values at any time. For some devices, this dynamically changes things like displayed LED colors or other "real time" parameters. These values are manufacturer- and device-dependent, so please refer to the device manufacturer's documentation for information.

The `set_config` action takes a `bitmask` argument that can be used to set or reset specific bits on a configuration value when needed. For some device configuration, this makes it easier to enable/disable specific features or behaviors controlled by individual bits in the value without disrupting other bits that control other things. If the `bitmask` is 0 or empty, the `value` given is set on the parameter and replaces the current value entirely. But, if `bitmask` is non-zero, only those bits having a 1 in their position of the bitmask are copied from the value onto the old value. For example, if the current value of a parameter is 13 (00001101b), and a value of 2 (00000010b) is given with a bitmask of 3 (00000011b), the parameter value written will be 14 (00001110b), because the bitmask only permits the lowest two bits to be written/copied. Turning a bit on can be accomplished by providing an identical value and bitmask with a 1 in the bit position; turning a bit off is accomplished with a value of 0 and a bitmask with a 1 in the bit position. For the techies, the specific statement used in establishing the new value is (roughly): `newvalue = ( oldvalue & ~mask ) | ( value & mask )`.

## Polling

Some legacy Z-Wave devices do not support "instant status," so a manual change on the device (i.e. turn the switch on or off at the switch itself) does not send a message to the Z-Wave controller, and thus Z-Wave JS and Reactor cannot "see" the change. One way around this is polling the devices periodically, which requests a refresh of the data explicitly.

Attention

Polling can have a deleterious impact on mesh performance. Only poll those nodes that absolutely require it. Typically, these are just legacy Z-Wave nodes that do not support some form of instant status or hail when their state is changed manually (e.g. a switch operated by hand). Z-Wave Plus nodes do not usually require polling. Also, battery nodes will not be polled; they are refreshed during their wake-up intervals. Dead nodes are not polled (polling cannot be used to try to "reincarnate" them). Polling is also not a cure for an unstable or poor-quality mesh — in fact, it will likely make things much worse.

To configure a node for polling, add the following template to your ZWaveJSController `config` section (`config` line is shown for indenting reference only, do not include it when copy-pasting):

        config:
          poll_interval: 3600
          poll_frequency: 20
          poll_nodes:
            - node: 30
              command_class: Binary Switch
            - entity: Upstairs Thermostat
              command_class: Thermostat Operating State
              interval: 120
            - entity: Upstairs Thermostat
              command_class: Thermostat Fan State
              interval: 120

The following is the meaning of each of the keys in this section:

`poll_interval`

  Defines the default polling interval for all nodes to be polled (in seconds). If a node does not specify a different polling interval, this is how often (at the earliest) the node will be polled. The default polling interval is 3600 seconds (one hour).

`poll_frequency`

  The ZWaveJSController poller polls one eligible node at a time. To keep network traffic down, this is the minimum time between polls of eligible nodes. If 10 nodes are all due to be polled, it will take 10 × `poll_frequency` seconds (at least) to poll them all. The default frequency is 20 seconds.

`poll_nodes`

  Only specified nodes are polled, and this is the list. It is an array of objects, each of which must contain either a `node` key and node ID, or an `entity` key with an entity name or local ID (not a canonical ID). For each node, an optional `interval` may be specified to override the default. A `command_class` may be specified so that only that command class is refreshed, which can considerably reduce mesh traffic and is highly recommended. If `command_class` is not given, the entire node (all values) is refreshed. Note in the example/template that you can enter the same node/entity multiple times to poll multiple command classes. The known command classes are listed in Z-Wave JS.

## Filtering Data from Z-Wave JS

In some cases, you may find it necessary or desirable to filter data coming from Z-Wave JS. The `config` section of your ZWaveJSController configuration allows this with the addition of a `filter_values` subsection, formatted as follows:

        config:
          filter_values:
            - nodeId: 123         # number, or array of numbers
              endpoint: 0         # number
              commandClass: 50    # number
              property: value     # number or string per Z-Wave JS
              propertyKey: 66049  # number or string per Z-Wave JS
            - nodeId: 222
              endpoint: 1
              # ...etc...

The `filter_values` data must be an array. The array elements are objects containing a `commandClass` key/value and any of the optional keys `nodeId`, `endpoint`, `property`, and `propertyKey`, which narrow the filter. For example, if an element contains `commandClass` and `nodeId`, then the specified command class is ignored only for the specified node (or nodes if `nodeId` is given as an array of node IDs).

## Device Location Handling

As of build 26108, device locations in Z-Wave JS will be mapped to room groups controlled by DynamicGroupController. Prior to this build, ZWaveJSController assigned the device location, if any, as an entity attribute only; it did not create group for all entities in that location. The new behavior allows one group to serve all Controllers having devices/entities in same-named rooms/areas/locations. For example, if you have a Z-Wave devices assign to a "Living Room" location and a Hubitat hub with devices its "Living Room" room, a single "Living Room" group under DynamicGroupController will be created with the combined set of devices/entities as members.

## Data Collection and Reporting Notice

In order to help improve device support, ZWaveJSController reports device data collected from your Z-Wave controller to me for evaluation. This encrypted transmission contains the list of devices reported by the Z-Wave controller and their associated command classes and values and does not contain any personal identifying information. It is "read only" to me; it cannot be used to control your system remotely, or make any changes to your system. This data will not be sold or given to any third party or used for any purpose other than improving device support in Reactor. You can opt out of this data collection at any time by setting data_collection_opt_out to true in the configuration of your ZWaveJSController instance. If you have any questions about this, please feel free to ask me on the SmartHome Community.

Updated: 2026-Apr-18