IoT Automation

Z-Wave JS UI MQTT Gateway Setup: Publish Z-Wave Device Values to a Broker

Configure Z-Wave JS UI's MQTT gateway to publish device values, write them back with /set topics, and reach a cloud broker securely through a small local bridge.

Z-Wave USB stick and sensors connected through a zwave-js-ui mqtt gateway to a broker
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

The zwave-js-ui MQTT gateway publishes every Z-Wave value, from door sensors to dimmer levels, as MQTT messages that any dashboard, script or automation tool can use. In this guide you will run Z-Wave JS UI in Docker, configure its MQTT and gateway settings, write values back with /set topics, and forward the data to a cloud broker over TLS.

What the Z-Wave JS UI MQTT Gateway Does

Z-Wave JS UI (formerly zwavejs2mqtt) wraps the Z-Wave JS driver in a web interface for including devices, healing the network and editing configuration parameters. It exposes the network in two ways: a WebSocket server, which Home Assistant's Z-Wave JS integration uses, and an MQTT gateway, which publishes values to a broker and accepts writes from it.

The MQTT side is what you want when the consumers are not Home Assistant: Node-RED flows, Telegraf feeding a time-series database, a custom backend, or a remote dashboard. Each of them simply subscribes to topics on the broker. If the role of the broker is new to you, read what an MQTT broker does first.

Step 1: Run Z-Wave JS UI

Docker is the most common way to run it. Find your Z-Wave stick's stable path with ls -l /dev/serial/by-id/ and start the container:

docker run -d --name zwave-js-ui --restart unless-stopped \
  -p 8091:8091 -p 3000:3000 \
  --device=/dev/serial/by-id/usb-Silicon_Labs_CP2102N_USB_to_UART_Bridge-if00-port0:/dev/zwave \
  -v zwave-config:/usr/src/app/store \
  zwavejs/zwave-js-ui:latest

Replace the device path with your own. Open http://<host>:8091, go to Settings then Z-Wave, set the serial port to /dev/zwave, generate the security keys and save. Include a device or two before moving on, so there is something to publish.

Step 2: Understand the Topic Structure

Values are published under this pattern:

<prefix>/<location>/<node name>/<command class>/<endpoint>/<property>

For a dimmer named desk_lamp in location office, with prefix zwave, the current level lives at zwave/office/desk_lamp/38/0/currentValue, where 38 is the Multilevel Switch command class. To change it, publish to the same topic with /set appended, usually on targetValue:

Purpose Topic Payload
Value update zwave/office/desk_lamp/38/0/currentValue {"time":1760600000000,"value":60}
Write a value zwave/office/desk_lamp/38/0/targetValue/set {"value":100}
Node status zwave/office/desk_lamp/status true / false or JSON
Gateway status zwave/_CLIENTS/ZWAVE_GATEWAY-<name>/status true / false
API call zwave/_CLIENTS/ZWAVE_GATEWAY-<name>/api/<api>/set {"args":[...]}

The gateway status topic is especially useful: it tells remote consumers whether Z-Wave JS UI itself is connected, independent of any device.

Step 3: Test the zwave-js-ui MQTT Gateway on a Public Broker

Before building anything permanent, prove the gateway works with the public MQTT test broker at broker.connectmq.com. It accepts anonymous connections with any unique client ID. Two details matter here:

  1. Z-Wave JS UI builds its client ID as ZWAVE_GATEWAY-<Name>, so the Name field must be unique. Use a random suffix.
  2. The public area allows a maximum of eight topic levels. A long prefix plus location, node name, command class, endpoint, property and /set easily exceeds that, so use a short prefix and turn off locations in topics.

In Settings, then MQTT, enter:

Field Value
Name zw-h2k8tp
Host url mqtts://broker.connectmq.com
Port 8883
Reconnect period 5000
Prefix connectmq/demo/h2k8tp
QoS 1
Retain on
Clean on
Auth off
Allow self signed certs off

Then in Settings, then Gateway, choose:

  • Type: ValueId topics.
  • Payload type: JSON Time-Value.
  • Use nodes name instead of numeric nodeIDs: on.
  • Ignore location: on.

Save, then subscribe from a terminal:

mosquitto_sub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -i watch-h2k8tp -t 'connectmq/demo/h2k8tp/#' -v

You will see the gateway status, then values for each node. Switch the lamp:

mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -i send-h2k8tp -t 'connectmq/demo/h2k8tp/desk_lamp/38/0/targetValue/set' \
  -m '{"value":40}'

A few moments later currentValue updates to 40. Remember the public area is visible to everyone; use it only for harmless tests, never for locks, alarms or garage doors.

Step 4: Production Setup with a Local Bridge

Private brokers issue credentials together with a fixed client ID. ConnectMQ binds the client ID to the issued username and password, and Z-Wave JS UI always prefixes its client ID with ZWAVE_GATEWAY-. In practice that means Z-Wave JS UI cannot present the exact issued ID.

The clean solution is a small Mosquitto bridge next to Z-Wave JS UI. Z-Wave JS UI connects to the local Mosquitto without TLS, and Mosquitto forwards the zwave/# topics to the cloud broker using your issued client ID, username and password over TLS. As a bonus, local automations keep working during an internet outage.

docker-compose.yml:

services:
  mosquitto:
    image: eclipse-mosquitto:2
    restart: unless-stopped
    volumes:
      - ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro
      - /etc/ssl/certs:/etc/ssl/certs:ro

  zwave-js-ui:
    image: zwavejs/zwave-js-ui:latest
    restart: unless-stopped
    ports:
      - "8091:8091"
      - "3000:3000"
    devices:
      - /dev/serial/by-id/usb-Silicon_Labs_CP2102N_USB_to_UART_Bridge-if00-port0:/dev/zwave
    volumes:
      - zwave-config:/usr/src/app/store
    depends_on:
      - mosquitto

volumes:
  zwave-config:

mosquitto.conf:

listener 1883
allow_anonymous true

connection connectmq
address broker.connectmq.com:8883
remote_clientid YOUR_CLIENT_ID
remote_username YOUR_USERNAME
remote_password YOUR_PASSWORD
bridge_capath /etc/ssl/certs
bridge_protocol_version mqttv311
try_private false
cleansession true
topic zwave/# both 1

The local listener is only reachable inside the Compose network, because port 1883 is not published to the host. In Z-Wave JS UI, change the MQTT settings to Host url mqtt://mosquitto, Port 1883, Prefix zwave, Auth off. You can turn Ignore location back off, since private brokers are not subject to the public eight-level limit.

try_private false matters: it disables a Mosquitto-specific bridge handshake that other brokers do not understand. topic zwave/# both 1 forwards in both directions, so /set messages from the cloud reach Z-Wave JS UI.

Consuming Z-Wave Data Remotely

Any MQTT client with its own credentials can now read the network from anywhere. A minimal Python consumer (paho-mqtt 2.x) that logs door sensors and the gateway status:

import json
import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe("zwave/_CLIENTS/+/status", qos=1)
    client.subscribe("zwave/+/48/0/+", qos=1)  # Binary Sensor command class

def on_message(client, userdata, msg):
    try:
        data = json.loads(msg.payload)
        value = data["value"] if isinstance(data, dict) else data
    except ValueError:
        value = msg.payload.decode()
    print(msg.topic, "->", value)

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 8883, keepalive=60)
client.loop_forever()

Use a separate credential set for this consumer; it cannot share the bridge's client ID.

Troubleshooting Checklist

  1. Gateway status stays false - check host url scheme (mqtt:// vs mqtts://) and port pairing: 1883 without TLS, 8883 with TLS.
  2. Writes do nothing - publish to targetValue/set, not currentValue, and send JSON with a value field.
  3. Some topics never appear on the public broker - they exceed eight levels. Shorten the prefix or ignore location.
  4. Bridge connects then drops - another client is using the same issued client ID.
  5. Values arrive as huge JSON objects - the payload type is set to "Entire ValueId Object"; switch to JSON Time-Value or Just value.

The official Z-Wave JS UI documentation lists every API name and gateway option.

Z-Wave rarely lives alone. The Zigbee2MQTT cloud broker guide applies the same remote pattern to Zigbee, and the rtl_433 MQTT guide adds cheap 433 MHz sensors. A consistent smart home MQTT topic structure keeps all of them tidy, and the IoT automation guide shows how the pieces combine.

Bring Your Z-Wave Network Online with ConnectMQ

A managed MQTT broker gives your bridge a stable, TLS-protected endpoint without maintaining a server on the internet. ConnectMQ provides MQTT 3.1.1 and 5.0, a live topic explorer for checking Z-Wave topics, and threshold alert rules for values such as temperature or battery level. Create a free Serverless broker at app.connectmq.com, no credit card needed.

Frequently Asked Questions

What client ID does Z-Wave JS UI use for MQTT?

It uses ZWAVE_GATEWAY- followed by the Name from the MQTT settings. Make the name unique so two gateways never share a client ID.

How do I change a Z-Wave value over MQTT?

Publish JSON such as {"value":100} to the value's topic with /set appended, typically .../38/0/targetValue/set. The new value appears on the currentValue topic.

Do I need the MQTT gateway if I use Home Assistant?

Not necessarily. Home Assistant's Z-Wave JS integration uses the WebSocket server; the MQTT gateway is for other consumers such as Node-RED, databases or remote dashboards.

Why use a Mosquitto bridge instead of connecting directly?

Because Z-Wave JS UI prefixes its client ID, it cannot use an issued client ID that the broker binds to credentials. The bridge presents the exact ID and also keeps local traffic working offline.

Related articles

Zigbee2MQTT Setup Guide: From Coordinator to Dashboard

Install Zigbee2MQTT with Docker, configure the coordinator and MQTT connection, pair and rename devices, control them over MQTT and view their states on a dashboard, testing first on a free public broker.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.