PV-Graphen in der Gira X1 App darstellen

Wer eine Fotovoltaikanlage betreibt und über eine Smarthome-Installation verfügt, möchte sicherlich gerne den zeitlichen Verlauf aller relevanten PV-Daten grafisch  in der Smarthome-App oder einem Tablet darstellen. Wie das beispielsweise in der Gira X1 App aussehen kann, ist rechts dargestellt.

Wenn man auf der App nach unten scrollt, können auch noch die jeweilige DC-Leistung, die String-Spannungen, Netzfrequenz, Netzspannung und der Ladezustand des PV-Speichers in Echtzeit angezeigt werden. Die gewünschte Zeitspanne kann oben rechts interaktiv eingestellt werden.

So etwas zu realisieren, ist zwar keine Raketenwissenschaft, überfordert aber doch mehr als ein paar IT-Grundkenntnisse, daher hier eine kleine Anleitung.

Verwendete Komponenten

Fronius Symo GEN24 Wechselrichter

mit LAN-Anschluss (WLAN geht prinzipiell auch, ist aber instabiler) und Abfrage über die REST API. Durch Abfrage einer bestimmten http://.. URL werden alle benötigten Parameter zyklisch abgefragt. Die meisten modernen Wechselrichter dürften über eine solche Schnittstelle verfügen, bei Verwendung eines anderen Wechselrichters müssen lediglich die REST-Abfragen und die JSON-Parameter angepasst werden.

InfluxDB Datenbank

In dieser für Zeitreihen optimierten Open-Source-Datenbank werden die abgefragten Daten abgelegt. Wenn man auch über einen längeren Zeitraum  Auswertungen machen möchte, muss dafür genügend Speicherplatz vorgesehen werden, unter 100 GB würde ich gar nicht erst anfangen wollen.

Telegraf Kollektor

Telegraf ist ein Open Source Tool, das Daten von beliebigen Quellen abfragen und in einer InfluxDB Datenbank ablegen kann. In unserem Fall werden zyklisch Daten vom Fronius-Wechselrichter über die REST-API abgefragt. Nebenbei werden auch noch Smarthome-Daten wie Raum- und Außentemperaturen vom X1 abgefragt, aber es könnten genauso gut auch Wettervorhersagedaten oder Börsenkurse sein. Es gibt eine breite Auswahl an Plugins für Telegraf und eine sehr aktive Entwickler-Community.

Grafana

Mit dieser Software kann man sich auf einer grafischen Oberfläche Grafiken definieren, in denen jeweils bestimmte Parameter von der InfluxDB abgefragt und in ansprechender Weise dargestellt werden. Theoretisch kann das InfluxDB-Paket auch alleine Grafiken erzeugen, wenn man aber Wert auf eine ansprechende Gestaltung und lesbare Formatierung der Graphen Wert legt, stößt man schnell an Grenzen, in dieser Beziehung ist Grafana flexibler.

Linux-Server

Für eine lückenlose Aufzeichnung der Daten muss im Haus ein kleiner Linux-Server betrieben werden, auf dem InfluxDB, Telegraf und Grafana rund um die Uhr laufen. In meinem Fall ist das eine Debian 11 VM auf einem Low Power VMware-Server, aber ein kleiner Raspi oder ein NAS tut es prinzipiell auch.

Software-Installation

Die Installation der drei genannten Softwarepakete gibt es unendlich viele Anleitungen, und alle führen irgendwie zum Ziel. Da ich auf stabilen Betrieb und einen Upgrade-Pfad mit möglichst wenigen Überraschungen Wert lege, habe ich Debian 11 gewählt und installiere alle Softwarepakete aus ihrem Repository und nicht manuell, damit ich sie mit apt gemeinsam und einfach updaten kann. Die Datei /etc/apt/sources.list sieht wie folgt aus:

deb http://deb.debian.org/debian stable main contrib non-free
deb http://deb.debian.org/debian-security/ stable-security main contrib non-free
deb http://deb.debian.org/debian stable-updates main contrib non-free
deb https://download.webmin.com/download/repository sarge contrib
deb https://repos.influxdata.com/debian bullseye stable
deb https://packages.grafana.com/oss/deb stable main

Wer genau hinsieht, wird hier auf Webmin entdecken, das ist ein nicht-intrusives, Web-basiertes Verwaltungsprogramm, das nach meiner Meinung nach auf keinem Linux-Server fehlen sollte, aber für Commandline-Puristen natürlich nicht notwendig ist.

Wie die Zertifikate der Repos eingespielt werden, spare ich mir, das kann man googeln. Die Installation der drei Pakete erfolgt mit:

apt install influxdb2 telegraf grafana<
systemctl enable influxdb
systemctl start influxdb
systemctl enable telegraf
systemctl enable telegraf
systemctl enable grafana-server
systemctl start grafana-server

Nun kann man sich mit http://<ip-address>:8086 an InfluxDB und mit http://<ip-address>:3000 an Grafana anmelden, Zugangsdaten einrichten und die Funktionalität kontrollieren.

Konfiguration InfluxDB und Telegraf

In der InfluxDB wird jetzt ein Bucket mit dem Namen SmarthomeData und einem Zugriffstoken erzeugt. Mit einem Texteditor wird dann die Datei /etc/telegraf/telegraf.conf wie folgt editiert:

# Telegraf Configuration

[agent]
interval = "10s"
round_interval = true
metric_batch_size = 1000
metric_buffer_limit = 10000
collection_jitter = "0s"
flush_interval = "10s"
flush_jitter = "0s"
precision = ""
debug = true
quiet = true
logtarget = "file"
logfile = "/var/log/telegraf/telegraf.log"
logfile_rotation_interval = "24h"
logfile_rotation_max_archives = 90
hostname = ""
omit_hostname = false

[[outputs.influxdb_v2]]
urls = ["http://127.0.0.1:8086"]
token = "<Das-generierte-InfluxDB-Token>"
# Wichtig, nicht leer lassen:
organization = "<Name>"
bucket = "SmarthomeData"

[[inputs.http]]
name_override = "wechselrichter"
interval = "1m"
urls = [
"http://<Fronius-Symo-GEEN24-IP>/solar_api/v1/GetInverterRealtimeData.cgi?DataCollection=3PInverterData&Scope=Device&DeviceId=1",
"http://<Fronius-Symo-GEEN24-IP>/solar_api/v1/GetInverterRealtimeData.cgi?DataCollection=CommonInverterData&Scope=Device&DeviceId=1"
]
method = "GET"
data_format = "json"

Der Fronius Wechselrichter muss natürlich eine feste IP-Adresse im LAN haben und im Webinterface muss die API-Schnittstelle unter Kommunikation / Solar API eingeschaltet sein. Nach dem Neustart von Telegraf sollten die Echtzeitdaten des Wechselrichters in die InfluxDB Datenbank eingelesen werden. Bitte in der Log-Datei und in der InfluxDB kontrollieren, ob das funktioniert. Wenn ja, kann im nächsten Schritt Grafana in Betrieb genommen werden.

Konfiguration Grafana

In der Datei /etc/grafana/grafana.ini stellen wir die folgenden Parameter ein:

[server]
# Nicht https, weil eine gesicherte Übertragung zu Zertifikatsproblemen im X1 führen würde:
protocol = http
http_port = 3000
# LAN-IP-Adresse des Grafana-Servers:
domain = 192.168.x.x
root_url = %(protocol)s://%(domain)s:%(http_port)s/
serve_from_sub_path = true

[security]
allow_embedding = true

[auth.anonymous]
# Wichtig, denn der X1 kann keine Anmeldeinformationen speichern:
enabled = true
# Hier MUSS (!) ein Name stehen, sinnvollerweise der organization Name von InfluxDB/Telegraf:
org_name = <Name>

Damit diese Einstellungen wirksam werden, nicht vergessen, mit systemctl restart grafana-server den Dienst neu zu starten. 

In der Grafana GUI wird jetzt unter "Configuration / Datasources" eine InfluxDB Datenquelle hinzugefügt. Als URL des InfluxDB-Servers wird http://<ip-address>:8086 angegeben, Access = Server (default). Alle Authentifizierungsarten einschließlich Basic Auth werden abgeschaltet. Stattdessen wird in InfluxDB ein Token generiert und in Grafana unter "influxDB Details" eingegeben. Als Organisation wird ein beliebiger Name angegeben, der aber an allen Stellen gleich sein muss, und zuletzt der Default Bucket Name, in unserem Fall "SmarthomeData".

In Grafana wird jetzt ein Fotovoltaik-Dashboard erstellt. Innerhalb des Dashboards erstellen wir dann nacheinander die Panels mit den gewünschten Graphen. Welche Werte aus der InfluxDB Datenquelle jeweils ausgelesen werden sollen, wird mit der Flux Query Language definiert. Für das erste Panel "Erzeugte AC-Leistung" geben wir Folgendes ein:

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) =>
  r._field == "Body_Data_PAC_Value"
  )

 

Im nächsten Panel wollen wir die "DC-Leistung" beider Strings, überlagert mit zwei unterschiedlichen Farben im gleichen Graphen anzeigen. Dazu machen wir ein neues Panel mit den beiden Abfragen: 

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) => r._field == "Body_Data_UDC_Value" or r._field == "Body_Data_IDC_Value" )
 |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
 |> map(fn: (r) => ( { time: r._time, "String 1": r.Body_Data_UDC_Value * r.Body_Data_IDC_Value}))

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) => r._field == "Body_Data_UDC_2_Value" or r._field == "Body_Data_IDC_2_Value" )
 |> pivot(rowKey:["_time"], columnKey: ["_field"], valueColumn: "_value")
 |> map(fn: (r) => ( { time: r._time, "String 2": r.Body_Data_UDC_2_Value * r.Body_Data_IDC_2_Value}))

 

Panel-Abfrage "DC Strom" für beide Strings:

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) =>
  r._field == "Body_Data_IDC_Value"
  )

from(bucket: "SmarthomeData")
|> range(start: v.timeRangeStart, stop:v.timeRangeStop)
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
|> filter(fn: (r) =>
  r._field == "Body_Data_IDC_2_Value"
  )

 

Panel-Abfrage "DC Strom" für beide Strings:

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) =>
  r._field == "Body_Data_UDC_Value"
  )

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) =>
  r._field == "Body_Data_UDC_2_Value"
  )

 

Panel-Abfrage "Netzfrequenz". Um den interessanten Bereich um die 50 Hz vergrößert darzustellen, geben wir unter "Standard Options" Min=45 und Max=55 ein.

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) =>
   r._field == "Body_Data_FAC_Value"
  )

 

Panel-Abfrage "Netzspannung". Auch hier passen wir die Y-Achse mit Min=190 und Max=260 auf den interessantsten Wertebereich an.

from(bucket: "SmarthomeData")
 |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
 |> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: false)
 |> filter(fn: (r) =>
   r._field == "Body_Data_UAC_Value"
   )

 

Über die JSON API des Fronius Wechselrichters lassen sich übrigens auch die Echtzeitdaten des Speichers, des Smartmeters und eines Ohm Pilots auslesen. Die Feldbezeichnungen dafür können der API-Dokumentation entnommen werden.

In den Optionen der Panels müssen natürlich noch jeweils die Beschriftungen und der gewünschte Grafik-Stil angepasst werden. Wenn das Dashboard fertig ist, erzeugen wir mit dem Share-Symbol eine anonyme http-Link-Url ohne gelocktem Time-Range, die etwa so aussieht:

http://<ip-address>:3000/d/lzILJ9rnz/photovoltaik-dashboard?orgId=1

 

Gira GPA Einstellungen für die X1 App

Im Gira Prject Assistant (GPA) erzeugen wir jetzt eine Kachel vom Typ "URL-Aufruf" und fügen im Parameter-Feld die o.g. URL ein. Nach der Inbetriebnahme auf dem X1 sollten jetzt in der Smartphone-App die entsprechenden Grafiken dargestellt werden.

Wichtig ist, dass der Zugriff anonym, also ohne Anmeldung erfolgen kann, denn es ist nicht möglich, im X1 irgendwelche Anmeldeinformationen zu hinterlegen.

Wenn das Gira S1 Modul für den Fernzugriff verwendet wird, müssten die Wechselrichter-Graphen eigentlich auch von außerhalb über das Internet abrufbar sein, denn der X1 fungiert als HTTP-Proxy. Mit normalen Webseiten funktioniert das auch prima, jedoch nicht mit Grafana. Es erscheint dann die berüchtigte rote Fehlermeldung: "If you're seeiing this Grafana has failed to load its application files".

Ein möglicher Workaround wäre es, Grafana über die Firewall erreichbar zu machen und nur externe URLs zu verwenden, das wäre aber wegen des anonymen Zugriffs bedenklich. Wenn vom Smartphone ein VPN-Tunnel ins LAN aufgebaut wird, funktioniert die Grafik übrigens auch von unterwegs, weil die X1 App dann den Grafana-Server unter seiner LAN-Adresse direkt erreichen kann, aber das ist keine schöne Lösung.

Ob die Ursache dieses Problems an Grafana oder an der veralteten HTTP-Engine im X1 liegt, ist nicht ganz klar. Mit der X1 Firmware 2.7.585 scheint das im Moment jedenfalls noch nicht zu funktionieren. Falls jemand dafür eine Lösung gefunden hat, würde ich mich über einen Hinweis freuen.

Viel Spaß bei der Realisierung!

Martin Kasztantowicz