diff --git a/k8s/monitoring/README_Z2M.md b/k8s/monitoring/README_Z2M.md new file mode 100644 index 0000000..2fb92bd --- /dev/null +++ b/k8s/monitoring/README_Z2M.md @@ -0,0 +1,76 @@ +# Zigbee2MQTT & Shelly Weather Station Integration + +This guide describes how to deploy Zigbee2MQTT in the `monitoring` namespace and pair your **Shelly Weather Station (Ecowitt WS90)** using the **SLZB-MR1U Zigbee multiradio** (`192.168.10.10`). + +> [!NOTE] +> These resources have already been successfully applied and verified running in the cluster. + +--- + +## 1. Deploy Zigbee2MQTT + +The Kubernetes secret and workload manifests are located in `k8s/monitoring/`: + +```bash +# 1. Apply the encrypted secrets (MQTT credentials & Traefik basic auth credentials) +# Note: Basic auth credentials are split into 'zigbee2mqtt-auth-secret' to satisfy +# Traefik's constraint requiring exactly one key in the basic auth secret. +kubectl apply -f k8s/monitoring/zigbee2mqtt.secret.yaml + +# 2. Apply the main deployment, PVC, ConfigMap, Service, Middleware, and Ingress +kubectl apply -f k8s/monitoring/zigbee2mqtt.yaml +``` + +--- + +## 2. Verify the Deployment + +1. Check that the Persistent Volume is provisioned and the pod starts: + ```bash + kubectl get pods -n monitoring -l app=zigbee2mqtt + ``` + +2. Inspect the logs to ensure Zigbee2MQTT connects successfully to the SLZB-MR1U coordinator (`192.168.10.10:7638` — port `7638` is the TI CC2652P7 coordinator chip on the MR1U) and the Mosquitto MQTT broker (`mosquitto.datalab.svc.cluster.local`): + ```bash + kubectl logs -n monitoring deploy/zigbee2mqtt + ``` + + **Expected healthy output:** + ``` + info: z2m: Starting Zigbee2MQTT version 1.40.1 (commit #403d3c0) + info: z2m: Starting zigbee-herdsman (0.57.3) + info: zh:zstack:znp: Opening TCP socket with 192.168.10.10:7638 + info: zh:zstack:znp: Socket connected + info: z2m: zigbee-herdsman started (resumed) + info: z2m: Connecting to MQTT server at mqtt://mosquitto.datalab.svc.cluster.local:1883 + info: z2m: Connected to MQTT server + info: z2m: Started frontend on port 0.0.0.0:8080 + info: z2m: Zigbee2MQTT started! + ``` + +--- + +## 3. Access the Dashboard + +The dashboard is exposed on your custom domain with TLS and basic authentication: +* **URL:** [https://zigbee2mqtt.moritzgraf.de](https://zigbee2mqtt.moritzgraf.de) +* **Username:** `admin` +* **Password:** `moritz-z2m-2026` + +*Note: You can change the basic auth password by running the `k8s/htpasswd.py` script on your machine to generate a new bcrypt hash, then updating the `users` value in `k8s/monitoring/zigbee2mqtt.secret.yaml`.* + +--- + +## 4. Pair the Weather Station (WS90) + +1. Open the Zigbee2MQTT dashboard and click **Permit join (All)** at the top right. +2. Go to your physical Ecowitt WS90 weather station and **double-press the physical CAL button** on the bottom of the sensor array. +3. The LED on the weather station should flash, indicating it is in pairing mode (active for 3 minutes). +4. Within a few seconds, the weather station will appear in the Zigbee2MQTT dashboard. You can rename it there (e.g. to `weather_station`). + +--- + +## 5. Verify Metrics flow + +* **Home Assistant:** The weather station will be automatically discovered via the MQTT integration. You can verify it under **Settings > Devices & Services > MQTT**. +* **Prometheus:** Home Assistant will automatically expose the new weather station entities. You can query them in Prometheus or Grafana (e.g. `homeassistant_sensor_temperature_celsius` or similar entity-based metrics). diff --git a/k8s/monitoring/zigbee2mqtt.secret.yaml b/k8s/monitoring/zigbee2mqtt.secret.yaml new file mode 100644 index 0000000..9545396 Binary files /dev/null and b/k8s/monitoring/zigbee2mqtt.secret.yaml differ diff --git a/k8s/monitoring/zigbee2mqtt.yaml b/k8s/monitoring/zigbee2mqtt.yaml new file mode 100644 index 0000000..59ac3df --- /dev/null +++ b/k8s/monitoring/zigbee2mqtt.yaml @@ -0,0 +1,163 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: zigbee2mqtt-pvc + namespace: monitoring +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: zigbee2mqtt-config-template + namespace: monitoring +data: + configuration.yaml: | + homeassistant: true + permit_join: false + mqtt: + base_topic: zigbee2mqtt + server: mqtt://mosquitto.datalab.svc.cluster.local:1883 + user: sender + serial: + port: tcp://192.168.10.10:7638 + frontend: + port: 8080 + host: 0.0.0.0 + advanced: + network_key: GENERATE +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: zigbee2mqtt + namespace: monitoring + labels: + app: zigbee2mqtt +spec: + replicas: 1 + selector: + matchLabels: + app: zigbee2mqtt + template: + metadata: + labels: + app: zigbee2mqtt + spec: + securityContext: + fsGroup: 1000 + runAsUser: 1000 + initContainers: + - name: init-config + image: busybox:latest + command: + - sh + - -c + - | + if [ ! -f /app/data/configuration.yaml ]; then + echo "Initializing configuration.yaml..." + cp /tmp/config/configuration.yaml /app/data/configuration.yaml + chmod 600 /app/data/configuration.yaml + else + echo "configuration.yaml already exists. Skipping initialization." + fi + securityContext: + runAsUser: 1000 + volumeMounts: + - name: data + mountPath: /app/data + - name: config-template + mountPath: /tmp/config + containers: + - name: zigbee2mqtt + image: koenkk/zigbee2mqtt:1.40.1 + env: + # Override MQTT credentials/settings using environment variables + - name: ZIGBEE2MQTT_CONFIG_MQTT_SERVER + value: "mqtt://mosquitto.datalab.svc.cluster.local:1883" + - name: ZIGBEE2MQTT_CONFIG_MQTT_USER + value: "sender" + - name: ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD + valueFrom: + secretKeyRef: + name: zigbee2mqtt-secret + key: mqtt-password + # Override Zigbee Coordinator settings using environment variables + - name: ZIGBEE2MQTT_CONFIG_SERIAL_PORT + value: "tcp://192.168.10.10:7638" + ports: + - name: http-frontend + containerPort: 8080 + resources: + requests: + cpu: 10m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi + volumeMounts: + - name: data + mountPath: /app/data + volumes: + - name: data + persistentVolumeClaim: + claimName: zigbee2mqtt-pvc + - name: config-template + configMap: + name: zigbee2mqtt-config-template +--- +apiVersion: v1 +kind: Service +metadata: + name: zigbee2mqtt + namespace: monitoring + labels: + app: zigbee2mqtt +spec: + ports: + - name: http-frontend + port: 8080 + targetPort: 8080 + protocol: TCP + selector: + app: zigbee2mqtt +--- +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: zigbee2mqtt-auth + namespace: monitoring +spec: + basicAuth: + secret: zigbee2mqtt-auth-secret + removeHeader: true +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: zigbee2mqtt + namespace: monitoring + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + traefik.ingress.kubernetes.io/router.middlewares: monitoring-zigbee2mqtt-auth@kubernetescrd +spec: + ingressClassName: traefik + rules: + - host: zigbee2mqtt.moritzgraf.de + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: zigbee2mqtt + port: + number: 8080 + tls: + - hosts: + - "zigbee2mqtt.moritzgraf.de" + secretName: zigbee2mqtt-tls