Skip to content

Repository files navigation

PRs Welcome Release Validation Issues PRs License

WyzeSense to MQTT Gateway

Configurable WyzeSense to MQTT Gateway intended for use with Home Assistant or other platforms that use the same MQTT discovery mechanisms. The gateway allows direct local access to Wyze Sense products without the need for a Wyze Cam or cloud services. This project and its dependencies have no relation to Wyze Labs Inc.

Please submit pull requests against the devel branch.

Special Thanks

Table of Contents

Installation

Docker

This is the most tested method of running the gateway. It allows for persistence and easy migration assuming the hardware dongle moves along with the configuration. All steps are performed from the Docker host, not the container. Images are published to ghcr.io.

  1. Plug the Wyze Sense Bridge into a USB port on the Docker host. Confirm that it shows up as /dev/hidraw0; if not, update the devices entry in the Docker Compose file with the correct path.
  2. Copy examples/hub/docker-compose.yml.example to docker-compose.yml and examples/hub/.env.example to .env in the same directory. Fill in at minimum WS2M_MQTT_HOST and VOL_DATA. See Docker Compose Docs for more details.
  3. Create your local volume mount directory using the same path you set for VOL_DATA:
mkdir -p /docker/ws2m-hub/data
  1. (Optional) Pre-populate a sensors.yaml file at <data>/sensors.yaml. This file is created automatically when sensors are first discovered.
  2. Start the container:
docker compose up -d
  1. Pair sensors following instructions below. Sensors already paired to the dongle are found automatically on start; they will be added with default values (unknown version, contact sensor class) until updated manually.

Multiple dongles: with dongle: auto (the default), every connected WyzeSense dongle is used. To pass an arbitrary number of dongles into the container without editing the compose file for each one — and to survive replugging — install the udev rule from examples/99-ws2m-dongles.rules.example, which collects all WyzeSense dongles under /dev/ws2m-dongles/. Bind mount that folder into the container (see the commented volume and device_cgroup_rules lines in the compose example) and either keep WS2M_DONGLE=auto or set it to /dev/ws2m-dongles.

Health monitoring: The container includes a HEALTHCHECK that monitors /tmp/ws2m_healthy. ws2m writes and periodically touches this file while running normally, and removes it on dongle failure. The container reports as unhealthy within ~90 seconds of a dongle failure or process hang. When unhealthy, check docker logs ws2m-hub — the failed dongle and all its sensors will also have been published offline to MQTT for automation triggers.

Remote Bridge (Docker)

To use WyzeSense USB dongles on a separate machine from the hub, run the ws2m-remote image on that machine. The remote forwards raw USB frames to the hub over authenticated WebSockets and relays any number of dongles (WS2M_DONGLE=auto detects them all).

On the hub: enable the WebSocket listener by setting hub_ws_enabled: true in config.yaml (or WS2M_HUB_WS_ENABLED=true in your hub .env). The hub advertises itself via mDNS on the local network by default.

On the remote machine:

  1. Copy examples/remote/docker-compose.yml.example to docker-compose.yml and examples/remote/.env.example to .env in the same directory.
  2. WS2M_HUB_URL is optional if the hub and remote are on the same network segment (auto-discovered via mDNS). Set it explicitly when crossing VLANs or in Docker without host networking: WS2M_HUB_URL=ws://192.168.1.10:8765.
  3. Start the remote: docker compose up -d
  4. Adopt the remote: see Adopting a Remote below.

Home Assistant App

WyzeSense2MQTT is available as a Home Assistant App for HAOS and Supervised installs via a dedicated app repository. The app auto-discovers the Mosquitto broker app so no MQTT configuration is needed in most cases.

Add to Home Assistant

Or add the repository URL manually in Settings → Apps → App Store → ⋮ → Repositories:

https://github.com/raetha/home-assistant-apps

See the home-assistant-apps repository for full installation and configuration documentation.

Linux Systemd

For hub and remote installations without Docker, see docs/linux_systemd_installation.md. This method is not actively tested.

Remote Bridge

The remote bridge lets the hub run on one machine (e.g. your Docker host or Home Assistant server) while USB dongles live on different machines (e.g. a Raspberry Pi in another room). The remote (ws2m-remote image) forwards raw HID frames to the hub over authenticated WebSockets. A single remote relays any number of dongles — it opens one connection per dongle, so each behaves exactly like an independently-connected dongle and one dongle's trouble never affects the others.

How it works

  1. The hub runs with hub_ws_enabled: true. The hub advertises itself via mDNS (_ws2m._tcp.local.) so remotes on the same network segment can connect without any explicit URL configuration.
  2. The remote connects to the hub — automatically via mDNS, or explicitly via WS2M_HUB_URL. On first start it generates a stable UUID and attempts to adopt with the hub.
  3. Adoption — press the Enable Remote Pairing button on the hub device in HA (or publish any payload to <self_topic_root>/hub/<uuid>/remote_pair). The hub enters pairing mode for hub_remote_pairing_seconds seconds; the <self_topic_root>/hub/<uuid>/remote_pairing state shows active. The next unauthenticated remote that connects receives a unique token, saved on both sides.
  4. On subsequent connects the remote presents its token; the hub validates it. No further adoption steps are needed.
  5. On reconnect, each dongle's relay replays its own ring buffer of recent frames (10-second TTL, 500-frame capacity) so brief network disruptions do not lose sensor events. Adoption happens once per remote: the first dongle's connection obtains the token, and every dongle then shares it.

Hub configuration

Enable and tune the WebSocket listener in your hub's config.yaml (or via ENV vars):

hub_ws_enabled: true              # enable WebSocket listener for remote connections
hub_ws_port: 8765                 # WebSocket listener port (default 8765)
hub_remote_pairing_seconds: 60    # how long pairing mode stays active (default 60)
hub_ws_mdns: true                 # advertise via mDNS for auto-discovery (default true)

These settings can also be toggled live from the hub device page in Home Assistant. The hub device page also has a Restart button that performs a clean shutdown — publishing all devices offline, saving state, and disconnecting MQTT — before exiting so Docker or systemd restarts the process automatically.

Adopting a remote

  1. Start the remote container on the machine with the USB dongle.
  2. In HA, go to the WyzeSense Hub device page and press Enable Remote Pairing. The pairing state sensor shows active for 60 seconds (or your configured duration).
  3. The remote is adopted automatically: its token is saved on both sides. Future restarts reconnect without any further action.

Remote environment variables

Variable Default Description
WS2M_HUB_URL (auto) Hub WebSocket URL, e.g. ws://192.168.1.10:8765. Optional when hub and remote are on the same network and mDNS is available.
WS2M_DONGLE auto HID device path (/dev/hidraw0), a directory of device nodes (/dev/ws2m-dongles — see the udev example), or auto to detect all matching dongles.
WS2M_DATA_DIR /app/data Directory for persistent state (remote_id, hub_token).
WS2M_HUB_ID (none) Preferred hub UUID when multiple hubs are discoverable via mDNS.
WS2M_DISCOVERY_TIMEOUT 30 mDNS discovery timeout in seconds.
WS2M_LOG_LEVEL INFO Log verbosity: DEBUG, INFO, WARNING, or ERROR. Also adjustable live from the remote device page in HA.

HA entities (remote)

Each adopted remote appears in HA as a WyzeSense Remote <UUID> device linked to the hub. The device shows as available (online) when the ws2m-remote service is connected to the hub, and unavailable (offline) when the WebSocket connection drops — this is the remote's connectivity indicator, mirroring how the hub device's availability tracks its MQTT connection. The remote device includes:

  • Healthhealthy / degraded, aggregated across the remote's dongles: degraded while any relayed dongle is lost, healthy once all have recovered.
  • Connected dongles — count of WyzeSense dongles currently being relayed by this remote.
  • Restart — performs a clean shutdown of the remote process (closes the HID connection, closes the WebSocket) and exits so Docker or systemd restarts it automatically. Use this to pick up config changes or recover from a degraded state without touching the machine the remote runs on.
  • Remove remote — button that clears all MQTT topics for this remote and its entire dongle and sensor chain, and deletes the remote's token so it cannot reconnect without re-pairing. Use this after permanently decommissioning a remote so HA removes all related devices cleanly.
  • Cleanup disconnected dongles — button that clears MQTT topics and data for dongles relayed by this remote that have failed or disconnected, leaving healthy dongles untouched. Use this when a remote's dongle was physically removed but the remote itself is still running.

Each dongle relayed by a remote appears as its own WyzeSense Dongle <MAC> device in HA — the same device type as locally-attached dongles — with the same set of entities: Connection state (online/offline), Scan for sensor, and Remove sensor.

Health is strictly scoped: the hub's health entity and container healthcheck reflect only the hub process and its local dongles, and a remote's health reflects only its own dongles. A remote going offline never shows the hub as unhealthy — the remote's device availability and health entities carry that state.

Configuration Files

The gateway uses three config files located in the config directory. Examples of each are below and in the repository.

config.yaml

This is the main configuration file. Aside from MQTT host, username, and password, the defaults should work for most people. A working configuration will be created automatically if ENV values are available for at least mqtt_host. So it does not need to be created in advance. Use log_level to control verbosity (DEBUG, INFO, WARNING, ERROR); default is INFO. Logs go to stdout and are captured by docker logs or journalctl automatically.

mqtt_host: <host>
mqtt_port: 1883
mqtt_username: <user>
mqtt_password: <password>
mqtt_client_id: ws2m
mqtt_clean_session: false
mqtt_keepalive: 60
self_topic_root: ws2m
hass_topic_root: homeassistant
hass_discovery: true
dongle: auto
hub_ws_enabled: false
hub_ws_port: 8765
hub_remote_pairing_seconds: 60
hub_ws_mdns: true
log_level: INFO
Key Default Notes
mqtt_host (required) MQTT broker hostname or IP
mqtt_port 1883 Broker port
mqtt_username / mqtt_password (none) Broker credentials
mqtt_client_id ws2m MQTT client identifier
mqtt_clean_session false Persist subscriptions across restarts
mqtt_keepalive 60 Broker keepalive in seconds
self_topic_root ws2m Topic prefix for all ws2m data topics. Change when running multiple instances on the same broker.
hass_topic_root homeassistant HA MQTT discovery prefix. Only change if you have set mqtt: discovery_prefix in HA's configuration.yaml.
hass_discovery true Publish HA MQTT discovery config. When false, ws2m clears all retained discovery config topics on startup. All ws2m/ state and data topics continue to function normally — only the homeassistant/ discovery payloads are suppressed.
dongle auto Dongle path. auto detects all connected dongles automatically; /dev/hidrawN pins to one specific device; a directory (e.g. /dev/ws2m-dongles) uses every device node inside it.
orphan_retention_days 7 Days an orphaned sensor's config is kept before automatic removal. Orphans show as unavailable and reactivate fully if they return within the window. 0 disables automatic removal. Also adjustable live from the hub device page in HA.
hub_ws_enabled false Enable the WebSocket listener to accept connections from ws2m-remote instances.
hub_ws_port 8765 WebSocket listener port for remote connections.
hub_remote_pairing_seconds 60 How long remote pairing mode remains active after pressing Enable Remote Pairing.
hub_ws_mdns true Advertise the hub via mDNS so remotes can auto-discover without WS2M_HUB_URL.
log_level INFO Log verbosity: DEBUG, INFO, WARNING, or ERROR. Also adjustable live from the hub device page in HA.

sensors.yaml

This file stores per-sensor configuration for every sensor known to the hub, across all dongles, at <data>/sensors.yaml. Sensor configuration is a property of the sensor itself: if a sensor is re-paired to a different dongle (local or remote), its name, class, and other settings follow it automatically — the runtime link to the owning dongle is tracked separately in state.yaml and transfers on the fly when events start arriving via the new dongle. Entries can be modified to set the sensor name, class, and invert_state as they will appear in Home Assistant. The class field maps to an HA binary_sensor device class (opening, door, window, motion, moisture, etc.). Availability timeouts are determined automatically by sensor type (8 h for V1, 4 h for V2, 24 h for chime) and are not user-configurable.

Many sensor settings can also be adjusted live from the Home Assistant device page without editing this file — changes are written back automatically.

Sensors added via the scan MQTT command will populate this file automatically with the correct sensor_type and sw_version. Sensors that were previously paired and auto-discovered will default to class: opening and will not have sw_version set. For v1 devices sw_version is typically 19; for v2 devices it is typically 23.

The Keypad v2 supports an optional pins list for PIN validation. If omitted, all PIN entries are treated as valid. See docs/keypad.md for full details.

The Chime supports optional ring_id (0–255, default 0), volume (1–9, default 5), and repeat_count (1–9, default 1) keys. These can also be adjusted live from the HA device page — changes are written back to this file automatically.

'AAAAAAAA':
  name: Entry Door
  sensor_type: switchv2
  class: door
  invert_state: false
  sw_version: 23
'BBBBBBBB':
  name: Office Window
  sensor_type: switchv2
  class: window
  invert_state: false
  sw_version: 23
'CCCCCCCC':
  name: Kitchen Fridge
  sensor_type: switch
  class: opening
  invert_state: true      # contact reads closed when door is open — swap payloads
  sw_version: 19
'DDDDDDDD':
  name: Hallway Motion
  sensor_type: motionv2
  class: motion
  invert_state: false
  sw_version: 23
'EEEEEEEE':
  name: Basement Leak
  sensor_type: leak
  sw_version: 23
'KPADKPAD':
  name: Front Door Keypad
  sensor_type: keypad
  pins:
    - "1234"
    - "5678"
'CHIMEMAC':
  name: Front Door Chime
  sensor_type: chime
  ring_id: 0
  volume: 5
  repeat_count: 1

Usage

Pairing a Sensor

At this time only a single sensor can be properly paired at once. Please repeat the steps below for each sensor.

With multi-dongle support, scan is scoped to a specific dongle. If you only have one dongle the MAC is shown in the startup log.

  1. Publish a blank message (payload scan) to the MQTT topic <self_topic_root>/dongle/<dongle_mac>/scan (e.g. ws2m/dongle/AABBCCDD/scan). This can be done via Home Assistant or any MQTT client. With HA discovery enabled, a Scan for sensor button appears on each dongle's device page.
  2. Use the pin tool that came with your Wyze Sense sensors to press the reset switch on the side of the sensor. Hold until the red LED blinks.

Removing a Sensor

Remove is also dongle-scoped — the sensor can only be removed from the dongle it is paired with.

  1. Publish the sensor MAC address as the payload to <self_topic_root>/dongle/<dongle_mac>/remove (e.g. ws2m/dongle/AABBCCDD/remove). The payload should be the 8-character MAC, e.g. AABBCCDD. With HA discovery enabled, a Remove sensor button also appears on each sensor's device page.

Reload Sensors

If you have modified a sensors.yaml while the gateway is running, you can trigger a reload of all dongles without restarting the service or Docker container.

  1. Publish a blank message (payload reload) to <self_topic_root>/hub/<uuid>/reload. With HA discovery enabled, a Reload config button appears on the WyzeSense Hub device page in HA.

Removing a Dongle

If a dongle is permanently removed (replaced, retired, or lost), ws2m retains its data directory and HA entities until you explicitly clean them up. A simple restart or USB glitch will not trigger cleanup — the data is preserved for recovery.

From Home Assistant: A Cleanup removed dongles button appears on the WyzeSense2MQTT hub device page (under the Configuration entity category, not the default dashboard view). Pressing it compares the dongles recorded in the sensor registry against the currently-connected dongles. Any dongle no longer connected has its retained MQTT discovery and status topics cleared and its sensors removed from the registry. A companion Cleanup orphaned sensors button removes individual sensors that are no longer paired to any connected dongle. The Paired sensors and Configured sensors diagnostic entities on the hub device show when a gap exists worth cleaning up. All operations are idempotent — if nothing is stale they do nothing.

Cleanup also happens automatically in stages: an hourly sweep marks any sensor no longer paired to a connected dongle as orphaned. Nothing visible changes — its entities remain in HA showing unavailable, and its configuration is untouched, so a rebooting remote or a temporary outage costs nothing; if the sensor comes back, the marker clears and discovery is republished automatically. Only after a sensor has been continuously orphaned for orphan_retention_days (default 7) are its retained MQTT topics cleared and its registry entries removed. If a sensor is re-paired to a different dongle while both are connected, the stale pairing is also removed from the old dongle's NVRAM automatically, so the paired-sensor count stays honest.

Note: Only press this button after a deliberate permanent removal. If a dongle is temporarily disconnected or experiencing a USB fault, wait until it is reconnected before using this button to avoid losing its sensor configuration.

For surgical single-dongle removal from the command line, see CLI Tools.

CLI Tools

For situations requiring direct dongle access or surgical MQTT cleanup outside the normal service workflow — such as pairing sensors without the bridge running, diagnosing a dongle the bridge cannot open, or clearing orphaned HA discovery topics — see docs/cli_tools.md.

Home Assistant

Home Assistant simply needs to be configured with the MQTT broker that the gateway publishes topics to. Once configured, the MQTT integration will automatically add a device for each sensor, along with entities for state, battery, and signal strength (plus temperature/humidity for climate and leak sensors). By default these entities will have a device_class of opening for contact sensors, motion for motion sensors, and moisture for leak sensors, and the device will be named WyzeSense <MAC>. The following settings are adjustable live from the HA sensor device page and are written back to sensors.yaml automatically:

  • Sensor name — renames the HA device and updates discovery
  • Device class — for contact sensors: door, window, opening, garage_door, lock; for motion sensors: motion, occupancy
  • Invert state — swaps payload_on/payload_off in HA discovery, useful for sensors installed in a non-standard orientation (e.g. a contact sensor in a doorbell chime box)

These can also be set directly in sensors.yaml and applied via Reload.

The Keypad v2 (WSKP1) creates an alarm_control_panel entity, a motion binary sensor, and PIN management entities (PIN count sensor, Arm PIN capture button, Clear all PINs button). To add a PIN: press Arm PIN capture in HA, then enter the PIN on the physical keypad — ws2m captures it and adds it to the configured list automatically. See docs/keypad.md for full setup instructions including entry/exit delay handling.

The Wyze Video Doorbell V1 Chime (WCHIME1) creates a button entity to trigger playback and number entities for ring tone, volume, and repeat count. These settings are adjustable directly from the HA device page and are persisted to sensors.yaml automatically. It's a mains-powered accessory, so — unlike other sensor types — it does not get battery/battery_voltage diagnostic entities; it still gets signal strength and chip temperature.

Discovery uses Home Assistant's device-based MQTT discovery format (one config topic per device, covering all of its entities). See docs/HA_MQTT_COMPLIANCE.md for the HA version this was last verified against and notes on the discovery schema and migrations.

Compatible Hardware

Wyze Branded

  • Wyze Sense Bridge (WHSB1)
  • Wyze Sense Bridge Sensors
    • Contact Sensor v1
    • Motion Sensor v1
  • Wyze Sense Hub Sensors - Requires installing the Wyze Sense Hub firmware onto a Wyze Sense Bridge (unsupported)
    • Entry Sensor v2 (WSES2)
    • Motion Sensor v2 (WSMS2)
    • Climate Sensor (WSCS1)
    • Leak Sensor (WSLS1)
    • Keypad v2 (WSKP1) — See docs/keypad.md for setup with Home Assistant and Alarmo
    • Wyze Video Doorbell V1 Chime (WCHIME1) — Partial support; play command and ring tone, volume, and repeat controls are available via HA. Ring tone IDs are undocumented — see docs/protocol.md.

Neos Smart Branded

  • Neos Smart Bridge (N-LSP-US1)
  • Neos Smart Sensors - Not tested, but theoretically compatible
    • Contact Sensor
    • Motion Sensor
    • Leak Sensor

About

Configurable WyzeSense to MQTT Gateway intended for use with Home Assistant or other platforms that use MQTT discovery mechanisms.

Topics

Resources

Stars

82 stars

Watchers

7 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages