| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157 |
- # $Id: 55_GDS.pm 11734 2016-07-03 14:12:48Z betateilchen $
- # copyright and license informations
- =pod
- ###################################################################################################
- #
- # 55_GDS.pm
- #
- # An FHEM Perl module to retrieve data from "Deutscher Wetterdienst"
- #
- # Copyright: betateilchen ®
- #
- # includes: some patches provided by jensb
- # forecasts provided by jensb
- #
- # 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 <http://www.gnu.org/licenses/>.
- #
- ###################################################################################################
- =cut
- package main;
- use strict;
- use warnings;
- use feature qw/switch/;
- use Blocking;
- #use Archive::Extract;
- use Net::FTP;
- use XML::Simple;
- #use Data::Dumper;
- eval "use GDSweblink";
- no if $] >= 5.017011, warnings => 'experimental';
- my ($bulaList, $cmapList, %rmapList, $fmapList, %bula2bulaShort,
- %bulaShort2dwd, %dwd2Dir, %dwd2Name, $alertsXml, %capCityHash,
- %capCellHash, $sList, $aList, $fList, $fcList, $fcmapList, $tempDir, @weekdays);
- my %allForecastData;
- ###################################################################################################
- #
- # Main routines
- sub GDS_Initialize($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
- return "This module must not be used on microso... platforms!" if($^O =~ m/Win/);
- $hash->{DefFn} = "GDS_Define";
- $hash->{UndefFn} = "GDS_Undef";
- $hash->{DeleteFn} = "GDS_Delete";
- $hash->{GetFn} = "GDS_Get";
- $hash->{SetFn} = "GDS_Set";
- $hash->{RenameFn} = "GDS_Rename";
- $hash->{ShutdownFn} = "GDS_Shutdown";
- # $hash->{NotifyFn} = "GDS_Notify";
- $hash->{NOTIFYDEV} = "global";
- $hash->{AttrFn} = "GDS_Attr";
-
- no warnings 'qw';
- my @attrList = qw(
- disable:0,1
- gdsAll:0,1
- gdsDebug:0,1
- gdsFwName
- gdsFwType:0,1,2,3,4,5,6,7
- gdsHideFiles:0,1
- gdsLong:0,1
- gdsPassiveFtp:1,0
- gdsPolygon:0,1
- gdsSetForecast
- gdsUseAlerts:0,1
- gdsUseForecasts:0,1
- );
- use warnings 'qw';
- $hash->{AttrList} = join(" ", @attrList);
- $hash->{AttrList} .= " $readingFnAttributes";
- $tempDir = "/tmp/";
- _fillMappingTables($hash);
- }
- sub _fillMappingTables($){
- my ($hash) = @_;
- $tempDir = "/tmp/";
-
- $aList = "no_data";
- $sList = $aList;
- $fList = $aList;
- $fcList = $aList;
- $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";
- $cmapList = "Deutschland,Mitte,Nordost,Nordwest,Ost,Suedost,Suedwest,West";
- %rmapList = (
- Deutschland => "",
- Mitte => "central/",
- Nordost => "northeast/",
- Nordwest => "northwest/",
- Ost => "east/",
- Suedost => "southeast/",
- Suedwest => "southwest/",
- West => "west/");
- $fmapList = "Deutschland_heute_frueh,Deutschland_heute_mittag,Deutschland_heute_spaet,Deutschland_heute_nacht,".
- "Deutschland_morgen_frueh,Deutschland_morgen_spaet,".
- "Deutschland_ueberm_frueh,Deutschland_ueberm_spaet,".
- "Deutschland_tag4_frueh,Deutschland_tag4_spaet,".
- "Mitte_heute_frueh,Mitte_heute_mittag,Mitte_heute_spaet,Mitte_heute_nacht,".
- "Mitte_morgen_frueh,Mitte_morgen_spaet,".
- "Mitte_ueberm_frueh,Mitte_ueberm_spaet,".
- "Mitte_tag4_frueh,Mitte_tag4_spaet,".
- "Nordost_heute_frueh,Nordost_heute_mittag,Nordost_heute_spaet,Nordost_heute_nacht,".
- "Nordost_morgen_frueh,Nordost_morgen_spaet,".
- "Nordost_ueberm_frueh,Nordost_ueberm_spaet,".
- "Nordost_tag4_frueh,Nordost_tag4_spaet,".
- "Nordwest_heute_frueh,Nordwest_heute_mittag,Nordwest_heute_spaet,Nordwest_heute_nacht,".
- "Nordwest_morgen_frueh,Nordwest_morgen_spaet,".
- "Nordwest_ueberm_frueh,Nordwest_ueberm_spaet,".
- "Nordwest_tag4_frueh,Nordwest_tag4_spaet,".
- "Ost_heute_frueh,Ost_heute_mittag,Ost_heute_spaet,Ost_heute_nacht,".
- "Ost_morgen_frueh,Ost_morgen_spaet,".
- "Ost_ueberm_frueh,Ost_ueberm_spaet,".
- "Ost_tag4_frueh,Ost_tag4_spaet,".
- "Suedost_heute_frueh,Suedost_heute_mittag,Suedost_heute_spaet,Suedost_heute_nacht,".
- "Suedost_morgen_frueh,Suedost_morgen_spaet,".
- "Suedost_ueberm_frueh,Suedost_ueberm_spaet,".
- "Suedost_tag4_frueh,Suedost_tag4_spaet,".
- "Suedwest_heute_frueh,Suedwest_heute_mittag,Suedwest_heute_spaet,Suedwest_heute_nacht,".
- "Suedwest_morgen_frueh,Suedwest_morgen_spaet,".
- "Suedwest_ueberm_frueh,Suedwest_ueberm_spaet,".
- "Suedwest_tag4_frueh,Suedwest_tag4_spaet,".
- "West_heute_frueh,West_heute_mittag,West_heute_spaet,West_heute_nacht,".
- "West_morgen_frueh,West_morgen_spaet,".
- "West_ueberm_frueh,West_ueberm_spaet,".
- "West_tag4_frueh,West_tag4_spaet";
- $fcmapList = "Deutschland_frueh,Deutschland_mittag,Deutschland_spaet,Deutschland_nacht,".
- "Deutschland_morgen_frueh,Deutschland_morgen_spaet,".
- "Deutschland_uebermorgen_frueh,Deutschland_uebermorgen_spaet,".
- "Deutschland_Tag4_frueh,Deutschland_Tag4_spaet,".
- "Mitte_frueh,Mitte_mittag,Mitte_spaet,Mitte_nacht,".
- "Mitte_morgen_frueh,Mitte_morgen_spaet,".
- "Mitte_uebermorgen_frueh,Mitte_uebermorgen_spaet,".
- "Mitte_Tag4_frueh,Mitte_Tag4_spaet,".
- "Nordost_frueh,Nordost_mittag,Nordost_spaet,Nordost_nacht,".
- "Nordost_morgen_frueh,Nordost_morgen_spaet,".
- "Nordost_uebermorgen_frueh,Nordost_uebermorgen_spaet,".
- "Nordost_Tag4_frueh,Nordost_Tag4_spaet,".
- "Nordwest_frueh,Nordwest_mittag,Nordwest_spaet,Nordwest_nacht,".
- "Nordwest_morgen_frueh,Nordwest_morgen_spaet,".
- "Nordwest_uebermorgen_frueh,Nordwest_uebermorgen_spaet,".
- "Nordwest_Tag4_frueh,Nordwest_Tag4_spaet,".
- "Ost_frueh,Ost_mittag,Ost_spaet,Ost_nacht,".
- "Ost_morgen_frueh,Ost_morgen_spaet,".
- "Ost_uebermorgen_frueh,Ost_uebermorgen_spaet,".
- "Ost_Tag4_frueh,Ost_Tag4_spaet,".
- "Suedost_frueh,Suedost_mittag,Suedost_spaet,Suedost_nacht,".
- "Suedost_morgen_frueh,Suedost_morgen_spaet,".
- "Suedost_uebermorgen_frueh,Suedost_uebermorgen_spaet,".
- "Suedost_Tag4_frueh,Suedost_Tag4_spaet,".
- "Suedwest_frueh,Suedwest_mittag,Suedwest_spaet,Suedwest_nacht,".
- "Suedwest_morgen_frueh,Suedwest_morgen_spaet,".
- "Suedwest_uebermorgen_frueh,Suedwest_uebermorgen_spaet,".
- "Suedwest_Tag4_frueh,Suedwest_Tag4_spaet,".
- "West_frueh,West_mittag,West_spaet,West_nacht,".
- "West_morgen_frueh,West_morgen_spaet,".
- "West_uebermorgen_frueh,West_uebermorgen_spaet,".
- "West_Tag4_frueh,West_Tag4_spaet";
- #
- # Bundesländer den entsprechenden Dienststellen zuordnen
- #
- %bula2bulaShort = (
- "baden-württemberg" => "bw",
- "bayern" => "by",
- "berlin" => "be",
- "brandenburg" => "bb",
- "bremen" => "hb",
- "hamburg" => "hh",
- "hessen" => "he",
- "mecklenburg-vorpommern" => "mv",
- "niedersachsen" => "ni",
- "nordrhein-westfalen" => "nw",
- "rheinland-pfalz" => "rp",
- "saarland" => "sl",
- "sachsen" => "sn",
- "sachsen-anhalt" => "st",
- "schleswig-holstein" => "sh",
- "thüringen" => "th",
- "deutschland" => "xde",
- "bodensee" => "xbo" );
- %bulaShort2dwd = (
- bw => "DWSG",
- by => "DWMG",
- be => "DWPG",
- bb => "DWPG",
- hb => "DWHG",
- hh => "DWHH",
- he => "DWOH",
- mv => "DWPH",
- ni => "DWHG",
- nw => "DWEH",
- rp => "DWOI",
- sl => "DWOI",
- sn => "DWLG",
- st => "DWLH",
- sh => "DWHH",
- th => "DWLI",
- xde => "xde",
- xbo => "xbo" );
- #
- # Dienststellen den entsprechenden Serververzeichnissen zuordnen
- #
- %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
- xde => "D",
- xbo => "Bodensee");
- # ???? => "FG" # Freiburg);
- %dwd2Name = (
- EM => "Essen",
- FG => "Freiburg",
- HA => "Hamburg",
- LZ => "Leipzig",
- MS => "München",
- OF => "Offenbach",
- PD => "Potsdam",
- SU => "Stuttgart");
-
- # German weekdays
- @weekdays = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa");
-
- return;
- }
- sub GDS_Define($$$) {
- my ($hash, $def) = @_;
- my $name = $hash->{NAME};
- my @a = split("[ \t][ \t]*", $def);
- my $skuser = getKeyValue($name."_user");
- my $skpass = getKeyValue($name."_pass");
- my $skhost = getKeyValue($name."_host");
- unless(defined($skuser) && defined($skpass)) {
- return "syntax: define <name> GDS <username> <password> [<host>]" if(int(@a) < 4 );
- }
- $hash->{helper}{URL} = defined($a[4]) ? $a[4] : "ftp-outgoing2.dwd.de";
- $hash->{helper}{INTERVAL} = 1200;
- setKeyValue($name."_user",$a[2]) unless(defined($skuser));
- setKeyValue($name."_pass",$a[3]) unless(defined($skpass));
- setKeyValue($name."_host",$hash->{helper}{URL}) unless(defined($skhost));
- $hash->{DEF} = undef;
- Log3($name, 4, "GDS $name: created");
- Log3($name, 4, "GDS $name: tempDir=".$tempDir);
- _GDS_addExtension("GDS_CGI","gds","GDS Files");
- # $hash->{firstrun} = 1;
- # retrieveData($hash,'conditions') unless (time > 1457996400);
- # delete $hash->{firstrun};
- readingsSingleUpdate($hash, 'state', 'active',1);
- return undef;
- }
- sub GDS_Undef($$) {
- my ($hash, $arg) = @_;
- my $name = $hash->{NAME};
- RemoveInternalTimer($hash);
- my $url = '/gds';
- delete $data{FWEXT}{$url} if int(devspec2array('TYPE=GDS')) == 1;
- return undef;
- }
- sub GDS_Delete() {
- my ($hash, $arg) = @_;
- my $name = $hash->{NAME};
- setKeyValue($name."_user",undef);
- setKeyValue($name."_pass",undef);
- setKeyValue($name."_host",undef);
- }
- sub GDS_Rename() {
- my ($new,$old) = @_;
- setKeyValue($new."_user",getKeyValue($old."_user"));
- setKeyValue($new."_pass",getKeyValue($old."_pass"));
- setKeyValue($new."_host",getKeyValue($old."_host"));
- setKeyValue($old."_user",undef);
- setKeyValue($old."_pass",undef);
- setKeyValue($old."_host",undef);
- return undef;
- }
- sub GDS_Shutdown($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
- RemoveInternalTimer($hash);
- Log3 ($name,4,"GDS $name: shutdown requested");
- return undef;
- }
- sub GDS_Notify ($$) {
- my ($hash,$dev) = @_;
- my $name = $hash->{NAME};
- return if($dev->{NAME} ne "global");
- my $type = $dev->{CHANGED}[0];
- return unless (grep(m/^INITIALIZED/, $type));
- $aList = "disabled_by_attribute" unless AttrVal($name,'gdsUseAlerts',0);
- $fList = "disabled_by_attribute" unless AttrVal($name,'gdsUseForecasts',0);
- $fcmapList = "disabled_by_attribute" unless AttrVal($name,'gdsUseForecasts',0);
- GDS_GetUpdate($hash);
-
- return undef;
- }
- sub GDS_Set($@) {
- my ($hash, @a) = @_;
- my $name = $hash->{NAME};
- my $usage = "Unknown argument, choose one of ".
- "clear:alerts,conditions,forecasts,all ".
- "forecasts:$fList ".
- "help:noArg ".
- "update:noArg "; ;
- my $command = lc($a[1]);
- my $parameter = $a[2] if(defined($a[2]));
- my ($result, $next);
- return $usage if $command eq '?';
- if(IsDisabled($name)) {
- readingsSingleUpdate($hash, 'state', 'disabled', 0);
- return "GDS $name is disabled. Aborting..." if IsDisabled($name);
- }
- readingsSingleUpdate($hash, 'state', 'active', 0);
- given($command) {
- when("clear"){
- CommandDeleteReading(undef, "$name a_.*")
- if(defined($parameter) && ($parameter eq "all" || $parameter eq "alerts"));
- CommandDeleteReading(undef, "$name c_.*")
- if(defined($parameter) && ($parameter eq "all" || $parameter eq "conditions"));
- CommandDeleteReading(undef, "$name g_.*")
- if(defined($parameter) && ($parameter eq "all" || $parameter eq "conditions"));
- CommandDeleteReading(undef, "$name fc.?_.*")
- if(defined($parameter) && ($parameter eq "all" || $parameter eq "forecasts"));
- }
- when("help"){
- $result = setHelp();
- break;
- }
- when("update"){
- RemoveInternalTimer($hash);
- GDS_GetUpdate($hash,'set update');
- break;
- }
- when("forecasts"){
- return "Error: Forecasts disabled by attribute." unless AttrVal($name,'gdsUseForecasts',0);
- CommandDeleteReading(undef, "$name fc.*") if($parameter ne AttrVal($name,'gdsSetForecast',''));
- $attr{$name}{gdsSetForecast} = $parameter;
- GDS_GetUpdate($hash,'set forecasts');
- break;
- }
- default { return $usage; };
- }
- return $result;
- }
- sub GDS_Get($@) {
- my ($hash, @a) = @_;
- my $command = lc($a[1]);
- my $parameter = $a[2] if(defined($a[2]));
- my $name = $hash->{NAME};
- my $usage = "Unknown argument $command, choose one of help:noArg rereadcfg:noArg ".
- "list:capstations ".
- "alerts:".$aList." ".
- "conditionsmap:".$cmapList." ".
- "forecasts:".$fcList." ".
- "forecastsmap:".$fmapList." ".
- "headlines ".
- "radarmap:".$cmapList." ".
- "warningsmap:"."Deutschland,Bodensee,".$bulaList." ".
- "warnings:".$bulaList;
- return $usage if $command eq '?';
- if(IsDisabled($name)) {
- readingsSingleUpdate($hash, 'state', 'disabled', 0);
- return "GDS $name is disabled. Aborting..." if IsDisabled($name);
- }
- readingsSingleUpdate($hash, 'state', 'active', 0);
- my $_gdsAll = AttrVal($name,"gdsAll", 0);
- my $gdsDebug = AttrVal($name,"gdsDebug", 0);
- my ($result, @datensatz, $found);
- given($command) {
- when("conditionsmap"){
- # retrieve map: current conditions
- $hash->{file}{dir} = "gds/specials/observations/maps/germany/";
- $hash->{file}{dwd} = $parameter."*";
- $hash->{file}{target} = $tempDir.$name."_conditionsmap.jpg";
- retrieveData($hash,'FILE');
- break;
- }
- when("forecastsmap"){
- # retrieve map: forecasts
- $hash->{file}{dir} = "gds/specials/forecasts/maps/germany/";
- $hash->{file}{dwd} = $parameter."*";
- $hash->{file}{target} = $tempDir.$name."_forecastsmap.jpg";
- retrieveData($hash,'FILE');
- break;
- }
- when("headlines"){
- return "Error: Alerts disabled by attribute." unless AttrVal($name,'gdsUseAlerts',0);
- $parameter //= "|";
- return gdsAlertsHeadlines($name,$parameter);
- }
- when("warningsmap"){
- # retrieve map: warnings
- if(length($parameter) != 2){
- $parameter = $bula2bulaShort{lc($parameter)};
- }
- $hash->{file}{dwd} = "Schilder".$dwd2Dir{$bulaShort2dwd{lc($parameter)}}.".jpg";
- $hash->{file}{dir} = "gds/specials/alerts/maps/";
- $hash->{file}{target} = $tempDir.$name."_warningsmap.jpg";
- retrieveData($hash,'FILE');
- break;
- }
- when("radarmap"){
- # retrieve map: radar
- $parameter = ucfirst($parameter);
- $hash->{file}{dir} = "gds/specials/radar/".$rmapList{$parameter};
- $hash->{file}{dwd} = "Webradar_".$parameter."*";
- $hash->{file}{target} = $tempDir.$name."_radarmap.jpg";
- retrieveData($hash,'FILE');
- break;
- }
- when("help"){
- $result = getHelp();
- break;
- }
- when("list"){
- given($parameter){
- when("capstations") {
- return "Error: Alerts disabled by attribute." unless AttrVal($name,'gdsUseAlerts',0);
- $result = getListCapStations($hash,$parameter); }
- default { $usage = "get <name> list <parameter>"; return $usage; }
- }
- break;
- }
- when("alerts"){
- return "Error: Alerts disabled by attribute." unless AttrVal($name,'gdsUseAlerts',0);
- if($parameter =~ y/0-9// == length($parameter)){
- while ( my( $key, $val ) = each %capCellHash ) {
- push @datensatz,$val if $key =~ m/^$parameter/;
- }
- # push @datensatz,$capCellHash{$parameter};
- } else {
- push @datensatz,$capCityHash{$parameter};
- }
- CommandDeleteReading(undef, "$name a_.*");
- if($datensatz[0]){
- my $anum = 0;
- foreach(@datensatz) {
- decodeCAPData($hash,$_,$anum);
- $anum++;
- };
- readingsSingleUpdate($hash,'a_count',$anum,1);
- } else {
- $result = "Keine Warnmeldung für die gesuchte Region vorhanden.";
- }
- my $_gdsAll = AttrVal($name,"gdsAll", 0);
- my $gdsDebug = AttrVal($name,"gdsDebug", 0);
- break;
- }
- when("rereadcfg"){
- DoTrigger($name, "REREAD", 1);
- $hash->{GDS_REREAD} = int(time());
- if (AttrVal($name,'gdsUseAlerts',0)) {
- %capCityHash = ();
- %capCellHash = ();
- retrieveData($hash,'capdata');
- retrieveListCapStations($hash);
- }
- retrieveData($hash,'forecast') if AttrVal($name,'gdsUseForecasts',0);
- # GDS_GetUpdate($hash);
- break;
- }
- when("warnings"){
- my $vhdl;
- $result = " VHDL30 = current | VHDL31 = weekend or holiday\n".
- " VHDL32 = preliminary | VHDL33 = cancel VHDL32\n".
- sepLine(31)."+".sepLine(38)."\n";
- if(length($parameter) != 2){
- $parameter = $bula2bulaShort{lc($parameter)};
- }
- my $dwd = $bulaShort2dwd{lc($parameter)};
- my $dir = "gds/specials/warnings/".$dwd2Dir{$dwd}."/";
- $hash->{file}{dir} = $dir;
- for ($vhdl=30; $vhdl <=33; $vhdl++){
- my $dwd2 = "VHDL".$vhdl."_".$dwd."*";
- my $target = $tempDir.$name."_warnings_$vhdl";
- unlink $target;
- $hash->{file}{dwd} = $dwd2;
- $hash->{file}{target} = $target;
- retrieveData($hash,'FILE');
- }
- sleep 2;
- for ($vhdl=30; $vhdl <=33; $vhdl++){
- my $target = $tempDir.$name."_warnings_$vhdl";
- $result .= retrieveText($hash, "warnings_$vhdl", "") if (-e $target);
- $result .= "\n".sepLine(70);
- }
- $result .= "\n\n";
- break;
- }
- when("forecasts"){
- return "Error: Forecasts disabled by attribute." unless AttrVal($name,'gdsUseForecasts',0);
- $parameter = "Daten_$parameter";
- my ($k,$v,$data);
- $result = sepLine(67)."\n";
- # retrieve from hash
- $data = undef;
- while(($k, $v) = each %allForecastData){
- if ($k eq $parameter) {
- $data = $v;
- last;
- };
- }
- $data //= "No forecast data found.";
- $data =~ s/\$/\n/g;
- $result .= $data;
- $result .= "\n".sepLine(67)."\n";
- break;
- }
- default { return $usage; };
- }
- return $result;
- }
- sub GDS_Attr(@){
- my @a = @_;
- my $hash = $defs{$a[1]};
- my ($cmd, $name, $attrName, $attrValue) = @a;
- $attrValue //= '';
- my $useUpdate = 0;
- given($attrName){
- when("gdsUseAlerts"){
- if ($attrValue == 0 || $cmd eq 'del') {
- $aList = "disabled_by_attribute";
- } else {
- $aList = "data_retrieval_running";
- retrieveData($hash,'capdata');
- retrieveListCapStations($hash);
- }
- }
- when("gdsUseForecasts"){
- if ($attrValue == 0 || $cmd eq 'del') {
- $fList = "disabled_by_attribute";
- $fcList = "disabled_by_attribute";
- } else {
- $fcList = $fcmapList;
- $fList = "data_retrieval_running";
- $attr{$name}{$attrName} = $attrValue;
- retrieveData($hash,'forecast');
- $useUpdate = 1;
- }
- }
- when("gdsHideFiles"){
- my $hR = AttrVal($FW_wname,'hiddenroom','');
- $hR =~ s/\,GDS.Files//g;
- if($attrValue) {
- $hR .= "," if(length($hR));
- $hR .= "GDS Files";
- }
- CommandAttr(undef,"$FW_wname hiddenroom $hR");
- break;
- }
- default {$attr{$name}{$attrName} = $attrValue;}
- }
- if(IsDisabled($name)) {
- readingsSingleUpdate($hash, 'state', 'disabled', 0);
- } else {
- readingsSingleUpdate($hash, 'state', 'active', 0);
- if ($useUpdate) {
- RemoveInternalTimer($hash);
- my $next = gettimeofday()+$hash->{helper}{INTERVAL};
- InternalTimer($next, "GDS_GetUpdate", $hash, 0);
- }
- }
- return;
- }
- sub GDS_GetUpdate($;$) {
- my ($hash,$local) = @_;
- $local //= 0;
- my $name = $hash->{NAME};
- RemoveInternalTimer($hash);
- my $fs = AttrVal($name, "gdsSetForecast", 0);
- if(IsDisabled($name)) {
- readingsSingleUpdate($hash, 'state', 'disabled', 0);
- Log3 ($name, 2, "GDS $name is disabled, data update cancelled.");
- } else {
- readingsSingleUpdate($hash, 'state', 'active', 0);
- if($fs) {
- retrieveData($hash,'forecast') ;
- my @a;
- push @a, undef;
- push @a, undef;
- push @a, $fs;
- retrieveForecasts($hash, "fc", @a);
- }
- }
- # schedule next update
- my $next = gettimeofday()+$hash->{helper}{INTERVAL};
- my $gdsAll = AttrVal($name,"gdsAll", 0);
- my $gdsDebug = AttrVal($name,"gdsDebug", 0);
- InternalTimer($next, "GDS_GetUpdate", $hash, 1);
- readingsSingleUpdate($hash, "_nextUpdate", localtime($next), 1) if($gdsAll || $gdsDebug);
- return 1;
- }
- ###################################################################################################
- #
- # FWEXT implementation
- sub _GDS_addExtension($$$) {
- my ($func,$link,$friendlyname)= @_;
-
- my $url = "/" . $link;
- Log3(undef,4,"Register gds webservice in FWEXT");
- $data{FWEXT}{$url}{FUNC} = $func;
- $data{FWEXT}{$url}{LINK} = "+$link";
- $data{FWEXT}{$url}{NAME} = $friendlyname;
- $data{FWEXT}{$url}{FORKABLE} = 0;
- }
- sub GDS_CGI {
- my ($request) = @_;
- my ($name,$ext)= _GDS_splitRequest($request);
- if(defined($name)) {
- my $filename= "$tempDir/$name.$ext";
- my $MIMEtype= filename2MIMEType($filename);
- my @contents;
- if(open(INPUTFILE, $filename)) {
- binmode(INPUTFILE);
- @contents= <INPUTFILE>;
- close(INPUTFILE);
- return("$MIMEtype; charset=utf-8", join("", @contents));
- } else {
- return("text/plain; charset=utf-8", "File not found: $filename");
- }
- } else {
- return _GDS_Overview();
- }
- }
- sub _GDS_splitRequest($) {
- my ($request) = @_;
- if($request =~ /^.*\/gds$/) {
- # http://localhost:8083/fhem/gds2
- return (undef,undef); # name, ext
- } else {
- my $call= $request;
- $call =~ s/^.*\/gds\/([^\/]*)$/$1/;
- my $name= $call;
- $name =~ s/^(.*)\.(jpg)$/$1/;
- my $ext= $call;
- $ext =~ s/^$name\.(.*)$/$1/;
- return ($name,$ext);
- }
- }
- sub _GDS_Overview {
- my ($name, $url);
- my $html= __GDS_HTMLHead("GDS Overview") . "<body>\n\n";
- foreach my $def (sort keys %defs) {
- if($defs{$def}{TYPE} eq "GDS") {
- $name = $defs{$def}{NAME};
- $url = __GDS_getURL();
- $html .= "$name<br>\n<ul>\n";
- $html .= "<a href=\"$url/gds/$name\_conditionsmap.jpg\" target=\"_blank\">Aktuelle Wetterkarte: Wetterlage</a><br/>\n";
- $html .= "<a href=\"$url/gds/$name\_forecastsmap.jpg\" target=\"_blank\">Aktuelle Wetterkarte: Vorhersage</a><br/>\n";
- $html .= "<a href=\"$url/gds/$name\_warningsmap.jpg\" target=\"_blank\">Aktuelle Wetterkarte: Warnungen</a><br/>\n";
- $html .= "<a href=\"$url/gds/$name\_radarmap.jpg\" target=\"_blank\">Aktuelle Wetterkarte: Radarkarte</a><br/>\n";
- $html .= "</ul>\n\n";
- }
- }
- $html.="</body>\n" . __GDS_HTMLTail();
- return ("text/html; charset=utf-8", $html);
- }
- sub __GDS_HTMLHead($) {
- my ($title) = @_;
- my $doctype = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" '.
- '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
- my $xmlns = 'xmlns="http://www.w3.org/1999/xhtml"';
- my $code = "$doctype\n<html $xmlns>\n<head>\n<title>$title</title>\n</head>\n";
- return $code;
- }
- sub __GDS_getURL {
- my $proto = (AttrVal($FW_wname, 'HTTPS', 0) == 1) ? 'https' : 'http';
- return $proto."://$FW_httpheader{Host}$FW_ME"; #".$FW_ME;
- }
- sub __GDS_HTMLTail {
- return "</html>";
- }
- ###################################################################################################
- #
- # Tools
- sub gdsAlertsHeadlines($;$) {
- my ($d,$sep) = @_;
- my $text = "";
- $sep = (defined($sep)) ? $sep : '|';
- my $count = ReadingsVal($d,'a_count',0);
- for (my $i = 0; $i < $count; $i++) {
- $text .= $sep if $i;
- $text .= ReadingsVal('gds','a_'.$i.'_headline','')
- }
- return $text;
- }
- sub gdsHeadlines($;$) {
- my $text = "GDS error: gdsHeadlines() is deprecated. Please use gdsAlertsHeadlines()";
- Log 1, $text;
- return $text;
- }
- sub setHelp(){
- return "Use one of the following commands:\n".
- sepLine(35)."\n".
- "set <name> clear alerts|all\n".
- "set <name> forecasts <regionName>/<stationName>\n".
- "set <name> help\n".
- "set <name> rereadcfg\n".
- "set <name> update\n";
- }
- sub getHelp(){
- return "Use one of the following commands:\n".
- sepLine(35)."\n".
- "get <name> alerts <region>\n".
- "get <name> forecasts <regionName>\n".
- "get <name> help\n".
- "get <name> list capstations|data\n".
- "get <name> rereadcfg\n".
- "get <name> warnings <region>\n";
- }
- sub sepLine($;$) {
- my ($len,$c) = @_;
- $c //= '-';
- my ($output, $i);
- for ($i=0; $i<$len; $i++) { $output .= $c; }
- return $output;
- }
- sub _readDir($) {
- my ($destinationDirectory) = @_;
- eval { opendir(DIR,$destinationDirectory) or warn "$!"; };
- if ($@) {
- Log3(undef,1,'GDS: file system error '.$@);
- return ("");
- }
- my @files = readdir(DIR);
- close(DIR);
- return @files;
- }
- sub retrieveText($$$) {
- my ($hash, $fileName, $separator) = @_;
- my $name = $hash->{NAME};
- my ($err,@a);
- $fileName = $tempDir.$name."_$fileName";
- ($err,@a) = FileRead({FileName=>$fileName,ForceType=>"file" });
- return "GDS error reading $fileName" if($err);
- @a = map (latin1ToUtf8($_), @a);
- return join($separator, @a);
- }
- sub gds_calctz($@) {
- my ($nt,@lt) = @_;
- my $off = $lt[2]*3600+$lt[1]*60+$lt[0];
- $off = 12*3600-$off;
- $nt += $off; # This is noon, localtime
- my @gt = gmtime($nt);
- return (12-$gt[2]);
- }
- ###################################################################################################
- #
- # Data retrieval (internal)
- sub getListCapStations($$){
- my ($hash, $command) = @_;
- my $name = $hash->{NAME};
- my (%capHash, $file, @columns, $key, $cList, $count);
- $file = $tempDir.'capstations.csv';
- return "GDS error: $file not found." unless(-e $file);
- # CSV öffnen und parsen
- my ($err,@a) = FileRead({FileName=>$file,ForceType=>"file" });
- return "GDS error reading $file" if($err);
- foreach my $l (@a) {
- next if (substr($l,0,1) eq '#');
- @columns = split(";",$l);
- $capHash{latin1ToUtf8($columns[4])} = $columns[0];
- }
- # Ausgabe sortieren und zusammenstellen
- foreach $key (sort keys %capHash) {
- $cList .= $capHash{$key}."\t".$key."\n";
- }
- return $cList;
- }
- sub retrieveListCapStations($){
- my ($hash) = @_;
- $hash->{file}{dir} = "gds/help/";
- $hash->{file}{dwd} = "legend_warnings_CAP_WarnCellsID.csv";
- $hash->{file}{target} = $tempDir."capstations.csv";
- unless(-e $hash->{file}{target}) {
- retrieveData($hash,'FILE');
- } else {
- # read capstationslist once a day
- my $alter = time() - (stat($hash->{file}{target}))[9];
- retrieveData($hash,'FILE') if ($alter > 86400);
- }
- }
- sub decodeCAPData($$$){
- my ($hash, $datensatz, $anum) = @_;
- my $name = $hash->{NAME};
- my $info = 9999; # to be deleted
- my $alert = int($datensatz/100);
- my $area = $datensatz-$alert*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);
- Log3($name, 4, "GDS $name: Decoding CAP record #".$datensatz);
- # topLevel informations
- if($gdsAll || $gdsDebug) {
- @dummy = split(/\./, $alertsXml->{alert}[$alert]{identifier});
- $readings{"a_".$anum."_identifier"} = $alertsXml->{alert}[$alert]{identifier};
- $readings{"a_".$anum."_idPublisher"} = $dummy[5];
- $readings{"a_".$anum."_idSysten"} = $dummy[6];
- $readings{"a_".$anum."_idTimeStamp"} = $dummy[7];
- $readings{"a_".$anum."_idIndex"} = $dummy[8];
- }
-
- $readings{"a_".$anum."_sent"} = $alertsXml->{alert}[$alert]{sent};
- $readings{"a_".$anum."_status"} = $alertsXml->{alert}[$alert]{status};
- $readings{"a_".$anum."_msgType"} = $alertsXml->{alert}[$alert]{msgType};
- # infoSet informations
- if($gdsAll || $gdsDebug) {
- $readings{"a_".$anum."_language"} = $alertsXml->{alert}[$alert]{info}{language};
- $readings{"a_".$anum."_urgency"} = $alertsXml->{alert}[$alert]{info}{urgency};
- $readings{"a_".$anum."_severity"} = $alertsXml->{alert}[$alert]{info}{severity};
- $readings{"a_".$anum."_certainty"} = $alertsXml->{alert}[$alert]{info}{certainty};
- }
-
- $readings{"a_".$anum."_category"} = $alertsXml->{alert}[$alert]{info}{category};
- $readings{"a_".$anum."_event"} = $alertsXml->{alert}[$alert]{info}{event};
- $readings{"a_".$anum."_responseType"} = $alertsXml->{alert}[$alert]{info}{responseType};
- # eventCode informations
- # loop through array
- $i = 0;
- while(1){
- ($n, $v) = (undef, undef);
- $n = $alertsXml->{alert}[$alert]{info}{eventCode}[$i]{valueName};
- if(!$n) {last;}
- $n = "a_".$anum."_eventCode_".$n;
- $v = $alertsXml->{alert}[$alert]{info}{eventCode}[$i]{value};
- $readings{$n} .= $v." " if($v);
- $i++;
- }
- # time/validity informations
- $readings{"a_".$anum."_effective"} = $alertsXml->{alert}[$alert]{info}{effective} if($gdsAll);
- $readings{"a_".$anum."_onset"} = $alertsXml->{alert}[$alert]{info}{onset};
- $readings{"a_".$anum."_expires"} = $alertsXml->{alert}[$alert]{info}{expires};
- $readings{"a_".$anum."_valid"} = _checkCAPValid($readings{"a_".$anum."_onset"},$readings{"a_".$anum."_expires"});
- $readings{"a_".$anum."_onset_local"} = _capTrans($readings{"a_".$anum."_onset"});
- $readings{"a_".$anum."_expires_local"} = _capTrans($readings{"a_".$anum."_expires"})
- if(defined($alertsXml->{alert}[$alert]{info}{expires}));
- $readings{"a_".$anum."_sent_local"} = _capTrans($readings{"a_".$anum."_sent"});
- $readings{a_valid} = ReadingsVal($name,'a_valid',0) || $readings{"a_".$anum."_valid"};
- # text informations
- $readings{"a_".$anum."_headline"} = $alertsXml->{alert}[$alert]{info}{headline};
- $readings{"a_".$anum."_description"} = $alertsXml->{alert}[$alert]{info}{description} if($gdsAll || $gdsLong);
- $readings{"a_".$anum."_instruction"} = $alertsXml->{alert}[$alert]{info}{instruction}
- if($readings{"a_".$anum."_responseType"} eq "Prepare" & ($gdsAll || $gdsLong));
- # area informations
- $readings{"a_".$anum."_areaDesc"} = $alertsXml->{alert}[$alert]{info}{area}[$area]{areaDesc};
- $readings{"a_".$anum."_areaPolygon"} = $alertsXml->{alert}[$alert]{info}{area}[$area]{polygon} if($gdsAll || $gdsPolygon);
- # area geocode informations
- # loop through array
- $i = 0;
- while(1){
- ($n, $v) = (undef, undef);
- $n = $alertsXml->{alert}[$alert]{info}{area}[$area]{geocode}[$i]{valueName};
- if(!$n) {last;}
- $n = "a_".$anum."_geoCode_".$n;
- $v = $alertsXml->{alert}[$alert]{info}{area}[$area]{geocode}[$i]{value};
- $readings{$n} .= $v." " if($v);
- $i++;
- }
- $readings{"a_".$anum."_altitude"} = $alertsXml->{alert}[$alert]{info}{area}[$area]{altitude} if($gdsAll);
- $readings{"a_".$anum."_ceiling"} = $alertsXml->{alert}[$alert]{info}{area}[$area]{ceiling} if($gdsAll);
- readingsBeginUpdate($hash);
- readingsBulkUpdate($hash, "_dataSource", "Quelle: Deutscher Wetterdienst");
- while(($k, $v) = each %readings){
- # skip update if no valid data is available
- next unless(defined($v));
- readingsSingleUpdate($hash, $k, latin1ToUtf8($v),1);
- }
- # convert color value to hex
- my $r = ReadingsVal($name, 'a_'.$anum.'_eventCode_AREA_COLOR', '');
- if(length($r)) {
- my $v = sprintf( "%02x%02x%02x", split(" ", $r));
- readingsSingleUpdate($hash, 'a_'.$anum.'_eventCode_AREA_COLOR_hex', $v,1);
- }
-
- readingsEndUpdate($hash, 1);
- return;
- }
- sub _checkCAPValid($$;$$){
- my ($onset,$expires,$t,$tmax) = @_;
- my $valid = 0;
-
- $t = time() if (!defined($t));
- my $offset = gds_calctz($t,localtime($t))*3600;
- $t -= $offset;
- $tmax -= $offset if (defined($tmax));
- $onset =~ s/T/ /;
- $onset =~ s/\+/ \+/;
- $onset = time_str2num($onset);
- $expires =~ s/T/ /;
- $expires =~ s/\+/ \+/;
- $expires = time_str2num($expires);
- if (defined($tmax)) {
- $valid = 1 if($tmax ge $onset && $t lt $expires);
- } else {
- $valid = 1 if($onset lt $t && $expires gt $t);
- }
- return $valid;
- }
- sub _capTrans($) {
- my ($t) = @_;
- my $valid = 0;
- my $offset = gds_calctz(time,localtime(time))*3600; # used from 99_SUNRISE_EL
- $t =~ s/T/ /;
- $t =~ s/\+/ \+/;
- $t = time_str2num($t);
- my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($t+$offset);
- $mon += 1;
- $year += 1900;
- $t = sprintf "%02s.%02s.%02s %02s:%02s:%02s", $mday, $mon, $year, $hour, $min, $sec;
- return $t;
- }
- ###################################################################################################
- #
- # nonblocking data retrieval
- sub retrieveData($$){
- my ($hash,$req) = @_;
- $req = uc($req);
- if ($req eq "FORECAST") {
- my $busyTag = "GDS_".$req."_BUSY";
- if (defined($hash->{$busyTag})) {
- return;
- } else {
- $hash->{$busyTag} = localtime(time());
- }
- }
- my $tag = "GDS_".$req."_READ";
- delete $hash->{$tag};
- $tag = "GDS_".$req."_ABORTED";
- delete $hash->{$tag};
- BlockingCall("_retrieve$req",$hash,"_finished$req",60,"_aborted$req",$hash);
- }
- # any file
- sub _retrieveFILE {
- my ($hash) = shift;
- my $name = $hash->{NAME};
- my $user = getKeyValue($name."_user");
- my $pass = getKeyValue($name."_pass");
- my $host = getKeyValue($name."_host");
- my $proxyName = AttrVal($name, "gdsProxyName", "");
- my $proxyType = AttrVal($name, "gdsProxyType", "");
- my $passive = AttrVal($name, "gdsPassiveFtp", 1);
- my $dir = $hash->{file}{dir};
- my $dwd = $hash->{file}{dwd};
- my $target = $hash->{file}{target};
- my $ret = "";
- eval {
- my $ftp = Net::FTP->new( $host,
- Debug => 0,
- Timeout => 10,
- Passive => $passive,
- FirewallType => $proxyType,
- Firewall => $proxyName);
- if(defined($ftp)){
- Log3($name, 4, "GDS $name: ftp connection established.");
- $ftp->login($user, $pass);
- $ftp->binary;
- $ftp->cwd($dir);
- my @files = $ftp->ls($dwd);
- if(@files) {
- @files = sort(@files);
- $dwd = $files[-1];
- Log3($name, 4, "GDS $name: file found.");
- Log3($name, 4, "GDS $name: retrieving $dwd");
- if(defined($target)) {
- $ftp->get($dwd,$target);
- my $s = -s $target;
- Log3($name, 4, "GDS: ftp transferred $s bytes");
- } else {
- my ($file_content,$file_handle);
- open($file_handle, '>', \$file_content);
- $ftp->get($dwd,$file_handle);
- $file_content = latin1ToUtf8($file_content);
- $file_content =~ s/\r\n/\$/g;
- $ret = $file_content;
- }
- }
- $ftp->quit;
- }
- };
- return "$name;;;$dwd;;;$ret";
- }
- sub _finishedFILE {
- my ($name,$file,$ret) = split(/;;;/,shift); #@_;
- my $hash = $defs{$name};
- DoTrigger($name,"REREADFILE $file",1);
- }
- sub _abortedFILE {
- my ($hash) = shift;
- }
- # CapData
- sub _retrieveCAPDATA {
- my ($hash) = shift;
- my $name = $hash->{NAME};
- my $user = getKeyValue($name."_user");
- my $pass = getKeyValue($name."_pass");
- my $host = getKeyValue($name."_host");
- my $proxyName = AttrVal($name, "gdsProxyName", "");
- my $proxyType = AttrVal($name, "gdsProxyType", "");
- my $passive = AttrVal($name, "gdsPassiveFtp", 1);
- my $dir = "gds/specials/alerts/cap/GER/status/";
- my $dwd = "Z_CAP*";
- my $datafile = "";
- my $targetDir = $tempDir.$name."_alerts.dir";
- my $targetFile = $tempDir.$name."_alerts.zip";
- mkdir $targetDir unless -d $targetDir;
- # delete archive file
- unlink $targetFile;
- eval {
- my $ftp = Net::FTP->new( $host,
- Debug => 0,
- Timeout => 10,
- Passive => $passive,
- FirewallType => $proxyType,
- Firewall => $proxyName);
- if(defined($ftp)){
- Log3($name, 4, "GDS $name: ftp connection established.");
- $ftp->login($user, $pass);
- $ftp->binary;
- $ftp->cwd("$dir");
- my @files = $ftp->ls($dwd);
- if(@files) {
- Log3($name, 4, "GDS $name: filelist found.");
- @files = sort(@files);
- $datafile = $files[-1];
- Log3($name, 5, "GDS $name: retrieving $datafile");
- $ftp->get($datafile,$targetFile);
- my $s = -s $targetFile;
- Log3($name, 5, "GDS: ftp transferred $s bytes");
- }
- $ftp->quit;
- }
- };
-
- # delete old files in directory
- if (-d $targetDir) {
- my @remove = _readDir($targetDir);
- foreach my $f (@remove){
- next if -d $f;
- next if $targetFile =~ m/$f$/;
- Log3($name, 4, "GDS $name: deleting $targetDir/$f");
- unlink("$targetDir/$f");
- }
- }
- # unzip
- qx(unzip -o $targetFile -d $targetDir);
- # merge
- my ($countInfo,$cF) = _mergeCapFile($hash);
- my ($aList,$cellData) = _buildCAPList($hash,$countInfo,$cF);
- unlink $targetFile unless AttrVal($name,'gdsDebug',0);
- return "$name;;;$datafile;;;$aList;;;$cF;;;$cellData";
- }
- sub _finishedCAPDATA {
- my ($name,$datafile,$aL,$capFile,$cellData) = split(/;;;/,shift);
- my $hash = $defs{$name};
- $aList = $aL;
- my @h = split(/;;/,$cellData);
- foreach(@h) {
- my ($n,$city,$cell) = split(/:/,$_);
- $capCityHash{$city} = $n;
- $capCellHash{"$cell$n"} = $n;
- }
- my $xml = new XML::Simple;
- eval {
- $alertsXml = $xml->XMLin($capFile, KeyAttr => {}, ForceArray => [ 'alert', 'eventCode', 'area', 'geocode' ]);
- };
- if ($@) {
- Log3($name,1,'GDS: error analyzing alerts XML:'.$@);
- return;
- }
- readingsSingleUpdate($hash, "_dF_alerts",$datafile,0) if(AttrVal($name, "gdsDebug", 0));
- $hash->{GDS_CAPDATA_READ} = int(time());
- DoTrigger($name,"REREADALERTS",1);
- }
- sub _abortedCAPDATA {
- my ($hash) = shift;
- delete $hash->{GDS_CAPDATA_READ};
- $hash->{GDS_CAPDATA_ABORTED} = localtime(time());
- }
- sub _mergeCapFile($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
-
- my $destinationDirectory = $tempDir.$name."_alerts.dir";
- my @capFiles = _readDir($destinationDirectory);
- my @alertsArray;
- my $xmlHeader = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>';
- push (@alertsArray,$xmlHeader);
- push (@alertsArray,"<gds>");
- my $countInfo = 0;
-
- foreach my $cF (@capFiles){
- # merge all capFiles
- $cF = $destinationDirectory."/".$cF;
- next if -d $cF;
- next unless -s $cF;
- next unless $cF =~ m/\.xml$/; # read xml files only!
- Log3($name, 4, "GDS $name: analyzing $cF");
- my ($err,@a) = FileRead({FileName=>$cF,ForceType=>"file" });
- foreach my $l (@a) {
- next unless length($l);
- next if($l =~ m/^\<\?xml version.*/);
- $l = "<alert>" if($l =~ m/^\<alert.*/);
- # next if($l =~ m/^\<alert.*/);
- # next if($l =~ m/^\<\/alert.*/);
- next if($l =~ m/^\<sender\>.*/);
- $countInfo++ if($l =~ m/^\<info\>/);
- push (@alertsArray,$l);
- }
- }
- push (@alertsArray,"</gds>");
- # write the big XML file if needed
- if(AttrVal($name,"gdsDebug", 0)) {
- my $cF = $destinationDirectory."/gds_alerts";
- unlink $cF if -e $cF;
- FileWrite({ FileName=>$cF,ForceType=>"file" },@alertsArray);
- }
- my $xmlContent = join('',@alertsArray);
- return ($countInfo,$xmlContent);
- }
- sub _buildCAPList($$$){
- my ($hash,$countInfo,$cF) = @_;
- my $name = $hash->{NAME};
- $alertsXml = undef;
- my $xml = new XML::Simple;
- my $area = 0;
- my $record = 0;
- my $n = 0;
- my ($capCity, $capCell, $capEvent, $capEvt, @a);
- my $destinationDirectory = $tempDir.$name."_alerts.dir";
-
- # make XML array and analyze data
- eval {
- $alertsXml = $xml->XMLin($cF, KeyAttr => {}, ForceArray => [ 'alert', 'eventCode', 'area', 'geocode' ]);
- };
- if ($@) {
- Log3($name,1,'GDS: error analyzing alerts XML:'.$@);
- return (undef,undef);
- }
- # analyze entries based on info and area array
- # array elements are determined by $info and $area
- #
- my $cellData = '';
- for (my $info=0; $info<=$countInfo;$info++) {
- $area = 0;
- while(1){
- $capCity = $alertsXml->{alert}[$info]{info}{area}[$area]{areaDesc};
- $capEvent = $alertsXml->{alert}[$info]{info}{event};
- last unless $capCity;
- $capCell = __findCAPWarnCellId($info, $area);
- $n = 100*$info+$area;
- $capCity = latin1ToUtf8($capCity.' '.$capEvent);
- push @a, $capCity;
- $capCity =~ s/\s/_/g;
- $cellData .= "$n:$capCity:$capCell$n;;";
- $area++;
- $record++;
- $capCity = undef;
- }
- }
- @a = sort(@a);
- $aList = undef;
- $aList = join(",", @a);
- $aList =~ s/\s/_/g;
- $aList = "No_alerts_published!" if !$record;
- return($aList,$cellData);
- }
- sub __findCAPWarnCellId($$){
- my ($info, $area) = @_;
- my $i = 0;
- while($i < 100){
- if($alertsXml->{alert}[$info]{info}{area}[$area]{geocode}[$i]{valueName} eq "WARNCELLID"){
- return $alertsXml->{alert}[$info]{info}{area}[$area]{geocode}[$i]{value};
- last;
- }
- $i++; # emergency exit :)
- }
- }
- # ForecastData
- sub _retrieveFORECAST {
- my ($hash) = shift;
- my $name = $hash->{NAME};
- my $user = getKeyValue($name."_user");
- my $pass = getKeyValue($name."_pass");
- my $host = getKeyValue($name."_host");
- my $proxyName = AttrVal($name, "gdsProxyName", "");
- my $proxyType = AttrVal($name, "gdsProxyType", "");
- my $passive = AttrVal($name, "gdsPassiveFtp", 1);
- my $useFritz = AttrVal($name, "gdsUseFritzkotz", 0);
- my $dir = "gds/specials/forecasts/tables/germany/";
- my $ret = "";
- eval {
- my $ftp = Net::FTP->new( $host,
- Debug => 0,
- Timeout => 10,
- Passive => $passive,
- FirewallType => $proxyType,
- Firewall => $proxyName);
- if(defined($ftp)){
- Log3($name, 4, "GDS $name: ftp connection established.");
- $ftp->login($user, $pass);
- $ftp->binary;
- $ftp->cwd("$dir");
- my @files = $ftp->ls();
- if(@files) {
- Log3($name, 4, "GDS $name: filelist found.");
- @files = sort(@files);
- $fcmapList = undef;
- map ( $fcmapList .= (split(/Daten_/,$_,2))[1].",", @files );
- my $count = 0;
- foreach my $file (@files) {
- my ($file_content,$file_handle);
- open($file_handle, '>', \$file_content);
- $ftp->get($file,$file_handle);
- next unless (length($file_content));
- $file_content = latin1ToUtf8($file_content);
- $file_content =~ s/\r\n/\$/g;
- $ret .= "$file:$file_content;";
- $count++;
- Log3 ($name, 5, "GDS $name retrieved forecast $file");
- }
- }
- $ftp->quit;
- }
- };
- return $name.";;;".$ret;
- }
- sub _finishedFORECAST {
- my ($name,$ret) = split(/;;;/,shift); #@_;
- my $hash = $defs{$name};
- my @a = split(/;/,$ret);
- %allForecastData = ();
- foreach my $l (@a) {
- my ($fn,$fc) = split(/\:/,$l);
- $allForecastData{$fn} = $fc;
- }
- $hash->{GDS_FORECAST_READ} = int(time());
- DoTrigger($name,"REREADFORECAST",1);
- getListForecastStations($hash);
- my $sf = AttrVal($name,'gdsSetForecast',0);
- # GDS_GetUpdate($hash,1) if $sf;
- my @b;
- push @b, undef;
- push @b, undef;
- push @b, $sf;
- retrieveForecasts($hash, "fc", @b);
- delete $hash->{GDS_FORECAST_BUSY};
- }
- sub _abortedFORECAST {
- my ($hash) = shift;
- delete $hash->{GDS_FORECAST_READ};
- $hash->{GDS_FORECAST_ABORTED} = localtime(time());
- delete $hash->{GDS_FORECAST_BUSY};
- }
- ###################################################################################################
- #
- # forecast retrieval - provided by jensb
- sub retrieveForecasts($$@) {
- #
- # parameter: hash, prefix, region/station, forecast index (0 .. 10)
- #
- my ($hash, $prefix, @a) = @_;
- my $name = $hash->{NAME};
- # extract region and station name
- return unless defined($a[2]);
- my ($area,$station) = split(/\//,$a[2]);
- return unless $station;
- my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
- my ($dataFile, $found, $line, %fread, $k, $v, $data);
- $area = utf8ToLatin1($area);
- $station =~ s/_/ /g; # replace underscore in station name by space
- my $searchLen = length($station);
- %fread = ();
- # define fetch scope (all forecasts or single forecast)
- my $fc = 0;
- my $fcStep = 1;
- if (defined($a[3]) && $a[3] > 0) {
- # single forecast
- $fc = $a[3] - 1;
- $fcStep = 10;
- }
- # fetch up to 10 forecasts for today and the next 3 days
- do {
- my $day;
- my $early;
- if ($fc < 4) {
- $day = 0;
- $early = 0;
- } else {
- $day = int(($fc - 2)/2);
- $early = $fc%2 == 0;
- }
- my $areaAndTime = $area;
- if ($day == 1) {
- $areaAndTime .= "_morgen";
- } elsif ($day == 2) {
- $areaAndTime .= "_uebermorgen";
- } elsif ($day == 3) {
- $areaAndTime .= "_Tag4";
- }
- my $timeLabel = undef;
- my $tempLabel = '_tAvgAir';
- my $copyDay = undef;
- my $copyTimeLabel = undef;
- if ($day == 0) {
- if ($fc == 0) {
- $areaAndTime .= "_frueh"; # .. 6 h
- $timeLabel = '06';
- $tempLabel ='_tMinAir';
- $copyDay = 1;
- $copyTimeLabel = '12';
- } elsif ($fc == 1) {
- $areaAndTime .= "_mittag"; # .. 12 h
- $timeLabel = '12';
- $tempLabel .= $timeLabel;
- } elsif ($fc == 2) {
- $areaAndTime .= "_spaet"; # .. 18 h
- $timeLabel = '18';
- $tempLabel ='_tMaxAir';
- $copyDay = 1;
- $copyTimeLabel = '24';
- } elsif ($fc == 3) {
- $areaAndTime .= "_nacht"; # .. 24 h
- $timeLabel = '24';
- $tempLabel .= $timeLabel;
- }
- } else {
- if ($early) {
- $areaAndTime .= "_frueh"; # .. 12 h
- $timeLabel = '12';
- $tempLabel ='_tMinAir';
- if ($day < 3) {
- $copyDay = $day + 1;
- $copyTimeLabel = '12';
- }
- } else {
- $areaAndTime .= "_spaet"; # .. 24 h
- $timeLabel .= '24';
- $tempLabel ='_tMaxAir';
- if ($day < 3) {
- $copyDay = $day + 1;
- $copyTimeLabel = '24';
- }
- }
- } # if ($day == 0) {
- # define forecast date (based on "now" + day)
- my $fcEpoch = time() + $day*86400;
- if ($fc == 3) {
- # night continues at next day
- $fcEpoch += 86400;
- }
- my ($fcSec,$fcMin,$fcHour,$fcMday,$fcMon,$fcYear,$fcWday,$fcYday,$fcIsdst) = localtime($fcEpoch);
- my $fcWeekday = $weekdays[$fcWday];
- my $fcDate = sprintf("%02d.%02d.%04d", $fcMday, 1+$fcMon, 1900+$fcYear);
- my $fcDateFound = 0;
- # retrieve from hash
- my $noDataFound = 1;
- $data = undef;
- my $hashSize = keys%allForecastData; # checking size of hash seems to make all entries "visible" in loop
- while(($k, $v) = each %allForecastData){
- if ($k eq "Daten_$areaAndTime") {
- $data = $v;
- last;
- };
- }
- if (defined($data) && $data) {
- my @data = split(/\$/,$data);
- foreach my $l (@data) {
- if (index($l, $fcDate) > 0) {
- # forecast date found
- $fcDateFound = 1;
- } # if
- if (index(substr(lc($l),0,$searchLen), substr(lc($station),0,$searchLen)) != -1) {
- # station found
- $line = $l;
- last;
- } # if
- } # foreach
- # parse file
- if ($fcDateFound && length($line) > 0) {
- if (index(substr(lc($line),0,$searchLen), substr(lc($station),0,$searchLen)) != -1) {
- # station found but there is no header line and column width varies:
- $line =~ s/---/ ---/g; # column distance may drop to zero between station name
- # and invalid temp "---" -> prepend 3 spaces
- $line =~ s/ /;/g; # now min. column distance is 3 spaces -> convert to semicolon
- $line =~ s/;+/;/g; # replace multiple consecutive semicolons by one semicolon
- my @b = split(';', $line); # split columns by semicolon
- $b[0] =~ s/^\s+|\s+$//g; # trim station name
- $b[1] =~ s/^\s+|\s+$//g; # trim temperature
- $b[2] =~ s/^\s+|\s+$//g; # trim weather
- if (scalar(@b) > 3) {
- $b[3] =~ s/^\s+|\s+$//g; # trim wind gust
- } else {
- $b[3] = ' ';
- }
- $fread{$prefix."_stationName"} = $area.'/'.$b[0];
- $fread{$prefix.$day.$tempLabel} = $b[1];
- $fread{$prefix.$day."_weather".$timeLabel} = $b[2];
- $fread{$prefix.$day."_windGust".$timeLabel} = $b[3];
- if ($fc != 3) {
- $fread{$prefix.$day."_weekday"} = $fcWeekday;
- }
- $noDataFound = 0;
- } else {
- # station not found, abort
- $fread{$prefix."_stationName"} = "unknown: $station in $area";
- last;
- }
- }
- } # unless
- if ($noDataFound) {
- # forecast period already passed or no data available
- $fread{$prefix.$day.$tempLabel} = "---";
- $fread{$prefix.$day."_weather".$timeLabel} = "---";
- $fread{$prefix.$day."_windGust".$timeLabel} = "---";
- if ($fc != 3) {
- $fread{$prefix.$day."_weekday"} = $fcWeekday;
- }
- }
- # day change preset by rotation
- my $ltime = ReadingsTimestamp($name, $prefix.$day."_weather".$timeLabel, undef);
- my ($lsec,$lmin,$lhour,$lmday,$lmon,$lyear,$lwday,$lyday,$lisdst);
- if (defined($ltime)) {
- ($lsec,$lmin,$lhour,$lmday,$lmon,$lyear,$lwday,$lyday,$lisdst) = localtime(time_str2num($ltime));
- }
- if (!defined($ltime) || $mday != $lmday) {
- # day has changed, rotate old forecast forward by one day because new forecast is not immediately available
- my $temp = $fread{$prefix.$day.$tempLabel};
- if (defined($temp) && substr($temp, 0, 2) eq '--') {
- if (defined($copyTimeLabel)) {
- $fread{$prefix.$day.$tempLabel} = ReadingsVal($name, $prefix.$copyDay.$tempLabel, '---');
- } else {
- # today noon/night and 3rd day is undefined
- $fread{$prefix.$day.$tempLabel} = ' ';
- }
- }
- my $weather = $fread{$prefix.$day."_weather".$timeLabel};
- if (defined($weather) && substr($weather, 0, 2) eq '--') {
- if (defined($copyTimeLabel)) {
- $fread{$prefix.$day."_weather".$timeLabel} =
- ReadingsVal($name, $prefix.$copyDay."_weather".$copyTimeLabel, '---');
- } else {
- # today noon/night and 3rd day is undefined
- $fread{$prefix.$day."_weather".$timeLabel} = ' ';
- }
- }
- my $windGust = $fread{$prefix.$day."_windGust".$timeLabel};
- if (defined($windGust) && substr($windGust, 0, 2) eq '--') {
- if (defined($copyTimeLabel)) {
- $fread{$prefix.$day."_windGust".$timeLabel} =
- ReadingsVal($name, $prefix.$copyDay."_windGust".$copyTimeLabel, '---');
- } else {
- # today noon/night and 3rd day is undefined
- $fread{$prefix.$day."_windGust".$timeLabel} = ' ';
- }
- }
- }
- $fc += $fcStep;
- } while ($fc < 10);
- readingsBeginUpdate($hash);
- while (($k, $v) = each %fread) {
- # skip update if no valid data is available
- unless(defined($v)) {delete($defs{$name}{READINGS}{$k}); next;}
- if($v =~ m/^--/) {delete($defs{$name}{READINGS}{$k}); next;};
- unless(length(trim($v))) {delete($defs{$name}{READINGS}{$k}); next;};
- readingsBulkUpdate($hash, $k, $v);
- }
- readingsEndUpdate($hash, 1);
- }
- sub getListForecastStations($) {
- my ($hash) = @_;
- my $name = $hash->{NAME};
- my @regions = keys(%rmapList);
- my (@a,$data,$k,$v);
- eval {
- foreach my $region (@regions) {
- $data = "";
- my $areaAndTime = 'Daten_'.$region.'_morgen_spaet';
- while(($k, $v) = each %allForecastData){
- if ($k eq $areaAndTime) {
- $data = $v;
- last;
- };
- }
- next unless $data;
- my @data = split(/\$/,$data);
- splice(@data, 0,2);
- splice(@data,-2);
- map ( push(@a,"$region/".(split(/(\s|--)/,$_,2))[0]), @data );
- }
- };
-
- Log3($name, 4, "GDS $name: forecast data not found") unless (@a);
- @a = sort(@a);
- $fList = join(",", @a);
- $fList =~ s/\s+,/,/g; # replace multiple spaces followed by comma with comma
- $fList =~ s/\s/_/g; # replace spaces in stationName with underscore for list in frontend
- return;
- }
- 1;
- # development documentation
- =pod
- ###################################################################################################
- #
- # ToDo
- #
- ###################################################################################################
- #
- # Changelog
- #
- ###################################################################################################
- #
- # 2016-06-05 changed use os based unzip for decoding capdata
- #
- # 2016-03-29 changed remove all conditions code
- #
- # 2016-01-27 changed use setKeyValue/getKeyValue for username and password
- #
- # 2016-01-01 fixed use txt file instead html for conditions (adopt DWD changes)
- #
- # 2015-12-31 fixed conditions retrieval on startup
- #
- # 2015-11-26 fixed wrong region handling
- # added gdsAlertsHeadlines()
- #
- # 2015-11-17 changed decodeCAPData - fix wrong cumulation (first try)
- # fixed minor bugs
- #
- # 2015-11-06 changed character encoding in forecast readings (jensb)
- # fixed problems after global rereadcfg
- # fixed delete CAP-zipfile unless gdsDebug set
- #
- # 2015-11-01 changed getListForecastStations: fixed inverted logging "data not found"
- # changed GDS_GetUpdate, retrieveData, _finishedFORECAST, _abortedFORECAST:
- # prevent multiple parallel processing
- # changed retrieveForecasts: make available data in hash "visible" for processing
- #
- # 2015-10-31 public new version released, SVN #9739
- #
- # 2015-10-30 public RC6 published, SVN #9727
- # changed use passive ftp per default
- #
- # 2015-10-27 changed add own function gds_calcTz due to announced
- # changes in SUNRISE_EL
- #
- # 2015-10-26 changed multiple instances are forbidden
- #
- # 2015-10-25 public RC5 published, SVN #9663
- # changed a lot of code cleanup
- #
- # 2015-10-24 public RC3 published, SVN #9627
- #
- # 2015-10-13 changed getListForecastStations() completed
- # changed retrieveForecasts() completed
- # added DoTrigger() according to reread
- #
- # 2015-10-12 changed conditions completed
- # changed capstationlist completed
- # changed conditionsmap completed
- # changed forecastsmap completed
- # changed radarmap completed
- # changed warningsmap completed
- # changed warnings completed
- # changed get alerts completed
- #
- # 2015-10-11 changed use Archive::Extract for unzip
- # changed code cleanup
- # changed forecast nonblocking retrieval:
- # hash generation completed
- # changed capstations nonblocking retrieval:
- # alertslist dropdown completed
- # datafile retrieval completed
- #
- #
- # ---------- public RC2 published, SVN #9429
- #
- # 2015-10-11 renamed 99_gdsUtils.pm to GDSweblink.pm
- # changed load GDSweblink.pm in eval() on module startup
- #
- # 2015-10-10 added attribute gdsHideFile to hide "GDS File" Menu
- # added optional parameter "host" in define() to override default hostname
- #
- # changed weblink generator moved into 99_gdsUtils.pm
- # changed perl module List::MoreUtils is no longer used
- # changed perl module Text::CSV is no longer needed
- # changed use binary mode for all ftp transfers to preven errors in images
- #
- # fixed handling for alert items msgType, sent, status
- # fixed handling for alert messages without "expires" data
- #
- # updated commandref documentation
- #
- # ---------- public RC1 published, SVN #9416
- #
- # 2015-10-09 removed createIndexFile()
- # added forecast retrieval
- # added weblink generator
- # added more "set clear ..." commands
- # changed lots and lots of code cleanup
- # feature make retrieveFile() nonblocking
- #
- #
- # 2015-10-08 changed added mergeCapFile()
- # code cleanup in buildCAPList()
- # use system call "unzip" instead of Archive::Zip
- # added NotifyFn for rereadcfg after INITIALIZED
- # improved startup data retrieval
- # improved attribute handling
- #
- # ---------- public first publication in ./contrib/55_GDS.2015 for testing
- #
- # 2015-10-07 changed remove LWP - we will only use ftp for transfers
- # added first solution for filemerge
- # added reliable counter for XML analyzes instead of while(1) loops
- # added (implementation started) forecast retrieval by jensb
- # changed make text file retrieval more generic
- #
- # 2015-10-06 removed Coro Support
- # removed $useFTP - always use http internally
- # changed use LWP::Parallel::UserAgent for nonblocking transfers
- # changed use Archive::ZIP for alert files transfer and unzip
- #
- # 2015-10-05 started redesign for new data structures provided by DWD
- #
- # ----------
- #
- # 2015-09-24 fixed prevent fhem crash on empty conditions file
- #
- # 2015-04-07 fixed a_X_valid calculation: use onset, too
- #
- # 2015-01-30 changed use own FWEXT instead of HTTPSRV
- #
- # 2015-01-03 added multiple alerts handling
- #
- # 2014-10-15 added attr disable
- #
- # 2014-05-23 added set <name> clear alerts|all
- # fixed some typos in docu and help
- #
- # 2014-05-22 added reading a_sent_local
- #
- # 2014-05-07 added readings a_onset_local & a_expires_local
- #
- # 2014-02-26 added attribute gdsPassiveFtp
- #
- # 2014-02-04 added ShutdownFn
- # changed FTP Timeout
- #
- # 2013-11-03 added error handling for malformed XML files from GDS
- #
- # 2013-08-13 fixed some minor bugs to prevent annoying console messages
- # added support for fhem installtions running on windows-based systems
- #
- # 2013-08-11 added retrieval for condition maps
- # added retrieval for forecast maps
- # added retrieval for warning maps
- # added retrieval for radar maps
- # modi use LWP::ua for some file transfers instead of ftp
- # due to transfer errors on image files
- # use parameter #5 = 1 in RetrieveFile for ftp
- # added get <name> caplist
- #
- # 2013-08-10 added some more tolerance on text inputs
- # modi switched from GetLogList to Log3
- #
- # 2013-08-09 added more logging
- # fixed missing error message if WARNCELLID does not exist
- # update commandref
- #
- # 2013-08-08 added logging
- # added firewall/proxy support
- # fixed XMLin missing parameter
- # added :noArg to setlist-definitions
- # added AttrFn
- # modi retrieval of VHDL messages 30-33
- #
- # 2013-08-07 public initial release
- #
- ###################################################################################################
- #
- # 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
- # Tn12 : Minimum der Lufttemperatur, 18 UTC Vortag bis 06 UTC heute, Grad Celsius
- # Tx12 : Maximum der Lufttemperatur, 18 UTC Vortag bis 06 UTC heute, Grad Celsius
- # Tg24 : Temperaturminimum 5cm ¸ber Erdboden, 22.05.2014 00 UTC bis 24 UTC, Grad Celsius
- # Tn24 : Minimum der Lufttemperatur, 22.05.2014 00 UTC bis 24 UTC, Grad Celsius
- # Tm24 : Mittel der Lufttemperatur, 22.05.2014 00 UTC bis 24 UTC, Grad Celsius
- # Tx24 : Maximum der Lufttemperatur, 22.05.2014 00 UTC bis 24 UTC, 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
- # RR12 : Niederschlagsmenge, 12st¸ndig, 18 UTC Vortag bis 06 UTC heute, mm = l/qm
- # RR24 : Niederschlagsmenge, 24stündig, 06 UTC Vortag bis 06 UTC heute, mm = l/qm
- # SSS : Gesamtschneehöhe in cm
- # SSS24 : Sonnenscheindauer 22.05.2014 in Stunden
- # SGLB24: Tagessumme Globalstrahlung am 22.05.2014 in J/qcm
- # DD : Windrichtung
- # FF : Windgeschwindigkeit letztes 10-Minutenmittel in km/h
- # FX : höchste Windspitze im Bezugszeitraum in km/h
- # --- : Wert nicht vorhanden
- #
- ###################################################################################################
- =cut
- # commandref documentation
- =pod
- =item device
- =begin html
- <a name="GDS"></a>
- <h3>GDS</h3>
- <ul>
- <b>Prerequesits</b>
- <ul>
-
- <br/>
- Module uses following additional Perl modules:<br/><br/>
- <code>Net::FTP, XML::Simple, Archive::Extract, Archive::Zip</code><br/><br/>
- If not already installed in your environment,
- please install them using appropriate commands from your environment.
- </ul>
- <br/><br/>
-
- <a name="GDSdefine"></a>
- <b>Define</b>
- <ul>
- <br>
- <code>define <name> GDS <username> <password> [<host>]</code><br>
- <br>
- This module provides connection to <a href="http://www.dwd.de/grundversorgung">GDS service</a>
- generated by <a href="http://www.dwd.de">DWD</a><br>
- <br/>
- Optional paramater host is used to overwrite default host "ftp-outgoing2.dwd.de".<br/>
- <br>
- </ul>
- <br/><br/>
- <a name="GDSset"></a>
- <b>Set-Commands</b><br/>
- <ul>
- <br/>
- <code>set <name> clear alerts|conditions|forecasts|all</code>
- <br/><br/>
- <ul>
- <li>alerts: Delete all a_* readings</li>
- <li>all: Delete all a_*, c_*, g_* and fc_* readings</li>
- </ul>
- <br/>
- <code>set <name> forecasts <region>/<stationName></code>
- <br/><br/>
- <ul>Retrieve forecasts for today and the following 3 days for selected station.<br/>
- Data will be updated periodically.</ul>
- <br/>
- <code>set <name> help</code>
- <br/><br/>
- <ul>Show a help text with available commands</ul>
- <br/>
- <code>set <name> update</code>
- <br/><br/>
- <ul>Update forecasts readings at selected station and restart update-timer</ul>
- <br/>
- <li>forecast readings generated by SET use prefix "fcd_" and a postfix of "hh"<br/>
- with d=relative day (0=today) and hh=last hour of forecast (exclusive)</li>
- <li>readings generated by SET will be updated automatically every 20 minutes</li>
- </ul>
- <br/><br/>
- <a name="GDSget"></a>
- <b>Get-Commands</b><br/>
- <ul>
- <br/>
- <code>get <name> alerts <region></code>
- <br/><br/>
- <ul>Retrieve alert message for selected region from previously read alert file (see rereadcfg)</ul>
- <br/>
- <code>get <name> conditionsmap <region></code>
- <br/><br/>
- <ul>Retrieve map (imagefile) showing current conditions at selected station</ul>
- <br/>
- <code>get <name> forecasts <region></code>
- <br/><br/>
- <ul>Retrieve forecasts for today and the following 3 days for selected region as text</ul>
- <br/>
- <code>get <name> forecastsmap <stationName></code>
- <br/><br/>
- <ul>Retrieve map (imagefile) showing forecasts for selected region</ul>
- <br/>
- <code>get <name> headlines [separator]</code>
- <br/><br/>
- <ul>Returns a string, containing all alert headlines. <br/>
- Default separator is | but can be overriden.</ul>
- <br/>
- <code>get <name> help</code>
- <br/><br/>
- <ul>Show a help text with available commands</ul>
- <br/>
- <code>get <name> list capstations</code>
- <br/><br/>
- <ul>
- <li><b>capstations:</b> Retrieve list showing all defined warning regions.
- You can find your WARNCELLID with this list.</li>
- </ul>
- <br/>
- <code>get <name> radarmap <region></code>
- <br/><br/>
- <ul>Retrieve map (imagefile) containig radar view from selected region</ul>
- <br/>
- <code>get <name> rereadcfg</code>
- <br/><br/>
- <ul>Reread all required data from DWD Server manually: station list and CAP data</ul>
- <br/>
- <code>get <name> warnings <region></code>
- <br/><br/>
- <ul>Retrieve current warnings report for selected region
- <ul>
- <br/>
- <li>report type VHDL30 = regular report, issued daily</li>
- <li>report type VHDL31 = regular report, issued before weekend or national holiday</li>
- <li>report type VHDL32 = preliminary report, issued on special conditions</li>
- <li>report type VHDL33 = cancel report, issued if necessary to cancel VHDL32</li>
- </ul>
- </ul>
- <br/>
- <code>get <name> warningssmap <region></code>
- <br/><br/>
- <ul>Retrieve map (imagefile) containig current warnings for selected region marked with symbols</ul>
- <br/><br/>
- <b>All downloaded mapfiles</b> can be found inside "GDS Files" area in left navigation bar.
- </ul>
- <br/><br/>
- <a name="GDSattr"></a>
- <b>Attributes</b><br/><br/>
- <ul>
- <li><a href="#do_not_notify">do_not_notify</a></li>
- <li><a href="#readingFnAttributes">readingFnAttributes</a></li>
- <br/>
- <li><b>disable</b> - if set, gds will not try to connect to internet</li>
- <li><b>gdsAll</b> - defines filter for "all data" from alert message</li>
- <li><b>gdsDebug</b> - defines filter for debug informations</li>
- <li><b>gdsSetForecast</b> - defines forecasts region/station to be used after system restart</li>
- <li><b>gdsLong</b> - show long text fields "description" and "instruction" from alert message in readings</li>
- <li><b>gdsPolygon</b> - show polygon data from alert message in a reading</li>
- <li><b>gdsHideFiles</b> - if set to 1, the "GDS Files" menu in the left navigation bar will not be shown</li>
- <br/>
- <li><b>gdsPassiveFtp</b> - set to 1 to use passive FTP transfer</li>
- <li><b>gdsFwName</b> - define firewall hostname in format <hostname>:<port></li>
- <li><b>gdsFwType</b> - define firewall type in a value 0..7 please refer to
- <a href="http://search.cpan.org/~gbarr/libnet-1.22/Net/Config.pm#NetConfig_VALUES">cpan documentation</a>
- for further informations regarding firewall settings.</li>
- </ul>
- <br/><br/>
- <b>Generated Readings:</b>
- <br/><br/>
- <ul>
- <li><b>_<readingName></b> - debug informations</li>
- <li><b>a_X_<readingName></b> - weather data from CAP alert messages. Readings will NOT be updated automatically<br/>
- a_ readings contain a set of alert inforamtions, X represents a numeric set identifier starting with 0<br/>
- that will be increased for every valid alert message in selected area<br/></li>
- <li><b>a_count</b> - number of currently valid alert messages, can be used for own loop iterations on alert messages</li>
- <li><b>a_valid</b> - returns 1 if at least one of decoded alert messages is valid</li>
- <li><b>fc?_<readingName>??</b> - weather data from SET weather forecasts,
- prefix by relative day and postfixed by last hour. Readings will be updated every 20 minutes.<br>
- <i><ul>
- <li>0_weather06 and ?_weather12 (with ? greater 0) is the weather in the morning</li>
- <li>0_weather12 is the weather at noon</li>
- <li>0_weather18 and ?_weather24 (with ? greater 0) is the weather in the afternoon</li>
- <li>0_weather24 is the weather at midnight</li>
- <li>0_windGust06 and ?_windGust12 (with ? greater 0) is the wind in the morning</li>
- <li>0_windGust12 is the wind at noon</li>
- <li>0_windGust18 and ?_windGust24 (with ? greater 0) is the wind in the afternoon</li>
- <li>0_windGust24 is the wind at midnight</li>
- <li>?_tMinAir is minimum temperature in the morning</li>
- <li>0_tAvgAir12 is the average temperature at noon</li>
- <li>?_tMaxAir is the maximum temperature in the afternoon</li>
- <li>0_tAvgAir24 is the average temperature at midnight</li>
- </ul></i>
- </li>
- </ul>
- <br/><br/>
- <b>Other events:</b>
- <br/><br/>
- <ul>
- <li><b>REREAD</b> - start of rereadcfg</li>
- <li><b>REREADFILE</b> - end of file read</li>
- <li><b>REREADALERTS</b> - end of alerts read and analyzing</li>
- <li><b>REREADFORECAST</b> - end of forecast read and analyzing</li>
- </ul>
- <br/><br/>
- <b>Author's notes</b><br/><br/>
- <ul>
- <li>Module uses following additional Perl modules:<br/><br/>
- <code>Net::FTP, XML::Simple, Archive::Extract, Archive::Zip</code><br/><br/>
- If not already installed in your environment, please install them using appropriate commands from your environment.</li>
- <br/><br/>
- <li>Have fun!</li><br/>
- </ul>
- </ul>
- =end html
- =begin html_DE
- <a name="GDS"></a>
- <h3>GDS</h3>
- <ul>
- Sorry, keine deutsche Dokumentation vorhanden.<br/><br/>
- Die englische Doku gibt es hier: <a href='http://fhem.de/commandref.html#GDS'>GDS</a><br/>
- </ul>
- =end html_DE
- =cut
|