PV Ertragsprognose mit KI - Datenerfassung / Übernahme

Sie befinden sich hier:
Smart Home Lösungen
»
PV Ertragsprognose mit KI
»
Datenerfassung / Übernahme

Datenerfassung / Übernahme

Die Erfassung der jeweiligen Parameter erfolgt in FHEM. Die eigentliche Herausforderung war es, die Parameter außerhalb des FHEM Eco-Systems zu speichern, um sie für die PV-Prognose in einer flachen Struktur verfügbar zu haben.

Lesen Sie hier die Details dazu.

Übernahme der Parameter in FHEM

Die Parameter, die für die PV-Ertragsprognose verwendet werden, sind entweder schon langjährig in FHEM als history vorhanden oder wurden über neue Devices gesammelt. Ziel ist es dabei, die Parameter letztendlich in einem stündlichen Intervall in einer separaten Tabelle (flache Struktur) zu speichern. Wie bereits erwähnt, hat mein Betrachtungstag 20 Stunden. Somit kommen je Tag 20 neue Datensätze hinzu, was über ein Jahr ca. 7000 neue Sätze ergibt.

Im Folgenden sind die wesentlichen Auszüge der FHEM-Devices gelistet.

PV-Ertrag

Der PV-Ertrag wird von den Wechselrichtern mit SBFSpot gelesen und per MQTT-Protokoll an FHEM in ein MQTT-Device übertragen. Hier beispielhaft der relevante Wert eines Wechselrichters.


Internals:
   DEF        SB5000
   IODev      mq_Server
   NAME       KG_ws_WR_2
   TYPE       MQTT2_DEVICE
   READINGS:
     2024-10-21 18:15:13   EToday          17.882
Attributes:
   readingList inverter:2100522126:.* { json2nameValue($EVENT, '', $JSONMAP) }

Da ich zwei Wechselrichter im Einsatz habe, werden die Werte der beiden Wechselrichter in einem Dummy-Device addiert. Innerhalb des Dummies wird der Ertrag stündlich betrachtet. Hier der wesentliche Ausschnitt:


Internals:
   NAME       du_KG_ws_WR_Ges
   TYPE       dummy
   READINGS:
     2024-10-21 20:02:00   EHour           0.000
     2024-10-21 20:02:00   EHourLast       25.285
     2024-10-21 20:02:00   EToday          25.285
Attributes:
   event-on-change-reading EToday
   userReadings
      EHour {round(ReadingsVal("du_KG_ws_WR_Ges","EToday",0) - ReadingsVal("du_KG_ws_WR_Ges","EHourLast",0),3)},
      EToday {ReadingsVal("KG_ws_WR_1","EToday",0) + ReadingsVal("KG_ws_WR_2","EToday",0)}

Helligkeit / Temperatur

Die Helligkeit und Temperatur liefert ein Multisensor von Eltako. Hier ist es wichtig, mit Mittelwerten und nicht mit Momentanwerten zu agieren. Wenn z.B. von 11:00 Uhr bis 11:55 Uhr die Sonne scheint und sich um 12:00 Uhr eine größere Wolke vor die Sonne schiebt, dann ist es wenig sinnvoll, den Wert von 12:00 Uhr 1:1 zu übernehmen. Das würde die gesamte Stunde verfälschen. Hier ist der Mittelwert über die Stunde sinnvoller. So können punktuelle Ausreißer vermieden werden. Der Mittelwert für jeweils eine Stunde wurde mit der FHEM Subroutine movingAverage berechnet.


Internals:
   NAME       EG_au_MS_1
   TYPE       EnOcean
   READINGS:
     2024-10-21 20:35:25   brightness      0
     2024-10-21 20:35:25   brightnessAV60m 0
	 2024-10-21 20:35:25   temperature     12.7
     2024-10-21 20:35:25   temperatureAV60m 13.6
Attributes:
   eep        A5-13-01
   subType    environmentApp
   userReadings
      temperatureAV60m {round(myUtils_movingAverageT("EG_au_MS_1", "temperature", 3600),1)},
      brightnessAV60m {round(myUtils_movingAverageT("EG_au_MS_1", "brightness", 3600),0)}

Sonnenwinkel

Die Sonnenwinkel (SunAlt, SunAz) werden mit dem FHEM AstroModul zu Beginn des Tages einmalig ermittelt und in ein Dummy-Device geschrieben. Angestoßen wir die Berechnung mit einem AT-Befehl. Es wird dabei der Sonnenstand für den aktuellen Tag (fc0_...) und den Folgetag (fc1_...) unter Berücksichtigung des Azimut der Anlage berechnet. Die Routine, die das Dummy füllt, finden Sie am Ende dieser Seite.


Internals:
   NAME       du_SunPos
   TYPE       dummy
   READINGS:
     2024-03-05 19:16:30   azimut          -19.5
     2024-10-21 04:02:00   fc0_10_SunAlt   14.5
     2024-10-21 04:02:00   fc0_10_SunAz    146
     2024-10-21 04:02:00   fc0_11_SunAlt   21.1
     2024-10-21 04:02:00   fc0_11_SunAz    159.6
	 :
	 :
     2024-10-21 04:02:00   fc0_Date        2024-10-21
     2024-10-21 04:02:01   fc1_10_SunAlt   14.2
     2024-10-21 04:02:01   fc1_10_SunAz    146.3
     2024-10-21 04:02:01   fc1_11_SunAlt   20.8
     2024-10-21 04:02:01   fc1_11_SunAz    159.8
	 :
	 :
     2024-10-21 04:02:01   fc1_Date        2024-10-22

Wetterdaten

Die Wetterdaten werden mit dem FHEM-Modul DWD_OpenData ebenfalls in ein FHEM-Device übernommen. Hier die wesentlichen Readings und Attribute:


   NAME       WetterFc
   STATE      forecast updated
   TYPE       DWD_OpenData
   READINGS:
     2024-10-21 12:00:07   fc0_10_Neff     75
     2024-10-21 12:00:07   fc0_10_RRad1    24.00
     2024-10-21 12:00:07   fc0_10_Rad1h    260.00
     2024-10-21 12:00:07   fc0_10_SunD1    480.00
     2024-10-21 12:00:07   fc0_10_TTT      12.1
	 :
	 :
	 2024-10-21 21:00:06   fc1_10_Neff     80
	 2024-10-21 21:00:06   fc1_10_RRad1    25.00
     2024-10-21 21:00:06   fc1_10_Rad1h    270.00
     2024-10-21 21:00:06   fc1_10_SunD1    480.00
     2024-10-21 21:00:06   fc1_10_TTT      13
	 :
	 :
Attributes:
   forecastDays 1
   forecastProperties Neff, Rad1h, RRad1, SunD1, TTT
   forecastPruning 1
   forecastResolution 1
   forecastStation 10763

Hinzu kommt noch das Attribute CloudCover vom FHEM-Modul OpenWeather:


   API        OpenWeatherMapAPI
   DEF        API=OpenWeatherMapAPI,cachemaxage:900,version:3.0 apikey=key interval=900
   MODEL      OpenWeatherMapAPI
   NAME       WetterFc2
   TYPE       Weather
   READINGS:
     2024-12-09 18:57:33   hfc1_CloudCover 100
     2024-12-09 18:57:33   hfc2_CloudCover 85
	 :
	 :
Attributes:
   forecast   hourly
   forecastLimit 44

Speichern der Daten außerhalb von FHEM

FHEM kennt nur die beiden Tabellen current und history.
Da über die Jahre erhebliche Datenmengen entstehen werden, erschien es als sinnvoll, die Daten in einer separaten Tabelle zu speichern. Somit sind die Daten von dem „Alltagsgeschäft“ in FHEM getrennt. Auf eine flache Struktur lässt sich in diesen Anwendungsfall auch leichter zugreifen.

Hier der Aufbau der separaten Tabelle, in die stündlich die Daten hineingeschrieben werden (Maria-DB).


CREATE TABLE IF NOT EXISTS `SolarEnergyFc` (
  `TimeStamp`		datetime NOT NULL,
  `Year`   			int NOT NULL,
  `Month`  			int NOT NULL,
  `Day`    			int NOT NULL,
  `Hour`   			int NOT NULL,
  `TTT`				float NULL DEFAULT 0 COMMENT "Temperature 2m above surface - source: DWD",
  `Temp`    		float NULL DEFAULT null COMMENT "Temperature - source: local sensor",
  `RRad1`			int NULL DEFAULT 0 COMMENT "Global irradiance within the last hour % (0..80) - source: DWD",
  `Rad1h`  			int NULL DEFAULT 0 COMMENT "Global irradiance kJ/m2 - source: DWD",
  `SunAlt`  		float NULL DEFAULT 0 COMMENT "Sun altitude - source: FHEM module Astro",
  `SunAz`  			float NULL DEFAULT 0 COMMENT "Sun azimuth - source: FHEM module Astro",
  `SunD1`  			int NULL DEFAULT 0 COMMENT "Sunshine duration during the last Hour s - source: DWD",
  `Neff`  			int NULL DEFAULT NULL COMMENT "Effective cloud cover % (0..100) - source: DWD",
  `CloudCover`		int NULL DEFAULT NULL COMMENT "Effective cloud cover % (0..100) - source: OpenWeather",
  `Brightness` 		int NULL DEFAULT 0 COMMENT "Brightness lux - source: local sensor",
  `BrightnessFc`	int NULL DEFAULT 0 COMMENT "Brightness forecast lux - source: prediction",
  `EnergyHour` 		float NULL DEFAULT 0 COMMENT "Total energy/hour kWh - source: local converter",
  `EnergyHourFc`	float NULL DEFAULT 0 COMMENT "Total energy/hour forecast kWh - source: prediction",
  PRIMARY KEY (Year,Month,Day,Hour),
  INDEX (Year,Month,Day,Hour)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

Um das FHEM Eco-System zu verlassen werden die Parameter in FHEM gesammelt und über eine stored Procedure in die separate Datenbanktabelle geschrieben. Die Schrittfolge kann wie folgt zusammengefasst werden:

  • Per Perl-Subroutine (aus einem eigenen FHEM-Modul heraus) werden die Paramater an ein Shell-Script übergeben.
  • Das Shell-Script enthält eine stored Procedure, die die Parameter entgegennimmt und in die Tabelle schreibt.
  • Die Aktion wird durch einen AT-Befehl aus FHEM heraus stündlich getriggert und per notify ausgeführt.

In Summe erfolgt dieser Aufruf gemeinsam für zwei Perl-Subroutinen:

  • Eine Routine übergibt die Istwerte der aktuellen Stunde (saveCurValuesSolarFc).
  • Die zweite Routine (saveNextValuesSolarFc) übergibt die Vorhersagewerte, d.h. beginnend mit der nächsten Stunde des aktuellen Tages bis zum Ende des nächsten Tages.

Hier die Perl-Subroutinen, um die Istwerte der aktuellen Stunde an das Shell-Script zu übergeben:


##############################################################################################################################
# save current values provided by DWD and locally in order to fill the AI model (runs from 04:02 to 23:02 hourly)
##############################################################################################################################
sub mySolarForecastUtils_saveCurValuesSolarFc(){

	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
	
	my $para = ReadingsVal("WetterFC","fc0_".$hour."_TTT",0).",";
	$para .= ReadingsVal("EG_au_MS_1","temperatureAV60m",0).",";
	$para .= ReadingsVal("WetterFc","fc0_".$hour."_RRad1",0).",";
	$para .= ReadingsVal("WetterFc","fc0_".$hour."_Rad1h",0).",";
	$para .= ReadingsVal("du_SunPos","fc0_".$hour."_SunAlt",0).",";
	$para .= ReadingsVal("du_SunPos","fc0_".$hour."_SunAz",0).",";
	$para .= ReadingsVal("WetterFc","fc0_".$hour."_SunD1",0).",";
	$para .= ReadingsVal("WetterFc","fc0_".$hour."_Neff",0).",";
	$para .= ReadingsVal("WetterFc2","hfc".mySolarForecastUtils_getHourIndex($hour)."_CloudCover",0).",";
	$para .= ReadingsVal("EG_au_MS_1","brightnessAV60m",0).",";
	$para .= ReadingsVal("du_KG_ws_WR_Ges","EHour",0);
		
	system("/opt/fhem/saveCurValuesSolarFc.sh $para >> /opt/fhem/log/saveCurValuesSolarFc.log");
	
	fhem("setreading du_KG_ws_WR_Ges EHourLast ".ReadingsVal("du_KG_ws_WR_Ges","EToday",0));
}

Das Script saveCurValuesSolarFc.sh enthält den Aufruf der stored Procedure:


#!/bin/bash
echo "-----------------------------------------------------------"
echo "Begin: "$(date +"%Y-%m-%d %H:%M:%S")
mysql -u <<user>> -<<passwort>> -D fhem --execute="call saveCurValuesSolarFc($@)"
echo "End: "$(date +"%Y-%m-%d %H:%M:%S")
echo "-----------------------------------------------------------"

Die Vorhersagedaten werden wie folgt zusammengestellt und übergeben:


##############################################################################################################################
# save forecast values provided by DWD in order to fill the AI model for today and tomorrow (runs from 04:02 to 23:02 hourly)
##############################################################################################################################
sub mySolarForecastUtils_saveNextValuesSolarFc(){

	my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
	$year = $year+1900;
	++$mon;
	
	my $para = "";

	my $curHour = mySolarForecastUtils_getHourIndex($hour)+1;
	
	for(my $i=4; $i<24; $i++) {		# today
	
		if ($i > $hour) {
			$para  = $year.",";
			$para .= $mon.",";
			$para .= $mday.",";
			$para .= $i.",";
			$para .= ReadingsVal("WetterFc","fc0_".$i."_TTT",0).",";
			$para .= ReadingsVal("WetterFc","fc0_".$i."_RRad1",0).",";
			$para .= ReadingsVal("WetterFc","fc0_".$i."_Rad1h",0).",";
			$para .= ReadingsVal("du_SunPos","fc0_".$i."_SunAlt",0).",";
			$para .= ReadingsVal("du_SunPos","fc0_".$i."_SunAz",0).",";
			$para .= ReadingsVal("WetterFc","fc0_".$i."_SunD1",0).",";
			$para .= ReadingsVal("WetterFc","fc0_".$i."_Neff",0).",";
			$para .= ReadingsVal("WetterFc2","hfc".$curHour."_CloudCover",0);

			system("/opt/fhem/saveNextValuesSolarFc.sh $para >> /opt/fhem/log/saveNextValuesSolarFc.log");
			$curHour++;
		}
	}
	
	($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time + 86400);
	$year = $year+1900;
	++$mon;
	
	$curHour = $curHour + 4;
	
	for(my $i=4; $i<24; $i++) {		# tomorrow
	
		$para  = $year.",";
		$para .= $mon.",";
		$para .= $mday.",";
		$para .= $i.",";
		$para .= ReadingsVal("WetterFc","fc1_".$i."_TTT",0).",";
		$para .= ReadingsVal("WetterFc","fc1_".$i."_RRad1",0).",";
		$para .= ReadingsVal("WetterFc","fc1_".$i."_Rad1h",0).",";
		$para .= ReadingsVal("du_SunPos","fc1_".$i."_SunAlt",0).",";
		$para .= ReadingsVal("du_SunPos","fc1_".$i."_SunAz",0).",";
		$para .= ReadingsVal("WetterFc","fc1_".$i."_SunD1",0).",";
		$para .= ReadingsVal("WetterFc","fc1_".$i."_Neff",0).",";
		$para .= ReadingsVal("WetterFc2","hfc".$curHour."_CloudCover",0);

		system("/opt/fhem/saveNextValuesSolarFc.sh $para >> /opt/fhem/log/saveNextValuesSolarFc.log");
		$curHour++;
	}	
}

Das Script saveNextValuesSolarFc.sh hat folgenden Inhalt:


#!/bin/bash
echo "-----------------------------------------------------------"
echo "Begin: "$(date +"%Y-%m-%d %H:%M:%S")
mysql -u <<user>> -<<passwort>> -D fhem --execute="call saveNextValuesSolarFc($@)"
echo "End: "$(date +"%Y-%m-%d %H:%M:%S")
echo "-----------------------------------------------------------"

Die Subroutine mySolarForecastUtils_getHourIndex($) ist notwendig, da die Readings bei DWD und OpenWeather unterschiedliche Sichtweisen haben. Bei DWD beschreibt z.B. ein Reading "fc1_11_SunD1" den Wert für 11 Uhr, bei OpenWeather würde die 11 bedeuten: in 11 Stunden, d.h. bei DWD sind die Angaben absolut und bei OpenWeather relativ. Diesen Unterschied gleicht die folgende Subroutine aus.


##############################################################################################################################
# get the index for the given hour
# para: hour
##############################################################################################################################
sub mySolarForecastUtils_getHourIndex($){

	my ($hour) = @_;
	
	for(my $i=1; $i<=44; $i++) {
		my @parts = split /[ :]/, ReadingsVal("WetterFc2","hfc".$i."_day_of_week","");
		
		if ($parts[1] == sprintf("%02d",$hour)) {
			return $i;
		}
	}
	return 0;
}

Die wesentlichen Sequenzen des AT-Befehl, Dummy und Notify womit der Schreibvorgang ausgelöst wird, sind hier zusammengefasst:


######################## AT-Befehl ##################################

   NAME       at_WetterFc
   DEF        +*01:00:00 set du_WetterFc  Ausführen
   COMMAND    set du_WetterFc  Ausführen
   NTM        13:02:00
   PERIODIC   yes
   RELATIVE   yes
   TYPE       at
   READINGS:
     2024-12-28 12:02:05   state           Next: 13:02:00
Attributes:
   DbLogExclude .*
   alignTime  00:02
   disabledForIntervals 23:30-24:00 00:00-03:30
  
######################## Dummy ######################################
   
   NAME       du_WetterFc
   STATE      Ausführen
   TYPE       dummy
   READINGS:
     2024-12-28 12:02:00   state           Ausführen
  
######################## Notify #####################################

   NAME       ny_WetterFc
   DEF        du_WetterFc:Ausführen.* {
		if ($hour == 4) {
			mySolarForecastUtils_calcSunPosition();
		}
		mySolarForecastUtils_saveCurValuesSolarFc();
		mySolarForecastUtils_saveNextValuesSolarFc();
	}


Scripte - Zusammenfassung

Um sich aus den einzelnen Ausschnitten ein besseres Gesamtbild ableiten zu können, sind hier die beiden wesentlichen Scripte nochmal am Stück:


Kontakt

Fragen oder Anregungen nehme ich gern über die Kontaktbox entgegen oder direkt per Email.

kontakt@kaempf-nk.de

Fragen / Anregungen?

Sicherheitsabfrage:
Hinweise zur Datenerfassung finden Sie unter: Datenschutz