In diesem Tutorial erfährst du, wie du in wenigen Schritten aus einem Raspberry Pi eine Smart Home Zentrale machst.
Die Smart Home Zentrale, die wir hier bauen, kann an sich noch nicht viel. Allein kann sie eigentlich gar nichts. Wir brauchen sie aber, um später die Daten, die wir mit z.B. eine Wetterstation sammeln, anzeigen können. Die Daten werden von unterschiedlichen Datenquellen gesammelt, in einer Datenbank gespeichert und dann in einer grafischen Oberfläche dargestellt. Der Weg Rückwärts wird auch funktionieren: Wir werden auch in der Lage sein, Befehle von unseren Smart Home Zentrale aus zu senden und daraufhin bestimmte Aktionen, wie z.B. das Schalten einer Lampe, auszuführen.
Wie bereits erwähnt, werden die Daten von verschiedenen Geräten gesammelt. Ein dafür häufig verwendetes Protokoll ist MQTT. Hier werden Botschaften (Messages) unter verschiedenen Themen (Topics) versendet (published). Andere Geräte können diese Themen abonnieren (subscribe). Sie erhalten dann alle Botschaften zu diesen Themen und ggf. auch Unterthemen.
Das muss natürlich irgendwer verwalten, und zwar der Broker. Wir werden als Broker Mosquitto verwenden. Er nimmt die Botschaften entgegen und verteilt sie an die Geräte, die sie empfangen wollen. Diese Geräte müssen aber nicht unbedingt echte Geräte sein, es können auch andere Programme sein.
Eines dieser Programme ist Telegraf. Telegraf kann unter anderem Botschaften abonnieren und sie weiterleiten. Er kann noch viel mehr, aber das Sammeln und Weiterleiten von Daten ist die Hauptaufgabe von Telegraf. In unserem Fall werden sie an InfluxDB weitergeleitet.
InfluxDB ist ein Datenbankserver, der auf Zeitreihen spezialisiert ist. Man speichert hier also Kundendaten oder Artikellisten, sondern bevorzugt Sensorwerte.
Die Daten aus InfluxDB stellen wir dann mit Grafana dar. Mit Grafana können wir einfach aussagekräftige Dashboards erstellen. Genau das, was wir brauchen, um Sensordaten darzustellen. Mit Grafana können wir allerdings keine Befehle senden. Hierfür nutzen wir ioBroker.
ioBroker kann ebenfalls MQTT-Botschaften abonnieren, aber auch senden. Hier könnten wir theoretisch auch Dashboards anzeigen, allerdings nicht so wie in Grafana. Viel wichtiger ist hier aber, dass wir auch Botschaften senden können und so Aktionen, wie z.B. Lampen ein- und ausschalten, auslösen können.
Das alles läuft aber nicht direkt auf einem Raspberry Pi, sondern in Docker. Mit Docker können die Programme einfach gekapselt werden und man hat einen besseren Überblick, welches Programm welche Daten speichert. Backups gestalten sich so einfacher.
Docker wiederum läuft dann auf einem Raspberry Pi. Eigentlich reicht ein Raspberry Pi 3, vielleicht auch ein 2er, aus. Ich habe hier aber einen 4er verfügbar und nutze diesen. Ich gehe davon aus, das Raspberry Pi OS bereits installiert ist.
Wir starten mit der Installation von Docker. Dazu führen wir das vom Hersteller bereitgestellte Installationsskript aus:
pi@raspberrypi:~ $ sudo curl -fsSL https://get.docker.com | sh
pi@raspberrypi:~ $ sudo docker version
Client: Docker Engine - Community
Version: 19.03.13
API version: 1.40
...
Wir wollen in Docker mehrere Container starten und deren Beziehungen zueinander beschreiben. Dazu bietet sich docker-compose an:
pi@raspberrypi:~ $ sudo apt-get install -y python3-pip
pi@raspberrypi:~ $ sudo pip3 install docker-compose
pi@raspberrypi:~ $ docker-compose version
docker-compose version 1.27.4, build unknown
docker-py version: 4.3.1
CPython version: 3.7.3
OpenSSL version: OpenSSL 1.1.1d 10 Sep 2019
Ich will alle Daten, die von den Containern gespeichert werden, in einem gemeinsamen Ordner speichern. Dazu erstelle ich mir einen Ordner:
pi@raspberrypi:~ $ mkdir docker
pi@raspberrypi:~ $ cd docker
In diesem Ordner erstelle ich mir die Datei docker-compose.yml, die das Zusammenspiel der Docker-Container beschreibt:
version: '3'
services:
mosquitto:
image: 'eclipse-mosquitto:latest'
influxdb:
image: 'influxdb:latest'
telegraf:
image: 'telegraf:latest'
grafana:
image: 'grafana/grafana:latest'
iobroker:
image: 'buanet/iobroker:latest'
Zunächst das Grundgerüst:
pi@raspberrypi:~ $ sudo apt-get install -y python3-pip
pi@raspberrypi:~ $ sudo pip3 install docker-compose
pi@raspberrypi:~ $ docker-compose version
docker-compose version 1.27.4, build unknown
docker-py version: 4.3.1
CPython version: 3.7.3
OpenSSL version: OpenSSL 1.1.1d 10 Sep 2019
Unser Raspberry Pi sieht jetzt ungefähr so aus:
Es gibt die einzelnen Container, aber noch keine Verbindungen und sie wissen auch noch nicht, wo sie ihre Daten speichern sollen. Dafür müssen wir unsere docker-compose.yml erweitern. Wir müssen die Ports der Container freigeben, die Speicherorte zuweisen, einige Umgebungsvariablen setzen und die Startreihenfolge festlegen. Die docker-compose.yml sieht dann so aus:
version: '3'
services:
mosquitto:
image: 'eclipse-mosquitto:latest'
ports:
- '1883:1883'
influxdb:
image: 'influxdb:latest'
ports:
- '8086:8086'
volumes:
- 'vol_influxdb:/var/lib/influxdb'
environment:
- INFLUXDB_DB=telegraf
- INFLUXDB_ADMIN_USER=admin
- INFLUXDB_ADMIN_PASSWORD=admin
telegraf:
image: 'telegraf:latest'
volumes:
- '/home/pi/docker/telegraf/telegraf.conf:/etc/telegraf/telegraf.conf'
environment:
- INFLUXDB_ADMIN_USER=admin
- INFLUXDB_ADMIN_PASSWORD=admin
depends_on:
- influxdb
grafana:
image: 'grafana/grafana:latest'
ports:
- '3000:3000'
volumes:
- '/home/pi/docker/grafana/grafana.ini:/etc/grafana/grafana.ini'
- 'vol_grafana_var:/var/lib/grafana'
- 'vol_grafana_etc:/etc/grafana/'
depends_on:
- influxdb
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=admin
iobroker:
image: 'buanet/iobroker:latest'
ports:
- '8081:8081'
- '8082:8082'
volumes:
- 'vol_iobroker:/opt/iobroker'
volumes:
vol_influxdb:
vol_telegraf:
vol_grafana_var:
vol_grafana_etc:
vol_iobroker:
Die Passwörter werden später natürlich noch geändert. Als nächstes erstelle ich die Ordner, in die die Container ihre Config-Daten speichern:
pi@raspberrypi:~/docker $ mkdir telegraf
pi@raspberrypi:~/docker $ mkdir grafana
Die einzelnen Container müssen jetzt noch konfiguriert werden.
Mosquitto passt soweit, hier müssen wir nichts ändern.
Telegraf braucht eine Konfigurationsdatei. Diese können wir uns von Telegraf selbst erstellen lassen. Später werden wir sie noch bearbeiten.
pi@raspberrypi:~/docker $ sudo docker run --rm telegraf telegraf config > ./telegraf/telegraf.conf
InfluxDB braucht erstmal keine Konfiguration. Grafana konfigurieren wir zum Teil über die Web-Oberfläche, wir brauchen aber auch eine Config-Datei. Diese kopieren wir uns aus dem Container:
pi@raspberrypi:~/docker $ sudo docker cp grafana:/etc/grafana/grafana.ini ./grafana/
Wenn wir jetzt alles richtig gemacht haben, können wir die Container das erste Mal starten. Docker lädt die Images für die Container zunächst herunter, das kann ein paar Minuten dauern (um einen Container zu starten, braucht man seinen Bauplan, das Image).
pi@raspberrypi:~/docker $ sudo docker-compose up -d
Nach ein paar Minuten sollten unsere Container laufen:
pi@raspberrypi:~/docker $ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2ab20b82d26 telegraf:latest "/entrypoint.sh tele…" 25 seconds ago Up 22 seconds 8092/udp, 8125/udp, 8094/tcp telegraf
d4858e24f3b5 grafana/grafana:latest "/run.sh" 25 seconds ago Up 22 seconds 0.0.0.0:3000->3000/tcp grafana
c48336bbe0c2 eclipse-mosquitto:latest "/docker-entrypoint.…" 28 seconds ago Up 25 seconds 0.0.0.0:1883->1883/tcp mosquitto
633efeb2321c influxdb:latest "/entrypoint.sh infl…" 28 seconds ago Up 25 seconds 0.0.0.0:8086->8086/tcp influxdb
4b2b855ebde0 buanet/iobroker:latest "/bin/bash -c /opt/s…" 28 seconds ago Up 25 seconds (healthy) 0.0.0.0:8081->8081/tcp iobroker
Jetzt sollten von außen auch die Oberflächen von Grafana und ioBroker erreichbar sein. Dazu einfach http://ip-des-RPi:3000/ für Grafana bzw. http://ip-des-RPi:8081/ für ioBroker in den Browser eingeben. Es sollten sich zwei Weboberflächen aufbauen.
Jetzt laufen erstmal alle Programme, aber es passiert noch nichts. Noch haben wir kein Grafana-Dashboard und selbst wenn wir eines hätten, hätten wir keine Daten zum darstellen.
Um einfach mal irgendwelche Daten darstellen zu können, habe ich mir überlegt, ein kurzes Python-Skript zu schreiben, mit dem wir eine Liste von Servern anpingen und uns die Laufzeiten zurückgeben lassen können. Diese Laufzeiten werden wir als MQTT-Botschaft an Mosquitto senden. Telegraf wird sich ebenfalls bei Mosquitto anmelden, diese Nachrichten abonnieren und sie dann an InfluxDB weiterleiten. Dort werden sie gespeichert und wir können sie mit Grafana darstellen. Der ioBroker wird hier noch keine Rolle spielen.
Also los geht’s!
Für das Python-Skript brauchen wir zunächst eine zusätzliche Bibliothek, die wir mit pip installieren. Danach erstellen wir die Datei ping.py:
pi@raspberrypi:~/docker $ pip3 install paho-mqtt
pi@raspberrypi:~/docker $ nano ping.py
Ich verzichte an dieser Stelle auf eine ausführliche Erklärung des Python-Codes. Die Kommentare sollten das nötigste erklären:
import subprocess
import re
import paho.mqtt.client as mqtt
import json
import time
client = mqtt.Client()
client.connect("localhost", 1883, 60)
data = {}
while True:
for host in ["fritz.box", "google.com"]:
try:
x = str(subprocess.Popen(
["ping", "-c", "1", host], stdout=subprocess.PIPE).communicate()[0])
m = re.search(
"\d* bytes from (.*?): icmp_seq=\d* ttl=\d* time=(\d*\.?\d*) ms", x)
data[host] = {
"addr": m.group(1),
"duration": float(m.group(2))
}
except:
pass
print(data)
client.publish(topic="test/ping", payload=json.dumps(data))
time.sleep(10)
Dieses Skript läuft in einer Endlosschleife und sendet Daten an Mosquitto. Starten können wir es dann mit:
pi@raspberrypi:~/docker $ python3 ping.py
Und stoppen einfach mit Strg+C.
Als nächstes bearbeiten wir Telegraf. Dazu öffnen wir die vorhin erzeugte Datei telegraf.conf. Damit das Python-Skript im Hintergrund weiterlaufen kann, öffnen wir ein neues Terminal.
pi@raspberrypi:~/docker $ nano telefgraf/telegraf.conf
Hier müssen wir einige Zeilen anpassen. Die stehen so nicht direkt hintereinander. Es sind noch einige Kommentare dazwischen, die ich hier aber weglasse:
[[inputs.mqtt_consumer]]
servers = ["tcp://mosquitto:1883"]
topics = [
"test/#"
]
data_format = "json"
[[outputs.influxdb]]
urls = ["http://influxdb:8086"]
database = "telegraf"
Jetzt einfach die Container und das Ping-Skript neu starten:
pi@raspberrypi:~/docker $ sudo docker-compose down
pi@raspberrypi:~/docker $ sudo docker-compose up -d
pi@raspberrypi:~/docker $ python ping.py &
Ab jetzt sollten die Ping-Daten in der Datenbank gespeichert werden.
Jetzt geht es weiter mit Grafana. Dazu einfach im Webbrowser eintippen: http://ip-des-RPi:3000/
Man gelangt zu einer Login-Seite und wird als erstes aufgefordert, sein Passwort zu ändern.
Wenn das gesehen ist, können wir die InfluxDB-Datenbank “telegraf”, in die unser Ping-Skript seine Messergebnisse speichert, als Datenquelle angeben. Dazu klicken wir links auf das Zahnrad (Configuration) und dann auf “Data Sources”.
Hier können wir eine neue Datenquelle erstellen. Wir klicken auf “Add Data Source” und dann auf “InfluxDB”. Hier tragen wir unter dem Punkt HTTP bei URL den Wert “http://influxdb:8086” ein (influxdb, weil wir docker nutzen) und unter dem Punkt InfluxDB Details bei Database den Wert “telegraf”. Ganz unten klicken wir dann auf “Save & Test”. Wenn alles funktioniert hat, erscheit die Meldung “Data Source is working”.
Super, jetzt sind wir in der Lage, die Daten aus der Datenbank abzurufen. Das tun wir im nächsten Schritt auch. Wir erstellen unser erstes Dashboard. Dazu klicken wir links auf das Plus (Create), dann auf “Dashboard”. Auf der neuen Seite dann auf “Add new panel”.
Hier machen wir dann die Einstellungen, die ich im Screenshot rot umrandet habe, ändern den rechts den Namen des Dashboards und klicken dann oben rechts auf “Apply”.
Damit haben wir unser erstes Dashboard erstellt.
Wenn ihr noch mehr zu den behandelten Themen Grafana, InfluxDB, Telegraf, usw. wissen wollt, schaut euch spezielle Tutorials dazu an.
Wir haben in diesem Tutorial die Grundlage dazu gelegt, Messdaten von Sensoren (oder Programmen) grafisch darzustellen. Im folgenden werden ich noch einige Tutorials zum Bau solcher Sensoren vorstellen.
Natürlich hätten wir die Daten aus unserem Ping-Skript auch direkt in die Datenbank speichern können, dann hätten wir aber Mosquitto und Telegraf nicht testen können. Weiterhin müsste man, wenn das Ping-Skript wirklich produktiv eingesetzt werden soll, noch einige Fehlerfälle abfangen. Was passiert, wenn ein Server nicht erreichbar ist? Was passiert, wenn Mosquitto nicht erreichbar? Das war aber nicht Thema des Tutorials. In meinem Github-Repository werde ich das Skript noch ein wenig ausbauen. Wer Interesse hat, kann es sich dort gern anschauen: https://github.com/eydam-prototyping/tutorials_de/tree/master/raspberry_pi/smart_home_server
Wir haben von allen Programmen, die ich hier gezeigt habe, wirklich nur die Oberfläche angekratzt. Es ist damit noch viel mehr möglich. Schaut euch dazu einfach weitere Tutorials an.
Eydam-Prototyping
Saccasner Straße 19
03096 Schmogrow-Fehrow
Germany