diff --git a/fhem/FHEM/55_GDS.pm b/fhem/FHEM/55_GDS.pm new file mode 100644 index 000000000..2d414dcfc --- /dev/null +++ b/fhem/FHEM/55_GDS.pm @@ -0,0 +1,964 @@ +# $Id$ +############################################################################## +# +# 55_GDS.pm +# +# An FHEM Perl module to retrieve data from "Deutscher Wetterdienst" +# +# Copyright: betateilchen ® +# e-mail : fhem.development@betateilchen.de +# +# This file is part of fhem. +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +############################################################################## +# Changelog: + +package main; + +use strict; +use warnings; +use feature qw/say switch/; +use Time::HiRes qw(gettimeofday); +use Net::FTP; +use List::MoreUtils 'first_index'; +use XML::Simple; +#use Data::Dumper; + +sub getListStationsDropdown(); +sub buildCAPList(); + +my $bulaList = "Baden-Württemberg,Bayern,Berlin,Brandenburg,Bremen,". + "Hamburg,Hessen,Mecklenburg-Vorpommern,Niedersachsen,". + "Nordrhein-Westfalen,Rheinland-Pfalz,Saarland,Sachsen,". + "Sachsen-Anhalt,Schleswig-Holstein,Thüringen"; + +# +# Bundesländer den entsprechenden Dienststellen zuordnen +# +my %bula2dwd = ( + "Baden-Württemberg" => "DWSG", + "Bayern" => "DWMG", + "Berlin" => "DWPG", + "Brandenburg" => "DWPG", + "Bremen" => "DWHG", + "Hamburg" => "DWHH", + "Hessen" => "DWOH", + "Mecklenburg-Vorpommern" => "DWPH", + "Niedersachsen" => "DWHG", + "Nordrhein-Westfalen" => "DWEH", + "Rheinland-Pfalz" => "DWOI", + "Saarland" => "DWOI", + "Sachsen" => "DWLG", + "Sachsen-Anhalt" => "DWLH", + "Schleswig-Holstein" => "DWHH", + "Thüringen" => "DWLI" ); + +# +# Dienststellen den entsprechenden Serververzeichnissen zuordnen +# +my %dwd2Dir = ( + DWSG => "SU", # Stuttgart + DWMG => "MS", # München + DWPG => "PD", # Potsdam + DWHG => "HA", # Hamburg + DWHH => "HA", # Hamburg + DWOH => "OF", # Offenbach + DWPH => "PD", # Potsdam + DWHG => "HA", # Hamburg + DWEH => "EM", # Essen + DWOI => "OF", # Offenbach + DWLG => "LZ", # Leipzig + DWLH => "LZ", # Leipzig + DWLI => "LZ", # Leipzig + DWHC => "HA", # Hamburg + DWHB => "HA", # Hamburg + DWPD => "PD", # Potsdam + DWRW => "PD", # Potsdam + DWEM => "EM", # Essen + LSAX => "LZ", # Leipzig + LSNX => "LZ", # Leipzig + THLX => "LZ", # Leipzig + DWOF => "OF", # Offenbach + DWTR => "OF", # Offenbach + DWSU => "SU", # Stuttgart + DWMS => "MS" # München +# ???? => "FG" # Freiburg +); + +my %dwd2Name = ( + EM => "Essen", + FG => "Freiburg", + HA => "Hamburg", + LZ => "Leipzig", + MS => "München", + OF => "Offenbach", + PD => "Potsdam", + SU => "Stuttgart" +); + +my %iiList = ( + "31" => "Gewitter", + "33" => "Starkes Gewitter", + "34" => "Starkes Gewitter", + "36" => "Starkes Gewitter", + "38" => "Starkes Gewitter", + "40" => "Schweres Gewitter", + "41" => "Schweres Gewitter mit extremen Orkanböen", + "42" => "Schweres Gewitter", + "44" => "Schweres Gewitter", + "45" => "Schweres Gewitter mit extremen Orkanböen", + "46" => "Schweres Gewitter", + "48" => "Schweres Gewitter", + "49" => "Schweres Gewitter mit extremen Orkanböen", + "51" => "Windböen", + "52" => "Sturmböen", + "53" => "Schwere Sturmböen", + "54" => "Orkanartige Böen", + "55" => "Orkanböen", + "56" => "Extreme Orkanböen", + "59" => "Nebel", + "61" => "Starkregen", + "62" => "Heftiger Starkregen", + "63" => "Dauerregen", + "64" => "Ergiebiger Dauerregen", + "65" => "Extrem ergiebiger Dauerregen", + "66" => "Extrem heftiger Starkregen", + "70" => "Schneefall", + "71" => "Schneefall", + "72" => "Starker Schneefall", + "73" => "Extrem starker Schneefall", + "74" => "Schneeverwehung", + "75" => "Starke Schneeverwehung", + "76" => "Schneeverwehung", + "77" => "Starke Schneeverwehung", + "78" => "Extrem starke Schneeverwehung", + "81" => "Frost", + "82" => "Strenger Frost", + "83" => "Glätte", + "84" => "Glätte", + "85" => "Glatteis", + "86" => "Glätte", + "87" => "Glätte", + "88" => "Tauwetter", + "89" => "Starkes Tauwetter", + "94" => "Schweres Gewitter", + "95" => "Schweres Gewitter mit extrem heftigem Starkregen", + "96" => "Schweres Gewitter mit extrem heftigem Starkregen" +); + +my ($alertsXml, %capCityHash, %capCellHash); + +my $sList; +if (-e "/tmp/conditions"){ + $sList = getListStationsDropdown(); +} else { + $sList = "please_use_rereadcfg_first"; +} + +my ($aList, $numCAPCount); +if (-e "/tmp/alerts"){ + ($aList, $numCAPCount) = buildCAPList(); +} else { + $aList = "please_use_rereadcfg_first"; + $numCAPCount = 0; +} + + +#################################################################################################### +# +# Main routines +# + +sub GDS_Initialize($) { + my ($hash) = @_; + + $hash->{DefFn} = "GDS_Define"; + $hash->{UndefFn} = "GDS_Undef"; + $hash->{GetFn} = "GDS_Get"; + $hash->{SetFn} = "GDS_Set"; + $hash->{AttrList} = "loglevel:0,1,2,3,4,5 ". + "gdsAll:0,1 gdsDebug:0,1 gdsLong:0,1 gdsPolygon:0,1 ". + $readingFnAttributes; +} + + +sub GDS_Define($$$) { + my ($hash, $def) = @_; + my @a = split("[ \t][ \t]*", $def); + + return "syntax: define GDS " if(int(@a) != 4 ); + + $hash->{helper}{USER} = $a[2]; + $hash->{helper}{PASS} = $a[3]; + $hash->{helper}{URL} = "ftp-outgoing2.dwd.de"; + $hash->{helper}{INTERVAL} = 3600; + + retrieveFile($hash,"conditions"); + $sList = getListStationsDropdown(); + retrieveFile($hash,"alerts"); + ($aList, undef) = buildCAPList(); + + $hash->{STATE} = "active"; + + return undef; +} + + +sub GDS_Undef($$) { + my ($hash, $arg) = @_; + + RemoveInternalTimer($hash); + return undef; +} + + +sub GDS_GetUpdate($) { + my ($hash) = @_; + my $name = $hash->{NAME}; + my (@a, $next); + + push @a, undef; + push @a, undef; + push @a, ReadingsVal($name, "c_stationName", ""); + retrieveConditions($hash, "c", @a); + + $next = gettimeofday()+$hash->{helper}{INTERVAL}; + readingsSingleUpdate($hash, "c_nextUpdate", localtime($next), 1); + InternalTimer($next, "GDS_GetUpdate", $hash, 1); + + return 1; +} + + +sub GDS_Get($@) { + my ($hash, @a) = @_; + my $command = lc($a[1]); + my $parameter = $a[2] if(defined($a[2])); + my $name = $hash->{NAME}; + + $hash->{LOCAL} = 1; + + my $usage = "Unknown argument $command, choose one of help rereadcfg ". + "list:stations,data ". + "alerts:".$aList." ". + "conditions:".$sList." ". + "warnings:".$bulaList; + + + my ($result, $datensatz, $found); + + given($command) { + when("help"){ + $result = getHelp(); + break; + } + + when("list"){ + given($parameter){ + when("data") { $result = getListData($hash,@a); break; } + when("stations") { $result = getListStationsText($hash,@a); break; } + default { $usage = "get list "; return $usage; } + } + break; + } + + when("alerts"){ + if($parameter =~ y/0-9// == length($parameter)){ + $datensatz = $capCellHash{$parameter}; + } else { + $datensatz = $capCityHash{$parameter}; + } + CommandDeleteReading(undef, "$name a_.*"); + decodeCAPData($hash, $datensatz); + break; + } + + when("conditions"){ + retrieveConditions($hash, "g", @a); + break; + } + + when("rereadcfg"){ + eval { + retrieveFile($hash,"conditions"); + $sList = getListStationsDropdown(); + }; + eval { + retrieveFile($hash,"alerts"); + ($aList, undef) = buildCAPList(); + }; + break; + } + + when("warnings"){ + (undef, $found) = retrieveFile($hash, $command, $parameter); + if($found){ + $result = retrieveTextWarn($hash,@a); + } else { + $result = "Keine Warnmeldung für $parameter gefunden."; + } + break; + } + + when("map"){ + $result = ""; + } + + default { return $usage; }; + } + return $result; +} + + +sub GDS_Set($@) { + my ($hash, @a) = @_; + my $name = $hash->{NAME}; + my $usage = "Unknown argument, choose one of clear help rereadcfg update ". + "conditions:".$sList." "; + + my $command = lc($a[1]); + my $parameter = $a[2] if(defined($a[2])); + + my ($result, $next); + + $hash->{LOCAL} = 1; + $hash->{STATE} = "active"; + + given($command) { + when("clear"){ + CommandDeleteReading(undef, "$name a_.*"); + CommandDeleteReading(undef, "$name c_.*"); + CommandDeleteReading(undef, "$name g_.*"); + } + when("help"){ + $result = setHelp(); + break; + } + + when("rereadcfg"){ + eval { + retrieveFile($hash,"conditions"); + $sList = getListStationsDropdown(); + }; + eval { + retrieveFile($hash,"alerts"); + ($aList, undef) = buildCAPList(); + }; + break; + } + + when("update"){ + RemoveInternalTimer($hash); + GDS_GetUpdate($hash); + break; + } + + when("conditions"){ + retrieveConditions($hash, "c", @a); + $next = gettimeofday()+$hash->{helper}{INTERVAL}; + readingsSingleUpdate($hash, "c_nextUpdate", localtime($next), 1); + RemoveInternalTimer($hash); + InternalTimer($next, "GDS_GetUpdate", $hash, 1); + break; + } + + default { return $usage; }; + } + return $result; +} + + +#################################################################################################### +# +# Routines used by "get" +# + +sub getHelp(){ + return "Use one of the following commands:\n". + sepLine(35)."\n". + "get alerts \n". + "get conditions \n". + "get help\n". + "get list stations|data\n". + "get rereadcfg\n". + "get warnings \n"; +} + +sub getListData($@){ + my ($line, @a); + + open WXDATA, "/tmp/conditions"; + while (chomp($line = )) { + push @a, latin1ToUtf8($line); + } + close WXDATA; + + return join("\n", @a); +} + +sub getListStationsText($@){ + my ($line, @a); + + open WXDATA, "/tmp/conditions"; + while (chomp($line = )) { + push @a, substr(latin1ToUtf8($line),0,19); + } + close WXDATA; + + splice(@a,0,6); + splice(@a,first_index { /Höhe/ } @a); + splice(@a,-1); + @a = sort(@a); + unshift(@a, "Use one of the following stations:", sepLine(40)); + + return join("\n", @a); +} + + +#################################################################################################### +# +# Routines used by "set" +# + +sub setHelp(){ + return "Use one of the following commands:\n". + sepLine(35)."\n". + "set clear\n". + "set conditions \n". + "set rereadcfg\n". + "set update\n". + "set help\n"; +} + + +#################################################################################################### +# +# some tools +# called by various routines +# + +sub buildCAPList(){ + my $xml = new XML::Simple; + $alertsXml = undef; + $alertsXml = $xml->XMLin('/tmp/alerts', ForceArray => [ 'info', 'eventCode', 'area', 'geocode' ]); + my $info = 0; + my $area = 0; + my $record = 0; + my $n = 0; + my ($capCity, $capCell, $capExit, @a, $list); + + %capCityHash = (); + %capCellHash = (); + $aList = undef; + + while(1) { + $area = 0; + while(1){ + $capCity = $alertsXml->{info}[$info]{area}[$area]{areaDesc}; + $capExit = $alertsXml->{info}[$info]{event}; + if(!$capCity) {last;} + $capCell = findCAPWarnCellId($info, $area); + $n = 100*$info+$area; + $capCity = latin1ToUtf8($capCity); + push @a, $capCity; + $capCity =~ s/\s/_/g; + $capCityHash{$capCity} = $n; + $capCellHash{"$capCell"} = $n; + $area++; + $record++; + $capCity = undef; + } + if(!$capExit){last;} + $info++; + } + + @a = sort(@a); + $list = join(",", @a); + $list =~ s/\s/_/g; + return ($list, $record); +} + +sub decodeCAPData($$){ + my ($hash, $datensatz) = @_; + my $name = $hash->{NAME}; + my $info = int($datensatz/100); + my $area = $datensatz-$info*100; + + my (%readings, @dummy, $i, $k, $n, $v, $t); + + my $_gdsAll = AttrVal($name,"gdsAll", 0); + my $_gdsDebug = AttrVal($name,"gdsDebug", 0); + my $_gdsLong = AttrVal($name,"gdsLong", 0); + my $_gdsPolygon = AttrVal($name,"gdsPolygon", 0); + +# topLevel informations + @dummy = split(/\./, $alertsXml->{identifier}); + + $readings{a_identifier} = $alertsXml->{identifier} if($_gdsAll || $_gdsDebug); + $readings{a_idPublisher} = $dummy[5] if($_gdsAll); + $readings{a_idSysten} = $dummy[6] if($_gdsAll); + $readings{a_idTimeStamp} = $dummy[7] if($_gdsAll); + $readings{a_idIndex} = $dummy[8] if($_gdsAll); + $readings{a_sent} = $alertsXml->{sent}; + $readings{a_status} = $alertsXml->{status}; + $readings{a_msgType} = $alertsXml->{msgType}; + +# infoSet informations + $readings{a_language} = $alertsXml->{info}[$info]{language} if($_gdsAll); + $readings{a_category} = $alertsXml->{info}[$info]{category}; + $readings{a_event} = $alertsXml->{info}[$info]{event}; + $readings{a_responseType} = $alertsXml->{info}[$info]{responseType}; + $readings{a_urgency} = $alertsXml->{info}[$info]{urgency} if($_gdsAll); + $readings{a_severity} = $alertsXml->{info}[$info]{severity} if($_gdsAll); + $readings{a_certainty} = $alertsXml->{info}[$info]{certainty} if($_gdsAll); + +# eventCode informations +# loop through array + $i = 0; + while(1){ + ($n, $v) = (undef, undef); + $n = $alertsXml->{info}[$info]{eventCode}[$i]{valueName}; + if(!$n) {last;} + $n = "a_eventCode_".$n; + $v = $alertsXml->{info}[$info]{eventCode}[$i]{value}; + $readings{$n} .= $v." " if($v); + $i++; + } + +# time/validity informations + $readings{a_effective} = $alertsXml->{info}[$info]{effective} if($_gdsAll); + $readings{a_onset} = $alertsXml->{info}[$info]{onset}; + $readings{a_expires} = $alertsXml->{info}[$info]{expires}; + $readings{a_valid} = checkCAPValid($readings{a_expires}); + +# text informations + $readings{a_headline} = $alertsXml->{info}[$info]{headline}; + $readings{a_description} = $alertsXml->{info}[$info]{description} if($_gdsAll || $_gdsLong); + $readings{a_instruction} = $alertsXml->{info}[$info]{instruction} if($readings{a_responseType} eq "Prepare" + && ($_gdsAll || $_gdsLong)); + +# area informations + $readings{a_areaDesc} = $alertsXml->{info}[$info]{area}[$area]{areaDesc}; + $readings{a_areaPolygon} = $alertsXml->{info}[$info]{area}[$area]{polygon} if($_gdsAll || $_gdsPolygon); + +# area geocode informations +# loop through array + $i = 0; + while(1){ + ($n, $v) = (undef, undef); + $n = $alertsXml->{info}[$info]{area}[$area]{geocode}[$i]{valueName}; + if(!$n) {last;} + $n = "a_geoCode_".$n; + $v = $alertsXml->{info}[$info]{area}[$area]{geocode}[$i]{value}; + $readings{$n} .= $v." " if($v); + $i++; + } + + $readings{a_altitude} = $alertsXml->{info}[$info]{area}[$area]{altitude} if($_gdsAll); + $readings{a_ceiling} = $alertsXml->{info}[$info]{area}[$area]{ceiling} if($_gdsAll); + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "_copyright", "Quelle: Deutscher Wetterdienst"); + while(($k, $v) = each %readings) { readingsBulkUpdate($hash, $k, latin1ToUtf8($v)); } + readingsEndUpdate($hash, 1); + + return; +} + +sub checkCAPValid($){ + my ($expires) = @_; + my $valid = 0; + $expires =~ s/T/ /; + $expires =~ s/\+/ \+/; + $expires = time_str2num($expires); + $valid = 1 if($expires gt time); + return $valid; +} + +sub findCAPWarnCellId($$){ + my ($info, $area) = @_; + my $i = 0; + while($i < 100){ + if($alertsXml->{info}[$info]{area}[$area]{geocode}[$i]{valueName} eq "WARNCELLID"){ + return $alertsXml->{info}[$info]{area}[$area]{geocode}[$i]{value}; + last; + } + $i++; + } +} + +sub retrieveTextWarn($@){ + my ($line, @a); + + open WXDATA, "/tmp/warnings"; + while (chomp($line = )) { push @a, latin1ToUtf8($line); } + close WXDATA; + + return join("\n", @a); +} + +sub retrieveConditions($$@){ + my ($hash, $prefix, @a) = @_; + my $name = $hash->{NAME}; + my $user = $hash->{helper}{USER}; + my $pass = $hash->{helper}{PASS}; + (my $myStation = utf8ToLatin1($a[2])) =~ s/_/ /g; # replace underscore in stationName by space + my $searchLen = length($myStation); + + my (%alignment, $dataFile, $decodeDummy, $debug, @files, $found, $ftp, $item, $line, %pos, %wx, $wx); + + $debug = AttrVal($name, "gdsDebug", 0); + + ($dataFile, $found) = retrieveFile($hash,"conditions"); + open WXDATA, "/tmp/conditions"; + while (chomp($line = )) { + map {s/\r//g;} ($line); + if ($line =~ /Station/) { # Header line... find out data positions + @a = split(/\s+/, $line); + foreach $item (@a) { + $pos{$item} = index($line, $item); + } + } + if (index(substr(lc($line),0,$searchLen), substr(lc($myStation),0,$searchLen)) != -1) { last; } + } + close WXDATA; + + %alignment = ("Station" => "l", "H\xF6he" => "r", "Luftd." => "r", "TT" => "r", "Tmin" => "r", "Tmax" => "r", + "RR1" => "r", "RR24" => "r", "SSS" => "r", "DD" => "r", "FF" => "r", "FX" => "r", "Wetter/Wolken" => "l", "B\xF6en" => "l"); + + foreach $item (@a) { + $wx{$item} = &readItem($line, $pos{$item}, $alignment{$item}, $item); + } + + CommandDeleteReading(undef, "$name $prefix"."_.*"); + + $decodeDummy = latin1ToUtf8($wx{"Station"}); + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, "_copyright", "Quelle: Deutscher Wetterdienst"); + readingsBulkUpdate($hash, "_dF_".$prefix, $dataFile) if($debug); + if(length($decodeDummy) > 1){ + readingsBulkUpdate($hash, "_copyright", "Quelle: Deutscher Wetterdienst"); + readingsBulkUpdate($hash, $prefix."_stationName", $decodeDummy); $decodeDummy = undef; + readingsBulkUpdate($hash, $prefix."_altitude", $wx{"H\xF6he"}); + readingsBulkUpdate($hash, $prefix."_pressure-nn", $wx{"Luftd."}); + readingsBulkUpdate($hash, $prefix."_temperature", $wx{"TT"}); + readingsBulkUpdate($hash, $prefix."_tempMin", $wx{"Tmin"}); + readingsBulkUpdate($hash, $prefix."_tempMax", $wx{"Tmax"}); + readingsBulkUpdate($hash, $prefix."_rain1h", $wx{"RR1"}); + readingsBulkUpdate($hash, $prefix."_rain24h", $wx{"RR24"}); + readingsBulkUpdate($hash, $prefix."_snow", $wx{"SSS"}); + readingsBulkUpdate($hash, $prefix."_windDir", $wx{"DD"}); + readingsBulkUpdate($hash, $prefix."_windSpeed", $wx{"FF"}); + readingsBulkUpdate($hash, $prefix."_windPeak", $wx{"FX"}); + $decodeDummy = latin1ToUtf8($wx{"Wetter\/Wolken"}) if($wx{"Wetter\/Wolken"}); + readingsBulkUpdate($hash, $prefix."_weather", $decodeDummy); $decodeDummy = undef; + $decodeDummy = latin1ToUtf8($wx{"B\xF6en"}) if($wx{"B\xF6en"}); + readingsBulkUpdate($hash, $prefix."_windGust", $decodeDummy); $decodeDummy = undef; + } else { + readingsBulkUpdate($hash, "_copyright", "Quelle: Deutscher Wetterdienst"); + readingsBulkUpdate($hash, $prefix."_stationName", "unknown: $myStation"); $decodeDummy = undef; + } + readingsEndUpdate($hash, 1); + + $hash->{STATE} = "active"; + + return ; +} + +sub retrieveFile($$;$){ +# +# request = type, e.g. alerts, conditions, warnings +# parameter = additional selector, e.g. Bundesland +# + my ($hash, $request, $parameter) = @_; + my $name = $hash->{NAME}; + my $user = $hash->{helper}{USER}; + my $pass = $hash->{helper}{PASS}; + my $debug = AttrVal($name, "gdsDebug",0); + my ($dwd, $dir, $ftp, @files, $dataFile, $targetFile, $found, $readingName); + + + given($request){ + + when("alerts"){ + $dir = "gds/specials/warnings/xml/PVW/"; + $dwd = "Z_CAP*"; + $targetFile = "/tmp/".$request; + break; + } + + when("conditions"){ + $dir = "gds/specials/observations/tables/germany/"; + $dwd = "*"; + $targetFile = "/tmp/".$request; + break; + } + + when("warnings"){ + $dwd = $bula2dwd{$parameter}; + $dir = $dwd2Dir{$dwd}; + $dwd = "VHDL3?_".$dwd."*"; + $dir = "gds/specials/warnings/".$dir."/"; + $targetFile = "/tmp/".$request; + break; + } + } + + $found = 0; + eval { + $ftp = Net::FTP->new("ftp-outgoing2.dwd.de", Debug => 0); + if(defined($ftp)){ + $ftp->login($user, $pass); + $ftp->cwd("$dir"); + @files = undef; + @files = $ftp->ls($dwd); + if(@files){ + @files = sort(@files); + $dataFile = $files[-1]; + $ftp->get($files[-1], $targetFile); + $found = 1; + } else { + $found = 0; + } + $ftp->quit; + } + }; + return ($dataFile, $found); +} + +sub getListStationsDropdown(){ + my ($line, $liste, @a); + + open WXDATA, "/tmp/conditions"; + while (chomp($line = )) { + push @a, trim(substr(latin1ToUtf8($line),0,19)); + } + close WXDATA; + + splice(@a,0,6); + splice(@a,first_index { /Höhe/ } @a); + splice(@a,-1); + @a = sort(@a); + + $liste = join(",", @a); + $liste =~ s/\s+,/,/g; # replace multiple spaces followed by comma with comma + $liste =~ s/\s/_/g; # replace spaces in stationName with underscore for list in frontende + return $liste; +} + +sub readItem { + my ($line, $pos, $align, $item) = @_; + my $x; + + if ($align eq "l") { + $x = substr($line, $pos); + $x =~ s/ .+$//g; # after two spaces => next field + } + if ($align eq "r") { + $pos += length($item); + $x = substr($line, 0, $pos); + $x =~ s/^.+ //g; # remove all before the item + } + return $x; +} + +sub sepLine($) { + my ($len) = @_; + my ($output, $i); + for ($i=0; $i<$len; $i++) { $output .= "-"; } + return $output; +} + + +1; + + +#################################################################################################### +# +# Further informations +# + +# DWD's data format is unpleasant to read, +# since the data columns change depending on the available data +# (e.g. the SSS column for snow disappears when there is no snow). +# It's also in ISO8859-1, i.e. it contains non-ASCII characters. To +# avoid problems, we need some conversion subs in this program. + +# Höhe : m über NN +# Luftd.: reduzierter Luftdruck auf Meereshöhe in hPa +# TT : Lufttemperatur in Grad Celsius +# Tmin : Minimum der Lufttemperatur, 06 UTC Vortag bis 06 UTC heute, Grad Celsius +# Tmax : Maximum der Lufttemperatur, 06 UTC Vortag bis 06 UTC heute, Grad Celsius +# RR1 : Niederschlagsmenge, einstündig, mm = l/qm +# RR24 : Niederschlagsmenge, 24stündig, 06 UTC Vortag bis 06 UTC heute, mm = l/qm +# SSS : Gesamtschneehöhe in cm +# DD : Windrichtung +# FF : Windgeschwindigkeit letztes 10-Minutenmittel in km/h +# FX : höchste Windspitze im Bezugszeitraum in km/h +# --- : Wert nicht vorhanden + + +#################################################################################################### +# +# Documentation +# + +=pod +=begin html + + +

GDS

+
    + + Define +
      + +
      + define <name> GDS <username> <password> +

      + This module provides connection to GDS service generated by DWD
      + +
    +

    + + + Set-Commands
    +
      + +
      + set <name> clear +

      +
        Delete all a_*, c_* and g_* readings
      +
      + + set <name> conditions <stationName> +

      +
        Retrieve current conditions at selected station. Data will be updated periodically.
      +
      + + set <name> help +

      +
        Show a help text with available commands
      +
      + + set <name> rereadcfg +

      +
        Reread all required data from DWD Server manually: station list and CAP data
      +
      + + set <name> update +

      +
        Update conditions readings at selected station and restart update-timer
      +
      + +
    • condition readings generated by SET use prefix "c_"
    • +
    • readings generated by SET will be updated automatically every 60 minutes
    • + +
    +

    + + + Get-Commands
    +
      + +
      + get <name> alerts <region> +

      +
        Retrieve alert message for selected region from DWD server
      +
      + + get <name> conditions <stationName> +

      +
        Retrieve current conditions at selected station
      +
      + + get <name> help +

      +
        Show a help text with available commands
      +
      + + get <name> list data +

      +
        List current conditions for all available stations in one single table
      +
      + + get <name> list stations +

      +
        List all available stations that provide conditions data
      +
      + + get <name> rereadcfg +

      +
        Reread all required data from DWD Server manually: station list and CAP data
      +
      + + get <name> warnings <region> +

      +
        Retrieve current warnings report for selected region
      +
      + +
    • alert readings use prefix "a_"
    • +
    • condition readings generated by GET use prefix "g_"
    • +
    • readings generated by GET will not be updated automatically
    • + +
    +

    + + + Attributes

    +
      + +
    • loglevel
    • +
    • do_not_notify
    • +
    • readingFnAttributes
    • +
      +
    • gdsAll - defines filter for "all data" from alert message
    • +
    • gdsDebug - defines filter for debug informations
    • +
    • gdsLong - show long text fields "description" and "instruction" from alert message in readings
    • +
    • gdsPolygon - show polygon data from alert message in a reading
    • + +
    +

    + + Generated Readings/Events: +

    +
      + +
    • _<readingName> - debug informations
    • +
    • a_<readingName> - weather data from CAP alert messages. Readings will NOT be updated automatically
    • +
    • c_<readingName> - weather data from SET weather conditions. Readings will be updated every 60 minutes
    • +
    • g_<readingName> - weather data from GET weather conditions. Readings will NOT be updated automatically
    • + +
    +

    + + Author's notes

    +
      + +
    • Module uses following additional Perl modules:

      +
        Net::FTP, List::MoreUtils, XML::Simple


      + If not installed in your environment, please install with

      +
        cpan install moduleName
    • +

      +
    • Have fun!

    • + +
    + +
+ +=end html +=cut