kpine / zwave-js-server-docker

A minimal docker container for zwave-js-server
MIT License
51 stars 10 forks source link
z-wave zwave zwave-js

zwave-js-server-docker

A minimal docker container for Z-Wave JS Server. This container provides a usable Z-Wave JS Server and little else.

For a more functional application that also provides the Server, use Z-Wave JS UI instead.

Docker Configuration

The Z-Wave JS driver stores information about the Z-Wave network in a set of cache files. When the server restarts, the driver loads the network information from the cache. Without this information the network will not be fully usable right away. Therefore it is very important that the cache files are persisted between container restarts.

The docker run examples below use an environment file to provide all of the Z-Wave network keys.

$ cat .env
S2_ACCESS_CONTROL_KEY=7764841BC794A54442E324682A550CEF
S2_AUTHENTICATED_KEY=66EA86F088FFD6D7497E0B32BC0C8B99
S2_UNAUTHENTICATED_KEY=2FAB1A27E19AE9C7CC6D18ACEB90C357
S0_LEGACY_KEY=17DFB0C1BED4CABFF54E4B5375E257B3
LR_S2_ACCESS_CONTROL_KEY=61BEF779F9DF0827CD9870B719D074BB
LR_S2_AUTHENTICATED_KEY=905B869063266296AE5159EEDBEE038D
RF_REGION=USA (Long Range)

Run with a volume mount

# Create a persistent volume for the driver cache
$ docker volume create zjs-storage

# starts the server and uses the volume as the persistent cache directory
$ docker run -d -p 3000:3000 --name=zjs -v zjs-storage:/cache --env-file=.env --device "/dev/serial/by-id/usb-0658_0200-if00:/dev/zwave" ghcr.io/kpine/zwave-js-server:latest

Run with a bind mount

# starts the server and uses a local folder as the persisent cache directory
$ docker run -d -p 3000:3000 --name=zjs -v "$PWD/cache:/cache" --env-file=.env --device "/dev/serial/by-id/usb-0658_0200-if00:/dev/zwave" ghcr.io/kpine/zwave-js-server:latest

Docker Compose

Docker Compose is a simple way to maintain a container configuration.

Example of a minimal docker-compose.yaml file:

services:
  zjs:
    container_name: zjs
    image: ghcr.io/kpine/zwave-js-server:latest
    restart: unless-stopped
    environment:
      - "S2_ACCESS_CONTROL_KEY=7764841BC794A54442E324682A550CEF"
      - "S2_AUTHENTICATED_KEY=66EA86F088FFD6D7497E0B32BC0C8B99"
      - "S2_UNAUTHENTICATED_KEY=2FAB1A27E19AE9C7CC6D18ACEB90C357"
      - "S0_LEGACY_KEY=17DFB0C1BED4CABFF54E4B5375E257B3"
      - "LR_S2_ACCESS_CONTROL_KEY=61BEF779F9DF0827CD9870B719D074BB"
      - "LR_S2_AUTHENTICATED_KEY=905B869063266296AE5159EEDBEE038D"
      - "RF_REGION=USA (Long Range)"
    devices:
      - "/dev/serial/by-id/usb-0658_0200-if00:/dev/zwave"
    volumes:
      - ./cache:/cache
    ports:
      - "3000:3000"

Environment variables

Directories

Ports

Details

Network Keys

All network keys must specified as 16-byte hexadecimal strings (32 characters). A simple way to generate a random network key is with the following command:

$ < /dev/urandom tr -dc A-F0-9 | head -c32 ; echo
8387D66323E8209C58B0C317FD1F4251

All keys should be unique; sharing keys between multiple security classes is a security risk. See the Z-Wave JS Key management docs for further details.

At a minimum, the S0 (Legacy) network key is required, otherwise the zwave-js-server will fail to start. The S2 keys are optional but highly recommended. If unspecified, S2 inclusion will not be available.

USB Path

Instead of using the USB_PATH environment variable, map the USB controller device path to the container's default of /dev/zwave.

Controller Firmware Updates (OTW)

The @zwave-js/flash command line utility is included in the Docker image to support Over-The-Wire (OTW) firmware updates of controllers. Download the appropriate firmware file for your controller and issue the flash command. Be sure to stop any running Z-Wave JS server before doing so.

docker run --rm -it -v "$PWD/fw:/fw" --device "/dev/ttyUSB0:/dev/zwave" ghcr.io/kpine/zwave-js-server:latest flash /fw/fw.gbl

The command expects the device path to be /dev/zwave by default, or whatever environment variable USB_PATH is set to.

See the wiki page for more information.

User Device Configuration Files

Use the /cache/config directory to easily test new device configuration files or modifications to existing ones. The files located in this directory will supplement or override the embedded device configuration database. When the container is restarted the driver logs will indicate which file was loaded:

2021-06-19T06:19:18.506Z CNTRLR   [Node 007] Embedded device config loaded
2021-06-19T06:21:43.793Z CNTRLR   [Node 008] User-provided device config loaded

Serial Soft-Reset

Z-Wave JS performs a soft-reset (restart) of the Z-Wave controller during startup, and during certain operations such as NVM backups and restores. The soft-reset can result in a USB disconnect for some Z-Wave controllers, which may cause problems with certain container runtimes or host configurations. If you observe that Z-Wave JS has trouble finding the USB device, you may try opting out of this functionality by setting the ZWAVEJS_DISABLE_SOFT_RESET environment variable, or setting the softReset driver feature option to false. For more details, see the softReset and ZWaveOptions documentation.

If you continue to have issues even after disabling soft-reset, you may also need to disable the unresponsive controller recovery feature. You can opt out of this functionality by setting the ZWAVEJS_DISABLE_UNRESPONSIVE_CONTROLLER_RECOVERY environment variable, or setting the unresponsiveControllerRecovery driver feature option to false. For more details, see the ZWaveOptions documentation.

Cache Lock Files

Z-Wave JS uses directories as lock files to prevent the cache files from being modified by multiple concurrent processes; this helps prevent against cache corruption. These directories are located in the cache directory, next to the actual cache files. The locking technique updates the mtime (Modified time) of the lock file every ~1 second. If your storage media is sensitive to frequent writes, such as an SD card, you may want to relocate the lock files to another directory, such as a tmpfs. To relocate the cache files, set the ZWAVEJS_LOCK_DIRECTORY environment variable to an alternate path, preferably some kind of tmpfs on the host. Here is an extract of a compose file:

services:
  zjs:
    environment:
      ZWAVEJS_LOCK_DIRECTORY: "/run/lock/zwave-js"
    volumes:
      - /run/lock/zwave-js:/run/lock/zwave-js

The environment variable tells Z-Wave JS to store the lock files in the directory /run/lock/zwave-js. The volume entry maps the hosts /run/lock/zwave-js directory, which is a tmpfs, to the container directory with the same name. The end result is that the lock files created in the container are located in the tmpfs of the host. The path /run/lock is usually mounted as a tmpfs. Another candidate might be /tmp/zwave-js.

Note that locating the locks in a central place on the host ensures that multiple containers with the same configuration will be aware of the locks. However, if Z-Wave JS is run in another instance without the same lock directory configuration, it will not see the locks and this will bypass the lock protections, allowing for the chance that the cache will be corrupted. When the locks are located in the default location, all instances of Z-Wave JS using the default configuration will see the locks. So use this feature with care.

Firmware Update API Key

Z-Wave JS has an online web service that provides information about firmware updates for your devices. Use of this service requires an API key. The Z-Wave JS organization has provided this project with its own key. This key is only valid when used for non-commercial purposes, and is not permitted for commercial usage. If you are a commercial organization using this application, you must request and configure your own key.

If you are using the Z-Wave integration with Home Assistant, you do not need to enable or install an API key. The integration provides the key when accessing the firmware update APIs. This is true for any client application that makes use of the firmware update APIs with its own keys.

If you use a client that does not support its own API key, and you are a non-commercial user, you can enable the built-in key by setting the FIRMWARE_UPDATE_API_KEY to -. By doing so you agree that you are not a commercial user.

If you have your own key, i.e. you are a commercial user, you can set FIRMWARE_UPDATE_API_KEY to that key.

If the FIRMWARE_UPDATE_API_KEY environment variable is empty (the default), no API key will be configured. In that case a client application would be responsible for setting it, otherwise the firmware update service may not be functional or incur more restrictive rate-limiting.