Skip to content


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.


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.


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       # 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 (e.g. /bin/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).


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:

      - id: zwavejs
        implementation: ZWaveJSController
        enabled: true
          source: ws://  # 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 ).


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.


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):

      poll_interval: 3600
      poll_frequency: 20
        - 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:


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).


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.


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.

Data Collection and Reporting

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: 2023-Jul-26