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:
- Z-Wave JS UI builds its client ID as
ZWAVE_GATEWAY-<Name>, so the Name field must be unique. Use a random suffix. - The public area allows a maximum of eight topic levels. A long prefix plus location, node name, command class, endpoint, property and
/seteasily 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
- Gateway status stays
false- check host url scheme (mqtt://vsmqtts://) and port pairing: 1883 without TLS, 8883 with TLS. - Writes do nothing - publish to
targetValue/set, notcurrentValue, and send JSON with avaluefield. - Some topics never appear on the public broker - they exceed eight levels. Shorten the prefix or ignore location.
- Bridge connects then drops - another client is using the same issued client ID.
- 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.
Related Smart Home Guides
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.



