Freitag, 2. Juli 2021
Temperatur-Sensor / Smart Home Setup: Zigbee2MQTT, Mosquitto, Telegraf, InfluxDB, Grafana, Home-Aasistant, ConBee
Vorwort
Ich bin kein Freund von Hitze. Im letzten Jahr habe ich einen Klima-Monoblock in unsere Mietwohnung gestellt, um wenigstens im Corona-Homeoffice etwas arbeiten zu können. Schlafen ist bei der Lautstärke leider nicht drin.
Dieses Jahr habe ich in den ersten heißen Tagen auch das Gerät aufgestellt und mich gefragt, wie lang die Kühlung überhaupt anhält. Um das auszuwerten hilft der gelegentliche Blick auf das Thermostat nur wenig, also hatte ich die simple Idee, mal auswertbare Sensoren in der Wohnung zu verteilen und Messreihen zu starten.
Es folgt das Ergebnis von ungefähr 6 Stunden Recherche, 2 Stunden Einrichtung, 4 Stunden Konfiguration, 4 Stunden Haare-raufen und 2 Stunden Dokumentation. Wie ich das überhaupt abends mit zwei Kindern in der Hand im Lauf von 2 Wochen hinbekommen habe, ist wohl das größte Wunder.
(Sorry für das veraltete, nicht-responsive Layout dieses Blogs. Es ist eigentlich ungewartet. Ich komme doch zu nix. Habe ich schon von meinen Kindern erzählt?)
Inhaltsverzeichnis
- Erste Recherche
- Zweite Recherche
- Das Setup
- Die Konfiguration
- Linux Ubuntu
- Docker
- Grafana Dashboards
- Home-Automation (hass.io)
- Philips Hue Outdoor Motion Sensor
- Docker-Compose Updates
- Ausblick
Erste Recherche
Mein erster Plan war: Irgendein Gerät holen, das seine Messwerte regelmäßig per Wi-Fi irgendwie auslesbar zugänglich macht, damit ich dann daraus Graphen erstellen könnte.
Die Recherche nach so einem Gerät war frustrierend; die paar wenigen Smart-Home Geräte mit so einer Funktion sind alle vercloudet und teuer (z.B. Netatmo Gerät mit nur 2 Sensoren für 170 Euro) oder bedürfen noch individueller (nicht günstiger) Hubs.
Es gibt Shelly H/T Sensoren (die auch relativ günstig sind), aber mit ihrem eigenen Wi-Fi-System daherkommen. Erste Reviews dazu sagten, dass die Messgenauigkeit so la/la wäre und die Batterie auch nicht wirklich lange halten würde.
Schnell habe ich auf Twitter zu meiner Frage die Empfehlung gehört, einen eigenen Sensoren zusammenzustöpseln (ESP8266 Board, BME280 Sensor). Das ist zwar fast unschlagbar günstig (~15 Euro), Nachteil aber: Man braucht sowohl etwas Löt/Stöpselerfahrung, und wenn man es stromlos (batteriegebunden) haben möchte, kommt noch mehr Frickelei dafür in Frage. Zudem braucht man trotzdem einen Hub (z.B. Raspberry Pi) um die Sensoren dann auszulesen und einen Server-Teil, um es auszulesen.
Bei mir ist ein Philips Hue Hub im Einsatz, von dem ich naiv dachte, damit ja Temperatursensoren einbinden und auslesen zu können, aber da der Hub nicht wirklich offen anbindbar ist und die Sensoren von Philips dann ein Outdoor-System (dazu später mehr) sein würden, bin ich von dieser Hub-Anbindung schnell weggekommen.
Was dann übrig bleibt: Xiaomi Mi Temperatur Sensor und ein offen ansprechbarer Zigbee Hub. Zigbee ist das energiesparende Funksystem, auf dem auch das Hue-System basiert.
Ich wurde dann auf Zigbee2mqtt gestoßen, ein Softwarepaket zum Auslesen von Zigbee-Hubs. Auf dessen Seite unterstützter Hubs findet man konkrete USB-Stick-Empfehlungen. Die beiden Hauptempfehlungen (Electrolama zig-a-zig-ah! und Slaesh's CC2652RB) waren für mich nicht beziehbar, auch die anderen Produkte waren eher merkwürdige Import-Dinger.
Also folgte ich der Empfehlung für den Phoscon ConBee II Stick (30 Euro). Der war zwar als experimental ausgewiesen, aber die Erfahrungsberichte im Netz waren positiv, solange man den Stick an USB2 hängt und ein USB-Verlängerungskabel für weniger Empfangsstörung benutzt.
^Zweite Recherche
Nachdem der Fokus nun auf Zigbee-Hub und Xiaomi-Sensor gelegt wurde, konnte ich etwas tiefer recherchieren, wie ich mir das ganze Setup vorstelle.
Ich wollte mehrere Sensoren einbinden, deren Messwerte historisch über längere Zeit auswertbar machen. Die Sensoren müssen variabel platzierbar sein, batteriebetrieben lange Zeit aushalten, stabil ausgewertet werden.
Fokus war vorerst keine weitere Heim-Automation oder Verbindung mit meinem Philips Hue Setup (was mit einem Dimmer-Button und der HomeKit-Integration unserer iPhones bereits gut funktioniert).
Ich stieß bei der Recherche auf den Home-Assistant (hass.io), der wohl relativ gut anbindbar ist, modularisiert und einfach auch via iPhone zu bedienen. Vorteil dieser Software ist, dass man damit Sensoren (und auch weitere HomeKit-, zigbee-Geräte und auch Daten vom iPhone) nicht nur auslesen sondern auch steuern könnte. Inklusive Notifications, Events und weiterem. Also durchaus Potential für die Zukunft, wenn mir das zigbee-Setup gut gefällt.
Ich plante also erstmal ein, Home-Assistant zu evaluieren und parallel zu benutzen. Schnell habe ich auch entdeckt, dass direkt der Home-Assistant die grafische Sensor-Auswertung ermöglicht, für die ich mir Grafana herausgepickt hatte - also ein weiterer Grund für die Nutzung der Software, um schnell zum Ziel zu kommen.
Nachdem ich mir alle Komponenten so zurecht gesucht hatte stieß ich viel zu spät auf Kris' Köhntopp Home Sensor Network-Artikel, und musste enorm schmunzeln, weil mein Zielsetup so ziemlich genau seinem entsprach. Gut, das sollte also meine Einrichtung deutlich erleichtern, da er (wie immer) mit viel Detail-Konfiguration und verständlich dokumentiert hat. Ich hoffe, dass mein deutschsprachiger Artikel seinen zumindest etwas ergänzen kann, empfehle die Lektüre der Vorlage nachdrücklich.
^Das Setup
Ich habe bei der Vielzahl der involvierten Komponenten recht schnell den Überblick verloren und mich gefragt "was ist denn jetzt ein Broker, wofür brauche ich genau den USB-Stick, was geht über Wi-Fi, warum brauche ich InfluxDB UND grafana, ist das nicht dasselbe...".
Ich habe mich für Kris' Lösung eines Docker-Setups entschieden. Ich besitze einen physikalischen Linux-Server als Fileserver mit einem Ubuntu OS, wo ich genügend Kapazität für mehrere Docker-Container habe. Grundsätzlich kann man auch Raspberry-Pi Kleingeräte für diese Container einsetzen, oder ein altes (Windows) Notebook oder ähnliches. Ich hatte wenig Berührung mit Docker, also war mein Interesse stark, da was zu lernen.
Nativ habe ich hass.io per Ubuntu-Paket-Manager installiert. Da ich diese Software potentiell gerne regelmäßig auf dem aktuellen Stand halten möchte, war mir das aufwändige Docker-Container-Warten für diese Komponente zu hoch. Man könnte dies sicher auch als Docker-Container mit konfigurieren. Anders herum könnte man die Docker-Container auch natürlich alle nativ auf bare metal laufen lassen, oder sogar einzelne Container auf unterschiedlichen Rechnern laufen lassen (z.B. nur zigbee-USB Stick mit der USB-Auslesesoftware zigbee2mqtt (siehe unten) an einem Rechner, und alle Container dann auf einem anderen. Da möge sich jeder selber arrangieren.
Zum Verständnis, folgende Komponenten sind involviert; Screenshots jeweils im zugehörigen Block:
Sensor (Hardware) -> Zigbee-Protokoll ("Hardware") -> Zigbee-Hub/Endpunkt (Hardware) -> zigbee2mqtt (Software, Datenauslesung) -> Mosquitto (Software, MQTT Broker, empfängt und sendet die ausgelesenen Sensordaten als API) -> Telegraf (Software, MQTT Client, kann von mosquitto Daten in gewünschte Systeme speichern oder weitere dorthin senden) -> InfluxDB (Software, telegraf persistiert über diese Datenbank seine Messwerte) -> Grafana (Software, stellt die Messwerte grafisch dar)
hass.io läuft eher parallel dazu. Es schreibt seine ausgewerteten Daten bei mir lediglich auch in InfluxDB, um dort weitere Messwerte der iOS-Anwendung zu ermöglichen. Die Sensoren liest es über die MQTT-Konfiguration direkt von Mosquitto mit aus. Zudem kann die Software auch direkt mit dem Philipes Hue Hub sprechen um meine Lampen zu steuern, oder mit UPNP/IGD-Devices (z.B. meine Fritzbox oder Statusbericht des Wi-Fi-Druckers) kommunizieren. Alles das schreibt es dann nettwerweise auch in meine InfluxDB.
Hier also meine Grundkomponenten, deren Konfiguration folgt im Block danach:
^Zigbee
Die Sensoren benutzen eine eigene Funk-Technik (im Wi-Fi-Frequenzband) mit einem eigenen Lightweight-Protokoll. Das nennt sich Zigbee. Sobald der Sensor eingeschaltet ist, muss er gepairt werden (ähnlich wie Wi-Fi oder Bluetooth-Geräte). Nach dem Pairing sendet der Sensor in einem gewissen Format seine Statusänderungen an das angebundene Funknetz. Jedes Zigbee-Gerät agiert dabei in einem Mesh, d.h. man kann mit Einsatz beliebiger Zigbee-Geräte seinen Empfangsradius erweitern. Die Xiaomi-Sensoren liefern nicht alle paar milisekunden ihre Sensorwerte, sondern nur bei gewissen Updates. Das hält den Energiebedarf gering.
^Zigbee Hub (ConBee 2)
Da man die Sensordaten natürlich nicht nur im Mesh herumsenden will, sondern auch empfangen und verarbeiten, bedarf es eines Hubs. Das ist ein Extra-Stück Hardware in unterschiedlicher Form. Beim Philips Hue System ist das eine schöne Box, die die (proprietären/modifizierten) Zigbee-Daten dann ins Wi-Fi "konvertiert" und sich dann an HomeKit&Co Endgeräte anschließen lässt. In meinem Setup übernimmt diese Aufgabe der USB-Stick ConBee 2, da man die Xiaomi-Sensoren nicht am Hue-Hub betreiben kann. Der nimmt also alle Funksignale auf und macht sie auf dem angeschlossenen Computer auslesbar.
^Server
Der USB-Stick lässt sich z.B. an einem Linux-Server (aber auch Windows, MacOS etc.) anschließen und stellt seine Daten im Falle von Linux per /dev/ttyACM0 zur Verfügung. Genaueres zum Device erzählt einem lsusb.
^Software
Jetzt geht es mit der eigentlichen Software los, die die Daten vom Zigbee-Stick abholt.
^zigbee2mqtt
Die Daten vom USB-Stick müssen ausgelesen und verarbeitet werden. Dies übernimmt zigbee2mqtt. Es hat einen "Treiber" für den ConBee 2 Stick und kann sich mit einem MQTT Broker verbinden, um die Nachrichten zu transportieren.
^mosquitto
Mosquitto ist der MQTT-Broker (MQ Telemetry Transport, das MQ steht wohl nicht, wie ich dachte, für MessageQueue sondern der Name eines IBM Produkts). Es ist quasi ein Daemon, der ein Publish/Subscribe-Protokoll zur Verfügung stellt, wo Clients Daten sowohl schreiben (publish) als auch lesen (subscribe) können. Daten lassen sich relativ pauschal in beliebige topics einordnen, wie z.B. Temperatur-Daten.
Mit Tools wie dem MQTT Explorer kann man sich sehr einfach mit dem Broker verbinden und schauen was da so über den Äther huscht und auslesbar ist. So erscheinen dort direkt auch die MQTT Daten eines jeden Sensors, und man sieht welche Clients sich wo wie hereinhängen.
^telegraf
Telegraf ist ein plugin-basiertes Telemetrie-Erfassungssystem, das (unter anderem) den MQTT-Stream regelmäßig abfragen kann, und ausgelesene Daten in ein passendes Zielformat wandeln kann, um es dort zu persistieren. Es stammt von demselben Anbieter wie InfluxDB, und ist unter anderem deshalb auch eine empfehlenswerte Ergänzung.
In Kris' Ursprungsartikel wird ein eigenes Script von ihm genutzt, um den (simplen) MQTT-Stream auszulesen und die gewünschten Daten im gewünschten Format weiterzuleiten. Der Vorteil des Telegraf-Setups ist, dass man damit noch viel mehr auslesen kann (Systemwerte wie CPU-Last, Prozesse) und auch pluginbasiert mit allem möglichen verheiraten kann, also sehr leicht erweiterbar ist. Und es lässt sich ideal simpel in das Docker-Setup integrieren.
^InfluxDB
Da MQTT eher ein temporäres System ist, brauchen wir einen Speicherort um Daten chronologisch dauerhaft abrufbar zu machen. Denn ich will ja gerade Messwerte der vergangenen Tage, Wochen, Monate, Jahre bereithalten können.
Solche Datenpunkte könnte ich jetzt wie gewohnt in eine MySQL-Datenbank kippen, und das würde für eine gewisse Zeit (oder eher, gewisse Datenmenge) auch gut gehen. Aber ich mache das Setup ja auch, um was zu lernen, und dazu zählt: Time-series bzw. chronologische Daten speichert man heutzutage in geeigneteren Systemen. Dazu zählte z.B. ganz früher RRD, was auch immer in Kombination mit hübschen Graphen kam und z.B. in meinem aktuell eingesetzten Munin gute Dienste machte.
Offenbar gab es dann irgendwann ein hippes, cooles neues System namens Prometheus, und heutzutage ein noch hipperes InfluxDB, was ich also dann mal evaluieren wollte.
InfluxDB kommt direkt mit einem browserbasierten Konzept daher, mit dem man Daten verwaltet und auch konfiguriert. Erst habe ich lange nach einer GUI oder Client dafür gesucht, um mir Daten anzuschauen - bis ich dann verstanden habe, dass es direkt mit zum Produkt gehört.
An dieser Stelle kommen wir zu meiner GRÖSSTEN ZEITRAUBENDEN ENTSCHEIDUNG des ganzen Setups. Weil ich ja schon bleeding edge sein wollte, dachte ich das auf InfluxDB1 basierende Setup von Kris nicht exakt nachzubauen, sondern direkt auf das aktuelle InfluxDB2 zu setzen.
Irgendwie eine doofe Idee (TM).
InfluxDB2 schmeißt seine SQL-angelehnte Sprachsyntax zugunsten einer JavaScript-ähnlichen Logiksprache über Bord. Alle Tutorials, die man so zu InfluxDB und Grafana (siehe später) liest, basieren fast immer auf InfluxQL - und InfluxDB2 bietet hiervon zwar noch fragmentarisch Dinge an, macht einem aber sehr deutlich, dass man davon bitte die Finger lässt. Der neue Weg ist also die Abfragesprache Influx, zu dem man zwar einiges an Doku findet, aber keine konkreten Beispiele. Vor allem bei der späteren Verbindung zu Grafana habe ich mir mit meiner V2 Entscheidung ziemlich in den Fuß geschossen und kann auch bis jetzt noch eigentlich triviale Dinge (mehrere Achsen in einem Graph) nur mäßig umsetzen. Hier steckt also noch die meiste Einarbeitung vor mir, wenn ich fortgeschrittenere Graphen erzeugen möchte.
^Grafana
Die letzte Komponenten des Setups ist Grafana, mit der ich die gespeicherten Daten dann visualisieren kann. Grafana bietet hochgradig visualisierbare Graphen mit beliebigen Datenquellen und -Abfragen an, die man sich mit einer GUI ganz gefällig zusammenschieben und klicken kann.
Das ist jedoch auch direkt einer der größten Nachteile, denn irgendwie Konfigurationsbasiert Dashboards zu erzeugen (und damit versionieren zu können) scheint mir nicht vorgesehen. Da muss ich aber womöglich noch etwas mehr Recherche hineinstecken.
^Home-Automation (hass.io)
Wie erwähnt läuft Home-Assistant eher als Parallelprojekt nebenher (auch unabhängig von Docker). Hier geht es eher weniger um das auslesen von Sensoren, sondern wirklich das Steuern von Geräten oder das Auslösen von gewissen Events. Man kann zwar auch hier Dashboards erstellen und Graphen anlegen, aber die historische Messwertanzeige mit beliebigen Zeitbereichen kommt mir hier zu kurz. Beispiel-Screenshots hierzu am Ende des Artikels.
Dafür kann man jedoch Push-Notifications erhalten, kann die iOS App Telemetriedaten an die Software senden, und das ganze dann auch wieder retour in eine (eigenständige) InfluxDB-Datenquelle schieben.
^Die Konfiguration
Bravo, ihr habt bis hierhin durchgehalten, und jetzt geht's ans eingemachte. Dafür seid ihr doch alle hier! Are you not entertained?
^Linux Ubuntu
Mein Ubuntu-Host-System hat aus dem Repository einfach:
sudo apt-get install docker
installiert, und bietet mir damit das Docker-Grundsetup zur Container-Virtualisierung an.
^Docker
Wie erwähnt laufen bis auf hass.io alle Komponenten in Docker-Containern. Diese kann man zum Glück komfortabel gebündelt "orchestrieren", dafür gibt es docker-compose. Dies nutzt eine eigene Konfigurationsdatei (docker-compose.yml), in der man alle einzelnen Container konfiguriert.
Hier meine vollständige Konfigurationsdatei (basierend auf Kris' Vorlage), auf die ich in den Unterbereichen dann Bezug nehme. Dazu habe ich auch bei mir ein Basis-Verzeichnis /export/iot/ angelegt. Jenes Verzeichnis enthält später alle gemounteten Docker-Container-Daten und Konfigurationsobjekte, und den Ordner sichere ich bei mir vollständig in Backups (aktuell grob 300MB). Logfiles darin werde ich noch rotieren lassen, wenn ich dazu komme es einzurichten.
Fast alle Services haben in dieser zentralen Konfigurationsdatei die volumes Konfiguration gemeinsam, in der definiert wird, wo ausserhalb des Docker-Containers die Daten dauerhaft gespeichert werden können - auch damit im Falle eines Container-Updates die Nutzdaten nicht plötzlich weg sind.
Ebenfalls definieren alle Services jeweils einen Container-Namen und die Imagequelle, wo meistens die latest Versionen zum Einsatz kommen. Weiterhin die Konfiguration der Ports, damit die Container im Netzwerk untereinander kommunizieren können.
Einige Dinge sind als Umgebungs-Variablen ausgelagert, die aus einer .env Datei im selben Verzeichnis stammen, bei mir:
.envDATA_DIR=/export/iot MQTT_PORT=1883 INFLUX_PORT=8086 GRAFANA_PORT=3000 ZIGBEE_DEVICE=/dev/ttyACM0 TZ=Europe/Berlin GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource DOCKER_HOST_ADDRESS=192.168.0.42docker-compose.yml
version: "3" services: mosquitto: image: "eclipse-mosquitto:latest" container_name: "mosquitto" hostname: "mosquitto" user: "1000" ports: - "${MQTT_PORT}:1883" volumes: - "./mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf" - "./mosquitto/users:/mosquitto/config/users" - "${DATA_DIR}/mosquitto/data:/mosquitto/data" - "${DATA_DIR}/mosquitto/log:/mosquitto/log" restart: "always" influxdb: image: "influxdb:latest" container_name: "influxdb" hostname: "influxdb" ports: - "${INFLUX_PORT}:8086" volumes: - "${DATA_DIR}/influxdb:/var/lib/influxdb" - "${DATA_DIR}/influxdb2:/var/lib/influxdb2" restart: "always" grafana: image: "grafana/grafana:latest" container_name: "grafana" hostname: "grafana" user: "1000" depends_on: - influxdb ports: - "${GRAFANA_PORT}:3000" volumes: - "${DATA_DIR}/grafana/data:/var/lib/grafana" - "${DATA_DIR}/grafana/log:/var/log/grafana" - "${DATA_DIR}/grafana/config:/etc/grafana" restart: "always" z2m: image: "koenkk/zigbee2mqtt" container_name: "z2m" hostname: "z2m" devices: - "${ZIGBEE_DEVICE}:${ZIGBEE_DEVICE}" privileged: true environment: - "TZ=${TZ}" volumes: - "${DATA_DIR}/z2m:/app/data" - "/run/udev:/run/udev:ro" depends_on: - "mosquitto" restart: "always" telegraf: image: telegraf container_name: telegraf restart: always extra_hosts: - "influxdb:${DOCKER_HOST_ADDRESS}" environment: HOST_PROC: /rootfs/proc HOST_SYS: /rootfs/sys HOST_ETC: /rootfs/etc security_opt: - apparmor:unconfined volumes: - ./telegraf/conf/telegraf.conf:/etc/telegraf/telegraf.conf:ro - /var/run/docker.sock:/var/run/docker.sock:ro - /sys:/rootfs/sys:ro - /proc:/rootfs/proc:ro - /etc:/rootfs/etc:ro
Hier nun die konkreten einzelnen Container. Bestenfalls legt man erstmal alle aufgeführten Dateien und Verzeichnisse an, damit das ganze docker-compose Setup lauffähig ist, und geht dann in die weitere Konfiguration (z.B. um Datenbanken, User und weiteres anzulegen). Die Dateien/Verzeichnisse habe ich mit meinem Haupt-User (nicht root) angelegt, dem auch die UID 1000 entspricht.
Beim Bearbeiten von Konfigurationsdateien immer beachten, dass innerhalb des Containers die lokal gemounteten Pfade erwartet werden, nicht die vom Host-System.
^mosquitto
/export/iot/mosquitto/log/Leeres Verzeichnis/export/iot/mosquitto/data/
Leeres Verzeichnis/export/iot/mosquitto/users
#Dummy-Passwort dummy:dummy, eigene erstellen (s.u.)! (Bei Copy&Paste: Kein Umbruch hier!) dummy:$7$101$e1QzevJ9J3TTl5fH$zTfozOcXFJsYDUsy2aDBR/Ov16KhFQP2zazi26s XHXyphAB39d9GwbESnwiyl+dcciRYjwF2zyXpEJuoFbQRJg==/export/iot/mosquitto/mosquitto.conf
listener 1883 persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log password_file /mosquitto/config/users
Mosquitto benötigt einen Usernamen und Passwort für den Broker-Zugang. Das Passwort wird mit einem Hashsystem erzeugt das man bestenfalls direkt im Docker-Container erzeugt. Um das zu tun, startet man also erstmal gezielt den Mosquitto-Container und erzeugt ein neues Passwort für den gewünschten Benutzernamen:
docker exec -it mosquitto sh > mosquitto_passwd -c /mosquitto/config/users (((BENUTZERNAME)))^
influxdb
/export/iot/influxdb2Leeres Verzeichnis
InfluxDB wird nach Ausführen des Containers komplett per Browser unter http://localhost:8086/ konfiguriert. Dort vergibt man auch den Admin-User und Passwort.
Ursprünglich war in Kris' Artikel das InfluxDB-Storage innerhalb des Dockers, aber ich wollte es gerne auf meinem Host-System haben, um meine Daten dort auch sichern zu können. Daher habe ich mir ${DATA_DIR}/influxdb2 zu /var/lib/influxdb2 gemountet. Man beachte den Unterschied von /influxdb2 (V2) zu /influxdb (V1)!
Sobald InfluxDB im Browser geöffnet ist, kann man auf den Tab Data wechseln, dort wird man erschlagen von potentiellen Datenquellen. Jede Datenquelle ist anklickbar und enthält Details, wie sie zu konfigurieren sind. Das hilft ganz gut, wenn man ein beliebiges Produkt mit InfluxDB verbinden möchte, bevor man überall eigenständige Dokumentationen heraussuchen muss.
Relevant sind hier die Unter-Tabs Buckets und Tokens.
Unter Buckets erstellt man etwas, das bei MySQL die Datenbanken wären. Ich habe mir einen Bucket hass-io und einen Bucket mqtt angelegt. Beim Anlegen eines Buckets muss man lediglich den Namen angeben und wann Daten gelöscht werden sollen (bei mir: nie). Schemas (Tabellen) werden automagisch beim Einspielen der ersten Daten durch den Client erzeugt.
Unter Tokens legt man dann Zugangsdaten an, damit externe Services wie Telegraf und Grafana und auch hass.io später auf die Datenbasis zugreifen können. In meinem Fall habe ich einfach einen pauschalen Full Access Token erzeugt, und den jedem System zugeordnert. Wer es aufgeräumter mag, erstellt für jedes System einen eigenen Token. Jene Tokens müssen entsprechend in den aufgeführten Konfigurationsdateien hinterlegt werden!
Der Tab Telegraf verleitet dazu zu glauben, darüber konfiguriere man Telegraf. Es ist aber in wirklichkeit eher nur ein Dokumentations-Panel, wo man Änderungen in seinen Telegraf-Container überspielen müsste.
^Grafana
/export/iot/grafana/data/(Leeres Verzeichnis)/export/iot/grafana/log/
(Leeres Verzeichnis)/export/iot/grafana/config/grafana.ini
[auth.anonymous] enabled = true # Organization name that should be used for unauthenticated users org_name = homeassistant # Role for unauthenticated users, other valid values are `Editor` and `Admin` org_role = Viewer
Damit mein Grafana auch im Netzwerk nach aussen hin verfügbar sein kann (um Graphen zu sehen), habe ich den Anonymous-Zugang aktiviert. In Grafana habe ich meine Organisationsnamen homeassistant vergeben.
Ähnlich wie InfluxDB konfiguriert man Grafana nach dem Start komplett via Browser unter http://localhost:3000 und vergibt Gruppe/Benutzername/Passwort.
Das wichtigste in Grafana ist nun das Anlegen der Datenquelle im Tab Configuration > Data sources. Nach dem Klick auf Add Data Source wählt man InfluxDB.
Dort vergibt man einen Namen (bei mir: MQTT), ich habe Default aktiviert. Als QueryLanguage dann (leider) Flux.
Für HTTP benutzt man die URL des InfluxDB Servers: http://influxdb:8086. Access auf Server (default). Bei Auth habe ich Basic auth und With Credentials aktiviert, bei den Basic Auth details dann Benutzername und Passwort meines InfluxDB-Users.
Darauf folgt bei InfluxDB Details der Name meiner InfluxDB-Organisation (homeassistant), der Token und Name des Default-Buckets (mqtt). Ich dachte zwar dass der Token alleine reichen müsste, aber die oben genannte Eingabe meines InfluxDB-Users war irgendwie auch notwendig.
Zuletzt habe ich die Max Series bei mir noch von 1000 auf 5000 angehoben.
Sobald die Datenquelle dann konfiguriert ist, kann man loslegen und seine Dashboards zusammenklicken (siehe Kapitel weiter unten).
^z2m
/export/iot/z2m/configuration.yamlhomeassistant: true permit_join: true mqtt: base_topic: zigbee2mqtt server: 'mqtt://mosquitto' user: dummy password: dummy serial: port: /dev/ttyACM0 devices: '0x00158d0005363333': friendly_name: Schlafzimmer/SENSOR '0x00158d000581757f': friendly_name: Arbeitszimmer/SENSOR '0x00158d00056e477a': friendly_name: Kueche/SENSOR '0x00158d00056e46d5': friendly_name: Wohnzimmer/SENSOR '0x00178801064997e2': friendly_name: 'Balkon/SENSOR'/export/iot/z2m/log/
(Leeres Verzeichnis)
zigbee2mqtt bietet eigenständige MQTT discovery an, die also verfügbare Sensoren auch direkt an Home-Assistant weiterreichen kann, daher ist das bei mir aktiviert.
Ansonsten wird der MQTT Broker mit Benutzername und Passwort konfiguriert, wie auch das auszulesende USB-Device.
Initial ist die Konfigurationsdatei bei devices: leer. Sobald z2m gestartet ist sollte man die Konfigurationsdatei bearbeiten und sicherstellen, dass permit_join: true gesetzt ist.
^Pairing!
Jetzt kommt der tollste Teil des ganzen, nämlich seine Sensoren mit zigbee zu pairen, damit der USB-Stick die Daten überhaupt empfängt und weiterreicht.
Man nimmt also die Sensor-Hardware von Xiaomi in die Hand und hält den unteren Schalter am Gerät für gut 6 Sekunden gedrückt, bis eine LED aufflasht. Dann drückt man gefühlt alle 1-2 sekunden immer noch einmal kurz auf den Knopf, um die LED aktiv zu halten. Kurz darauf sollte z2m die configuration.yaml dann angepasst haben und es sollte eine Sensor-ID und ein Friendly-Name Feld auftauchen. Mit anderen Sensoren läuft es dann genauso.
Erstmals klappte das bei mir nicht, weil ich etwas zu hastig war und ständig parallel auf zig Sensor-Buttons herumgedrückt habe, zumal die LED-Flashes ständig unterschiedlich sind. Hier als eher etwas Geduld haben, der 6-Sekunden-Druck am Anfang reicht aus. Und ein Sensor nach dem anderen.
Wenn alle Geräte gepairt sind, die Konfigurationsdatei bearbeiten und überall menschlich lesbare Namen hinterlegen sowie permit_join: false setzen.
^telegraf
/export/telegraf/conf/telegraf.conf[global_tags] [agent] interval = "60s" round_interval = true metric_batch_size = 1000 metric_buffer_limit = 10000 collection_jitter = "0s" flush_interval = "10s" flush_jitter = "0s" precision = "" hostname = "192.168.0.42" omit_hostname = false [[outputs.influxdb_v2]] urls = ["http://influxdb:8086"] token = "(((INFLUXDB_TOKEN_HIER_EINFÜGEN)))" organization = "homeassistant" bucket = "mqtt" [[inputs.ping]] interval = "5s" urls = ["192.168.0.42", "google.com", "amazon.com", "github.com"] count = 4 ping_interval = 1.0 timeout = 2.0 [[inputs.mqtt_consumer]] servers = ["tcp://mosquitto:1883"] topics = [ "zigbee2mqtt/Arbeitszimmer/SENSOR", "zigbee2mqtt/Schlafzimmer/SENSOR", "zigbee2mqtt/Wohnzimmer/SENSOR", "zigbee2mqtt/Kueche/SENSOR", "zigbee2mqtt/Balkon/SENSOR" ] qos = 0 connection_timeout = "30s" persistent_session = false client_id = "telegraf" username = "dummy" password = "dummy" data_format = "json" [[inputs.cpu]] percpu = true totalcpu = true collect_cpu_time = false report_active = false [[inputs.disk]] ignore_fs = ["tmpfs", "devtmpfs", "devfs", "iso9660", "overlay", "aufs", "squashfs"] [[inputs.diskio]] [[inputs.kernel]] [[inputs.mem]] [[inputs.swap]] [[inputs.system]]
In der telegraf-Konfiguration ist vor allem der Abschnitt zu mqtt_consumer relevant, weil er alle konfigurierten Sensoren enthält, die man in der InfluxDB persistieren möchte. Zudem habe ich noch ein paar andere Inputs konfiguriert, um in Zukunft mehr Teile meines Systems auszuwerten. inputs.processes musste ich bei mir übrigens deaktivieren, weil es irgendwie apparmor-syslog Fehler bei mir erzeugt hat.
^... es LEBT!
Nach dieser Konfigurationsorgie wird das Docker-Setup gestartet:
docker-compose up
Es erscheint eine Ausgabe vom Start aller Container (hoffentlich). Gestoppt wird das ganze mit docker-compose stop. Einzelne Instanzen kann man z.B. via docker-compose up -d z2m gezielt starten (oder mit stop stoppen).
Um einzelne Befehle in einer konkreten Docker-Instanz ausführen nutzt man docker exec -it (((NAME_DER_INSTANZ))) (((BEFEHL))), also z.B. docker exec -it influxdb bash.
Auch jenes docker-compose Setup kann man natürlich in seinen systemd-Workflow mit integrieren, wenn Docker beim System-Start via
sudo systemctl enable dockeraktiviert wird. ^
Grafana Dashboards
Nun sollte eigentlich alles schön lauffähig sein, und man muss sich bestenfalls nur noch in Grafana herumtreiben (oder in zigbee2mqtt, wenn man neue Sensoren pairen will). Hier das Dashboard:
Ich habe mir ein eigenes MQTT Panel angelegt, wo ich die Temperatur, Feuchtigkeits und Batteriedaten meiner Sensoren mittels Influx dargestellt habe. Das war schon relativ frickelig per GUI.
Jedes einzelne Dashboard, oder auch ein einzelnes Panel, kann man via Share Button bei Mouseover exportieren, um es woanders per HTML iframe einzubetten (interaktiv).
Woran ich am längsten gehangen habe, war Grafana dazu zu bekommen eigenständige Label der Messwert-Legende zu vergeben, und auch einzelne Grafen-Farben zu verändern. Die Lösung dazu war, im Options pane der Panelbearbeitung dann sogenannte Field Overrides zu konfigurieren. Hier kann man quasi scriptmäßig bestimmen, welche seiner Ausgabedatenreihen mit einem gewissen Namen vorliegen, dann einen Transformationstypen auswählen (z.b.: "Ändere dargestellten Namen" oder "Ändere Linienstärke"), und dann den Wert zu definieren.
Per GUI alles super super nervig, daher hier nur ein Beispielausschnitt:
Meine verwendeten Influx-Queries (jeweils eine pro Panel) sind folgende:
Temperaturfrom(bucket: "mqtt") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "mqtt_consumer") |> filter(fn: (r) => r["_field"] == "temperature") |> filter(fn: (r) => r["host"] == "192.168.0.42") |> filter(fn: (r) => r["topic"] =~ /SENSOR$/) |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> yield(name: "mean")Luftfeuchtigkeit
from(bucket: "homeassistant") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "mqtt_consumer") |> filter(fn: (r) => r["_field"] == "humidity") |> filter(fn: (r) => r["host"] == "192.168.0.42") |> filter(fn: (r) => r["topic"] =~ /SENSOR$/) |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> yield(name: "mean")Batterie
from(bucket: "homeassistant") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "mqtt_consumer") |> filter(fn: (r) => r["_field"] == "battery") |> filter(fn: (r) => r["host"] == "192.168.0.42") |> filter(fn: (r) => r["topic"] =~ /SENSOR$/) |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> yield(name: "mean")
Per Field Override habe ich aus einem angezeigten Namen wie battery {host="192.168.0.42", topic="zigbee2mqtt/Arbeitszimmer/SENSOR"} dann ein Arbeitszimmer gemacht.
Ganz cool ist, dass man im InfluxDB Interface die Influx-Queries direkt im Container mit einer Filter-GUI zusammenklicken kann, und die dann per Copy+Paste in Grafana überträgt. Dann sind dort alle Abfragefelder (measurement, field, host, topic) genau so enthalten. Und InfluxDB visualisiert ebenfalls direkt die Datenpunkte so, wie man sie in Grafana dann auch sehen kann.
Die weitere Bedienung von Grafana sollte halbwegs intuitiv sein: Man kann die Datenpunkte zoomen, per Mouseover einzelne Messwerte genauer anschauen, kann die Datentypen z.B. von Time Series auf Gauge (z.B. Batteriezustände) umstellen und sofort einsehen.
^Home-Automation (hass.io)
Da hass.io unabhängig von Docker läuft, musste ich das auch unabhängig konfigurieren. Dafür gibt es einen Offiziellen Guide, im Schnelldurchlauf:
sudo apt-get install -y python3 python3-dev python3-venv python3-pip \ libffi-dev libssl-dev libjpeg-dev zlib1g-dev autoconf build-essential \ libopenjp2-7 libtiff5 tzdata sudo useradd -rm homeassistant sudo mkdir /srv/homeassistant sudo chown homeassistant:homeassistant /srv/homeassistant sudo -u homeassistant -H -s cd /srv/homeassistant python3.8 -m venv . source bin/activate python3 -m pip install wheel pip3 install homeassistant hass
Danach sollte ein Python-Prozess laufen der einen Webserver unter http://localhost:8123 anbietet, und unter /home/homeassistant/.homeassistant seine Konfigurationsdatei verwaltet.
Man kann das ganze dann mit einem Ubuntu Systemd Service verbinden, um den Prozess beim Systemstart zu starten:
/etc/systemd/system/home-assistant.service[Unit] Description=Home Assistant After=network-online.target [Service] Type=simple User=%i WorkingDirectory=/home/%i/.homeassistant ExecStart=/srv/homeassistant/bin/hass -c "/home/%i/.homeassistant" [Install] WantedBy=multi-user.target
und danach:
sudo systemctl --system daemon-reload sudo systemctl enable home-assistant
Meine /home/homeassistant/.homeassistant/configuration.yaml sieht wie folgt aus, und konfiguriert auch direkt die Speicherung in einem InfluxDB2 Bucket. Eigenen InfluxDB Zugriffstoken und Organization ID an markierter Stelle eintragen; die Organization-Hex-ID findet ihr im InfluxDB-Kontrollzentrum in der URL-Zeile, das ist etwas tricky gewesen. Als Bucket-Name tragt ihr den Namen des angelegten Buckets ein, bei mir "hass-io".
default_config: tts: - platform: google_translate group: !include groups.yaml automation: !include automations.yaml script: !include scripts.yaml scene: !include scenes.yaml influxdb: api_version: 2 ssl: false verify_ssl: false host: 192.168.0.42 port: 8086 token: "(((EIGENER_INFLUXDB_TOKEN_HIER)))" organization: "(((EIGENE_ORGANIZATION_HEX_ID_HIER)))" bucket: "(((BUCKETNAME)))" tags: source: HA tags_attributes: - friendly_name default_measurement: units
Das Dashboard sieht dann z.B. nach Konfiguration (auch via GUI) so aus:
^Philips Hue Outdoor Motion Sensor
Nachdem ich meine Sensorwerte so schön auswerten konnte fehlte mir natürlich eine richtige Aussentemperatur.
Kris hat sich dazu auch einen Xiamoi-Sensor an einer geschützten Stelle an ein Fenster mit Plastikhülle drumherum zur Wetterfestigkeit montiert. So etwas ging bei mir leider nicht, da alle Stellen auf unserem Balkon zum einen von Sonnenlicht geplagt sind, und zum anderen auch nicht halbwegs wettergeschützt angebracht werden können.
Also führte mich die Recherche zum Philips Hue Outdoor Sensor (Vorsicht, kein Affiliate-Link...). Eigentlich ist das ein Motion-Sensor, wie unschwer am Namen zu erkennen - aber netterweise ist ein wettergeschützer Temperatur-Sensor enthalten.
Sensor bestellt, ausgepackt, auf den Balkon in die geschützteste schattige Ecke wie möglich gelegt, Pairing-Button gedrückt, zigbee2mqtt in permit_join-Modus versetzt, lesbaren Namen in z2m-Konfiguration hinterlegt, selben Namen in der Telegraf-Konfiguration hinterlegt, und schon sind die Daten automatisch im Grafana-Dashboard mit dringewesen. Supereasy, war ein Aufwand von 2-3 Minuten. Genauso habe ich jetzt meine insgesamt 4 Xiaomi-Sensoren im Haus konfiguriert.
^Docker-Compose aktualisieren
Es könnte vorkommen, dass man seine zugrundeliegenden Images aktualisieren möchte:
docker-compose stop docker-compose pull docker-compose up --force-recreate --build -d
Daumen drücken, dass man auch wirklich alle Daten brav persistiert hat und beim Image-Update nichts verliert.
Ausblick
Natürlich gibt es noch einige Dinge, die nicht so richtig rund laufen und fertiggestellt sind. Auf meiner Todo-Liste steht:
- Logfile-Rotation aller Service-Logfiles (im gemounteten Verzeichnis)
- Logfile-Rotation der Docker Container Logfiles (auf meinem Ubuntu Host in /var/lib/docker, da sammelt sich wohl viel an)
- Einrichtung von Grafana-Panels um die MQTT-Daten von iPhone-Sensor (z.B. Akku-Verlauf, Anzahl Schritte), Hue-Lampenstatus (z.b. Einschalt-Dauer), Fritzbox (Up/Download, Ping), CPU/Board-Temperatur/Speicherplatz (liefert Telegraf bereits) zu visualisieren
- Eigener Telegraf-Job um mal spaßeshalber Dinge via API in InfluxDB zu speichern, z.B.: Anzahl gelesener/ungelesener Mails)
Linkdump
Ein paar Links die mir geholfen haben:
- Zigbee2mqtt
- Isotopp: My home sensor network
- Running InfluxDB 2.0 and Telegraf using docker
- Guide for Zigbee2mqtt
- Connecting Grafana, InfluxDB and Home Assistant
Historie
Ich versuche, dieses Dokument bei weiteren Erkenntnissen/Konfigurationen auf dem Laufenden zu halten.
v1.1: Docker-compose updates (2021-12-08).
v1.0: Init (2021-07-02).