Skip to content

Z Wave JS UI (MQTT)

raman325 edited this page Aug 24, 2026 · 2 revisions

Z-Wave JS UI (MQTT) Integration

Lock Code Manager supports Z-Wave locks bridged into Home Assistant through zwave-js-ui's MQTT discovery gateway — locks that show up as lock.* entities from the mqtt integration, not the zwave_js integration. This is a separate provider from Z-Wave, which covers Z-Wave JS's official websocket integration.

Consider the official Z-Wave integration instead. zwave-js-ui itself discourages MQTT discovery in favor of its websocket server, and the zwave_js integration is the better-supported path in both Home Assistant and Lock Code Manager. Switching does not require re-pairing your locks or touching your Z-Wave network — enable the WS server in zwave-js-ui and add the Z-Wave JS integration in Home Assistant; only entity IDs change. This provider exists for setups already built around the MQTT entities.

How It Works

zwave-js-ui can auto-discover its nodes into Home Assistant as mqtt-platform entities. Lock Code Manager identifies which of those entities are Z-Wave locks bridged by zwave-js-ui (as opposed to Zigbee2MQTT or any other MQTT bridge) from the device registry identifier zwave-js-ui publishes: zwavejs2mqtt_<home_id>_node<node_id>.

Unlike the Zigbee2MQTT provider, which reads and writes by publishing directly to value topics, this provider drives zwave-js-ui's MQTT API (<prefix>/_CLIENTS/ZWAVE_GATEWAY-<name>/api/sendCommand) to call the Z-Wave User Code command class's get, set, and clear methods — the same command class surface the Z-Wave provider reaches over the websocket integration. Each API call is tagged with a request nonce and matched against the response, so other MQTT clients calling the same gateway (the zwave-js-ui frontend, other automations) don't cross wires.

Only User Code CC locks are supported. User Credential (U3C) locks — the newer per-user model described on the Z-Wave page — are not supported by this provider; use the official Z-Wave JS integration for those locks.

Requirements

  • zwave-js-ui running with its MQTT gateway enabled, connected to the same broker as Home Assistant's MQTT integration
  • Gateway Type Named or ValueID gives full functionality. Manual gateway type works in a reduced, polling-only mode — see Limitations
  • Retain enabled in zwave-js-ui's MQTT settings is recommended. The gateway is normally identified directly from the lock's own discovery data; the fallback discovery path additionally needs the gateway's retained client-status topic
  • A Z-Wave lock exposing the User Code command class (0x63)
  • The lock entity must appear as lock.* from the mqtt integration in Home Assistant

Supported Operations

Operation Supported Notes
Set PIN Via zwave-js-ui's sendCommand API → User Code CC set
Clear PIN Via zwave-js-ui's sendCommand API → User Code CC clear
Read PIN Via zwave-js-ui's sendCommand API → User Code CC get, one slot per round trip
Push updates Via MQTT subscription to the node's value topics (Named or ValueID gateway type only)
Code slot events Via Notification CC publications (keypad lock/unlock)

Setup

Locks discovered through zwave-js-ui's MQTT gateway require no provider-specific configuration in Lock Code Manager — they appear in the config flow's lock selector like any other lock, alongside websocket-integrated Z-Wave locks, Zigbee2MQTT locks, and everything else.

  1. In zwave-js-ui, confirm the MQTT gateway is enabled, Type is Named or ValueID, and Retain is on.
  2. Confirm the lock shows up as a lock.* entity from Home Assistant's mqtt integration (via zwave-js-ui's discovery payloads).
  3. Add the lock to a Lock Code Manager config entry's Locks selector, same as any other lock. See Adding and Removing Locks.

If you select an MQTT lock that Lock Code Manager can't attribute to a supported bridge (Zigbee2MQTT or zwave-js-ui), the config flow rejects the submission with an error naming the entity, rather than accepting it and leaving it permanently disconnected.

Gateway discovery

Lock Code Manager resolves which zwave-js-ui gateway serves a lock automatically. zwave-js-ui embeds its own status topic in every discovery payload it publishes, so each lock names its gateway directly — this handles any number of gateways, including several sharing one MQTT prefix. When a lock's discovery data lacks that entry, a fallback scan of the prefix's client-status topics runs instead:

  • If exactly one zwave-js-ui gateway is visible under the lock's MQTT prefix, it's used directly.
  • Multiple zwave-js-ui gateways are supported — for example, more than one Z-Wave network bridged under different prefixes on the same broker. When more than one gateway shares a prefix, Lock Code Manager queries each and matches the one reporting the lock's Z-Wave home ID.
  • In the fallback scan only: if two gateways under the same prefix report the same home ID — typically a primary and secondary controller bridging one network — resolution is ambiguous and is refused rather than guessed.

Push Updates and Keypad Events

Lock Code Manager subscribes to the lock's node topic and applies updates as they arrive:

  • User Code value publications (PIN set/cleared, in either the Named or ValueID topic shape) confirm the slot's state without waiting for the next poll.
  • Keypad lock/unlock events are delivered via Notification CC publications and fire the same pin_used code slot events used by the Blueprints usage-limiting automations.

A hard refresh runs hourly to re-read every slot and catch anything a dropped MQTT message missed. Locks running in polling-only mode (Manual gateway type) are instead polled on a regular cadence.

Limitations

  • Manual gateway type runs in a reduced mode. Custom per-value topics can't be shape-parsed for the lock's node topic, so push updates and keypad events are unavailable; PIN reads, writes, and sync still work through the gateway api, with polling standing in for push. Switch to Named or ValueID for full functionality, including "unlocked by user" events.
  • User Credential (U3C) locks are not supported. This provider currently speaks User Code CC only; U3C-only locks need the official Z-Wave integration.
  • Duplicate gateways on one prefix are refused in the fallback discovery path only. See Gateway discovery above.
  • Sequential slot reads: like the Zigbee2MQTT provider, PIN codes are read one slot at a time rather than in parallel, to avoid overwhelming the gateway and mesh.

Security: PINs Travel Over MQTT in Cleartext

As with the Zigbee2MQTT provider's expose_pin data, both directions of this provider's traffic — PIN reads, writes, and value publications — pass through your MQTT broker unencrypted. Secure the broker accordingly: require authentication, use TLS, and scope ACLs so only trusted clients can subscribe to or publish on the zwave-js-ui prefix. Avoid pasting raw MQTT traffic or debug logs containing PIN values into public bug reports.

Verified Locks

Support for this provider is new. If you have tested Lock Code Manager with a Z-Wave lock bridged through zwave-js-ui's MQTT gateway, please open an issue or PR to add it to this list.

Troubleshooting

Lock not detected / shows as disconnected

Confirm the lock's device identifier in Home Assistant's device registry ends with the <home_id>_node<node_id> pattern zwave-js-ui publishes (the prefix is zwavejs2mqtt_ by default but honors a custom UID_DISCOVERY_PREFIX).

"Cannot resolve gateway" / lock stuck disconnected

Confirm Retain is enabled in zwave-js-ui's MQTT settings. Lock Code Manager discovers the gateway by reading its retained client-status topic (_CLIENTS/ZWAVE_GATEWAY-*/status); with Retain off, there's nothing to read on subscribe.

Setup rejected with "unsupported MQTT lock"

The selected MQTT lock's device identifier doesn't match a bridge Lock Code Manager recognizes (Zigbee2MQTT or zwave-js-ui). Confirm it's actually discovered by zwave-js-ui and not some other MQTT integration.

Codes not syncing

Check that the MQTT integration is connected in Home Assistant, that the zwave-js-ui gateway is online, and that the underlying Z-Wave node is not in a failed state in zwave-js-ui's control panel.

Clone this wiki locally