Back to Zigbee IoT Monitor

README

Zigbee Monitor

A full-stack web application for monitoring and controlling Zigbee devices via Zigbee2MQTT, with face recognition and sensor data charting.

Features

Monitor

  • Real-time event log from Zigbee devices with toast notifications
  • Device inventory with vendor/model information
  • Light controls (power, color, brightness) for RGB/LED devices
  • Per-device flash alerts — flash an LED strip when a specific device sends events
  • Per-device sound notifications — play configurable sounds (ting, chime, buzz, doorbell, blip) through the server's speakers on device events
  • Pairing mode control (permit join with countdown timer)
  • Device renaming (click any device name to edit)
  • Bridge status monitoring (online/offline, version, coordinator info)
  • System health dashboard — CPU, RAM, temperature, load average, Go runtime stats
  • WebSocket subscriptions for live updates

Charts

  • Time-series plotting of sensor data (temperature, humidity, illuminance, etc.)
  • Device and field selection with checkboxes
  • Time range selector: 1h, 6h, 24h, 7d, 30d
  • Battery level summary with color-coded bars
  • Auto-detection of numeric fields from device payloads

Face Recognition

  • Enroll faces via browser camera (HTTPS) or file upload (HTTP)
  • Face detection using OpenCV DNN with YuNet ONNX model
  • Face recognition using SFace ONNX model with cosine similarity matching
  • Profile management — view enrolled faces, delete profiles
  • Detection history with confidence scores and thumbnails
  • Works on ARM64 (KickPi K2B) — ~230ms per image for detection

Architecture

shell
┌─────────────┐     ┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Zigbee    │────>│ Zigbee2MQTT │────>│    MQTT     │────>│   Backend   │──── sound out
│   Devices   │     │  (Docker)   │     │   Broker    │     │  (Go/GQL)   │     (aplay)
└─────────────┘     └─────────────┘     └─────────────┘     └──────┬──────┘
                                                                   │
                                                            HTTP/WS + HTTPS/WSS
                                                                   │
                                                            ┌──────v──────┐
                                                            │  Frontend   │
                                                            │   (React)   │
                                                            └─────────────┘

┌─────────────┐
│   Python    │◄──── HTTP ────┐
│ Face Sidecar│               │
│  (OpenCV)   │         ┌─────┴─────┐
└─────────────┘         │  Backend  │
  port 8089             │   (Go)    │
                        └───────────┘

Tech Stack

ComponentTechnology
BackendGo, GraphQL (gqlgen), SQLite
FrontendReact 18, Vite, Apollo Client, Chart.js
Real-timeWebSocket (graphql-ws)
MessagingMQTT (Paho)
Face RecognitionPython, OpenCV DNN, YuNet + SFace ONNX models
AudioWAV generation + aplay (ALSA)
TLSSelf-signed ECDSA P256 certificates (auto-generated)

Prerequisites

  • Go 1.23+
  • Node.js 18+
  • Python 3.10+ (for face recognition)
  • Docker
  • MQTT broker (e.g., Mosquitto)
  • Zigbee2MQTT running and connected to your Zigbee adapter
  • aplay (ALSA utils) for server-side sound notifications

Quick Start

bash
./dev-all.sh

This checks prerequisites, sets up the Python virtual environment, builds the backend, starts Zigbee2MQTT (Docker), launches all services, and shows stats:

  • Bridge status and version
  • Device count
  • Service URLs (HTTP + HTTPS)
Press Ctrl+C to stop all services.

Manual Start

Python Face Service:

bash
cd backend/python
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python face_service.py

The face service starts on http://localhost:8089. ONNX models (~37MB) are downloaded automatically on first run.

Backend:

bash
cd backend
go build -o server ./cmd/server
./server

The backend starts on http://localhost:8080 (HTTP) and https://localhost:8443 (HTTPS with self-signed cert).

VariableDefaultDescription
PORT8080HTTP server port
TLS_PORT8443HTTPS server port
DB_PATH./zigbee.dbSQLite database path
MQTT_BROKERtcp://localhost:1883MQTT broker address
FACE_SERVICE_URLhttp://localhost:8089Python face sidecar URL
FRONTEND_DIR../frontend/distPath to built frontend assets
Frontend:

bash
cd frontend
npm install
npm run dev

The frontend starts on http://localhost:5173 and proxies /query and /api to the backend.

Production Install (KickPi / any Linux)

Clone and run the install script:

bash
git clone https://github.com/jonathanleahy/zigbee.git /home/kickpi/zigbee
cd /home/kickpi/zigbee
sudo bash install.sh

The install script:

  • Builds the Go backend
  • Creates a Python venv and installs OpenCV dependencies
  • Builds the frontend (if npm is available)
  • Creates two systemd services that start automatically on boot:
  • - zigbee-faces — Python face recognition sidecar (port 8089) - zigbee-monitor — Go backend serving HTTP (8080) + HTTPS (8443)
  • Enables and starts both services immediately
  • Managing services:

    bash
    sudo systemctl status zigbee-monitor     # Check status
    sudo systemctl status zigbee-faces
    sudo journalctl -u zigbee-monitor -f     # View logs
    sudo journalctl -u zigbee-faces -f
    sudo systemctl restart zigbee-monitor    # Restart
    sudo systemctl restart zigbee-faces

    Access via https://\<host\>:8443 for camera support (getUserMedia requires HTTPS).

    HTTPS / Camera Access

    The backend auto-generates a self-signed TLS certificate on startup, including all local IP addresses as SANs. This enables:

    • Browser camera access via getUserMedia (requires secure context)
    • WebSocket connections over wss://
    On first visit, accept the browser's self-signed certificate warning.

    Project Structure

    shell
    zigbee/
    ├── backend/
    │   ├── cmd/server/
    │   │   ├── main.go               # Entry point, HTTP/HTTPS servers, routing
    │   │   └── health.go             # System health endpoint (/health)
    │   ├── graph/
    │   │   ├── schema.graphqls       # GraphQL schema
    │   │   ├── schema.resolvers.go   # Resolver implementations
    │   │   └── resolver.go           # Dependency injection
    │   ├── internal/
    │   │   ├── db/sqlite.go          # SQLite database layer (events, devices, faces)
    │   │   ├── faces/
    │   │   │   ├── handlers.go       # REST API handlers for face operations
    │   │   │   └── python.go         # HTTP client for Python face sidecar
    │   │   ├── mqtt/client.go        # MQTT client + event broadcasting
    │   │   └── sound/sound.go        # WAV tone generation + playback
    │   └── python/
    │       ├── face_service.py       # Python face detection/recognition server
    │       └── requirements.txt      # Python dependencies
    ├── frontend/
    │   ├── src/
    │   │   ├── main.jsx              # React entry + Apollo setup (auto WS/WSS)
    │   │   ├── App.jsx               # Tab navigation + bridge status
    │   │   ├── App.css               # All styles (dark theme)
    │   │   ├── components/
    │   │   │   ├── DeviceList.jsx    # Device grid, light controls, flash/sound settings
    │   │   │   ├── EventLog.jsx      # Event table with filtering + pagination
    │   │   │   ├── Toast.jsx         # Toast notifications + LED flash triggers
    │   │   │   ├── Charts.jsx        # Sensor data time-series charts
    │   │   │   ├── FaceRecognition.jsx # Face enroll/identify with camera
    │   │   │   └── SystemHealth.jsx  # CPU, RAM, temperature dashboard
    │   │   └── graphql/queries.js    # GraphQL queries, mutations, subscriptions
    │   └── vite.config.js            # Vite + proxy config
    ├── zigbee2mqtt-data/             # Zigbee2MQTT config volume
    ├── install.sh                    # Production install (systemd services)
    └── dev-all.sh                    # Dev environment launcher

    API

    GraphQL (POST /query)

    Queries:

    • events(limit, offset, deviceName) — paginated event log with optional device filter
    • devices — list all paired devices
    • bridgeStatus — bridge connection status, version, coordinator
    • soundSettings — per-device sound notification settings
    Mutations:
    • permitJoin(enable, duration) — enable/disable pairing mode
    • renameDevice(ieeeAddress, newName) — rename a device
    • setDeviceState(deviceId, payload) — send commands to devices (power, color, brightness)
    • setSoundSetting(deviceName, sound) — set notification sound for a device
    Subscriptions:
    • eventAdded — real-time event stream via WebSocket

    REST — Face Recognition

    MethodEndpointDescription
    POST/api/faces/enrollEnroll a face (JSON: {name, image_b64})
    POST/api/faces/identifyIdentify faces in an image (JSON: {image_b64})
    GET/api/faces/profilesList enrolled profiles with thumbnails
    DELETE/api/faces/profiles/{id}Delete a profile
    GET/api/faces/profiles/{id}/thumbnailServe profile thumbnail JPEG
    GET/api/faces/detectionsList detection history
    GET/api/faces/detections/{id}/thumbnailServe detection thumbnail JPEG

    REST — System Health

    MethodEndpointDescription
    GET/healthSystem stats: CPU, RAM, temperature, uptime, Go runtime

    Database

    SQLite database is created automatically on first run with tables:

    • events — event log (topic, device_name, payload, timestamp)
    • devices — device inventory (ieee_address, friendly_name, vendor, model)
    • sound_settings — per-device notification sound preferences
    • face_profiles — enrolled faces (name, encoding blob, thumbnail blob)
    • face_detections — detection history (profile_name, confidence, thumbnail blob)

    Development

    Regenerate GraphQL Code

    After modifying schema.graphqls:

    bash
    cd backend
    go generate ./...

    Target Hardware

    Tested on KickPi K2B (Allwinner H618, ARM64, 4 cores, 4GB RAM) running Ubuntu. Face detection runs at ~230ms per image on this hardware.

    License

    MIT

    © 2026 Jonathan Leahy · v1.0.5-2-g88a26b5