ZwaveJS installation Control Panel

@firstof9

The Device Diagnostic logs from balenaCloud have lots of information, but I’m not seeing anything indicating any problems with dev/serial/by-id/usb-0658_0200-if00. Any thoughts on what I should look for?

Not sure if this matters, but you shouldn’t specify any ports for Home Assistant in your compose. Once you are running in network_mode: host all connections between Host and the Home Assistant container are open and no additional ports need to or should be specified.

Nope you’re using a flavor of linux that’s not normal, it’s almost like you’re inside a VM rather than on the bare metal.

Yeah I have to agree with @firstof9 Balena is not a version of linux I’m familiar with. I’m just running docker on Ubuntu, I really have no experience with Balena. Not saying its bad and from a quick read it looks like its specialized to handle containers specifically, but I just don’t know much about it. I don’t know if it is somehow creating a barrier to configuration here, especially with needing access to a physical USB port.

I double checked on specifying ports in network host mode and it appears docker just discards the port info and issues a warning, but it shouldn’t impact the setup so that might be a dead end.

I’m assuming based on your logs Home Assistant comes up around 17:08. It looks like it is continuously trying to GET settings from the zwavejs integration, including the control panel. Not sure if it is stuck in a loop or what exactly is causing this, but it appears to make it crash somehow.

Maybe your Zstick is drawing too much power off the PI and causing issues? Do you have an externally powered USB hub to try?

As for the USB stick on my setup, I created a Symlink with a UDEV rule to name the stick ttyusb.zstick and mapped it to the ttyACM0 in the zwavejs2mqtt container. I think I followed directions at these websites for the UDEV rule. That seemed to help with the USB stick staying put and not driving around on me to different ports, and being easily found by the container.

This is my device config in my compose:

        devices:
            - '/dev/ttyusb.zstick:/dev/ttyACM0'

Thanks for the help. Makes sense that it would be related to Balena. Balena is great, but it does need atypical settings at times. I’ll see what I can figure out on that end.

If and when I have a solution, I’ll post it here.

Any additional suggestions/advice continue to be welcome!

I’m not entirerly sure where to look but check whatever logs you can for anything related to usb or /dev/serial/by-id....

I sought some clarification about the disconnected message from the zwavejs2mqtt devs, and it is not the connection to the zwave controller that is disconnected, it is zwavejs2mqtt’s websocket connection to the server. They suggested that the issue is with my nginx setup.

Here is my nginx setup:

map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name *.balena-devices.com;

    location / {
        proxy_pass http://sh_homeassistant:8123;
        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location /api/websocket {
        proxy_pass http://sh_homeassistant:8123/api/websocket;
        proxy_set_header Host $host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location /zwavejs2mqtt {
        rewrite '^/zwavejs2mqtt(/.*)$' $1 break;
        proxy_pass http://zwavejs2mqtt:8091;
        proxy_set_header X-External-Path /zwavejs2mqtt;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

Any ideas why homeassistant would be preventing zwavejs2mqtt’s websocket connection from connecting?

Sounds like your websockets are getting overwhelmed or blocked. Aren’t home assistant and zwavejs running on the same machine? Unless I’m missing something about your network architecture the connection between them shouldn’t be going through the proxy.

My nginx proxy is only for external connections to Home Assistant or the zwavejs2mqtt config page outside the network. If you stop the nginx container and temporarily disable the proxy does everything work fine? Or do you need the proxy for internal connections as well? If so I have no experience with nginx on an internal network so someone else would have to chime in.

My nginx config for Home Assistant is in this post RPI - docker installed with external access HA,problem with fail2ban and external IP - #6 by mwav3

@mwave3

Sounds like your websockets are getting overwhelmed or blocked

Yes, I think this might well be the problem.

Aren’t home assistant and zwavejs running on the same machine? Unless I’m missing something about your network architecture the connection between them shouldn’t be going through the proxy.

Yes, they are on the same machine. This occurs before there is any connection between home assistant and zwavejs - just having both on the same machine with the integration not even set up causes the webssocket connection for zwavejs to fail to connect.

If you stop the nginx container and temporarily disable the proxy does everything work fine?

I can’t access either interface without the proxy, so I can’t tell. But I can disable the proxy for zwavejs, and then set up the integration, which would let me know if zwavejs is able to connect to the websocket server. Worth trying, certainly.

Or do you need the proxy for internal connections as well?

The proxy is just for ports 8123 (home assistant) and 8091 (zwavejs control panel), port 3000 isn’t using the proxy.

Ok i understand- so you need the proxy to get to the zwavejs control panel. Your config for zwavejs is setup as if it’s a websocket server (like homeassistant is) but the control panel on port 8091 just uses basic http. That misconfiguratuon probably overwhelms the websockets. I can access my zwavejs2mqtt control panel with just this basic http nginx config:

##########################################################
#################
### SUBDOMAIN 4 Zwave JS#######################################
#################
server {
        listen 443 ssl;

        root /config/www;
        index index.html index.htm index.php;

        server_name something.duckdns.org;

        include /config/nginx/ssl.conf;

        client_max_body_size 0;

        location / {
           #   auth_basic "Restricted";
          #    auth_basic_user_file /config/nginx/.htpasswd;
                include /config/nginx/proxy.conf;
                proxy_pass http://192.168.0.126:8091;
        }
}

@mwav3 I think zwavejs2 requires websocket connectivity.

I disabled the port 3000 websocket server to be sure it wasn’t involved, and still, when I attempted removing the websocket connectivity settings from the nginx config, I got the following error:

It requires websocket connectivity on port 3000 but not the admin control panel on port 8091. Access to the admin control panel is just http.

Not sure if it will work but just try removing these 3 lines from your nginx proxy under location /zwavejs2mqtt

proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

These are websocket configs that I think are jamming port 8091 open. They’re not necessary and I can access my zwave control panel through my nginx proxy without them. I’m using the Swag container that combines letsencrypt for secure https and also fail to ban so it could be a little different then your setup.

There is a websocket connection with the browser. It’s not http only.

The proxy settings posted above are also recommended by the zwavejs2mqtt docs for a subpath configuration. The only difference I see is a missing rewrite rule, not sure if that’s a problem.

I definitely can’t argue with the documentation. It’s just weird my reverse proxy to the control panel works just fine without those extra websocket settings. I even have http auth enabled with nginx for an extra password requirement to get to the config panel, which I’ve never had work with websocket connections. I just figured trying to post my config and what I did from a working setup could help, but unfortunately at this point I’m definitely in over my head and not really sure anymore what could be going wrong here.

I’m also using subdomains vs subpaths so that could be a difference too.

@mwav3 Another difference between your config and mine is that you are using ssl, which I am not. Unfortunately, with balena only port 80 can be made publicly accessible.

Switching to ssl may be the next thing to try.

@freshcoast The missing rewrite rule does not seem to affect the issue at hand. I added rewrite ^ $request_uri; and encountered the same problem. But thanks for pointing it out!

Any other ideas?

Unfortunately, I don’t have any other suggestions. I don’t proxy to zwavejs2mqtt with a subpath, I’m using a domain, and I also use Traefik which handles websockets automatically without any additional setup.

@freshcoast I was actually considering switching to Traefik to see if that helped resolve any of these issues. However, I’ve never used it before. Would you be willing to share the relevant bits of your Traefik setup for zwavejs2mqtt? (I completely understand if you don’t feel comfortable doing so).

Here’s a sanitized and simplified docker-compose file for my setup. It’s not necessarily conventional.

  • http requests are always re-directed to https
  • A DNS Challenge configures the certificates with my own custom domain
  • The zwave-js websocket server is proxied as a subpath, so my integration setup can use a wss url. I wouldn’t necessarily recommend doing this part, you can just expose port 3000 as usual. This is not very useful w/o authentication, but I just wanted to see if it worked, it does, and so I just left it. In HA I use wss://my.domain/ws as the integration server URL. This would obviously break if zwavejs2mqtt also used the /ws path, so a more unique path would be better, or a separate domain name.
  • https://my.domain therefore goes to zwavejs2mqtt
  • All containers proxied by Traefik use the frontend network
services:
  proxy:
    container_name: proxy
    image: traefik:v2.4
    restart: always
    command:
      - "--api=true"
      - "--log.level=INFO"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=frontend"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "[email protected]"
      - "--certificatesresolvers.mydns.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.mydns.acme.dnschallenge=true"
      - "--certificatesresolvers.mydns.acme.dnschallenge.provider=mydnsprovider"
    environment:
      YOUR_DNS_PROVIDER_API_VARS_FILE: "/letsencrypt/vars_file"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./proxy/letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.entrypoints=web"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
    networks:
      - frontend

  z2m:
    container_name: z2m
    image: zwavejs/zwavejs2mqtt:5.4.5
    stop_signal: SIGINT
    stop_grace_period: 1m
    restart: unless-stopped
    environment:
       - "TZ=<MYTZ>"
    networks:
      - frontend
    devices:
      - "/dev/serial/by-id/usb-Silicon_Labs_CP2102N_USB_to_UART_Bridge_Controller_7a6bb087ac08ea11b486db812473482f-if00-port0:/dev/zwave"
    volumes:
      - ./z2m/data:/usr/src/app/store
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.zjs.rule=Host(`my.domain`) && Path(`/ws`)"
      - "traefik.http.routers.zjs.entrypoints=websecure"
      - "traefik.http.routers.zjs.tls.certresolver=mydns"
      - "traefik.http.routers.zjs.service=zjs"
      - "traefik.http.routers.z2m.rule=Host(`my.domain`)"
      - "traefik.http.routers.z2m.entrypoints=websecure"
      - "traefik.http.routers.z2m.tls.certresolver=mydns"
      - "traefik.http.routers.z2m.service=z2m"
      - "traefik.http.services.zjs.loadbalancer.server.port=3000"
      - "traefik.http.services.z2m.loadbalancer.server.port=8091"

networks:
  frontend:
    name: "frontend"

@freshcoast Thank you very much! I am making some progress on the switch over to traefik, and this should help a great deal. Hopefully switching to traefik will resolve the original issue.