# Reading a SONOFF SNZB-02P on a Raspberry Pi 5 The simplest setup is: ```text SONOFF sensor -> Zigbee USB dongle -> Zigbee2MQTT -> MQTT -> terminal/application ``` You do not need to open the sensor until Zigbee2MQTT is ready. ## 1. Connect and identify the dongle Attach the dongle's antenna before powering it. Connect it to the Raspberry Pi, preferably through a short USB extension cable to reduce USB 3 radio interference. Run: ```bash lsusb ls -l /dev/serial/by-id/ ``` You should see a persistent device path similar to: ```text /dev/serial/by-id/usb-ITead_Sonoff_Zigbee_3.0_USB_Dongle_Plus_2281293efa9aef1190f1b69061ce3355-if00-port0 -> ../../ttyUSB0 ``` Use this `/dev/serial/by-id/...` path instead of `/dev/ttyUSB0` or `/dev/ttyACM0`, because those shorter names can change after a reboot. Check the label on the dongle to determine its adapter type: | Dongle model | Zigbee2MQTT adapter type | | --- | --- | | `ZBDongle-P` | `zstack` | The two dongles have similar names but contain different radio chips. References: - [Zigbee2MQTT adapter configuration](https://www.zigbee2mqtt.io/guide/configuration/adapter-settings.html) - [ZBDongle-P documentation](https://www.zigbee2mqtt.io/guide/adapters/zstack.html) ## 2. Install the Mosquitto MQTT broker On Raspberry Pi OS, run: ```bash sudo apt update sudo apt install -y mosquitto mosquitto-clients sudo systemctl enable --now mosquitto systemctl status mosquitto --no-pager ``` ```bash sudo tee /etc/mosquitto/conf.d/zigbee2mqtt.conf >/dev/null <<'EOF' listener 1883 192.168.178.56 allow_anonymous true EOF ``` and restart the service For an initial setup confined to the Pi and a trusted LAN, the local broker is sufficient. Configure authentication before exposing MQTT outside your trusted network. Reference: [Mosquitto downloads](https://mosquitto.org/download/) ## 3. Install Docker and Docker Compose Identify the operating system and architecture first: ```bash grep -E '^(ID|VERSION_CODENAME)=' /etc/os-release dpkg --print-architecture ``` On a Raspberry Pi 5, the architecture should normally be `arm64`. Select the repository below that matches the reported `ID`. Do not combine an Ubuntu codename such as `noble` with the Debian repository. Install the prerequisites: ```bash sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings ``` ### Ubuntu (`ID=ubuntu`, including `VERSION_CODENAME=noble`) If an earlier attempt installed the wrong Debian source, replace it with Docker's Ubuntu source: ```bash sudo rm -f /etc/apt/sources.list.d/docker.list /etc/apt/sources.list.d/docker.sources sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null sudo apt-get update ``` For Ubuntu 24.04, that correctly uses the Ubuntu repository and `noble` suite. ### Raspberry Pi OS or Debian (`ID=raspbian` or `ID=debian`) Add Docker's Debian source: ```bash sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list >/dev/null sudo apt-get update ``` Install Docker Engine and the Compose plugin: ```bash sudo apt-get install -y \ docker-ce \ docker-ce-cli \ containerd.io \ docker-buildx-plugin \ docker-compose-plugin ``` Enable Docker at boot and verify the installation: ```bash sudo systemctl enable --now docker sudo docker run --rm hello-world sudo docker version sudo docker compose version ``` Docker Compose is now invoked as `docker compose`. The old `docker-compose` command is the legacy standalone version and is not needed. ### Optional: run Docker without `sudo` Add your current account to the `docker` group: ```bash sudo usermod -aG docker "$USER" ``` Then log out and back in, or reboot: ```bash sudo reboot ``` After reconnecting, verify that it works without `sudo`: ```bash docker run --rm hello-world docker compose version ``` Membership of the `docker` group effectively gives the account root-level control of the machine. Only add trusted users. If `dpkg --print-architecture` reports `armhf`, you are using a 32-bit OS. A 64-bit Raspberry Pi OS or Ubuntu installation is recommended on the Pi 5. References: - [Install Docker Engine on Debian](https://docs.docker.com/engine/install/debian/) - [Install Docker Engine on Ubuntu](https://docs.docker.com/engine/install/ubuntu/) - [Install Docker Engine on 32-bit Raspberry Pi OS](https://docs.docker.com/engine/install/raspberry-pi-os/) - [Install the Docker Compose plugin](https://docs.docker.com/compose/install/linux/) ## 4. Run Zigbee2MQTT with Docker Zigbee2MQTT recommends Docker because it avoids most Node.js and dependency setup problems. Its image supports the Raspberry Pi's ARM architecture. If Docker and Docker Compose are already installed, create a working directory: ```bash mkdir -p "$HOME/zigbee2mqtt/data" cd "$HOME/zigbee2mqtt" ``` Create a file named `compose.yaml` containing: ```yaml services: zigbee2mqtt: container_name: zigbee2mqtt image: ghcr.io/koenkk/zigbee2mqtt:latest restart: unless-stopped ports: - "8080:8080" volumes: - ./data:/app/data - /run/udev:/run/udev:ro devices: - /dev/serial/by-id/REPLACE_WITH_YOUR_DONGLE:/dev/ttyACM0 environment: - TZ=Europe/Amsterdam ``` Replace `REPLACE_WITH_YOUR_DONGLE` with the exact filename returned by: ```bash ls /dev/serial/by-id/ ``` Start Zigbee2MQTT and follow its logs: ```bash docker compose up -d docker compose logs -f zigbee2mqtt ``` From another computer, open: ```text http://RASPBERRY_PI_IP:8080 ``` For example: ```text http://192.168.1.50:8080 ``` The first-run onboarding page should appear. Configure these values: ```text MQTT server: mqtt://172.17.0.1 Serial port: /dev/ttyACM0 Adapter: ZBDongle-P -> zstack ZBDongle-E -> ember ``` If `172.17.0.1` does not reach Mosquitto, use the Raspberry Pi's LAN IP instead. Zigbee2MQTT may detect the adapter automatically. ```bash vi "$HOME/zigbee2mqtt/data/configuration.yaml frontend: enabled: true ``` restart the container: ```bash sudo docker compose restart zigbee2mqtt sudo docker compose logs -f zigbee2mqtt ``` References: - [Official Zigbee2MQTT Docker installation](https://www.zigbee2mqtt.io/guide/installation/02_docker.html) - [Zigbee2MQTT onboarding](https://www.zigbee2mqtt.io/guide/getting-started/) ## 5. Pair the SNZB-02P sensor In the Zigbee2MQTT web interface: 1. Click **Permit join**. 2. Open the SNZB-02P. 3. Remove the battery insulation tab. 4. Hold its button for about five seconds, until the LED flashes. 5. Keep the sensor close to the dongle during pairing. 6. Wait for Zigbee2MQTT to report that the device was successfully interviewed. 7. Rename it to something simple, such as `living_room_sensor`. 8. Turn **Permit join** off afterward. The device page should now show temperature, humidity, and battery level. The SNZB-02P uses a CR2477 battery. Reference: [SNZB-02P device support](https://www.zigbee2mqtt.io/devices/SNZB-02P.html) ## 6. Read the temperature Listen for sensor updates on the Raspberry Pi: ```bash mosquitto_sub -h localhost \ -t 'zigbee2mqtt/living_room_sensor' \ -v ``` A message will look similar to: ```json { "battery": 100, "humidity": 48.2, "linkquality": 132, "temperature": 21.6 } ``` To print only the temperature, install `jq` if necessary: ```bash sudo apt install -y jq ``` Then run: ```bash mosquitto_sub -h localhost \ -t 'zigbee2mqtt/living_room_sensor' | jq --unbuffered '.temperature' ``` Battery-powered sensors normally report periodically or when their readings change, rather than continuously. The SNZB-02P publishes `temperature` in °C and `humidity` as a percentage. Reference: [Zigbee2MQTT MQTT topics](https://www.zigbee2mqtt.io/guide/usage/mqtt_topics_and_messages.html) ## 7, reading with curl ```bash curl --silent --max-time 2 "mqtt://192.168.178.247/$topic" | dd bs=1 skip=$((2 + ${#topic})) status=none {"battery":100,"humidity":56.3,"humidity_calibration":0,"temperature":27.1,"temperature_calibration":0,"update":{"installed_version":8704,"latest_release_notes":null,"latest_source":"https://raw.githubusercontent.com/Koenkk/zigbee-OTA/master/images/Sonoff/snzb-02p_v2.2.0.ota","latest_version":8704,"state":"idle"}} ``` ## 8. Store six sensors in RRD files and create graphs Zigbee2MQTT must retain each sensor's state so the collector can obtain the latest reading without waiting for a sleeping sensor: ```yaml device_options: retain: true ``` Install RRDtool and create a Python virtual environment in this project: ```bash sudo apt install -y rrdtool python3-venv cd "$HOME/service_zigbee" python3 -m venv .venv .venv/bin/pip install -r requirements.txt ``` Edit the supplied `config.yaml`. MQTT settings, collection timing, storage paths, logging, all six sensors, RRD archives, and graph presentation are configured in this one file. Replace all five `CHANGE_ME` topics with the friendly names shown by Zigbee2MQTT: ```bash nano config.yaml ``` Sensor entries whose `name` begins with `CHANGE_ME` are placeholders and are excluded from MQTT polling, RRD creation, graphs, and the HTML dashboard. Rename an entry when its physical sensor is ready. The marker is configurable: ```yaml collection: excluded_name_prefix: CHANGE_ME ``` By default, the databases are written to `/var/lib/rrd` and the generated graphs to `/var/www/html/sensors`. Create those directories and grant the user running the collector ownership: ```bash sudo install -d -o "$USER" -g "$USER" /var/lib/rrd sudo install -d -o "$USER" -g "$USER" /var/www/html/sensors ``` Both paths can be changed under `storage` in `config.yaml`: ```yaml storage: data_directory: /var/lib/rrd graph_directory: /var/www/html/sensors ``` Test one collection cycle: ```bash .venv/bin/python read_temperature.py --once ``` The script creates one RRD file per sensor in the configured data directory and three PNG files per sensor in the configured graph directory: - `*_day.png` covers the last 24 hours. - `*_month.png` covers the last 31 days. - `*_two_years.png` covers the last two years. Each graph contains a filled temperature area, a solid humidity line, and a dotted battery line. Initially, most of each graph is blank because no historic samples existed before the RRD was created. Temperature uses the left axis with a default range of 0–30°C. Humidity and battery use the right axis with a default range of 0–100%. These ranges and labels can be changed in `config.yaml` under `graph.axes`. Temperature grid labels default to five-degree intervals, and both axes display integers. Since RRDtool ties the right axis to the left, the corresponding percentage labels are rounded to integers. Light-grey reference lines are drawn at 15, 20, and 25°C by default and can be changed under `graph.axes.temperature.reference_lines`. The collector also generates `/var/www/html/sensors/index.html`. It displays six graphs at a time and has controls for switching between the daily, monthly, and two-year periods. It remembers the selected period and refreshes the PNGs at the interval configured under `html.refresh_seconds`. With a web server serving `/var/www/html`, open: ```text http://RASPBERRY_PI_IP/sensors/ ``` The page settings are configurable in `config.yaml`: ```yaml html: filename: index.html title: Zigbee sensor history default_period: day refresh_seconds: 600 ``` RRD structure settings are used only when each `.rrd` file is first created. Changing `rrd.step_seconds`, data sources, or archives does not rewrite an existing database; preserve or remove the old RRD deliberately before creating a replacement. Run the ten-minute collection loop in the foreground with: ```bash .venv/bin/python read_temperature.py ``` To run it automatically as a user service, copy the supplied service file. It assumes this repository is located at `$HOME/service_zigbee`: ```bash cp zigbee-sensors.service.example /etc/systemd/system systemctl daemon-reload systemctl enable --now zigbee-sensors.service ``` Inspect its status and live logs with: ```bash systemctl --user status zigbee-rrd.service journalctl --user -u zigbee-rrd.service -f ``` Graphs can be regenerated without collecting a new MQTT reading: ```bash .venv/bin/python read_temperature.py --graph-only ``` References: - [RRDtool database creation](https://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html) - [RRDtool graph elements](https://oss.oetiker.ch/rrdtool/doc/rrdgraph_graph.en.html)