Montag, 16. Januar 2023
Erweiterung Smart Home: Energie/Strom-Überwachung mit Tasmota Plugs Steckdosen und MQTT, Telegraf, InfluxDB, Grafana
Nachdem nun eine ganze Weile mein Sensor-Tracking der Temperatur und Feuchtigkeit gut funktioniert hat, wollte ich mein Setup auch um Stromüberwachung erweitern.
Das Rabbithole ging mal wieder tiefer als gedacht.
Initial habe ich nach Gosund SP111 Steckdosen gesucht, und dann aber irgendwie bei Amazon falsch abgebogen und "Porik SP11" gekauft. Ich ging in meiner jugendlichen Naivität davon aus, dass diese Steckdosen alle dieselben Chips haben und es eigentlich egal ist: Denn man möchte diese Geräte mit einer eigenen Firmware Tasmota flashen.
Die Geräte kamen an, ich wollte mein Glück erstmal mit der regulär mitgelieferten App nutzen. Die ist aber wirklich direkter Schrott. Man kann die Graphen kaum sinnvoll auswerten, es wird einem eigentlich nur eine tägliche Verbrauchssumme angezeigt, und das war natürlich nicht mein Ziel. Zusätzlich ist diese App wohl Whitelabel-mäßig für zigtausend (wirklich tausend) verschiedene Reseller erstellt, sieht immer gleich aus, und verbindet sich auch immer mit einer Zwangs-Cloud in China, die dann meine ganze Elektrik fernsteuern könnte. Nein, danke, so weit geht mein Vertrauen dann nicht.
Ich wollte gerne genau erkennen können, zu welchen Uhrzeiten welche Last von Geräten anliegt und was das in Summe ausmacht - und dann natürlich in kombinierten Graphen für mehrere Steckdosen.
Dann stellte sich heraus, dass man die Tasmota-Firmware mittlerweile nur noch mit Hardwareschrauberei lösen kann, mit separaten Kabelverbindungen und Flash-Hardware - und da hörte meine Motivation doch auf.
Glücklicherweise hat mindestens eine Firma den Bedarf an kaufbaren Tasmota-Geräten erkannt: Die Nous A1T Smarte Steckdose kommt pre-flashed.
Ursprünglich ging ich auch davon aus, dass diese Plugs alle mittels Zigbee kommunizieren. Das stellte sich ebenfalls als Fehlannahme heraus: Die Plugs bei Tasmota nutzen reguläres 2,4GHz WLAN. Meine Überlegungen, wie ich das in mein Netz aus Conbee -> zigbee2mqtt -> mosquitto -> Telegraf -> InfluxDB -> Grafana eingliedern sollte, fanden überraschend schnell ein positives Ergebnis: Die Tasmotas können nativ MQTT übermitteln. Das muss dann mein MQTT Broker (mosquitto) also empfangen, das wiederum liest Telegraf also "nur" noch aus, schreibt es in die InfluxDB-Buckets, und dort lese ich es via Grafana aus.
Nun zu den detailierten Schritten, die ich durchgeführt habe:
Einrichtung Nous A1T Steckdose
Ausgepackt, in die Stromdose gesteckt, Steckdosenleiste dahinter, Verbrauchtsgeräte angeschlossen.
Dann geht die Tasmota automatisch in einen WLAN-AccessPoint-Modus. Ich kann mit einem beliebigen Client (hier: mein iPhone) mich in dieses WLAN einloggen. Darin kann ich dann meinen tatsächlichen WLAN-AP konfigurieren, mich mit meinem Client aus dem Tasmota-WLAN ausloggen, und dann kriegt jede Steckdose eine eigene IP von meinem DHCP zugeteilt und ich kann auf ein simples Web-Interface zugreifen (via http://192.168.0.xx). Das zeigt auch direkt den aktuellen Energie-Wert und einen ON/OFF-Schalter.
Das ist total genial, diese custom Firmware ist ein Traum.
Das Web-Interface ermöglicht direkt die Konfiguration des MQTT-Brokers, ich musste dort einfach nur folgende Dinge eintragen:
- Host: 192.168.0.10 (IP meines Servers wo das ganze SmartHome-Setup liegt, bei den meisten ist dies ja ein RaspberryPi, bei mir ein richtiger Linux-Server)
- Port: 1883 (Standard MQTT Port)
- Client: TasmotaOffice (So habe ich mein Plug genannt)
- User: mqtt (Username von meinem mqtt-Broker mosquitto)
- Password: mqtt (Passwort von meinem mqtt-Broker mosquitto)
- Topic: tasmota_office (Das ist der konkrete Name später für die Auswertung der Daten, beliebig vergebbar)
- Full Topic: %prefix%/%topic%/ (Beim Standard belassen, für das Auslesen der MQTT-Daten relevant)
Nach dem Speichern habe ich die Konsole aufgerufen um die Intervalle des Reportings vom Standard (alle 10 Minuten oder so) auf minütlich herunterzusetzen, damit ich spätere Peaks in dem Graphen auch korrekt visualisieren kann. Das geht per Konsolenbefehl, der Wert ist in Sekunden:
TelePeriod 60
Weitere Nettigkeit: Mit folgendem Befehl habe ich aktivieren können, dass immer wenn durch ein Gerät mindestens 15 Watt Energieverbrauch zum letzten Messewrt dazukamen, dass der aktuelle Wert an den MQTT-Broker übermittelt wird:
Powerdelta 15
Zusätzlich habe ich auch noch ein Firmware-Update auf die neueste Version durchgeführt, was auch komfortabel per Klick über das Web-Frontend läuft.
Danach konnte ich direkt schon die aktuellen Stromverbrauchswerte meiner Endgeräte über das Web-Interface in Echtzeit sehen. Wow. Wie smooth!
Tasmota kalibrieren
Der erste, und auch eigentlich einzige, Schmerz entstand bei mir nun bei der Kalibrierung der Tasmotas.
Ich bin kein Elektriker, und obwohl ich ohne Elektrik nicht arbeiten könnte, verstehe ich die Prinzipien dahinter doch eher nur oberflächlich. Also habe ich mich nun erstmals damit auseinandergesetzt, was es jetzt mit Gleich/Wechselstrom, Spannung und Verbrauch auf sich hat.
Kurzfassung: Damit Tasmotas den tatsächlichen Energieverbrauch in Watt korrekt anzeigen können, muss man sie an die Gegebenheiten des eigenen Haushalts anpassen. Denn die Spannung an Steckdosen schwankt, ist nicht konstant, und beeinflusst aber die Stromverbrauchsmessung. Wenn 230V auf der deutschen Steckdose steht, dann darf das um +/- 10% abweichen - also Werte zwischen 207V und 253V sind theoretisch tolerabel. Praktisch schwankt es wohl meist zwischen 225V und 235V.
Wenn man nun penibel wie ich daran geht und auch gerne Standby-Geräte mit geringem Verbrauch irgendwie halbwegs realistisch auswerten möchte, dann muss man eine Kalibration durchführen. Alle anderen können in Tasmota einfach VoltageSet 230 in der Konsole eintragen und das Thema ignorieren.
Nun braucht man zur offiziell ganz gut beschriebenen Kalibrations-Dokumentation tatsächlich etwas mehr, als ich dachte: Nämlich entweder einen unabhängigen Stromverbrauchsmesser oder ein Multi-Meter zum Spannungsmessen.
Hunderprozentig habe ich nicht verstanden, warum ich einen externen Spannungsmesser brauche, wo die Tasmota doch auch selber die Spannung misst, aber ich habe die Kröte letztlich geschluckt und nochmal 20 Euro in ein Multi-Meter vom Baumarkt investiert. Mit etwas Frust, denn das Ding weist auch eine Messungenauigkeit von +/-1,5% +5 aus. Was das "+5" am Ende heißen soll ist mir nicht ganz klar, aber im schlimmsten Fall heißt es wohl, dass bei der Spannungsmessung ich um bis zu -8.5 bis +8.5 (also stolze 17V) falsch liegen könnte.
Anyhow. Ich habe die Kalibrierung dann wie folgt durchgeführt:
- 2er Mehrfachsteckdose an die Steckdose gesteckt, wo später mein Plug davor sitzt.
- In die eine Steckdose dann die Tasmota gesteckt, und daran eine Lampe mit einer 60W Glühbirne (KEINE LED!) angeschlossen und angemacht.
- Während die Glühbirne also leuchtet, dann das Webinterface der Tasmota im Browser geöffnet.
- Nun in die Konsole dort PowerSet 60.0 eingegeben.
- Danach mein Multimeter in die zweite Steckdose gesteckt, Multi-Meter auf Wechselstrom-Spannungsmessung mit 300V Maximalwert eingestellt und den Wert beobachtet. Bei mir kam hier 227V heraus.
- Nun in der Tasmota-Konsole eingetippt: VoltageSet 227
- Direkt im Anschluss ausgerechnet: (60W / 227V) * 1000 = 264.317 und eingetippt: CurrentSet 264.317.
Dasselbe habe ich mit meiner zweiten Tasmota-Steckdose an anderer Stelle auch durchgeführt (dort war die Spannung bei der Messung allerdings 231V).
MQTT-Daten zu InfluxDB transportieren via telegraf
Die MQTT-Daten postet die Tasmota bereits fleißig an meinem mosquitto MQTT-Broker. Dort muss sie mein Telegraf nun noch auslesen und als InfluxDB-Datenpunkt persistieren.
Dafür habe ich die telegraf.conf angepasst und folgenden Konfigurationsblock ergänzt:
[[inputs.mqtt_consumer]] name_override = "tasmota" servers = ["tcp://mosquitto:1883"] topics = [ "tele/tasmota_office/SENSOR", "tele/tasmota_living/SENSOR" ] qos = 0 connection_timeout = "30s" persistent_session = false client_id = "telegraf" username = "mqtt" password = "mqtt" data_format = "json_v2" [[inputs.mqtt_consumer.json_v2]] #timestamp_path = "Time" #timestamp_format = "2006-01-02T15:04:05" #timestamp_timezone = "Europe/Berlin" [[inputs.mqtt_consumer.json_v2.object]] path = "ENERGY"
Das ist nötig, da ich bereits einen existierenden MQTT-Consumer-Bereich habe, der meine Zigbee-Daten entgegennimmt. Man kann beliebig viele [[inputs.mqtt_consumer}} Blöcke im Telegraf hinterlegen, muss nur darauf achten, jeden Block individuell zu benennen, und die Topics anzupassen.
Zur konkreten Konfiguration:
- Servername (servers): In der Konfiguration ist der Servername mosquitto der Name des Docker-Containers.
- Topics: Die Topics sind entsprechend der Tasmota-Konfiguration bei mit tasmota_office und tasmota_living benannt. Grundsätzlich kann man zur Einsicht in die aktuellen MQTT-Werte Tools wie MQTT-Explorer benutzen. Da sieht man dann, wie die Hierarchie aktuell vorhanden ist, und auch welche Werte da gerade auflaufen. Reminder: MQTT zeigt immer nur den letzten Wert an! Und erst wenn während des "Lauschens" ein Telemetrie-Wert übermittelt wird, taucht dieser auf.
- [[inputs.mqtt_consumer.json_v2]]: Hier kann man für die JSON-Struktur Anpassungen wie z.B. bei der Timestamp-Formatierung machen. Letztlich habe ich diese Werte auskommentiert, Begründung siehe folgendes Kapitel zum Zeitversatz.
- [[inputs.mqtt_consumer.json_v2.object]]: Die Tasmotas liefern ihre Daten nicht in der ersten Ebene des JSON-Objekts, sondern verschachteln dies im Key ENERGY. Daher wird dies in Telegraf derart konfiguriert und dann als Hauptebene herausgegriffen, damit man später in InfluxDB auch Einzelwerte wie Power hat. Würde man dies nicht tun, würde der Key als ENERGY_Power auslesbar sein.
Daten visualisieren: Stichprobe
Nun ist es endlich so weit, die Daten landen via Telegraf in InfluxDB, also möchte ich sie gerne visualisieren.
Der erste Check findet im InfluxDB-Webinterface (Data Explorer) statt. Dort sehe ich alle konfigurierten Buckets (siehe Telegraf-Config). Der zentrale Bucket bei mir heißt homeassistant. Wenn ich also FROM: homeassistant auswähle, erscheint danach ein weiteres Fensterchen für Filter: _measurement. Dort kann ich dann tasmota auswählen (entspricht der telegraf-Konfiguration name_override!).
Dann folgt ein weiterer Filter bei mir nach _field, und dort sehe ich schon alles das, was die Tasmota überliefert: ApparentPower, Current, Factor, Period, Power, ReactivePower, Today, Total, TotalStartTime, Voltage, Yesterday. Konkret interessiert mich davon eigentlich nur Power (effektiver aktueller Verbrauch), Total (Verbrauch gesamt seit Messbeginn, daraus kann ich monatliche/jährliche Verbrauchssummen berechnen), Yesterday (Verbrauchssumme des Vortags).
Man könnte danach noch z.B. im Data-Explorer gezielt nach dem Host und Topic filtern, aber ich möchte in den Graphen ja alle Werte aggregieren, daher interessiert mich dies vorerst nicht.
Habe ich dann also einfach diese Kette aus bucket:homeassistant > _measurement:tasmota > _field:Total definiert, erhalte ich in InfluxDB bereits einen Graphen (den Zeitraum habe ich dabei mal auf "Past 7d" gesetzt, die Aggregation auf "last"):
Wenn man im Data Explorer wechselt auf den Script-Editor, sieht man die zusammengeklickte Abfrage dann als Flux wie folgt:
from(bucket: "homeassistant") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "tasmota") |> filter(fn: (r) => r["_field"] == "Total") |> aggregateWindow(every: v.windowPeriod, fn: last, createEmpty: false) |> yield(name: "last")
Das ist relevant, weil viele Tutorials in meiner Recherche immer noch auf InfluxQL basieren, was eher SQL nahekommt. Flux ist die deklarative Abfragesprache, die etwas an JavaScript erinnert. Buckets können nur mit Flux ausgelesen werden, daher kann ich im Legacy-Mode mit InfluxQL nämlich genau nichts anfangen. Aus meiner Sicht war dieser Bruch übrigens das größte Problem von influxdb, weshalb glaube ich viele Leute auf Prometheus gewechselt sind.
Daten visualisieren: Jetzt erst recht, mit Grafana
Da nun klar ist, dass die Daten auslesbar sind, darf nun Grafana ran. Dort muss man sich dann geeignete Dashboard für die Visualisierung zusammenklicken.
Leider habe ich nichts wirklich vordefiniertes gefunden. In den Grafana-Dashboard sieht man leider oft Beispiele mit Prometheus statt InfluxDB, oder mit InfluxQL statt mit Flux - beides konnte ich nicht ohne weiteres verwerten.
Die absolut simpelste Panel-Konfiguration einer Abfrage die mir einfiel war also schlicht der Verlaufsgraf der Verbrauchsanzeige, einmal mit beiden Steckdosen in einem Graphen, und einmal als nach Steckdose separiertem Graf inkl. großer Darstelung des aktuellen Verbrauchs:
Der Graph für den Verbrauchsverlauf nutzt folgende simple Query:
from(bucket: "homeassistant") |> range(start: v.timeRangeStart, stop: v.timeRangeStop) |> filter(fn: (r) => r["_measurement"] == "tasmota") |> filter(fn: (r) => r["_field"] == "Power") |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false) |> yield(name: "mean")
Konfiguriert ist dies für das aktuelle Zeitfenster, das man sich in Grafana aussucht. Das konkrete Panel JSON dafür:
{ "id": 9, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 12 }, "type": "timeseries", "title": "Strom", "fieldConfig": { "defaults": { "custom": { "drawStyle": "line", "lineInterpolation": "linear", "barAlignment": 0, "lineWidth": 1, "fillOpacity": 0, "gradientMode": "none", "spanNulls": false, "showPoints": "auto", "pointSize": 5, "stacking": { "mode": "none", "group": "A" }, "axisPlacement": "auto", "axisLabel": "", "scaleDistribution": { "type": "linear" }, "hideFrom": { "tooltip": false, "viz": false, "legend": false }, "thresholdsStyle": { "mode": "off" } }, "color": { "mode": "palette-classic" }, "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "mappings": [] }, "overrides": [ { "matcher": { "id": "byName", "options": "Power {host=\"192.168.0.42\", topic=\"tele/tasmota_living/SENSOR\"}" }, "properties": [ { "id": "color", "value": { "fixedColor": "dark-orange", "mode": "fixed" } } ] } ] }, "options": { "tooltip": { "mode": "single" }, "legend": { "displayMode": "list", "placement": "bottom", "calcs": [] } }, "targets": [ { "datasource": { "type": "influxdb", "uid": "RbO_24k7z" }, "query": "from(bucket: \"homeassistant\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r[\"_measurement\"] == \"tasmota\")\n |> filter(fn: (r) => r[\"_field\"] == \"Power\")\n |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)\n |> yield(name: \"mean\")\n ", "refId": "A" } ], "datasource": null }
Das ist noch relativ roh und unkonfiguriert. Das Panel darunter ist quasi genau dasselbe, nur nicht als type: timeseries sondern type: stat
Problem: Zeitversatz der Energie-Auswertung in Grafana
Soweit so schön. Relativ schnell stellte ich heraus, dass die Darstellugn von Grafana um eine Stunde zeitversetzt war.
Sprich, ich habe meinen Stromverbrauch immer mit einer Stunde Verzögerung gesehen - das was mir aktuell angezeigt wurde, war der Zustand vor einer Stunde.
Ich dachte erst, dass die Zeitzone meiner Tasmota falsch wäre, aber das Web-Interface zeigte mir dort alles als stimmig an. Letztlich stellte sich heraus, dass ich für Telegraf eine Zeitzonenkonfiguration übernommen hatte, die für UTC vorgesehen war. Ich nutze aber eine lokale Zeitzone. Daher habe ich diese Telegraf-Konfiguration deaktiviert, und seitdem werden die Zeitwerte auch wirklich korrekt übermittelt und dann auch ausgelesen.
Ausblick: Mehr Visualisierung!
Natürlich möchte ich meine Graphen in Zukunft noch etwas besser haben. Daher steht auf meiner Wunschliste:
- Gesamter Stromverbrauch des Vortags, sowohl einzeln pro Steckdose als auch als Summe (Einfache Zahlen).
- Gesamter Stromverbrauch einer gewählten Periode: Monat, Jahr (Einfache Zahlen).
- Gesamter Stromverbrauchsverlauf einer gewählten Periode als Graph.
- Gesamte Stromkosten für: Vortag, Periode.
- Tortendiagramm: Anteil der beiden Steckdosen für gewählte Periode (Wohnzimmer/Büro)
- Maximale Peak-Power-Usage des aktuellen Tags und des Vortags (Zahlen)
Es sieht ganz danach aus, dass ChatGPT mir hier bei der Formulierung meiner Flux-Queries helfen werden darf/kann/muss.
Am Ende wäre es aber ja wahrscheinlich nicht so schlimm, wenn die Messung dadurch etwas ungenauer wird.