DWD OpenData: Unterschied zwischen den Versionen

Aus FHEMWiki
K (<pre> auf <syntaxhighlight> umgestellt)
 
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 75: Zeile 75:
=== Attributes ===
=== Attributes ===
Siehe {{Link2CmdRef|Anker=DWD_OpenDataattr|Label=Commandref}}.
Siehe {{Link2CmdRef|Anker=DWD_OpenDataattr|Label=Commandref}}.
=== Readings ===
Die Prognosewerte sind wie folgt aufgebaut:
fc<day>_[<sample>_]<property>
Eine Beschreibung der über 70 verfügbaren Eigenschaften und ihrer Maßeinheiten finden Sie hier. Die Maßeinheiten für Temperaturen und Windgeschwindigkeiten werden jeweils in °C und km/h umgerechnet. In den folgenden Abschnitten sind nur einige ausgewählte Eigenschaften aufgeführt:
* day – relativer Tag (0 .. 9) basierend auf dem Zeitzonenattribut, wobei 0 für heute steht
* sample – relative Zeit (0 .. 3, 7 oder 23), die einem Vielfachen von 6, 3 oder 1 Stunde UTC entspricht, abhängig vom Attribut forecastResolution
* day properties (normalerweise für 06:00 Uhr Stationszeit, siehe Rohdaten der Station für tatsächliche Zeitbeziehung)
    date – Datum basierend auf dem Zeitzonenattribut
    weekday – abgekürzter Wochentag basierend auf dem Zeitzonenattribut in der Sprache Ihres FHEM-Systems
    Tn [°C] – Mindesttemperatur der letzten 12 Stunden
    Tx [°C] – Höchsttemperatur der letzten 12 Stunden (normalerweise um 18:00 Stationszeit)
    Tm [°C] – Durchschnittstemperatur der letzten 24 Stunden
    Tg [°C] – Mindesttemperatur 5 cm über dem Boden der letzten 12 Stunden
    PEvap [kg/m2] – Evapotranspiration der letzten 24 Stunden
    SunD [s] - Gesamtsonnenscheindauer des vorherigen Tages
* hour properties
    time - Zeit basierend auf dem Zeitzonenattribut
    TTT [°C] - Trockentemperatur in 2 Metern Höhe über dem Boden
    Td [°C] - Taupunkttemperatur in 2 Metern Höhe über dem Boden
    DD [°] - durchschnittliche Windrichtung in 10 m Höhe über dem Boden
    FF [km/h] - durchschnittliche Windgeschwindigkeit in 10 m Höhe über dem Boden
    FX1 [km/h] - maximale Windgeschwindigkeit in der letzten Stunde
    SunD1 [s] - Sonnenscheindauer in der letzten Stunde
    SunD3 [s] - Sonnenscheindauer in den letzten 3 Stunden
    RR1c [kg/m2] - Niederschlagsmenge in der letzten Stunde
    RR3c [kg/m2] - Niederschlagsmenge in den letzten 3 Stunden
    RR6c [kg/m2] - Niederschlagsmenge in den letzten 6 Stunden
    R600 [%] - Regenwahrscheinlichkeit in den letzten 6 Stunden
    RRhc [kg/m2] - Niederschlagsmenge in den letzten 12 Stunden
    Rh00 [%] - Regenwahrscheinlichkeit in den letzten 12 Stunden
    RRdc [kg/m2] - Niederschlagsmenge in den letzten 24 Stunden
    Rd00 [%] - Regenwahrscheinlichkeit in den letzten 24 Stunden
    ww - Wettercode (siehe WMO 4680/4677, SYNOP)
    wwd - deutsche Wettercodebeschreibung
    VV [m] - horizontale Sichtweite
    Neff [%] - effektive Wolkenbedeckung
    Nl [%] - untere Wolkenbedeckung unter 2000 m
    Nm [%] - mittlere Wolkenbedeckung unter 7000 m
    Nh [%] - hohe Wolkenbedeckung über 7000 m
    PPPP [hPa] - Druckäquivalent auf Meereshöhe
* Zusätzliche Tageseigenschaften, nicht vom DWD bereitgestellt
    SunRise - Sonnenaufgangszeit basierend auf dem Zeitzonenattribut
    SunSet - Sonnenuntergangszeit basierend auf dem Zeitzonenattribut
* Zusätzliche Stundeneigenschaften, nicht vom DWD bereitgestellt
    SunAz [°] - Sonnenazimut
    SunEl [°] - Sonnenhöhe
    SunUp - Sonnenaufgang (0: Nacht, 1: Tag)
Zusätzlich gibt es globale Prognosewerte:
* fc_state - Status der letzten Prognoseaktualisierung, mögliche Werte sind „updated“ und „error: ...“
* fc_station - Prognosestationscode (WMO oder DWD)
* fc_description - Stationsbeschreibung
* fc_coordinates - Weltkoordinate und Höhe der Station
* fc_time - Zeitpunkt der Veröffentlichung der Prognose basierend auf dem Zeitzonenattribut
* fc_copyright - rechtliche Informationen, müssen mit Prognosedaten angezeigt werden, siehe DWD-Nutzungsbedingungen
Die Alarmwerte sind nach Beginn sortiert und folgendermaßen aufgebaut:
a_<index>_<property>
* index - Alarmindex, beginnend mit 0, Gesamtanzahl a_count, sortiert nach Beginn
* alert properties
    category - „Met“ oder „Health“
    event - numerischer Ereigniscode, siehe DWD-Dokumentation für Details
    eventDesc - kurze Ereignisbeschreibung in ausgewählter Sprache
    eventGroup - Ereignisgruppe, siehe DWD-Dokumentation für Details
    responseType - „None“ = nein Anweisungen, „Vorbereiten“ = Anweisungen, „AllClear“ = Alarm gelöscht
    urgency – „Immediate“ = Warnung oder „Zukünftig“ = Information
    severity – „Minor, „Moderate“, „Severe“ or „Extreme“
    areaColor – RGB-Farbe je nach Dringlichkeit und Schweregrad, durch Komma getrennte Dezimalzahlen
    onset – Startzeit des Alarms basierend auf dem Zeitzonenattribut
    expires – Endzeit des Alarms basierend auf dem Zeitzonenattribut
    headline – Überschrift in ausgewählter Sprache, normalerweise eine Kombination der Eigenschaften Dringlichkeit und Ereignis
    description – Beschreibung des Alarms in ausgewählter Sprache
    instruction – Sicherheitsanweisungen in ausgewählter Sprache
    area – numerische Warnzellen-ID
    areaDesc – Beschreibung des Bereichs, z. B. „Stadt Berlin“
    altitude – min. Höhe [m]
    ceiling – max. Höhe [m]
Zusätzlich gibt es einige globale Alarmwerte:
* a_state – Status der letzten Alarmaktualisierung, mögliche Werte sind „aktualisiert“ und „Fehler: ...“
* a_time – Zeitpunkt des Downloads der letzten Alarmaktualisierung, basierend auf der Zeitzone attribut
* a_count – Anzahl der für die ausgewählte Warnzellen-ID verfügbaren Alarme
* a_copyright – rechtliche Informationen, müssen mit Prognosedaten angezeigt werden, siehe DWD-Nutzungsbedingungen, nicht verfügbar, wenn count null ist
Alarme sollten unabhängig von der Dringlichkeit für onset <= now < expires und responseType != „AllClear“ als aktiv betrachtet werden. Inaktive Alarme mit responseType = „AllClear“ können relevante Anweisungen enthalten.
Beachten Sie, dass alle Alarmwerte bei jeder Aktualisierung vollständig ersetzt und neu indexiert werden!
Weitere Informationen zu den Alarmeigenschaften finden Sie in der Dokumentation des CAP DWS-Profils.


== Anwendungsbeispiele ==
== Anwendungsbeispiele ==

Aktuelle Version vom 28. Juni 2024, 20:45 Uhr

Info blue.png
Versionsänderung: Die Vorhersagedaten wurden Mitte September 2018 von CSV auf KML umgestellt. Diese Umstellung ist inkompatibel: das DWD_OpenData-Modul muss aktualisiert werden, einige Reading-Namen haben sich geändert und der Weblink muss deinstalliert und neu installiert werden (siehe diesen Beitrag).


DWD_OpenData
Zweck / Funktion
Daten vom DWD OpenData Server abrufen
Allgemein
Typ Gerätemodul
Details
Dokumentation EN / DE
Support (Forum) Unterstützende Dienste/Wettermodule
Modulname 55_DWD_OpenData.pm
Ersteller JensB (Forum /Wiki)
Wichtig: sofern vorhanden, gilt im Zweifel immer die (englische) Beschreibung in der commandref!

Der Deutsche Wetterdienst (DWD) stellt Wetterdaten über den Open Data Server zur Verfügung. Die Verwendung dieses Dienstes und der vom DWD zur Verfügung gestellten Daten unterliegt den auf der OpenData Webseite beschriebenen Bedingungen. Einen Überblick über die verfügbaren Daten findet man in der Tabelle OpenData_weather_content.xls oder hier: https://opendata.dwd.de/weather/lib/MetElementDefinition.xml.

Anwendung

Aufgrund der unterschiedlichen vom DWD bereitgestellten Datenformate benötigt das Modul zur Dekodierung je nach gewünschtem Funktionsumfang zusätzliche Perl-Module, die nicht standardmäßig vorinstalliert sind und je nach Systemkonfiguration nachinstalliert werden müssen. Außerdem unterstützt das Modul Zeitzonen, um z.B. die Wettervorhersage internationaler Stationen in Ortszeit darstellen zu können. Hierfür sind je nach Systemkonfiguration weitere Einstellungen erforderlich. Auch die Sprache der Wochentage für die Wettervorhersage ist frei wählbar. Je nach Systemkonfiguration ist aber die Standardsprache u.U. auf Englisch eingestellt, was weitere Anpassungen der Systemkonfiguration erforderlich macht.

Vorbereitung

Im folgenden wird z.T. von einem System mit Rasbian oder Debian mit System-V Startsystem ausgegangen. Auf anderen Betriebssystemen und auf Systemen mit System-D müssen die Installationsschritte ggf. angepasst werden.

1. Für die Wettervorhersage und die Wetterwarnungen: Installation des Perl-Moduls XML::LibXML über die System-Kommandozeile mit

sudo apt-get install libxml-libxml-perl

oder mit CPAN.

2. Weiterhin ist das Perl-Modul DateTime erforderlich. Dieses lässt sich mit

sudo apt-get install libdatetime-perl

installieren.

3. Falls man einen eigenen Internet-Proxy nutzt: Die Konfiguration des Proxies für FHEM erfolgt z.B. durch Eintrag von

 attr global proxy <myProxyHost>:<myProxyPort>

in die Konfiguration, wobei myProxyHost durch den Namen oder die IP-Adresse des Proxy-Servers ersetzt werden muss und für myProxyPort der Port des Proxy-Servers eingetragen werden muss (oft 3128).

3. FHEM Uhrzeit und Zeitzone: Durch Eingabe von

 {localtime()}

in die FHEM-Kommandozeile überprüfen, ob die FHEM-Uhrzeit plausibel ist. Ist dies nicht der Fall sollten die Uhrzeit auf Systemebene überprüft werden und ggf. die Systemeinstellungen angepasst werden. Ermitteln der Bezeichnung der eigenen Zeitzone über die System-Kommandozeile mit

  tzselect

und diese Bezeichnung in die Datei /etc/timezone eintragen und

 export TZ=`cat /etc/timezone`

der Datei /etc/profile hinzufügen und dann das System neu starten. Nun muss die Zeitanzeige in FHEM korrekt sein.

4. FHEM Sprache: Durch Eingabe von

 {$ENV{LANG}}

in die FHEM-Kommandozeile überprüfen, in welcher Sprache der Wochentag angezeigt wird. Stimmt die Sprache nicht, dann auf der System-Kommandozeile

 dpkg-reconfigure locales

eingeben und aus der Liste z.B. de_DE.UTF-8 zusätzlich auswählen. Dieser Befehl gilt für Debian-basierte Betriebssysteme - für andere Betriebssysteme ist eine analoge Funktion zu verwenden. Mit Eingabe von

 locale -a

auf der System-Kommandozeile überprüfen, ob die ausgewählte Sprache nun zur Verfügung steht. Sollte FHEM nach einem Neustart noch immer nicht die gewünschte Sprache verwenden, kann man auf einem System V Linux durch Hinzufügen von z.B.

 export LANG=de_DE.UTF-8

in die Datei /etc/init.d/fhem (irgendwo zwischen start und perl) und erneutem Neustart von FHEM die Sprache für FHEM erzwingen. Bei einem Systemd Linux kann man die Einstellungen mit systemctl edit --full fhem.service bearbeiten. Spätestes jetzt muss die Wochentagsanzeige in FHEM korrekt sein.

5. Nur für den Weblink erforderlich: Installation des Perl-Moduls DateTime über die System-Kommandozeile mit

 sudo apt-get install libdatetime-perl

oder mit CPAN.

Define

Siehe Commandref.

Attributes

Siehe Commandref.

Readings

Die Prognosewerte sind wie folgt aufgebaut:

fc<day>_[<sample>_]<property>

Eine Beschreibung der über 70 verfügbaren Eigenschaften und ihrer Maßeinheiten finden Sie hier. Die Maßeinheiten für Temperaturen und Windgeschwindigkeiten werden jeweils in °C und km/h umgerechnet. In den folgenden Abschnitten sind nur einige ausgewählte Eigenschaften aufgeführt:

  • day – relativer Tag (0 .. 9) basierend auf dem Zeitzonenattribut, wobei 0 für heute steht
  • sample – relative Zeit (0 .. 3, 7 oder 23), die einem Vielfachen von 6, 3 oder 1 Stunde UTC entspricht, abhängig vom Attribut forecastResolution
  • day properties (normalerweise für 06:00 Uhr Stationszeit, siehe Rohdaten der Station für tatsächliche Zeitbeziehung)
   date – Datum basierend auf dem Zeitzonenattribut
   weekday – abgekürzter Wochentag basierend auf dem Zeitzonenattribut in der Sprache Ihres FHEM-Systems
   Tn [°C] – Mindesttemperatur der letzten 12 Stunden
   Tx [°C] – Höchsttemperatur der letzten 12 Stunden (normalerweise um 18:00 Stationszeit)
   Tm [°C] – Durchschnittstemperatur der letzten 24 Stunden
   Tg [°C] – Mindesttemperatur 5 cm über dem Boden der letzten 12 Stunden
   PEvap [kg/m2] – Evapotranspiration der letzten 24 Stunden
   SunD [s] - Gesamtsonnenscheindauer des vorherigen Tages
  • hour properties
   time - Zeit basierend auf dem Zeitzonenattribut
   TTT [°C] - Trockentemperatur in 2 Metern Höhe über dem Boden
   Td [°C] - Taupunkttemperatur in 2 Metern Höhe über dem Boden
   DD [°] - durchschnittliche Windrichtung in 10 m Höhe über dem Boden
   FF [km/h] - durchschnittliche Windgeschwindigkeit in 10 m Höhe über dem Boden
   FX1 [km/h] - maximale Windgeschwindigkeit in der letzten Stunde
   SunD1 [s] - Sonnenscheindauer in der letzten Stunde
   SunD3 [s] - Sonnenscheindauer in den letzten 3 Stunden
   RR1c [kg/m2] - Niederschlagsmenge in der letzten Stunde
   RR3c [kg/m2] - Niederschlagsmenge in den letzten 3 Stunden
   RR6c [kg/m2] - Niederschlagsmenge in den letzten 6 Stunden
   R600 [%] - Regenwahrscheinlichkeit in den letzten 6 Stunden
   RRhc [kg/m2] - Niederschlagsmenge in den letzten 12 Stunden
   Rh00 [%] - Regenwahrscheinlichkeit in den letzten 12 Stunden
   RRdc [kg/m2] - Niederschlagsmenge in den letzten 24 Stunden
   Rd00 [%] - Regenwahrscheinlichkeit in den letzten 24 Stunden
   ww - Wettercode (siehe WMO 4680/4677, SYNOP)
   wwd - deutsche Wettercodebeschreibung
   VV [m] - horizontale Sichtweite
   Neff [%] - effektive Wolkenbedeckung
   Nl [%] - untere Wolkenbedeckung unter 2000 m
   Nm [%] - mittlere Wolkenbedeckung unter 7000 m
   Nh [%] - hohe Wolkenbedeckung über 7000 m
   PPPP [hPa] - Druckäquivalent auf Meereshöhe
  • Zusätzliche Tageseigenschaften, nicht vom DWD bereitgestellt
   SunRise - Sonnenaufgangszeit basierend auf dem Zeitzonenattribut
   SunSet - Sonnenuntergangszeit basierend auf dem Zeitzonenattribut
  • Zusätzliche Stundeneigenschaften, nicht vom DWD bereitgestellt
   SunAz [°] - Sonnenazimut
   SunEl [°] - Sonnenhöhe
   SunUp - Sonnenaufgang (0: Nacht, 1: Tag)

Zusätzlich gibt es globale Prognosewerte:

  • fc_state - Status der letzten Prognoseaktualisierung, mögliche Werte sind „updated“ und „error: ...“
  • fc_station - Prognosestationscode (WMO oder DWD)
  • fc_description - Stationsbeschreibung
  • fc_coordinates - Weltkoordinate und Höhe der Station
  • fc_time - Zeitpunkt der Veröffentlichung der Prognose basierend auf dem Zeitzonenattribut
  • fc_copyright - rechtliche Informationen, müssen mit Prognosedaten angezeigt werden, siehe DWD-Nutzungsbedingungen


Die Alarmwerte sind nach Beginn sortiert und folgendermaßen aufgebaut:

a_<index>_<property>
  • index - Alarmindex, beginnend mit 0, Gesamtanzahl a_count, sortiert nach Beginn
  • alert properties
   category - „Met“ oder „Health“
   event - numerischer Ereigniscode, siehe DWD-Dokumentation für Details
   eventDesc - kurze Ereignisbeschreibung in ausgewählter Sprache
   eventGroup - Ereignisgruppe, siehe DWD-Dokumentation für Details
   responseType - „None“ = nein Anweisungen, „Vorbereiten“ = Anweisungen, „AllClear“ = Alarm gelöscht
   urgency – „Immediate“ = Warnung oder „Zukünftig“ = Information
   severity – „Minor, „Moderate“, „Severe“ or „Extreme“
   areaColor – RGB-Farbe je nach Dringlichkeit und Schweregrad, durch Komma getrennte Dezimalzahlen
   onset – Startzeit des Alarms basierend auf dem Zeitzonenattribut
   expires – Endzeit des Alarms basierend auf dem Zeitzonenattribut
   headline – Überschrift in ausgewählter Sprache, normalerweise eine Kombination der Eigenschaften Dringlichkeit und Ereignis
   description – Beschreibung des Alarms in ausgewählter Sprache
   instruction – Sicherheitsanweisungen in ausgewählter Sprache
   area – numerische Warnzellen-ID
   areaDesc – Beschreibung des Bereichs, z. B. „Stadt Berlin“
   altitude – min. Höhe [m]
   ceiling – max. Höhe [m]

Zusätzlich gibt es einige globale Alarmwerte:

  • a_state – Status der letzten Alarmaktualisierung, mögliche Werte sind „aktualisiert“ und „Fehler: ...“
  • a_time – Zeitpunkt des Downloads der letzten Alarmaktualisierung, basierend auf der Zeitzone attribut
  • a_count – Anzahl der für die ausgewählte Warnzellen-ID verfügbaren Alarme
  • a_copyright – rechtliche Informationen, müssen mit Prognosedaten angezeigt werden, siehe DWD-Nutzungsbedingungen, nicht verfügbar, wenn count null ist

Alarme sollten unabhängig von der Dringlichkeit für onset <= now < expires und responseType != „AllClear“ als aktiv betrachtet werden. Inaktive Alarme mit responseType = „AllClear“ können relevante Anweisungen enthalten.

Beachten Sie, dass alle Alarmwerte bei jeder Aktualisierung vollständig ersetzt und neu indexiert werden!

Weitere Informationen zu den Alarmeigenschaften finden Sie in der Dokumentation des CAP DWS-Profils.

Anwendungsbeispiele

Beispiel zur Modul-Einrichtung

  • Wettervorhersage

Für die Wettervorhersage muss der Stationscode ermittelt werden. Dazu den MOSMIX Stationskatalog herunterladen, öffnen und die gewünschte Station heraussuchen (Spalte id). Einen Teil der deutschen Stationen kann man auch der Bodenmessnetzkarte entnehmen. Den ermittelten Stationscode kann man dann entweder für eine Einzelabfrage als Parameter für das Kommando get forecast verwenden oder für die automatische Aktualisierung als Attribut forecastStation hinterlegen. Insgesamt werden vom DWD mehr als 70 Merkmale zur Verfügung gestellt. Eine Auflistung findet sich im Dokument MetElementDefinition. Nur ein kleiner Teil der Merkmale wird in der Standardeinstellung des Moduls als Reading zur Verfügung gestellt. Wer andere Merkmale benötigt muss sie mit Komma getrennt dem Attribut forecastProperties zuweisen. Es empfiehlt sich zur Performance-Optimierung sowohl das Attribut forecastDays als auch das Attribut forecastProperties auf den tatsächlichen Bedarf einzustellen.

  • Wetterwarnungen

Für die Wetterwarnungen muss die Warnzelle ermittelt werden. Dazu Warnzellen-ID Katalog herunterladen, öffnen und die gewünschte Warnzelle heraussuchen. Unterstützt werden Gemeinden (beginnen mit 8), Landkreise (beginnen mit 1 oder 9) oder Küste (beginnen mit 5). Die Warnzellen-ID kann man dann entweder für eine Einzelabfrage als Parameter für das Kommando get alerts verwenden oder für die automatische Aktualisierung als Attribut alertArea hinterlegen. Die Warnzellen-ID ist ab der 2. Stelle identisch mit den ersten Ziffern des amtlichen Gemeindeschlüssels. Man kann z.B. den Namen von Gemeinde oder Landkreis bei den Statistischen Ämtern des Bundes und der Länder eingeben, den amtlichen Gemeindeschlüssel ermitteln und dann mit vorangestellter 1, 8 oder 9 im Warnzellen-ID Katalog des DWD nach einem Eintrag mit exakter oder weitgehender Übereinstimmung suchen. Wem das zu kompliziert vorkommt, der kann bei mehrdeutigen Gemeinde- bzw. Landkreisbezeichnungen auch versuchen auszuprobieren, welche die richtige Warnzellen-ID ist.

Um das Modul mit automatisch Aktualisierung von Vorhersage und Wetterwarnungen zu nutzen, kann z.B. folgendes in die Konfiguration eingetragen werden:

 define DWD DWD_OpenData
 attr DWD alertArea 111000000
 attr DWD forecastStation 99810
 attr DWD forecastDays 3
 attr DWD forecastWW2Text 1
  • Performance

Das DWD_OpenData-Modul ist ein Datenmodul mit relativ vielen Readings. In der Standardkonfiguration wird, wie bei jedem anderen FHEM Modul, bei jeder Änderung jedes Readings ein FHEM internes Ereignis erzeugt, damit andere FHEM Devices auf diese Änderungen reagieren können. Diese Ereignisse sorgen auch dafür, dass man Änderungen in der Weboberfläche sofort angezeigt bekommt, ohne die Webseite neu aufzurufen. Interessiert sich aber kein FHEM Device für die Änderungsbenachrichtigung bestimmter Readings, entsteht unnötiger Verarbeitungs-Overhead, der je nach FHEM Konfiguration und verfügbarer CPU-Leistung auch mehrere Sekunden benötigen kann und in dieser Zeit andere Abläufe blockiert. Um die Performance zu optimieren, empfiehlt es sich, mit event-on-update-reading für eine angepasste und dadurch effizientere Verarbeitung zu sorgen, indem man nur für jene Readings Ereignisse zulässt, welche man für die Weiterverarbeitung braucht. Der DWD_OpenData_Weblink braucht z.B. gar keine Ereignisse, um zu funktionieren. Hier eine minimalistische Beispielkonfiguration:

 attr DWD event-on-update-reading state,fc_state,a_state

Weitere Details zur Installation und Konfiguration des Moduls finden sich in der Commandref.

Beispiel für die Einrichtung eines Meteogramms

Um den vorhergesagten Wetterverlauf als Meteogramm in Kurvenform mit SVG darzustellen, kann man wie folgt vorgehen:

1. 99_myUtils.pm

Für die Bestimmung der Werte für den SVG-Plot wird eine Perl-Funktion benötigt, die in die Datei 99_myUtils.pm vor dem Ende eingefügt wird. Wer diese FHEM-Datei noch nicht nutzt, legt sie einfach an (siehe 99_myUtils_anlegen).

package main;

use strict;
use warnings;
 
...

#
# (c) mumpitzstuff 19.12.2018
#
# see https://forum.fhem.de/index.php/topic,83097.msg874150.html#msg874150
#
sub logProxy_dwd2Plot($$$$;$$$)
{
  my ($device, $fcValue, $from, $to, $fcHour, $expMode, $shiftTime) = @_;
  my $regex;
  my @rl;

  return undef if(!$device);

  if ($fcValue =~ s/_$//)
  {
    $regex = "^fc[\\d]+_[\\d]+_".$fcValue."\$";
  }
  else
  {
    $regex = "^fc[\\d]+_".$fcValue."\$";
  }

  $fcHour = 12 if(!defined($fcHour));
  $expMode = "point" if(!defined($expMode));
  #Log3 undef,2, "Regex: ".$regex;

  # ermitteln aller relevanten Readings
  if ( defined($defs{$device}) )
  {
    if ( $defs{$device}{TYPE} eq "DWD_OpenData" )
    {
      @rl = sort
      {
        my ($an) = ($a =~ m/fc(\d+)_.*/);
        my ($bn) = ($b =~ m/fc(\d+)_.*/);
        my ($ao) = ($a =~ m/fc\d+_(\d+).*/);
        my ($bo) = ($b =~ m/fc\d+_(\d+).*/);
        $an <=> $bn or $ao <=> $bo or $a cmp $b;
      } ( grep /${regex}/,keys %{$defs{$device}{READINGS}} );
      return undef if ( !@rl );
    }
    else
    {
      Log3 undef, 2, "logProxy_dwd2Plot: $device is not a DWD_OpenData device";
      return undef;
    }
  }

  my $fromsec = SVG_time_to_sec($from);
  my $tosec   = SVG_time_to_sec($to);
  my $sec = $fromsec;
  my ($h, $hp, $fcDay, $mday, $mon, $year);
  my $timestamp;

  my $reading;
  my $value;
  my $prev_value;
  my $min = 999999;
  my $max = -999999;
  my $ret = "";

  # while not end of plot range reached
  while (($sec < $tosec) && @rl)
  {
    #remember previous value for start of plot range
    $prev_value = $value;

    $reading = shift @rl;
    ($fcDay) = $reading =~ m/^fc(\d+).*/;
    ($hp) = $reading =~ m/^fc\d+_(\d+).*/;
    #Log 1, "hp: ".$hp;

    if ($hp)
    {
      $h = ReadingsVal($device, "fc".$fcDay."_".$hp."_time", $fcHour);
      if ($h =~ m/^(\d+):\d+/)
      {
        $h = $1;
      }
    }
    else
    {
      $h = $fcHour;
    }

    $value = ReadingsVal($device, $reading, undef);

    # calculate minutes of sunshine per hour
    if ($fcValue =~ /^SunD(\d+)/)
    {
      if (defined($1))
      {
        $value = $value / ($1 * 36);
      }
      else
      {
        $value = $value / (12 * 36);
      }
    }

    # calculate amount of rain per hour
    if ($fcValue =~ /^RR(\d+)c$/)
    {
      if (defined($1))
      {
        $value /= $1;
      }
    }

    ($year, $mon, $mday) = split('\-',ReadingsVal($device, "fc".$fcDay."_date",undef));
    $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $year, $mon, $mday, $h, 0, 0);
    $sec = SVG_time_to_sec($timestamp);
    if (defined($shiftTime))
    {
      $sec += $shiftTime;
      $timestamp = logProxy_shiftTime($timestamp, $shiftTime);
    }

    # skip all values before start of plot range
    next if ( $sec < $fromsec );

    # add first value at start of plot range
    if ( !$ret && $prev_value )
    {
      $min = $prev_value if ( $prev_value < $min );
      $max = $prev_value if ( $prev_value > $max );
      $ret .= "$from $prev_value\n";
    }

    # done if after end of plot range
    last if ($sec > $tosec);

    $min = $value if ( $value < $min );
    $max = $value if ( $value > $max );

    # add actual control point
    $ret .= "$timestamp $value\n";
  }

  if (($sec < $tosec) && !@rl && ($expMode eq "day"))
  {
    $timestamp = sprintf("%04d-%02d-%02d_%02d:%02d:%02d", $year, $mon, $mday, 23, 59, 59);
    $_ = SVG_time_to_sec($timestamp);
    if (defined($shiftTime))
    {
      $_ += $shiftTime;
      $timestamp = logProxy_shiftTime($timestamp, $shiftTime);
    }

    if ($_ < $tosec)
    {
      $ret .= "$timestamp $value\n";
    }
    else
    {
      $ret .= "$to $value\n";
    }
  }
  elsif (($sec > $tosec) && ($expMode eq "day"))
  {
    $value = $prev_value + ($value - $prev_value) * (86400 + ($tosec - $sec)) / 86400;
    $ret .= "$to $value\n";
  }

  return ($ret, $min, $max, $prev_value);
} 

1;

2. LogProxy

Wer bereits ein logProxy-Device hat, muss den Namen seines logProxy-Devices in die weiter unter aufgeführte GPlot-Konfiguration eintragen. Ansonsten wird es wie folgt neu erstellt:

define LogProxy logProxy

3. FileLog

Das Meteogramm kennt anders als andere FHEM-Plots keine Vergangenheit außer heute. Trotzdem wird aus formalen Gründen ein FileLog-Device benötigt. Dazu kann ein beliebiges vorhandenes FileLog-Device verwendet werden. Wer will, kann hierfür aber auch ein neues FileLog Device für das DWD_OpenData-Device anlegen (Achtung: wird schnell umfangreich):

define FileLog_DWD FileLog ./log/DWD-%Y-%m.log DWD

Im folgenden wird für das FileLog-Device der Platzhalter FileLog_Dummy verwendet, der mit dem Namen des gewählten FileLog-Devices zu ersetzen ist.

4. SVG Plot

Das eigentliche Meteogramm ist ein SVG-Device. Will man mehrere Tage darstellen bietet sich die folgende Definition an:

define SVG_DWD SVG FileLog_Dummy:SVG_DWD:CURRENT
attr SVG_DWD fixedoffset 6
attr SVG_DWD fixedrange 7days
attr SVG_DWD nrAxis 1,2

Dabei muss das SVG-Attribut fixedoffset entweder gleich oder kleiner als die Einstellung des DWD_OpenData-Attributs forecastDays (Standardwert: 6 Tage) gewählt werden und das SVG-Attribut fixedrange um eins größer als fixedoffset eingestellt werden.

Will man stattdessen nur die nächsten 24 Stunden anzeigen, nimmt man folgende Einstellungen:

define SVG_DWD SVG FileLog_Dummy:SVG_DWD:CURRENT
attr SVG_DWD endPlotNow 1
attr SVG_DWD fixedoffset 1
attr SVG_FileLog_WETTER_DWD_3 label "Tmin: $data{max1} °C, Tmax: $data{min1} °C"
attr SVG_DWD nrAxis 1,2

Um die DWD_OpenData-Readings mit dem Plot zu verknüpfen, muss man nun die GPlot-Konfigurationsdatei des SVG-Plots manuell bearbeiten. Dazu auf den Link klicken, der im SVG_DWD-Device am Internal GPLOTFILE steht. Die y-Wertebereiche für die Regenmenge und die Temperaturen sind im Beispiel auskommentiert (Autorange). Das kann man so lassen oder man stellt sie nach den eigenen Wetter-Gegebenheiten ein. Je nach Konfiguration des DWD_OpenData-Attributs forecastResolution sind aber noch einige weitere Einstellungen anzupassen. Das folgende Beispiel ist für die 3h-Auflösung:

set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title 'Wettervorhersage'
set ytics 
set y2tics 
set grid ytics
set ylabel "Regen [mm]"
#set yrange [0:4]
set y2label "Temperatur [°C]"
#set y2range [-5:35]
set y3label "Wolken/Regen/Sonne [%]"
set y3range [0:100]

#LogProxy Func:logProxy_dwd2Plot("DWD","TTT_",$from,$to,0,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","Tx",$from,$to,18,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","Tn",$from,$to,6,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","SunD3_",$from,$to,0,"day",(-3*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","RR3c_",$from,$to,0,"day",(-3*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","FX1_",$from,$to,0,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","R600_",$from,$to,0,"day",(-3*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","Neff_",$from,$to,0,"day")
#LogProxy ConstX:TimeNow(),0,100
#LogProxy ConstY:0

plot "<IN>" using 1:2 axes x1y2 title 'T' ls l0 lw 2 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y2 title 'Tmax' ls l0dot lw 2 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y2 title 'Tmin' ls l0dot lw 2 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 title 'Sonne' ls l4fill lw 1 with lines,\
     "<IN>" using 1:2 axes x1y1 title 'Regenmenge' ls l2fill lw 1 with steps,\
     "<IN>" using 1:2 axes x1y3 title 'Wind' ls l1 lw 1 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 title 'Regen' ls l5fill lw 1 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 title 'Wolken' ls l6fill lw 1 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 notitle ls l5 lw 4 with lines,\
     "<IN>" using 1:2 axes x1y2 notitle ls l5 lw 1 with lines

Verwendet man dagegen eine Auflösung von 1 Stunde, sind folgende LogProxy-Einträge erforderlich:

#LogProxy Func:logProxy_dwd2Plot("DWD","TTT_",$from,$to,0,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","Tx",$from,$to,18,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","Tn",$from,$to,6,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","SunD1_",$from,$to,0,"day",(-1*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","RR1c_",$from,$to,0,"day",(-1*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","FX1_",$from,$to,0,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","R101_",$from,$to,0,"day",(-0.5*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","Neff_",$from,$to,0,"day")
#LogProxy ConstX:TimeNow(),0,100
#LogProxy ConstY:0

Für die Darstellung der nächsten 24 Stunden mit einer Auflösung von 1 Stunde nimmt man folgende GPlot-Konfiguration:

set terminal png transparent size <SIZE> crop
set output '<OUT>.png'
set xdata time
set timefmt "%Y-%m-%d_%H:%M:%S"
set xlabel " "
set title '<L1>'
set ytics
set y2tics
set y3tics
set grid xtics y2tics
set ylabel "Regen [mm]"
set y2label "Temperatur [°C]"
set y3label "Wolken/Regen/Sonne [%]"
set y3range [0:100]

#LogProxy Func:logProxy_dwd2Plot("DWD","TTT_",$from,$to,0,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","SunD1_",$from,$to,0,"day",(-1*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","RR1c_",$from,$to,0,"day",(-1*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","FX1_",$from,$to,0,"day")
#LogProxy Func:logProxy_dwd2Plot("DWD","R101_",$from,$to,0,"day",(-0.5*3600))
#LogProxy Func:logProxy_dwd2Plot("DWD","Neff_",$from,$to,0,"day")
#LogProxy ConstY:0

plot "<IN>" using 1:2 axes x1y2 title 'T' ls l0 lw 2 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 title 'Sonne' ls l4fill lw 1 with lines,\
     "<IN>" using 1:2 axes x1y1 title 'Regenmenge' ls l2fill lw 1 with steps,\
     "<IN>" using 1:2 axes x1y3 title 'Wind' ls l1 lw 1 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 title 'Regen' ls l5fill lw 1 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y3 title 'Wolken' ls l6fill lw 1 with quadraticSmooth,\
     "<IN>" using 1:2 axes x1y2 notitle ls l2 lw 2 with lines

Je nachdem, welche Auflösung man verwendet, werden unterschiedliche Readings des DWD_OpenData-Devices benötigt. Diese sind in den LogProxy-Zeilen in der GPlot-Konfigurationsdatei zu finden und müssen auf jeden Fall dem Attribut forecastProperties des DWD_OpenData-Device hinzugefügt werden.

Viele Detaileinstellungen des Meteogramms lassen sich individualisieren. Man kann z.B. mehr oder weniger Daten anzeigen und zumindest für heute auch aktuelle Messwerte hinzufügen.

Hier ein Beispiel für ein Meteogramm über 4 Tage mit 3 Stunden Auflösung:

DWDODmeteogram4d3h.png

Beispiel für die Einrichtung eines Weblinks

Zur graphischen Darstellung der Vorhersage und der Wetterwarnungen mit FHEMWEB steht das Modul 98_DWD_OpenData_Weblink.pm zur Verfügung.

Zunächst die Moduldatei herunterladen und in das Modulverzeichnis fhem/FHEM kopieren. Entweder die Datei direkt auf den FHEM-Server herunterladen (z.B. mit wget) oder beim indirekten Herunterladen darauf achten, dass dabei das Encoding nicht verändert wird (also z.B. bei WinSCP den Binär-Modus und nicht den Text-Modus verwenden). Ansonsten werden später z.B. Sonderzeichen wie °C falsch angezeigt.

Anschließend kann in die Konfiguration z.B. folgendes eingetragen werden:

 define DWD_Weblink_Generator DWD_OpenData_Weblink
 attr DWD_Weblink_Generator IODev DWD
 attr DWD_Weblink_Generator forecastDays 4
 # refreshRate nur dann setzten, wenn der Browser Java-Script beherrscht:
 attr DWD_Weblink_Generator refreshRate 900
 
 define DWD_Weblink weblink htmlCode { DWD_OpenData_Weblink::AsHtmlH("DWD_Weblink_Generator") }

Damit legt man das Weblink-Generator-Device an und verknüpft es mit dem DWD_OpenData-Device. Außerdem wird der weblink selbst angelegt, der seinerseits mit dem Weblink-Generator-Device verknüpft wird. Der Weblink benötigt für seine Funktion eine bestimmte Auswahl der Vorhersage-Merkmale. Wer das Attribut forecastProperties des DWD_OpenData-Moduls anpassen will, muss diese Merkmale auf jeden Fall mit berücksichtigen.

Wenn vom Weblink nichts im Browser zu sehen ist, kann das daran liegen, dass der Browser kein aktuelles JavaScript verwendet oder JavaScript deaktiviert ist. Dann sollte das Attribut refreshRate auf 0 Sekunden eingestellt oder gelöscht werden. Mit aktiviertem Refresh werden die Daten auch ohne Seitenneuaufbau regelmäßig und bei Tab-Wechsel aktualisiert.

Die Liste der erforderlichen Vorhersage-Merkmale finden sich zusammen mit allen weiteren Details zur Installation und Konfiguration in der Modulhilfe von 98_DWD_OpenData_Weblink.pm, die man z.B. nach dem Anlegen des Weblink-Generator-Devices über die Weboberfläche abrufen kann.

Hier ein Ausschnitt aus der Darstellung des Weblinks mit FHEMWEB: DWDODweblink.png

Beispiel für die Einbindung auf einer Webseite außerhalb von FHEM

Für manche Anwendungsfälle, etwa die Ansicht in FTUI, ist es erforderlich, das mit dem DWD_Weblink_Generator erzeugte Bild an anderer Stelle in eine Webseite einzubinden. Dazu kann man folgende Schritte verwenden.

Zunächst wird ein statisches CSS-Stylesheet erzeugt, etwa in der Datei ftui_weather.css im Unterverzeichnis css. Dorthin werden die ganzen CSS-Klassen geschrieben, die der Webbrowser benötigt, um aus den Daten des DWD_Weblink_Generator eine Tabelle zu machen (siehe den etwas länglichen Code am Ende dieses Abschnitts).

Dann ist zunächst dafür zu sorgen, dass im Header der Webseite, die später die Wettervorhersage anzeigen soll, dieses Stylesheet eingebunden wird. Dazu muss in den Header die Zeile

 <link rel="stylesheet" href="css/ftui_weather.css"/>

eingetragen werden.

Der zweite Schritt besteht darin, ebenfalls in den Header der betreffenden Webseite ein paar Zeilen JavaScript einzubinden, die dem Holen der Daten dienen:

<script>
    function getDWDOpenDataWeblink() {
        var fwcsrf = "<HIER DER csrf-Token DES ENTFERNTEN FHEM-SERVERS>";
        $. get ("http://<HIER DIE ADRESSE DES ENTFERNTEN FHEM-SERVERS>:8083/fhem?cmd=get%20DWD_Weblink_Generator%20horizontalForecast&XHR=1&fwcsrf=" + fwcsrf,
        function (forecastElements) {
            $("#DWD_OpenData_Weblink").html(forecastElements);
        });
    }
       
    function getdata() {
        getDWDOpenDataWeblink();
        setInterval(getDWDOpenDataWeblink, 900000);
        $(window).on("focus", getDWDOpenDataWeblink);
    };
</script>

Der dritte Schritt besteht darin, auf der betreffenden Webseite einen Ort zu definieren, an welchen das Wettervorhersagebild geschoben wird, z.B.

<div id="DWD_OpenData_Weblink" style="position:absolute; left:10px;top:500px; width:600px; height:200px; float:left; overflow-y:hidden;overflow-x:auto;"></div>

Der letzte Schritt sorgt dafür, dass die oben definierte JavaScript-Funktion beim Laden der Webseite auch aufgerufen wird. Dazu muss ein entsprechendes Attribut im body-Tag der Seite eingebaut werden:

 <body onload="getdata()">

Nachstehend nun der etwas längliche CSS-Code für das statische Stylesheet:

/* weather table with fixed column width */
.weatherForecast {
    display: table;
    table-layout: fixed;
    column-gap: 40px;
}
/* weather table header row */
.weatherHeaderRow {
    display: table-header-group;
    white-space: nowrap;
}
/* weather table data row */
.weatherDataRow {
    display: table-row-group;
}
/* weather table data cells */
#weatherFontBold {
    font-weight: bold;
}
.weatherWeekday {
    display: table-cell;
    min-width: 80px;
    text-align: center;
    vertical-align: middle;
}
.weatherCondition {
    display: table-cell;
    position: relative;
    top: -4px;
    text-align: center;
    vertical-align: middle;
    font-size: 80%;
    word-wrap: break-word;
}
.weatherTemperature {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
    font-size: 100%;
    white-space: nowrap;
}
.weatherWind {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
    font-size: 80%;
    padding-top: 3px;
}
/* weather table condition icon cell */
.weatherIcon {
    display: table-cell;
    position: relative;
    text-align: center;
}
/* weather icon */
.weatherIcon img {
    width: 100%;
    height: auto;
    position: relative;
}
/* embedded alert icon with pointer support */
.weatherAlertIcon {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 25px;
    height: 22px;
    background-size: 25px 22px;
    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAkCAMAAADM4ogkAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyppVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NjcyOSwgMjAxMi8wNS8wMy0xMzo0MDowMyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIEVsZW1lbnRzIDEyLjAgV2luZG93cyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1RjU1NzJBRDMyRTcxMUU1QTk4MkI0RTkwODEwODQ1QSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1RjU1NzJBRTMyRTcxMUU1QTk4MkI0RTkwODEwODQ1QSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjVGNTU3MkFCMzJFNzExRTVBOTgyQjRFOTA4MTA4NDVBIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjVGNTU3MkFDMzJFNzExRTVBOTgyQjRFOTA4MTA4NDVBIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+oFydjAAAAwBQTFRF9fX267W51Csy9d7c1VBV9uPc/vz89+nl5YB/2Flk2mZp1Cgv0Csw5pWR7K2t1Soy1Cow7b6y0iUr+/r61jI54X+A+uvq21xV44qO/fj400tU1kxG3nNx2mJp34Bz/Pj2a2tr4Ht91S43fn5+1jA31S0y0iYu1TA1/ff3+/TxEhIS3W1u1jA010Q++efj1kVH1Tk+gYGB6qWh4oWEtra21TI48sjH1VVc1tbW1jY22ltd4YF/1kFHl5eX/vz7/Pz84nZt+evt9+Tm2mNu1Sw14YSD1Ss04YKC0isv4X5/0zc21CUu8cnL0jA20jAz22xy1jE422Zv1i800isw1Cgx11xjKysr1FFZ4X190iQsGRkZ0jY+1TE41Cww0y0y0ykt1i431i4z0y800ikvPz4/AAAA////DQwM1TI30yYs1TE31TA21TE21S401S801S810ycs//7+1TA3///+/v791TI2/v//0Sov0iYt0yYt6urqkZGR1TM40ycu1S414ODg4YB3+/Hw7bK0/vv78fHxh4eH+OPf0i8w1DI30icu1y843Zia/PX13Nvb6aWZ4YKE+fDv21RT00E83G5y3m9x7tDF3Gdu99fW1EJK1ktK3t7e5I+R4oeH3WNp3oCF+vH16ZOJ1Sw27cG31i018dDL4qOf3Wde7Kqe+O7m2ERH+e3o+u/t4YuY/Pb15IqG5p2R4np52TA82mhx2V1p3mpx5oWL66uiRkZG4YOR/v7+1ygz1TxC8dDV9dHS2VJL1CUw8by79+Hg7bq3vr6+9dnR1Tc49d/X11le77yv22hl2lVY+Ofo33540icspqan+/f49N/h5J6m2HB7zczN3nZ88c3FsrKz3HZ74ICC4YGA1oJy10JD+Onj8c3J1Dk71T494oaE4HpxNTU14XRz7Ozs7+/vcG9v+/bz/Pj1/fr3IiIi676/5IJx4Hpr6amn56On6aGd2mlu4X2A22Vm2WFk1jI33GNk+/Ds2VJU2FZW8/Pz78jD3V1v55iY3W1x////C1yJRAAAAQB0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AFP3ByUAAAMKSURBVHjajNEFWBNxGAbwOXEoUzYnqCgGuPMUUFE8sRuwgDlUEJmK8z6/XWyEEjrswu7u7u7u7u4u7G7FWxoj/J6L9/7v77nnQpTpMM7XFkocV0UOK2VKbkt3/R+4KY0Zlnw8d7ikBeu9VroRcoXPg5F1Y5Mn5wYPaRnGqOxMfPbIGXZfFMxxJGtg0lxzhoWlKpYfSrCd9f4rc4JPKs1l9B1aeabGoLRQTnBxG8T2K2DwLV7FRVTOHi47yKKmxGyAagoGxb7x2cLSMuS0eZIAMopJUaWtkR3M1yUBpe8lkuoj4WYYm8Bf98gaxjdsSik7toKydPQ0OKEzUnGuWcOLWgZPXoCe3Wi6HlS+zVMG/35Zwfq7eqO+wxk4T9N0OYDXKQylnZ4VnKdxM5bfAtDTh6arAlT4IldxXYo6woyS3hwxpRHA12ia7gUA3yKUKL8f7wCPiClMOSsA0VPTMwIkeeoQm+39F1asZECZ02kBFGlH06eEM+Rt0RvFHyX/wNUy7JGex9Q3EGB1U4BCLVXK9D1/wxu1lJS8lMTc36Pp/eZQcaoM9Us9/oSJL8QUGbrDXMNmOvqBJe3TxqC22p/wgIbBlrMsLZSju1pT93dSZDsW/Q3rF/NG3r+xte4zqKY1QY0IBuVvwQ4LazFGUTzRVhexJziahkzyTht8PM6AwXUbWbutj3za2W+5IIxEovYAKxwlww+KV7buYTfzL7TODOGry/tb4KdaCSg/VsFWDfSx/ELLLK9CILl9hBnOEaOq4DN7BZe6lv36++plJ3ST/zDBfEOEN6udBNnNWDHFhTpnihIbNkXKUNfLyyuqeciVOwFvr7oHrApqHhTiPjog6EqdNVFVDIji/JmiiYQbopFM3R0XF6fT6VJ15jFFnUJhSqQRETV5RXfnGykhMea9B2M624ezBaOmuKiv1GDg9TxJ8sIxltB7E0QsT/BqpdrPT9g4P7VaTcZKL4vGDP/psq71BpdSLj9nTmod3mTC+HDfN+FN2jodjlwfGRnoFNg2cH2B7+ca/xJgAFL1l3PxpIx4AAAAAElFTkSuQmCC);
}
.weatherAlertIcon:hover {
    opacity: 0.7;
}
.weatherAlertIcon:focus {
    opacity: 1.0;
}
/* opaque white background for alerts dialog */
.weatherOverlay {
    display: none;
    position: fixed;
    z-index: 1;
    opacity: 0.2;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: white;
}
.weatherAlertIcon:focus .weatherOverlay {
    display: block;
}
/* alerts dialog box */
[class ^= weatherAlertBox] {
    display: none;
    position: absolute;
    z-index: 2;
    opacity: 1;
    top: 0;
    border: 3px solid white;
    border-radius: 3px;
    box-shadow: 0px 0px 6px 4px #666;
    background: #EEE;
}
.weatherAlertIcon:focus .weatherAlertBoxLeft {
    display: block;
    right: -268px;
}
.weatherAlertIcon:focus .weatherAlertBoxCenter {
    display: block;
    left: -178px;
}
.weatherAlertIcon:focus .weatherAlertBoxRight {
    display: block;
    left: -312px;
}
/* alerts dialog close button */
.weatherAlertsClose {
    position: absolute;
    top: -12px;
    right: -13px;

    border-radius: 12px;
    box-shadow: 1px 1px 3px #666;

    line-height: 21px;
    width: 21px;
    background: #606061;

    text-align: center;
    text-decoration: none;
    font-family: Sans-serif;
    font-weight: bold;
    color: white;
}
.weatherAlertsClose:hover {
    opacity: 0.8;
}
/* alerts dialog title */
.weaterAlertsTitle {
    font-weight: bold;
    color: black;
}
/* alert messages */
[class ^= weaterAlertMessage] {
    float: left;
    text-align: left;
    white-space: nowrap;
}
.weaterAlertMessage p {
    white-space: normal;
}