Zwave-JS-UI in place of OH Zwave binding

Just a short update to a side-problem:
(solved):Zwave remote connection rfc2217 vs tcp (raw). running zwave-js-ui with zwave binding - #10 by Oekel

My working stack/docker-compose:

version: '3.7'

services:
  openhab:
    image: "openhab/openhab:4.1.1"
    container_name: openhab-bremen
    restart: unless-stopped
    ports:
      - "8081:8080"
      - "8444:8443"
      - "9004:9001"
    #network_mode: host
    networks:
      vlan50:
        ipv4_address: 192.168.5.201
    volumes:
      - "/etc/localtime:/etc/localtime:ro"
      - "/etc/timezone:/etc/timezone:ro"
      - "openhab_addons:/openhab/addons"
      - "openhab_conf:/openhab/conf"
      - "openhab_userdata:/openhab/userdata"
    environment:
      CRYPTO_POLICY: "unlimited"
      EXTRA_JAVA_OPTS: "-Duser.timezone=Europe/Berlin"
      OPENHAB_HTTP_PORT: "8080"
      OPENHAB_HTTPS_PORT: "8443"

  mosquitto:
    image: eclipse-mosquitto
    container_name: mosquitto-bremen
    volumes:
      - /var/lib/docker/custom/mosquitto/config:/mosquitto/config
      - mosquitto_data:/mosquitto/data
      - mosquitto_log:/mosquitto/log
    ports:
      - 1884:1883
      - 9002:9001
    networks:
      vlan50:
        ipv4_address: 192.168.5.202
    environment:
      MOSQUITTO_USERNAME: "openhab"
      MOSQUITTO_PASSWORD: "mqtt63535"

  zwave-js-ui:
    container_name: zwave-js-ui-bremen
    image: zwavejs/zwave-js-ui:latest
    restart: unless-stopped
    tty: true
    stop_signal: SIGINT
    environment:
      - SESSION_SECRET=mysupersecretkey
      - ZWAVEJS_EXTERNAL_CONFIG=/usr/src/app/store/.config-db
      # Uncomment if you want logs time and dates to match your timezone instead of UTC
      # Available at https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
      - TZ=Europe/Berlin
    networks:
      vlan50:
        ipv4_address: 192.168.5.203
    #devices:
      # Do not use /dev/ttyUSBX serial devices, as those mappings can change over time.
      # Instead, use the /dev/serial/by-id/X serial device for your Z-Wave stick.
      #- 'tcp://192.168.1.102:4000'
    volumes:
      - zwave-config:/usr/src/app/store
    #ports:
      #- '8091:8091' # port for web interface
      #- '3000:3000' # port for Z-Wave JS websocket server

volumes:
  openhab_addons:
    driver: local
  openhab_conf:
    driver: local
  openhab_userdata:
    driver: local

  mosquitto_data:
    driver: local
  mosquitto_log:
    driver: local

  zwave-config:
    name: zwave-config

networks:
  vlan50:
    external: true

1 Like

Just for feedback, I moved from zwave binding to zwavejsui mqtt too.
I spent too much time trying to debug and optimize the old binding with my new aeotec zstick7 (which os not very performant).
Now with zwavejsui it is more powerfull and resilent in my case and my configuration.
I moved all devices from zwavebinding to MQTT, with same endpoint names, so I just had to change the channel prefix in my item file (from zwave:xxx to mqtt:xxx) with a replace all and voila.
With previous configuration examples (thanks again to them), I successfullly reconfigure all my devices that work flawlessly, and here is the list for information :

  • FGD212
  • FGS223
  • FGS213
  • FGMS001
  • NAS-PD07Z
  • ZW100
  • NAS-DS01Z
  • NEO-DS07Z
  • FGDW002

Tempting to move over, but question is when mqtt support will be dropped :grimacing:.

As I understand it, the whole point of ZUI is to make Z-Wave available over MQTT, so I don’t see any reason they’d drop it. Is there a particular reason for your concern?

My impression was that they are moving over to websockets?

Websockets is their preferred interface, but I’ve never heard anything about dropping MQTT altogether. The Home Assistant Discovery via MQTT isn’t well maintained, but I’ve made a few PRs recently and they’ve been accepted. I fully use Home Assistant Discovery with openHAB and zwave-js-ui now. No manually configured MQTT things.

3 Likes

For anyone wondering whether a move to an 800 series controller is possible as part of this migration I’m happy to report following the recent addition of 800 series backup/restore to Zwave-JS in Feb that this works really well.

I took the plunge and moved to a Zooz ZST39 800 series controller, the migration was trivial and my network has been as stable as with my 500 series controller with most of my nodes now talking directly with the controller where previously several of them had 2-3 hops via other nodes.

Thank you.
It works as you described.

I played around and after I correctly installed the REGEX transformation I came up with the following color channel which works for both outgoing and incoming values

    Type color : rgb [
        stateTopic   = "zwave/43/51/1/currentColor",
        commandTopic = "zwave/43/51/1/targetColor/set",
        colorMode="RGB", on="ON", off="OFF",
        transformationPattern="REGEX:s/.*\"red\":(\\d+),\"green\":(\\d+),\"blue\":(\\d+).*/$1,$2,$3/g",
        formatBeforePublish="{\"value\":{\"red\":%1$d,\"green\":%2$d,\"blue\":%3$d}}"
    ]
2 Likes

@ccutrer Thank you for working on the PRs to make the HA Discovery work with openHAB!

Community,
What is the expected behavior for using Home Assistant Discovery with the MQTT Binding and openHAB? Should I expect just a defined “Thing” or a “Thing” and “Channels”, or something else entirely?

Using the Home Assistant Discovery I see this.

For example, I currently see this in the GUI.
[Thing] Tab:

[Channel] Tab:

[Code] Tab:

UID: mqtt:homeassistant_zwavejs2mqtt_5F0xd4082e7e_5Fnode15:8fd56e1f3c:zwavejs2mqtt_5F0xd4082e7e_5Fnode15
label: nodeID_15 (3x Light, 10x Sensor, Switch, 36x number)
thingTypeUID: mqtt:homeassistant_zwavejs2mqtt_5F0xd4082e7e_5Fnode15
configuration:
  topics:
    - light/nodeID_15/dimmer
    - light/nodeID_15/dimmer_1
    - light/nodeID_15/dimmer_2
    - number/nodeID_15/config_number_1
    - number/nodeID_15/config_number_10
    - number/nodeID_15/config_number_11
    - number/nodeID_15/config_number_12
    - number/nodeID_15/config_number_13
    - number/nodeID_15/config_number_14
    - number/nodeID_15/config_number_15
    - number/nodeID_15/config_number_16
    - number/nodeID_15/config_number_17
    - number/nodeID_15/config_number_18
    - number/nodeID_15/config_number_19
    - number/nodeID_15/config_number_2
    - number/nodeID_15/config_number_20
    - number/nodeID_15/config_number_21
    - number/nodeID_15/config_number_22
    - number/nodeID_15/config_number_23
    - number/nodeID_15/config_number_24_16711680
    - number/nodeID_15/config_number_24_2130706432
    - number/nodeID_15/config_number_24_255
    - number/nodeID_15/config_number_24_65280
    - number/nodeID_15/config_number_25_16711680
    - number/nodeID_15/config_number_25_2130706432
    - number/nodeID_15/config_number_25_255
    - number/nodeID_15/config_number_25_65280
    - number/nodeID_15/config_number_26
    - number/nodeID_15/config_number_27
    - number/nodeID_15/config_number_28
    - number/nodeID_15/config_number_29
    - number/nodeID_15/config_number_3
    - number/nodeID_15/config_number_30
    - number/nodeID_15/config_number_31
    - number/nodeID_15/config_number_4
    - number/nodeID_15/config_number_5
    - number/nodeID_15/config_number_6
    - number/nodeID_15/config_number_7
    - number/nodeID_15/config_number_8
    - sensor/nodeID_15/electric_kwh_value
    - sensor/nodeID_15/electric_w_value
    - sensor/nodeID_15/scene_state_scene_001
    - sensor/nodeID_15/scene_state_scene_002
    - sensor/nodeID_15/scene_state_scene_003
    - sensor/nodeID_15/scene_state_scene_004
    - sensor/nodeID_15/scene_state_scene_005
    - sensor/nodeID_15/scene_state_scene_006
    - sensor/nodeID_15/scene_state_scene_009
    - sensor/nodeID_15/scene_state_slowrefresh
    - switch/nodeID_15/config_switch_51
  basetopic: homeassistant
bridgeUID: mqtt:broker:8fd56e1f3c

When I manually configure my thing, my thing (incomplete) definition looks like this:
mything.thing


Thing mqtt:topic:zwave01-nodeID_15 "F1_GuestRoom_Wall_Switch" (mqtt:broker:8fd56e1f3c) 
{
    Channels:
    Type dimmer : light_dimmer 
    [ 
        transformationPattern="JSONPATH:$.value",
        stateTopic="zwave01/nodeID_15/switch_multilevel/endpoint_1/currentValue", 
        commandTopic="zwave01/nodeID_15/switch_multilevel/endpoint_1/targetValue/set", 
        min=0, 
        max=100, 
        step=5 
    ]
    Type dimmer : fan_dimmer
    [ 
        transformationPattern="JSONPATH:$.value",
        stateTopic="zwave01/nodeID_15/switch_multilevel/endpoint_2/currentValue", 
        commandTopic="zwave01/nodeID_15/switch_multilevel/endpoint_2/targetValue/set", 
        min=0, 
        max=100, 
        step=5 
    ]
    Type number : kWh [stateTopic="zwave01/nodeID_15/meter/endpoint_0/value/65537", transformationPattern="JSONPATH:$.value"]
    Type number : watts [stateTopic="zwave01/nodeID_15/meter/endpoint_0/value/66049", transformationPattern="JSONPATH:$.value"]
    Type string : reset [stateTopic="zwave01/nodeID_15/meter/endpoint_0/reset", transformationPattern="JSONPATH:$.value",commandTopic="zwave01/nodeID_15/meter/endpoint_0/reset/set"]
    Type number : light-led-color 
    [
        stateTopic="zwave01/nodeID_15/configuration/endpoint_0/Light_LED_Indicator_Color", 
        transformationPattern="JSONPATH:$.value",
        commandTopic="zwave01/nodeID_15/configuration/endpoint_0/Light_LED_Indicator_Color/set"
    ]
    Type number : fan-led-color 
    [
        stateTopic="zwave01/nodeID_15/configuration/endpoint_0/Fan_LED_Indicator_Color", 
        transformationPattern="JSONPATH:$.value",
        commandTopic="zwave01/nodeID_15/configuration/endpoint_0/Fan_LED_Indicator_Color/set"
    ]
}

The associated YAML in the GUI looks like this:

UID: mqtt:topic:zwave01-nodeID_15
label: F1_GuestRoom_Wall_Switch
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:8fd56e1f3c
channels:
  - id: light_dimmer
    channelTypeUID: mqtt:dimmer
    label: Dimmer
    configuration:
      retained: false
      postCommand: false
      min: 0
      formatBeforePublish: "%s"
      max: 100
      commandTopic: zwave01/nodeID_15/switch_multilevel/endpoint_1/targetValue/set
      step: 5
      transformationPattern: JSONPATH:$.value
      stateTopic: zwave01/nodeID_15/switch_multilevel/endpoint_1/currentValue
      off: "0"
      on: "1"
  - id: fan_dimmer
    channelTypeUID: mqtt:dimmer
    label: Dimmer
    configuration:
      retained: false
      postCommand: false
      min: 0
      formatBeforePublish: "%s"
      max: 100
      commandTopic: zwave01/nodeID_15/switch_multilevel/endpoint_2/targetValue/set
      step: 5
      transformationPattern: JSONPATH:$.value
      stateTopic: zwave01/nodeID_15/switch_multilevel/endpoint_2/currentValue
      off: "0"
      on: "1"
  - id: kWh
    channelTypeUID: mqtt:number
    label: Number Value
    configuration:
      retained: false
      postCommand: false
      step: 1
      formatBeforePublish: "%s"
      stateTopic: zwave01/nodeID_15/meter/endpoint_0/value/65537
      transformationPattern: JSONPATH:$.value
  - id: watts
    channelTypeUID: mqtt:number
    label: Number Value
    configuration:
      retained: false
      postCommand: false
      step: 1
      formatBeforePublish: "%s"
      stateTopic: zwave01/nodeID_15/meter/endpoint_0/value/66049
      transformationPattern: JSONPATH:$.value
  - id: reset
    channelTypeUID: mqtt:string
    label: Text Value
    configuration:
      commandTopic: zwave01/nodeID_15/meter/endpoint_0/reset/set
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      stateTopic: zwave01/nodeID_15/meter/endpoint_0/reset
      transformationPattern: JSONPATH:$.value
  - id: light-led-color
    channelTypeUID: mqtt:number
    label: Number Value
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      commandTopic: zwave01/nodeID_15/configuration/endpoint_0/Light_LED_Indicator_Color/set
      step: 1
      stateTopic: zwave01/nodeID_15/configuration/endpoint_0/Light_LED_Indicator_Color
      transformationPattern: JSONPATH:$.value
  - id: fan-led-color
    channelTypeUID: mqtt:number
    label: Number Value
    configuration:
      retained: false
      postCommand: false
      formatBeforePublish: "%s"
      commandTopic: zwave01/nodeID_15/configuration/endpoint_0/Fan_LED_Indicator_Color/set
      step: 1
      stateTopic: zwave01/nodeID_15/configuration/endpoint_0/Fan_LED_Indicator_Color
      transformationPattern: JSONPATH:$.value

In writing this, I suppose my expectation is that the Home Assistant Discovery would create the thing and all of the associated channels, not just the thing.

I did read the documentation page here:

and just recently installed the required transformations plug-ins (it didn’t appear to change anything for me).

Am I misconfiguring something, or are my expectations off, or something else?

Appreciate the help!

Hardware: CWWK Intel N305/Proxmox/VM/Docker
openHAB version: 4.2

Due to how openHAB discovers things, and then adds them separately later, it needs to receive the discovery information from MQTT multiple times. ZwaveJS UI by default only sends it once. So you either need to go to each device in ZwaveJS UI and trigger Home Assistant discovery again, or configure MQTT in ZWaveJS UI to retain messages. If you do the latter, just be aware that you might have “zombie” topics that stick around and will need to be manually cleared if you have to remove a device from your network.

1 Like

A couple of weeks ago, I decided to join the “zwave-js-ui team”.

And now I wonder why I didn’t do that before, because the entire network works now much, much better. I do not know why, but especially, the occasional 30-second latency is now a thing of the past (maybe it had to do with the nightly network heal?).

The lack of autodiscovery meant quite some work, but it was worth it.
My personal biggest pain point was the confusion between switch and contact. For some reason, I configured my lock as a contact, and then I was not able to update it. On the other hand, some sensors would not work if configured as switch.
That said, it was just a couple of work of fine tuning, and now I am extremely satisfied with the result.

My personal experience is so positive, that I even believe zwave-js-ui should become the standard zwave binding for OH.

1 Like

See here

I started some work on a binding. I lack time to fully focus on it, so it might take a very long development time. If anyone else want to try it, do so, don’t wait for me.

At this moment it is only able to connect to the websocket and send/receive a basic version command. I plan to spend one or two days on it at the Christmas holidays.

The initial goal is to support autodiscovery for z-wave devices and send and receive data to their endpoints. At this moment i think all the configuration and management is better off controlled by the zwave-js-ui tool.

3 Likes

Agreed in terms of node management. But there are lots of things that are in the Configuration Command Class that one would want to automate. Examples for me include status LEDs for HomeSeer switches (I presume Inovelli ZWave switches are similar), as well as changing the operation mode of GE/Jasco switches (occupancy mode during the day, vacancy mode at night). So… maybe not necessary for an MVP, but should follow soon after.

I’d be happy to test things out for you, once you have something to that point. I don’t have a huge variety of devices, but I have a lot of them. Leak sensors, several models and manufacturers of switches, a few models of outlets, and some Kwikset locks.

If you can share the code somehow I can also spend some days to help…

My problem is that I have very basic programming skills with a lot of questions asking I can do some programming but very basic…

I also tried a MVP with connections to the websocket but I am pretty sure yours is much better. Therefore if you can share your current status I will try to improve it…

The goal should be to have a basic functionality like auto discovery and switching or reading out values … everything than can be added step by step…

This is my basic code

import okhttp3.*;
import okio.ByteString;

public class ZwaveJsWebSocketExample {

    private static final String WS_URL = "ws://localhost:3000"; // Replace with your Z-Wave JS WebSocket URL

    public static void main(String[] args) {
        OkHttpClient client = new OkHttpClient();

        // Build WebSocket request
        Request request = new Request.Builder().url(WS_URL).build();

        // Create WebSocket Listener
        WebSocketListener listener = new WebSocketListener() {
            @Override
            public void onOpen(WebSocket webSocket, Response response) {
                System.out.println("WebSocket Opened!");
                
                // Example: Send an API request to get the nodes
                String message = "{\"messageId\": 1, \"command\": \"getNodes\"}";
                webSocket.send(message);
            }

            @Override
            public void onMessage(WebSocket webSocket, String text) {
                System.out.println("Received Message: " + text);
            }

            @Override
            public void onMessage(WebSocket webSocket, ByteString bytes) {
                System.out.println("Received Binary Message: " + bytes.hex());
            }

            @Override
            public void onClosing(WebSocket webSocket, int code, String reason) {
                System.out.println("WebSocket Closing: " + code + " / " + reason);
                webSocket.close(1000, null);
            }

            @Override
            public void onClosed(WebSocket webSocket, int code, String reason) {
                System.out.println("WebSocket Closed: " + code + " / " + reason);
            }

            @Override
            public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                System.err.println("WebSocket Error: " + t.getMessage());
                t.printStackTrace();
            }
        };

        // Open WebSocket connection
        WebSocket webSocket = client.newWebSocket(request, listener);

        // The OkHttp WebSocket runs on its own thread, so you may need to keep the main thread alive.
        try {
            Thread.sleep(30000); // Keep the program running for 30 seconds
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            client.dispatcher().executorService().shutdown();
        }
    }
}

I could be off base, but I think the main challenge is to organize and sort the received messages into the OH construct of things (properties, configurations and channels). In MQTT each device/node has from 15 to 600 topics. In manual mode (Generic MQTT thing) you can pick out the ones you want for channels. In mqtt_homeassistant mode there is a preset assortment of config messages to pick from as needed. I’m only vaguely aware of HA, but it appears they have python templates to facilitate sorting various type of devices with their socket connection.

From my perspective, properties are unchanging (mfg, device ID, etc.), configurations (in Zwave -static values) are mostly unchanging and define how you want the device to respond in your home (e.g. LED status - always ON, always OFF) and the channels (in Zwave - dynamic values) motion, temperature, etc. However, configurations can be channels also (e.g. change the reporting frequency based on time of day)

The code is here: GitHub - lsiepel/openhab-addons at zwavejs

Don’t expect actual usefull features at this moment. Just pushed some code. Current state:

  • mDNS discovery is supported.
  • Bridge is connecting and setting some properties
  • Initial state and event updates are deserialized and routed to listeners.

Next would be to add the Things based on the inital state, that is where it gets complicated. As said, development will be slow

3 Likes

That’s much more than I expected…I will have a look into it on the weekend

@lsiepel I gave some thought to writing the integration. But when I looked at the HA integration code, it seemed pretty complex. I’ve written quite a few bindings (eight, I think), but this one would be considerably more complex than the others I’ve written. I have some free time (as long as it doesn’t detract from my golf game lol), and I might be interested in contributing, but I don’t want to own the binding.

1 Like