Self-hosted service
zwave-js/zwave-js-ui avatar
zwave-js/zwave-js-ui

Z-Wave JS UI: a control panel and an MQTT bridge for the mesh

Full featured Z-Wave Control Panel UI and MQTT gateway. Built using Nodejs, and Vue/Vuetify

1,247 stars252 forksVueMIT

At a glance

What is it?
Z-Wave JS UI is a Node.js and Vue application that talks to a Z-Wave network through the node-zwave-js driver, lets you manage nodes, associations and firmware from a browser, and republishes every device on an MQTT broker. It also keeps backups of the controller's NVM before anything destructive happens to the mesh.
Who is it for?
Z-Wave JS UI fits a Z-Wave household that wants to manage the mesh itself, that wants its devices on an MQTT broker, and that wants the diagnostics a radio network needs. It is the wrong choice for a house with no Z-Wave, since the whole application sits on top of one driver, and the wrong choice for a household that has already standardised on a home automation platform, because the addon route and the standalone route duplicate what that platform does.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Vue, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A panel and a gateway in one process

Z-Wave JS UI is described as a full featured Z-Wave Control Panel and MQTT Gateway, and the two halves sit in one Node.js application.

The backend is Node.js with Express for the HTTP surface, socket.io for the live connection to the browser, MQTTjs for the broker side, and the node-zwave-js driver, which is the library that actually speaks to a Z-Wave controller. The frontend is Vue with Vuetify for the components, and socket.io again, for the same live updates.

That stack is a good description of the project, because it is a desktop-class web application with a radio driver behind it. It talks to the USB or serial adapter of a Z-Wave controller, and it exposes what it finds in two directions: a panel for a person, and a stream of MQTT messages for a home automation system.

The feature list is where the project's actual scope shows. The control panel side covers nodes management, to add, remove and configure all the nodes in the Z-Wave network, firmware updates using manufacturer-supplied firmware files, and groups associations, to add, edit and remove direct node associations. There is also a Z-Wave JS Exposed mode that provides full access to the Z-Wave JS APIs, which is the escape hatch for anything the panel does not cover.

The gateway side exposes Z-Wave devices to an MQTT broker in a fully configurable manner, and there is scene management, where scenes are created and triggered through the MQTT APIs with timeout support. The whole thing supports HTTPS and user authentication, which matters because an MQTT bridge that publishes every device in a house onto a broker is a significant thing to expose to a network.

The project is MIT licensed, the primary language is Vue, the default branch is `master`, and the last push was on 2026-10-12. The recent releases are v11.24.3 from 2026-10-09, v11.24.2 from 2026-09-29 and v11.24.1 from 2026-09-15, and the homepage is `zwave-js.github.io/zwave-js-ui`.

The panel manages nodes, associations and firmware

The control panel is the part a person uses, and its three jobs are the three things you actually do to a Z-Wave network.

Nodes management is the first: add, remove and configure all the nodes in the network. In Z-Wave a node is a device, and adding one involves an inclusion, which is a handshake with the device over the radio, and the panel is what drives that. Removing one, and replacing a failed one, are the other two operations, and the panel is where the button is.

Groups associations are the second. A Z-Wave association is a direct link between two nodes, and the panel can add, edit and remove them. Associations are the part of the protocol that people get wrong, because a mesh is not a list of devices with a hub, it is a set of direct relationships, and a device that is associated incorrectly reports correctly and does nothing.

Firmware updates are the third, using manufacturer-supplied firmware files, which is a specific and slightly awkward operation: the manufacturer has to provide the file, and the tool has to push it over the radio.

And the Z-Wave JS Exposed mode is the escape hatch, giving full access to the Z-Wave JS APIs. That is what a user reaches for when the panel does not have a control for something, and it is also what makes the application a useful development surface rather than only an end-user interface.

Underneath all of this is the driver, node-zwave-js, which is maintained by the same organisation. The separation matters: the driver knows the protocol, the panel knows the interface, and the panel can therefore be updated for a new driver behaviour without the driver having to know anything about Vue.

MQTT in one direction, and scenes triggered from the other

The gateway is the second reason to run this application, and it is the part that other systems consume.

Every Z-Wave device is exposed to an MQTT broker in a fully configurable manner, which is the sentence that contains the design decisions. Configurable means the mapping from a device and a value to a topic is yours to set, rather than a fixed convention, and for a house with fifty devices that is the difference between a topic structure you can write rules against and one you cannot.

Scenes run the other way. A scene is created in the panel and triggered by using the MQTT APIs, with timeout support. So the direction is symmetrical: the panel publishes device state, and a message on a topic can put a group of devices into a state. Timeout support is the detail worth noting, because a scene that triggers a lock and a scene that triggers a light want different behaviour when the message is repeated.

The list of integrations in the README shows how far the MQTT direction has been taken. Home Assistant can use the official addon, or a standalone installation through plain MQTT or MQTT Discovery. Domoticz and OpenHAB are used through MQTT Discovery. Jeedom uses the official Z-Wave JS plugin. HomeSeer uses its Z-Wave Plus plugin, Homebridge uses the homebridge-zwave-usb plugin, and ioBroker uses the official ioBroker.zwave-ws adapter.

That list is the strongest argument for the project. A Z-Wave radio is not something every platform supports natively, and every one of these platforms needed somebody to write the bridge, and this is the one they point at. MQTT Discovery is the common denominator, because the discovery protocol lets a client learn the topic structure automatically, which is what makes a configurable mapping usable by software that was never told about it.

Diagnosing a radio mesh from the browser

The feature list contains a set of tools that have nothing to do with the panel or the gateway, and they are the reason an installer keeps the application around after the configuration is done.

The network graph provides a map showing how nodes are communicating with the controller. A Z-Wave mesh is a physical thing, and the most common fault in one is a node that cannot reach its neighbours, and a picture of the routes is worth a lot of guessing.

The diagnostics entry names two specific tools, Healthcheck and Link quality, for diagnosing network issues, and there is Zniffer support for debugging Z-Wave traffic, which is the equivalent of putting a radio receiver on the frequency and watching what is being said. Zniffer is a maintenance tool rather than a feature, and it is the sort of thing that separates a serious protocol implementation from a demo.

Debug logs are available directly in the UI, and the store files are accessible from the UI too, from the persistent `store` folder. The store is where the application keeps its own state, and being able to download a file from it without a shell on the machine is what makes it possible to diagnose a remote install.

Together with the HTTPS and user authentication support, that is a coherent position: the application is not only a control panel but the place you go when the network misbehaves.

Backups before the operations that can break a mesh

One feature deserves its own section, because it is the one that protects the installation rather than the user interface.

There are automatic and scheduled backups of the NVM and the store directory, and it is also possible to enable automatic backups of the NVM before every node inclusion, exclusion or replace, which ensures there is a safe restore point before any operation that can cause a network corruption.

The reason the NVM specifically matters is that it is the controller's own memory of the network, the node list, the associations and the routing information, and it is the thing that a bad inclusion damages. A Z-Wave controller that has lost its NVM has a network that appears healthy and does not work, and the recovery is a restore rather than a configuration change. Backing it up before every inclusion is cheap, and the inclusion is exactly the moment when the network state changes in a way that is hard to undo.

The scheduled backup of the store directory covers the other half, which is the application's own state, including the configuration and whatever the panels have written.

The feature list also mentions HTTPS and user authentication, and the Docker image and the Snap package in the badges, so the deployment paths are the usual ones: a container, a Snap, or a Node process.

The build, and a server directory that is being replaced

The package metadata is worth reading, because it shows a project in the middle of a migration.

json
  "scripts": {
    "dev": "vite",
    "dev:server": "nodemon",
    "server": "node --experimental-strip-types api/bin/www.ts",
    "start": "node --preserve-symlinks server/bin/www.js",
    "test": "vitest run",
    "test:server": "vitest run test/lib",
    "test:ui": "vitest run src/",
    "build:server": "tsc",
    "build:ui": "vite build"
  },

The `server` script runs `api/bin/www.ts` directly with Node's type stripping, while the `start` script runs the compiled `server/bin/www.js`. So there is an `api/` directory that holds a TypeScript server and a `server/` directory that holds the compiled JavaScript, and the project is in the middle of moving from one to the other. That is visible in the repository listing, where `api/`, `server/`, `pkg/`, `snippets/`, `certs/`, `kubernetes/`, `kustomization.yaml` and `docker/` all sit beside the older `src/` for the frontend.

The rest of the tooling is modern and conventional. The frontend is built by Vite and the server by tsc, the tests run with Vitest and are split into a server project and a user interface project, the documentation is served with docsify and generated by a script that runs with type stripping as well, and the package is assembled by a shell script. There is an esbuild bundle path as well, plus a mock server for a fake Z-Wave stick, which is a detail that says the project can be developed without the radio attached.

The deployment side has a Kubernetes directory with a kustomization file, a Docker directory, a certificates directory, an example environment file, and a release workflow for the container image, and the repository also carries an `AI_POLICY.md`, a `CLAUDE.md` and an `AGENTS.md` for automated contributors.

Where Z-Wave JS UI is the wrong tool

The limits are obvious and worth stating for a reviewer who is looking at the wrong kind of project.

The first is that the whole application is a front end for one radio. Without Z-Wave there is no node-zwave-js driver and no mesh, and the panel, the gateway and the diagnostics are all downstream of that dependency. A house with a handful of Zigbee or Thread devices and one Z-Wave device does not need this, it needs a bridge for the one device.

The second is the duplication with a home automation platform. If the house runs Home Assistant, the official addon gives the same panel inside a platform that already has the automations, the history and the other integrations, and the standalone route exists for the case where you do not run one. Installing the standalone version next to a platform that also publishes the same devices to the same broker is a choice with real consequences for rule ownership, and it is worth being deliberate about which system owns the automations.

The third is the surface area. This is a large application with a driver, an MQTT broker client, a websocket server, a build in two languages and a Kubernetes directory, and the security badge in the README, the Snyk one, is a reminder that an application that holds a controller and publishes a house full of device state deserves the same review as any other internet-facing service.

The alternative, for most houses, is the platform route: Home Assistant with the official addon, or the platform's own Z-Wave support where it exists, with this application used only for the mesh diagnostics that the platform does not provide. The difference in approach is that Z-Wave JS UI is the neutral component that everything else plugs into, including the platforms that have no Z-Wave support of their own, and the alternative is to make one platform the owner of the house.

Editorial conclusion

Z-Wave JS UI fits a Z-Wave household that wants to manage the mesh itself, that wants its devices on an MQTT broker, and that wants the diagnostics a radio network needs. It is the wrong choice for a house with no Z-Wave, since the whole application sits on top of one driver, and the wrong choice for a household that has already standardised on a home automation platform, because the addon route and the standalone route duplicate what that platform does. Before installing it, read the backup behaviour carefully, since the NVM and store backups are the only thing standing between a bad inclusion and a broken network, and decide whether you want the panel in your browser or a device on a wall, because the two configurations behave differently at the edges.

Frequently asked questions

What is Z-Wave JS UI?

It is a full featured Z-Wave Control Panel and MQTT Gateway, with a Node.js and Express backend that talks to the mesh through the node-zwave-js driver, a Vue and Vuetify frontend that receives live updates over socket.io, and an MQTT bridge that republishes every device on a broker.

Which smart home platforms does Z-Wave JS UI integrate with?

Home Assistant, through the official addon or a standalone installation over plain MQTT or MQTT Discovery, and Domoticz, OpenHAB, Jeedom, HomeSeer, Homebridge and ioBroker, each with its own documented route.

How does Z-Wave JS UI protect a network from a bad change?

It makes automatic and scheduled backups of the NVM and the store directory, and it can back up the NVM automatically before every node inclusion, exclusion or replacement, so a restore point exists before an operation that could corrupt the network.

How do I build Z-Wave JS UI from source?

The build runs tsc for the server and vite build for the interface, and the tests run with vitest, split into a server project and a user interface project. The server can be started from source with node using type stripping on api/bin/www.ts, and a mock server script stands in for the radio during development.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. zwave-js/zwave-js-ui on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/zwave-js-zwave-js-ui.svg)](https://hysenlabs.com/projects/zwave-js-zwave-js-ui)

Was this useful?