Wettervorhersage vom DWD: MOSMIX (flexibel Anpassbar) inkl. Globalstrahlung

Hallo zusammen, hier mal ein leicht erweiterbares Script, das die Daten vom DWD opendata Server auswertet, ohne Zwischenweg (quasi kleine Wetter API) .
Globalstrahlung / Bewölkung / Temperatur / Regen / Schnee - alles gut in stündlicher vorhersage enthalten!

<?php
/*
    Direkte Wettervorhersage vom DWD Evaluieren vom openData Server
    
    1. Station ID herausfinden über Stationsfinder: https://wettwarn.de/mosmix/mosmix.html
    2. Station ID unten eintragen.
    3. Starten -> Ergebnis wird in String Variable als JSON Array geschrieben.

    Funktion:
    - Script liest letzte Vorherhsage vom DWD Opendata-Server
    - Daten werden Extrahiert per Unzip
    - XML Namensräume werden entfernt
    - Daten werden in Array (JSON) transformiert und Timestamps / Stunden / Tage  optimiert.
    - Tagesvorhersage wird aus Stundenvorhersage generiert.

    Weitere Inhalte ergänzen:
    Weitere Daten könnne einfach dazu gelesen werden. Beschreibung hier:
        https://view.officeapps.live.com/op/view.aspx?src=https%3A%2F%2Fwww.dwd.de%2FDE%2Fleistungen%2Fopendata%2Fhelp%2Fschluessel_datenformate%2Fkml%2Fmosmix_elemente_xls.xlsx%3F__blob%3DpublicationFile%26v%3D7&wdOrigin=BROWSELINK
    Vorgehen:
    Unten im Script: getData("Rad1h", "radiation"); weitere Zeilen ergänzen um Parameter (siehe XLS vom DWD), parameter1 = DWD ID, parameter2 = name des wertes im Forecast (kann beliebig definiert werden)
    in der Funktion getData, können die Daten aus dem XML ggf. etwas aufbereitet werden.
    
    

  */
$FCAPI["STATION"]    = 10818; 
$FCAPI["CACHEFILE"]  = dirname(__FILE__) . "/cache/".$_IPS['SELF']."-".$FCAPI["STATION"].".cache";
$FCAPI["CACHEAGE"]   = 60 * 30;  // alle 30 Min
$FCAPI["XMLFILE"]   = dirname(__FILE__) . "/cache/".$_IPS['SELF']."-".$FCAPI["STATION"].".xml";
$FCAPI["URL"]        = "http://opendata.dwd.de/weather/local_forecasts/mos/MOSMIX_L/single_stations/" . $FCAPI["STATION"] . "/kml/MOSMIX_L_LATEST_" . $FCAPI["STATION"] . ".kmz";
date_default_timezone_set("Europe/Berlin");
//cache verzeichnis anlegen, wenn noch nicht da:
if(! is_dir(dirname(__FILE__) . "/cache")){
    mkdir(dirname(__FILE__) . "/cache");    
}


$fn = $FCAPI["URL"];
// Cache der Abfrage
$lastrun             = intval(@filemtime($FCAPI["CACHEFILE"]));
if (time() - $lastrun > $FCAPI["CACHEAGE"] || (isset($_GET["force"]))) {
    $response = file_get_contents($fn);
    file_put_contents($FCAPI["CACHEFILE"], $response); 
    $zip = new ZipArchive;
    $res = $zip->open($FCAPI["CACHEFILE"]);
    if ($res === TRUE) {
        $zc = $zip->statIndex(0);
        $zf = $zc["name"];
        $zip->extractTo(dirname(__FILE__) . "/cache/", $zf);
        $zip->close();
        copy(dirname(__FILE__) . "/cache/".$zf, $FCAPI["XMLFILE"]);
        unlink(dirname(__FILE__) . "/cache/".$zf);
    } else {
        echo 'Fehler, Code:' . $res;
    }
}
$xmlstr = file_get_contents($FCAPI["XMLFILE"]);

// Namespace aufräumen
$xmlstr = str_replace("<kml:", "<", $xmlstr);
$xmlstr = str_replace("</kml:", "</", $xmlstr);

$xmlstr = str_replace("<dwd:", "<", $xmlstr);
$xmlstr = str_replace("</dwd:", "</", $xmlstr);

$xmlstr = str_replace(" dwd:", " ", $xmlstr);

$xml = simplexml_load_string($xmlstr);
$ts = $xml->Document->ExtendedData->ProductDefinition->ForecastTimeSteps->TimeStep;

$fca["info"]["model"]=$xml->Document->ExtendedData->ProductDefinition->ProductID->__toString();
$fca["info"]["generation_time"]=$xml->Document->ExtendedData->ProductDefinition->IssueTime->__toString();


foreach ($ts as $t) {
  $fc = ["ts" => strtotime($t), 
         "tiso" => $t->__toString(), 
         "day"  =>  date("z", strtotime($t)) - date("z"),
         "hour"  => date("G", strtotime($t))];
  $fca["hourly"][] = $fc;
}

//################## DATEN AUS XML EXTRAHIEREN / FLEXIBEL ERWEITERBAR ###########################################
// Daten aus XML lesen (siehe oben verlinktes excel), 1 Parameter Wertekennung von DWD, 2. Parameter name im Json.
getData("Rad1h", "radiation");
getData("RRad1", "radiation_intensity");
getData("Neff", "clouds");
getData("RRL1c", "prec");
getData("RRS1c", "snow");
getData("SunD1", "sun");
getData("T5cm", "temp");
getData("ww", "wettercode");

// Tageswerte berechnen
$dayOld = 0;
$t_min = 999;
$t_max = -999;
$t_avg  = 0;
$cnt = 0;
$cloud_avg = 0;
$prec = 0;
$snow = 0;
$sun = 0;
foreach($fca["hourly"] as $fc){
    $day = date("z", $fc["ts"]) - date("z");
    
    if($day != $dayOld){
      $fca["daily"][$dayOld]["ts"] = $ts_old;
      $fca["daily"][$dayOld]["txtx"] = date("D d.m.Y", $fca["daily"][$dayOld]["ts"]);

      $fca["daily"][$dayOld]["temp_max"] = $t_max;
      $t_max = -999;

      $fca["daily"][$dayOld]["temp_min"] = $t_min;
      $t_min = 999;
      
      $fca["daily"][$dayOld]["temp_avg"] = round($t_avg / $cnt,1);
      $t_avg = 0;

      $fca["daily"][$dayOld]["cloud_avg"] = round($cloud_avg / $cnt);
      $cloud_avg = 0;

      $fca["daily"][$dayOld]["prec"] = $prec;
      $prec=0;

      $fca["daily"][$dayOld]["snow"] = $snow;
      $snow=0;

      $fca["daily"][$dayOld]["sun"] = round($sun / 60);
      $sun=0;

      $cnt = 0;      
    }
    
    $t_min = ($fc["temp"] < $t_min)? $fc["temp"] : $t_min;
    $t_max = ($fc["temp"] > $t_max)? $fc["temp"] : $t_max;
    $cnt++;
    $t_avg     += $fc["temp"];
    $cloud_avg += $fc["clouds"];
    $prec      += $fc["prec"];
    $snow      += $fc["snow"];
    $sun       += $fc["sun"];

    $dayOld = $day;
    $ts_old = mktime(0,0,0, date("m",$fc["ts"]), date("d",$fc["ts"]), date("Y",$fc["ts"]) );
    $tiso   = $fc["tiso"];
}
// Variable erzeugen und Daten als JSON String ausgeben.
$json = json_encode($fca);
$id= CreateVariableByName($_IPS['SELF'],"JSON Forecast",   3);
SetValue($id,$json);

print_r($fca);

function getData($idstr, $idtxt)
{
  global $xml;
  global $fca;
  $gs = $xml->Document->Placemark->ExtendedData;
  foreach ($gs->Forecast as $g) {
    $id = $g->attributes()["elementName"][0]->__toString();
    if ($id == $idstr) {
      $val = $g->value->__toString();
      if($idstr=="DD" || $idstr == "FF" || $idstr == "ww")$val = substr($val, 2); // DD ist um 3 Zeichen verschoben
      $splitChar = 11;
      $valA = str_split($val, $splitChar);
#      print_r($valA);
    }
  }
  foreach($fca["hourly"] as $k => $fc){
    $setval = trim($valA[$k]);
    // ############ Aufbereitung der Daten je nach Daten aus XML ################
    if($idstr == "DD") $setval = floatval($setval);
    if($idstr == "SunD1") $setval = $setval / 60;
    if($idstr == "RRad1") $setval = floatval($setval);
    if($idstr == "T5cm")  $setval = $setval - 273;

    $fca["hourly"][$k][$idtxt] = $setval;
    
  }
}

// Helper Funktion
function CreateVariableByName($id, $name, $type, $profile = "")
{
    # type: 0=boolean, 1 = integer, 2 = float, 3 = string;
    global $_IPS;
    $vid = @IPS_GetVariableIDByName($name, $id);
    if($vid === false)
    {
        $vid = IPS_CreateVariable($type);
        IPS_SetParent($vid, $id);
        IPS_SetName($vid, $name);
        IPS_SetInfo($vid, "this variable was created by script #".$_IPS['SELF']);
        if($profile !== "") { IPS_SetVariableCustomProfile($vid, $profile); }
    }
    return $vid;
}

In anderen Scripten kann man die Wetterdaten leicht verarbeiten - mit Getvalue den Wert aus der String Variable lesen:

$fc = json_decode(getValue(//VARIABLENID//), true);

print_r($fc);

3 „Gefällt mir“

Ich habe Probleme beim Extrahieren bestimmter Daten aus dem XML.
Super funktioniert etwa:
getData(„Neff“, „clouds“);
getData(„RRL1c“, „prec“);
getData(„RRS1c“, „snow“);
getData(„SunD1“, „sun“);
getData(„T5cm“, „temp“);

Probleme bereiten bei mir jedoch z. B. folgende Wetterparameter:
getData(„DD“, „winddirection“);
getData(„FF“, „windspeed“);
getData(„ww“, „wettercode“);
getData(„TTT“, „temp2m“);

Die json-Werte dieser Parameter stimmen nicht mehr mit dem XML überein! Als Beispiel sei hier getData(„DD“, „winddirection“); angeführt:
Beim ersten Timestep steht im JSON winddirection: „197.“, beim 2. Timestep * winddirection: „00 204.“, beim 3. Timestep * winddirection: „00 204.“,
Diese Werte machen keinen Sinn und es offensichtlich, dass hier etwas nicht passt.

Die korrekten Daten im XML lauten: 197.00 204.00 204.00 …

Hat hier jemand eine Idee?

Hat jemand Idee?

Hallo @wernert
ich habe den Fehler korrigiert, es liegt daran, dass die besagten Felder um 3 zeichen in der XML verschoben sind.

Das Script oben ist angepasst.
Viel Spass

Super, vielen Dank für die Korrektur. Jetzt passen die Werte (hourly)!

Bei den täglichen Werten (daily) steckt - so glaube ich - noch ein Fehler drin. Im JSON fängt die Ausgabe beispielsweise heute mit txtx: „Fri 28.10.2022“ an, aber eigentlich müsste es mit txtx: „Do 27.10.2022“ losgehen. Es ist also alles um einen Tag verschoben - auch die Werte passen so nicht zusammen.
Kannst du das bitte noch einmal anschauen?

Hallo Stele99,
zunächst vielen Dank für das tolle Skript, es pfunzt wunderbar.

Ich suche nach einer Möglichkeit, aus den Daten einen Wert für eine bestimmte Stunde auszulesen.
Hintergrund: z.B. für (m)eine Rollosteuerung wäre es neben dem Sonnenstand auch interessant, die aktuelle Bewölkung mit zu berücksichtigen.
Das Skript liefert ja stündliche Werte…
Gesucht ist z.B. der passende Wert Neff zu [day] => 0 [hour] => 08.
Leider breche ich mir beim Versuch, aus dem verschachtelten Array die passenden Daten auszulesen, mit meinen Anfänger-Kenntnissen in PHP die Ohren…
String-Funktionen, mit denen man die Liste durchsuchen könnte, kriege ich nicht ans Laufen, weil ich das Array auch mit „implode“ irgendwie nicht in einen durchsuchbaren Text konvertieren kann. Nach „implode“ erhalte ich nur einen String „ArrayArrayArray“.
Kann mir jemand einen Tipp geben -oder wäre das sogar eine Idee für eine Funktion?
Ich hoffe das ist nicht zu trivial, so dass ich mich schämen müsste… Bin ja lernwillig :wink:
Schon einmal vielen Dank für die Hilfe!

1 „Gefällt mir“

Danke @Stele99 für das Skript, was ich schon eine Weile für meinen kleinen täglichen Wetterbericht nutze!
Ich brauche aber eigentlich nur den aktuellen Tag. Mit dem JSON-Encoder werden da ja sehr viele Variablen angelegt.
Wenn ich das Skript durchgehe und auch wenn ich versuche, an manchen Stellen foreach z.B. für hourly zu deaktivieren, funktioniert das nicht wie gewünscht. Irgendwie ist daily und hourly miteinander verschränkt. Ich will das Skript auch nicht verpfuschen.
Wie müsste das Skript eingekürzt werden, dass daily>Index(1) entsteht? Auch hourly brauche ich komplett nicht.