From c11c2603c25df1795694c2dabc005c839fddc737 Mon Sep 17 00:00:00 2001 From: betateilchen <> Date: Sun, 11 Oct 2015 10:33:21 +0000 Subject: [PATCH] contrib/55_GDS.2015: deleted git-svn-id: https://svn.fhem.de/fhem/trunk@9431 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/contrib/55_GDS.2015/55_GDS.pm | 2356 ------------------------ fhem/contrib/55_GDS.2015/GDSweblink.pm | 319 ---- 2 files changed, 2675 deletions(-) delete mode 100644 fhem/contrib/55_GDS.2015/55_GDS.pm delete mode 100644 fhem/contrib/55_GDS.2015/GDSweblink.pm diff --git a/fhem/contrib/55_GDS.2015/55_GDS.pm b/fhem/contrib/55_GDS.2015/55_GDS.pm deleted file mode 100644 index 1c8f0234d..000000000 --- a/fhem/contrib/55_GDS.2015/55_GDS.pm +++ /dev/null @@ -1,2356 +0,0 @@ -# $Id$ -#################################################################################################### -# -# 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 -# weblinks 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 . -# -#################################################################################################### - -package main; - -use strict; -use warnings; -use feature qw/say switch/; -use Blocking; - -use Text::CSV; -use Net::FTP; -use List::MoreUtils 'first_index'; -use XML::Simple; - -no if $] >= 5.017011, warnings => 'experimental'; - -my ($bulaList, $cmapList, %rmapList, $fmapList, %bula2bulaShort, %bulaShort2dwd, %dwd2Dir, %dwd2Name, - $alertsXml, %capCityHash, %capCellHash, $sList, $aList, $fList, $fcmapList, $tempDir, @weekdays); - -#################################################################################################### -# -# 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->{GetFn} = "GDS_Get"; - $hash->{SetFn} = "GDS_Set"; - $hash->{ShutdownFn} = "GDS_Shutdown"; - $hash->{NotifyFn} = "GDS_Notify"; - $hash->{NOTIFYDEV} = "global"; - $hash->{AttrFn} = "GDS_Attr"; - $hash->{AttrList} = "disable:0,1 ". - "gdsFwName gdsFwType:0,1,2,3,4,5,6,7 gdsAll:0,1 ". - "gdsDebug:0,1 gdsLong:0,1 gdsPolygon:0,1 ". - "gdsSetCond gdsSetForecast gdsPassiveFtp:0,1 ". - $readingFnAttributes; - - $tempDir = "/tmp/"; - $aList = "please_use_rereadcfg_first"; - $sList = $aList; - $fList = $aList; - -} - -sub GDS_Define($$$) { - my ($hash, $def) = @_; - my @a = split("[ \t][ \t]*", $def); - my ($found, $dummy); - - return "syntax: define GDS " if(int(@a) != 4 ); - my $name = $hash->{NAME}; - - $hash->{helper}{USER} = $a[2]; - $hash->{helper}{PASS} = $a[3]; - $hash->{helper}{URL} = "ftp-outgoing2.dwd.de"; - $hash->{helper}{INTERVAL} = 1200; - - Log3($name, 4, "GDS $name: created"); - Log3($name, 4, "GDS $name: tempDir=".$tempDir); - - GDS_addExtension("GDS_CGI","gds","GDS Files"); - - fillMappingTables($hash); - initDropdownLists($hash); - - readingsSingleUpdate($hash, '_tzOffset', _calctz(time,localtime(time))*3600, 0); - 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_Shutdown($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - Log3 ($name,4,"GDS $name: shutdown requested"); - return undef; -} - -sub GDS_Set($@) { - my ($hash, @a) = @_; - my $name = $hash->{NAME}; - my $usage = "Unknown argument, choose one of ". - "clear:alerts,conditions,forecasts,all ". - "conditions:$sList ". - "forecasts:$fList ". - "help:noArg ". - "update:noArg "; ; - - readingsSingleUpdate($hash, '_tzOffset', _calctz(time,localtime(time))*3600, 0); - - my $command = lc($a[1]); - my $parameter = $a[2] if(defined($a[2])); - - my ($result, $next); - - $hash->{LOCAL} = 1; - - 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); - break; - } - - when("conditions"){ - retrieveConditions($hash, "c", @a); - $attr{$name}{gdsSetCond} = ReadingsVal($name,'c_stationName',undef); - $next = gettimeofday()+$hash->{helper}{INTERVAL}; - readingsSingleUpdate($hash, "_nextUpdate", localtime($next), 1); - RemoveInternalTimer($hash); - InternalTimer($next, "GDS_GetUpdate", $hash, 1); - break; - } - - when("forecasts"){ - CommandDeleteReading(undef, "$name fc.?_.*") if($parameter ne AttrVal($name,'gdsSetForecast','')); - retrieveForecasts($hash, "fc", @a); - my $station = ReadingsVal($name, 'fc_stationName', undef); - if (defined($station)) { - $attr{$name}{gdsSetForecast} = $station; - } - 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}; - - $hash->{LOCAL} = 1; - - my $usage = "Unknown argument $command, choose one of help:noArg rereadcfg:noArg ". - "list:stations,capstations,data ". - "alerts:".$aList." ". - "conditions:".$sList." ". - "conditionsmap:".$cmapList." ". - "forecasts:".$fcmapList." ". - "forecastsmap:".$fmapList." ". - "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); - readingsSingleUpdate($hash, '_tzOffset', _calctz(time,localtime(time))*3600, 0); - - my ($result, @datensatz, $found); - - given($command) { - - when("conditionsmap"){ - # retrieve map: current conditions - retrieveFile($hash,$command,$parameter,undef); - break; - } - - when("forecastsmap"){ - # retrieve map: forecasts - retrieveFile($hash,$command,$parameter,undef); - break; - } - - when("warningsmap"){ - # retrieve map: warnings - retrieveFile($hash,$command,$parameter,undef); - break; - } - - when("radarmap"){ - # retrieve map: radar - $parameter = ucfirst($parameter); - retrieveFile($hash,$command,$parameter,$rmapList{$parameter}); - break; - } - - when("help"){ - $result = getHelp(); - break; - } - - when("list"){ - given($parameter){ - when("capstations") { $result = getListCapStations($hash,$parameter); break,} - when("data") { $result = retrieveText($hash,"conditions","\n"); break; } - when("stations") { $result = retrieveText($hash,"conditions2","\n"); break; } - default { $usage = "get list "; return $usage; } - } - break; - } - - when("alerts"){ - 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); - readingsSingleUpdate($hash,'_lastAlertCheck','see timestamp ->',1) if($_gdsAll || $_gdsDebug); - break; - } - - when("headlines"){ - $result = gdsHeadlines($name); - break; - } - - when("conditions"){ - retrieveConditions($hash, "g", @a); - break; - } - - when("rereadcfg"){ - eval { - retrieveFile($hash,"alerts",undef,undef); - }; - eval { - retrieveFile($hash,"conditions",undef,undef); - }; - initDropdownLists($hash); - eval { - getListForecastStationsDropdown($hash); - }; - break; - } - - when("warnings"){ - my $vhdl; - $result = " VHDL30 = current | VHDL31 = weekend or holiday\n". - " VHDL32 = preliminary | VHDL33 = cancel VHDL32\n". - sepLine(31)."+".sepLine(38); - for ($vhdl=30; $vhdl <=33; $vhdl++){ - (undef, $found) = retrieveFile($hash, $command, $parameter, $vhdl); - if($found){ - $result .= retrieveText($hash, "warnings", ""); - $result .= "\n".sepLine(70); - } - } - $result .= "\n\n"; - break; - } - - when("forecasts"){ - $parameter = ucfirst($parameter); - $result = sepLine(67)."\n"; - (undef, $found) = retrieveFile($hash,$command,$parameter,undef); - if($found){ - $result .= retrieveText($hash, $command, "\n"); - } - $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; - - given($attrName){ - when("gdsDebug"){ - CommandDeleteReading(undef, "$name _dF.*") if($attrValue != 1 || $cmd eq 'delete'); - break; - } - when("gdsSetCond"){ - GDS_Set($hash,undef,'conditions',$attrValue) if($init_done && $cmd eq 'set'); - break; - } - when("gdsSetForecast"){ - GDS_Set($hash,undef,'forecasts',$attrValue) if($init_done && $cmd eq 'set'); - break; - } - default {$attr{$name}{$attrName} = $attrValue;} - } - if(IsDisabled($name)) { - readingsSingleUpdate($hash, 'state', 'disabled', 0); - } else { - readingsSingleUpdate($hash, 'state', 'active', 0); - } - return; -} - -sub GDS_Notify ($$) { - my ($hash,$dev) = @_; - my $name = $hash->{NAME}; - return if($dev->{NAME} ne "global"); - return if(!grep(m/^INITIALIZED/, @{$dev->{CHANGED}})); - - my $d; - - GDS_Get($hash,undef,'rereadcfg'); - - $d = AttrVal($name,'gdsSetCond',undef); - GDS_Set($hash,undef,'conditions',$d) if(defined($d)); - - $d = AttrVal($name,'gdsSetForecast',undef); - GDS_Set($hash,undef,'forecasts',$d) if(defined($d)); - - return undef; -} - -sub GDS_GetUpdate($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - my $next; - - my $interval = $hash->{helper}{INTERVAL}; - my $forcastsStationName = ReadingsVal($name, "fc_stationName", undef); - my $condStationName = ReadingsVal($name, "c_stationName", undef); - - 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($condStationName) { - my @a; - push @a, undef; - push @a, undef; - push @a, ReadingsVal($name, "c_stationName", ""); - retrieveConditions($hash, "c", @a); - } - if($forcastsStationName) { - my @a; - push @a, undef; - push @a, undef; - push @a, $forcastsStationName; - retrieveForecasts($hash, "fc", @a); - } - } - # schedule next update - $next = gettimeofday() + $interval; - readingsSingleUpdate($hash, "_nextUpdate", localtime($next), 1); - InternalTimer($next, "GDS_GetUpdate", $hash, 1); - - return 1; -} - -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= ; - 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") . "\n\n"; - foreach my $def (sort keys %defs) { - if($defs{$def}{TYPE} eq "GDS") { - $name= $defs{$def}{NAME}; - $url = GDS_getURL(); - $html .= "$name
\n\n\n"; - } - } - $html.="\n" . GDS_HTMLTail(); - - return ("text/html; charset=utf-8", $html); -} - -sub GDS_HTMLHead($) { - my ($title) = @_; - my $doctype= ''; - my $xmlns= 'xmlns="http://www.w3.org/1999/xhtml"'; - my $code= "$doctype\n\n\n$title\n\n"; - return $code; -} - -sub GDS_HTMLTail { - return ""; -} - -sub GDS_getURL { - my $proto = (AttrVal($FW_wname, 'HTTPS', 0) == 1) ? 'https' : 'http'; - return $proto."://$FW_httpheader{Host}$FW_ME"; #".$FW_ME; -} - -#################################################################################################### -# -# Tools -# -#################################################################################################### - -sub setHelp(){ - return "Use one of the following commands:\n". - sepLine(35)."\n". - "set clear alerts|all\n". - "set conditions \n". - "set forecasts /\n". - "set help\n". - "set rereadcfg\n". - "set update\n"; -} - -sub getHelp(){ - return "Use one of the following commands:\n". - sepLine(35)."\n". - "get alerts \n". - "get conditions \n". - "get forecasts \n". - "get help\n". - "get list capstations|stations|data\n". - "get rereadcfg\n". - "get warnings \n"; -} - -sub retrieveText($$$) { - my ($hash, $fileName, $separator) = @_; - my $name = $hash->{NAME}; - my ($err,@a); - - given ($fileName) { - when ("conditions2") { - # get conditions stations list - $fileName = $tempDir.$name."_conditions"; - ($err,@a) = FileRead({FileName=>$fileName,ForceType=>"file" }); - return "GDS error reading $fileName" if($err); - @a = map (substr(latin1ToUtf8($_),0,19), @a); - unshift(@a, "Use one of the following stations:", sepLine(40)); - } - default { - $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 getListCapStations($$){ - my ($hash, $command) = @_; - my $name = $hash->{NAME}; - my (%capHash, $file, $csv, @columns, $err, $key, $cList, $found); - - $file = $tempDir.'capstations.csv'; - $csv = Text::CSV->new( { binary => 1 } ); - $csv->sep_char (";"); - - # prüfen, ob CSV schon vorhanden, - # falls nicht: vom Server holen - if (!-e $tempDir."caplist.csv"){ - (undef, $found) = retrieveFile($hash, $command,undef,undef); - if(!$found){ - $cList = "Error: Unable to retrieve capstation list!"; - Log3($name, 2, "GDS $name: $cList"); - } - } - - if (!defined($cList)) { - # CSV öffnen und parsen - if (open (CSV, "<", $file)) { - while () { - next if ($. == 1); - if ($csv->parse($_)) { - @columns = $csv->fields(); - $capHash{latin1ToUtf8($columns[4])} = $columns[0]; - } else { - $err = $csv->error_input; - print "Failed to parse line: $err"; - } - } - close CSV; - } else { - Log3($name, 4, "GDS $name: Error: unable to open capstations file: $!!"); - } - - # Ausgabe sortieren und zusammenstellen - foreach $key (sort keys %capHash) { - $cList .= $capHash{$key}."\t".$key."\n"; - } - } - - return $cList; -} - -sub buildCAPList($){ - my ($hash) = @_; - my $name = $hash->{NAME}; - - %capCityHash = (); - %capCellHash = (); - $alertsXml = undef; - $aList = "please_use_rereadcfg_first"; - - 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 - my ($countInfo,$cF) = mergeCapFile($hash); - eval { - $alertsXml = $xml->XMLin($cF, KeyAttr => {}, ForceArray => [ 'info', '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 - # - for (my $info=0; $info<=$countInfo;$info++) { - $area = 0; - while(1){ - $capCity = $alertsXml->{info}[$info]{area}[$area]{areaDesc}; - $capEvent = $alertsXml->{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; - $capCityHash{$capCity} = $n; - $capCellHash{"$capCell$n"} = $n; - $area++; - $record++; - $capCity = undef; - } - } - - @a = sort(@a); - $aList = undef; - $aList = join(",", @a); - $aList =~ s/\s/_/g; - $aList = "No_alerts_published!" if !$record; - return; -} - -sub decodeCAPData($$$){ - my ($hash, $datensatz, $anum) = @_; - 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); - - Log3($name, 4, "GDS $name: Decoding CAP record #".$datensatz); - -# topLevel informations - @dummy = split(/\./, $alertsXml->{identifier}); - - $readings{"a_".$anum."_identifier"} = $alertsXml->{identifier} if($_gdsAll || $_gdsDebug); - $readings{"a_".$anum."_idPublisher"} = $dummy[5] if($_gdsAll); - $readings{"a_".$anum."_idSysten"} = $dummy[6] if($_gdsAll); - $readings{"a_".$anum."_idTimeStamp"} = $dummy[7] if($_gdsAll); - $readings{"a_".$anum."_idIndex"} = $dummy[8] if($_gdsAll); - $readings{"a_".$anum."_sent"} = $alertsXml->{sent}; - $readings{"a_".$anum."_status"} = $alertsXml->{status}; - $readings{"a_".$anum."_msgType"} = $alertsXml->{msgType}; - -# infoSet informations - $readings{"a_".$anum."_language"} = $alertsXml->{info}[$info]{language} if($_gdsAll); - $readings{"a_".$anum."_category"} = $alertsXml->{info}[$info]{category}; - $readings{"a_".$anum."_event"} = $alertsXml->{info}[$info]{event}; - $readings{"a_".$anum."_responseType"} = $alertsXml->{info}[$info]{responseType}; - $readings{"a_".$anum."_urgency"} = $alertsXml->{info}[$info]{urgency} if($_gdsAll); - $readings{"a_".$anum."_severity"} = $alertsXml->{info}[$info]{severity} if($_gdsAll); - $readings{"a_".$anum."_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_".$anum."_eventCode_".$n; - $v = $alertsXml->{info}[$info]{eventCode}[$i]{value}; - $readings{$n} .= $v." " if($v); - $i++; - } - -# time/validity informations - $readings{"a_".$anum."_effective"} = $alertsXml->{info}[$info]{effective} if($_gdsAll); - $readings{"a_".$anum."_onset"} = $alertsXml->{info}[$info]{onset}; - $readings{"a_".$anum."_expires"} = $alertsXml->{info}[$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"}); - $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->{info}[$info]{headline}; - $readings{"a_".$anum."_description"} = $alertsXml->{info}[$info]{description} if($_gdsAll || $_gdsLong); - $readings{"a_".$anum."_instruction"} = $alertsXml->{info}[$info]{instruction} if($readings{"a_".$anum."_responseType"} eq "Prepare" - && ($_gdsAll || $_gdsLong)); - -# area informations - $readings{"a_".$anum."_areaDesc"} = $alertsXml->{info}[$info]{area}[$area]{areaDesc}; - $readings{"a_".$anum."_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_".$anum."_geoCode_".$n; - $v = $alertsXml->{info}[$info]{area}[$area]{geocode}[$i]{value}; - $readings{$n} .= $v." " if($v); - $i++; - } - - $readings{"a_".$anum."_altitude"} = $alertsXml->{info}[$info]{area}[$area]{altitude} if($_gdsAll); - $readings{"a_".$anum."_ceiling"} = $alertsXml->{info}[$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)); - readingsBulkUpdate($hash, $k, latin1ToUtf8($v)); - } -# readingsEndUpdate($hash, 1); - - # convert color value to hex - eval { readingsBulkUpdate($hash, 'a_'.$anum.'_eventCode_AREA_COLOR_hex', - _rgbd2h(ReadingsVal($name, 'a_'.$anum.'_eventCode_AREA_COLOR', '')));}; - readingsEndUpdate($hash, 1); - - return; -} - -sub checkCAPValid($$){ - my ($onset,$expires) = @_; - my $valid = 0; - my $offset = _calctz(time,localtime(time))*3600; # used from 99_SUNRISE_EL - my $t = (time - $offset); - - $onset =~ s/T/ /; - $onset =~ s/\+/ \+/; - $onset = time_str2num($onset); - - $expires =~ s/T/ /; - $expires =~ s/\+/ \+/; - $expires = time_str2num($expires); - - $valid = 1 if($onset lt $t && $expires gt $t); - return $valid; -} - -sub capTrans($) { - my ($t) = @_; - my $valid = 0; - my $offset = _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; -} - -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 retrieveConditions($$@){ - my ($hash, $prefix, @a) = @_; - my $name = $hash->{NAME}; - (my $myStation = utf8ToLatin1($a[2])) =~ s/_/ /g; # replace underscore in stationName by space - my $searchLen = length($myStation); - - my ($line, $item, %pos, %alignment, %wx, %cread, $k, $v); - - Log3($name, 4, "GDS $name: Retrieving conditions data"); - retrieveFile($hash,"conditions",undef,undef); - - my $fileName = $tempDir.$name."_conditions"; - my ($err,@file) = FileRead({FileName=>$fileName,ForceType=>"file" }); - return "GDS error reading $fileName" if($err); - - foreach my $l (@file) { - $line = $l; # save line for further use - if ($l =~ /Station/) { # Header line... find out data positions - @a = split(/\s+/, $l); - foreach $item (@a) { - $pos{$item} = index($line, $item); - } - } - if (index(substr(lc($line),0,$searchLen), substr(lc($myStation),0,$searchLen)) != -1) { last; } - } - - %alignment = ("Station" => "l", "H\xF6he" => "r", "Luftd." => "r", "TT" => "r", "Tn12" => "r", "Tx12" => "r", - "Tmin" => "r", "Tmax" => "r", "Tg24" => "r", "Tn24" => "r", "Tm24" => "r", "Tx24" => "r", "SSS24" => "r", "SGLB24" => "r", - "RR1" => "r", "RR12" => "r", "RR24" => "r", "SSS" => "r", "DD" => "r", "FF" => "r", "FX" => "r", "Wetter/Wolken" => "l", "B\xF6en" => "l"); - - foreach $item (@a) { - Log3($hash, 4, "conditions item: $item"); - $wx{$item} = &readItem($line, $pos{$item}, $alignment{$item}, $item); - } - - %cread = (); - $cread{"_dataSource"} = "Quelle: Deutscher Wetterdienst"; - - if(length($wx{"Station"})){ - $cread{$prefix."_stationName"} = $wx{"Station"}; - $cread{$prefix."_altitude"} = $wx{"H\xF6he"}; - $cread{$prefix."_pressure-nn"} = $wx{"Luftd."}; - $cread{$prefix."_temperature"} = $wx{"TT"}; - $cread{$prefix."_tMinAir12"} = $wx{"Tn12"}; - $cread{$prefix."_tMaxAir12"} = $wx{"Tx12"}; - $cread{$prefix."_tMinGrnd24"} = $wx{"Tg24"}; - $cread{$prefix."_tMinAir24"} = $wx{"Tn24"}; - $cread{$prefix."_tAvgAir24"} = $wx{"Tm24"}; - $cread{$prefix."_tMaxAir24"} = $wx{"Tx24"}; - $cread{$prefix."_tempMin"} = $wx{"Tmin"}; - $cread{$prefix."_tempMax"} = $wx{"Tmax"}; - $cread{$prefix."_rain1h"} = $wx{"RR1"}; - $cread{$prefix."_rain12h"} = $wx{"RR12"}; - $cread{$prefix."_rain24h"} = $wx{"RR24"}; - $cread{$prefix."_snow"} = $wx{"SSS"}; - $cread{$prefix."_sunshine"} = $wx{"SSS24"}; - $cread{$prefix."_solar"} = $wx{"SGLB24"}; - $cread{$prefix."_windDir"} = $wx{"DD"}; - $cread{$prefix."_windSpeed"} = $wx{"FF"}; - $cread{$prefix."_windPeak"} = $wx{"FX"}; - $cread{$prefix."_weather"} = $wx{"Wetter\/Wolken"}; - $cread{$prefix."_windGust"} = $wx{"B\xF6en"}; - } else { - $cread{$prefix."_stationName"} = "unknown: $myStation"; - } - - readingsBeginUpdate($hash); - while (($k, $v) = each %cread) { - # 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, latin1ToUtf8($v)); - } - readingsEndUpdate($hash, 1); - - return ; -} - -sub retrieveFile($$$$){ - # - # request = type, e.g. alerts, conditions, warnings - # parameter = additional selector, e.g. Bundesland - # - my ($hash, $request, $parameter, $parameter2) = @_; - $hash->{helper}{request} = $request; - $hash->{helper}{parameter} = $parameter; - $hash->{helper}{parameter2} = $parameter2; - BlockingCall('_retrieveFile',$hash,undef,60,undef,undef); - delete $hash->{helper}{request}; - delete $hash->{helper}{parameter}; - delete $hash->{helper}{parameter2}; - - return(undef,undef); -} - -sub _retrieveFile($){ - my ($hash) = @_; - my $name = $hash->{NAME}; - my $user = $hash->{helper}{USER}; - my $pass = $hash->{helper}{PASS}; - my $request = $hash->{helper}{request}; - my $parameter = $hash->{helper}{parameter}; - my $parameter2 = $hash->{helper}{parameter2}; - -# my $debugString = "r: $request "; -# $debugString .= "p: $parameter " if(defined($parameter) && length($parameter)); -# $debugString .= "p2: $parameter2 " if(defined($parameter2) && length($parameter2)); -# Debug $debugString; - - my $proxyName = AttrVal($name, "gdsProxyName", ""); - my $proxyType = AttrVal($name, "gdsProxyType", ""); - my $passive = AttrVal($name, "gdsPassiveFtp", 0); - my $debug = AttrVal($name, "gdsDebug",0); - - my ($dwd, $dir, $ftp, @files, $dataFile, $targetFile, $found, $readingName); - - my $urlString = "ftp://$user:$pass\@ftp-outgoing2.dwd.de/"; - - given($request){ - - when("capstations"){ - $dir = "gds/help/"; - $dwd = "legend_warnings_CAP_WarnCellsID.csv"; - $targetFile = $tempDir.$request.".csv"; - break; - } - - when("conditionsmap"){ - $dir = "gds/specials/observations/maps/germany/"; - $dwd = $parameter."*"; - $targetFile = $tempDir.$name."_".$request.".jpg"; - break; - } - - when("forecastsmap"){ - $dir = "gds/specials/forecasts/maps/germany/"; - $dwd = $parameter."*"; - $targetFile = $tempDir.$name."_".$request.".jpg"; - break; - } - - when("warningsmap"){ - if(length($parameter) != 2){ - $parameter = $bula2bulaShort{lc($parameter)}; - } - $dwd = "Schilder".$dwd2Dir{$bulaShort2dwd{lc($parameter)}}.".jpg"; - $dir = "gds/specials/alerts/maps/"; - $targetFile = $tempDir.$name."_".$request.".jpg"; - break; - } - - when("radarmap"){ - $dir = "gds/specials/radar/".$parameter2; - $dwd = "Webradar_".$parameter."*"; - $targetFile = $tempDir.$name."_".$request.".jpg"; - break; - } - - when("alerts"){ - $dir = "gds/specials/alerts/cap/GER/status/"; - $dwd = "Z_CAP*"; - my $targetDir = $tempDir.$name."_alerts.dir"; - mkdir $targetDir unless -d $targetDir; - $targetFile = "$targetDir/$name"."_alerts.zip"; - break; - } - - when("conditions"){ - $dir = "gds/specials/observations/tables/germany/"; - $dwd = "*"; - $targetFile = $tempDir.$name."_".$request; - break; - } - - when("forecasts"){ - $dir = "gds/specials/forecasts/tables/germany/"; - $dwd = "Daten_".$parameter; - $targetFile = $tempDir.$name."_".$request; - break; - } - - when("warnings"){ - if(length($parameter) != 2){ - $parameter = $bula2bulaShort{lc($parameter)}; - } - $dwd = $bulaShort2dwd{lc($parameter)}; - $dir = $dwd2Dir{$dwd}; - $dwd = "VHDL".$parameter2."_".$dwd."*"; - $dir = "gds/specials/warnings/".$dir."/"; - $targetFile = $tempDir.$name."_".$request; - break; - } - } - - Log3($name, 4, "GDS $name: searching for $dir".$dwd." on DWD server"); - $urlString .= $dir; - - $found = 0; - eval { - $ftp = Net::FTP->new( "ftp-outgoing2.dwd.de", - Debug => 0, - Timeout => 10, - Passive => $passive, - FirewallType => $proxyType, - Firewall => $proxyName); - Log3($name, 4, "GDS $name: ftp connection established."); - if(defined($ftp)){ - $ftp->login($user, $pass); - $ftp->cwd("$dir"); - @files = undef; - @files = $ftp->ls($dwd); - if(@files){ - Log3($name, 4, "GDS $name: filelist found."); - $found = 1; - @files = sort(@files); - $dataFile = $files[-1]; - $urlString .= $dataFile; - Log3($name, 5, "GDS $name: retrieving $dataFile"); - $ftp->get($dataFile,$targetFile); - my $s = -s $targetFile; - Log3($name, 5, "GDS: ftp transferred $s bytes"); - } else { - Log3($name, 4, "GDS $name: filelist not found."); - $found = 0; - } - $ftp->quit; - } - Log3($name, 4, "GDS $name: updating readings."); - readingsBeginUpdate($hash); - readingsBulkUpdate($hash, "_dataSource", "Quelle: Deutscher Wetterdienst"); - readingsBulkUpdate($hash, "_dF_".$request, $dataFile) if(AttrVal($name, "gdsDebug", 0)); - readingsEndUpdate($hash, 0); - }; - return ($hash); -} - -sub getListStationsDropdown($){ - my ($hash) = @_; - my $name = $hash->{NAME}; - my ($line, $liste); - - my $fileName = $tempDir.$name."_conditions"; - return unless -e $fileName; - my $filesize = -s $fileName; - return unless $filesize != 0; - - my ($err,@a) = FileRead({FileName=>$fileName,ForceType=>"file" }); - return "GDS error reading $fileName" if($err); - @a = map (trim(substr(latin1ToUtf8($_),0,19)), @a); - - # delete header lines - splice(@a,0,6); - # delete legend - splice(@a,(first_index { /Höhe/ } @a)-1); - @a = sort(@a); - - $sList = join(",", @a); - $sList =~ s/\s+,/,/g; # replace multiple spaces followed by comma with comma - $sList =~ s/\s/_/g; # replace spaces in stationName with underscore for list in frontende - return; -} - -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; -} - -sub _rgbd2h($) { - my ($input) = @_; - my @a = split(" ", $input); - my $output = sprintf( "%02x%02x%02x", $a[0],$a[1],$a[2]); - return $output; -} - -sub fillMappingTables($){ - - $tempDir = "/tmp/"; - $aList = "please_use_rereadcfg_first"; - $sList = $aList; - $fList = $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 initDropdownLists($){ - my($hash) = @_; - my $name = $hash->{NAME}; - - # fill $aList - if (-e $tempDir.$name."_alerts.dir/$name"."_alerts.zip"){ - unzipCapFile($hash); - buildCAPList($hash); - } - - # fill $sList - getListStationsDropdown($hash) if(-e $tempDir.$name."_conditions"); - - # fill $fList - getListForecastStationsDropdown($hash) if(-e $tempDir.$name."_forecasts"); - - return; -} - -sub gdsHeadlines($;$) { - 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 _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 unzipCapFile($) { - my($hash) = @_; - my $name = $hash->{NAME}; - - my $destinationDirectory = $tempDir.$name."_alerts.dir"; - my $zipname = "$destinationDirectory/$name"."_alerts.zip"; - - if (-d $destinationDirectory) { - # delete old files in directory - my @remove = _readDir($destinationDirectory); - foreach my $f (@remove){ - next if -d $f; - next if $zipname =~ m/$f$/; - Log3($name, 4, "GDS $name: deleting $destinationDirectory/$f"); - unlink("$destinationDirectory/$f"); - } - } - - # unzip - system("/usr/bin/unzip $zipname -d $destinationDirectory"); - - # delete archive file - unlink $zipname unless AttrVal($name,"gdsDebug",0); - -} - -sub mergeCapFile($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - my $destinationDirectory = $tempDir.$name."_alerts.dir"; - my @capFiles = _readDir($destinationDirectory); - - my @alertsArray; - my $xmlHeader = ''; - push (@alertsArray,$xmlHeader); - push (@alertsArray,""); - 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.*/); - next if($l =~ m/^\.*/); - $countInfo++ if($l =~ m/^\/); - push (@alertsArray,$l); - } - } - push (@alertsArray,""); - - # 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); -} - -#################################################################################################### -# -# forecast retrieval -# provided by jensb -# -# improved by betateilchen -# - use FileRead instead of own I/O -# - do not set empty readings -# - allow temperature readings below zero degree -# - read forecasts on startup if attr gdsSetForecasts already defined before -# - delete all fc_.* readings in case of new station selection -# -#################################################################################################### - -sub retrieveForecasts($$@) { - # - # parameter: hash, prefix, region/station, forecast index (0 .. 10) - # - my ($hash, $prefix, @a) = @_; - my $name = $hash->{NAME}; - my $user = $hash->{helper}{USER}; - my $pass = $hash->{helper}{PASS}; - - # extract region and station name - if (!defined($a[2])) { - return; - } - my $i = index($a[2], '/'); - if ($i <= 0 ) { - return; - } - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(); - my ($dataFile, $found, $line, %fread, $k, $v); - my $area = utf8ToLatin1(substr($a[2], 0, $i)); - my $station = utf8ToLatin1(substr($a[2], $i+1)); - $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; - - # FTP retrieve - my $noDataFound = 1; - Log3($name, 4, "GDS $name: Retrieving forecasts data for day $day: $areaAndTime"); - retrieveFile($hash, "forecasts", $areaAndTime, undef); sleep 1; - - my $fileName = $tempDir.$name."_forecasts"; - my ($err,@data) = FileRead({FileName=>$fileName,ForceType=>"file" }); - return "GDS error reading $fileName" if($err); - - unless ($err) { - - 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} = utf8ToLatin1(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} = - utf8ToLatin1(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} = - utf8ToLatin1(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, latin1ToUtf8($v)); - } - readingsEndUpdate($hash, 1); -} - -sub getListForecastStationsDropdown($) { - my ($hash) = @_; - my $name = $hash->{NAME}; - - my @a; - my @regions = keys(%rmapList); - foreach (@regions) { - my $areaAndTime = $_.'_morgen_spaet'; - retrieveFile($hash, "forecasts", $areaAndTime, undef); - my $fileName = $tempDir.$name."_forecasts"; - my ($err,@data) = FileRead({FileName=>$fileName,ForceType=>"file" }); - return "GDS error reading $fileName" if($err); - my $lineCount = 0; - foreach my $line (@data) { - # skip header lines - $lineCount++; - if ($lineCount > 2) { - if (length($line) == 0 || substr($line, 0, 3) eq ' ') { - # empty line, done - last; - } else { - # line with station name found - $line = latin1ToUtf8($line); - $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 - push @a, $_.'/'.$b[0]; # concat region name and station name (1st column) - } - } - } # foreach @data - } # foreach @regions - - if (!@a) { - Log3($name, 4, "GDS $name: error: unable to read forecast data"); - } - @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; -} - -#################################################################################################### -# -# create weblinks -# provided by jensb -# -#################################################################################################### - -# weather description to icon name mapping -my %GDSDayWeatherIconMap = ( - 'bedeckt' => 'overcast', - 'bewölkt' => 'mostlycloudy', - 'Dunst oder flacher Nebel' => 'haze', - 'gefrierender Nebel' => 'icy', - 'gering bewölkt' => 'partlycloudy', - 'Gewitter' => 'thunderstorm', - 'Glatteisbildung' => 'icy', - 'Graupelschauer' => 'snow', - 'Hagelschauer' => 'snow', - 'heiter' => 'partlycloudy', - 'in Wolken' => 'mostlycloudy', - 'kein signifikantes Wetter' => 'na', - 'kräftiger Graupelschauer' => 'heavysnow', - 'kräftiger Hagelschauer' => 'heavysnow', - 'kräftiger Regen' => 'heavyrain', - 'kräftiger Regenschauer' => 'scatteredshowers', - 'kräftiger Schneefall' => 'heavysnow', - 'kräftiger Schneeregen' => 'rainsnow', - 'kräftiger Schneeregenschauer' => 'rainsnow', - 'kräftiger Schneeschauer' => 'heavysnow', - 'leicht bewölkt' => 'partlycloudy', - 'leichter Regen' => 'mist', - 'leichter Schneefall' => 'snow', - 'leichter Schneeregen' => 'rainsnow', - 'Nebel' => 'fog', - 'Regen' => 'rain', - 'Regenschauer' => 'scatteredshowers', - 'Sandsturm' => 'dust', - 'Schneefall' => 'snow', - 'Schneefegen' => 'snow', - 'Schneeregen' => 'rainsnow', - 'Schneeregenschauer' => 'rainsnow', - 'Schneeschauer' => 'snow', - 'schweres Gewitter' => 'thunderstorm', - 'stark bewölkt' => 'mostlycloudy', - 'starkes Gewitter' => 'thunderstorm', - 'wolkenlos' => 'sunny', - '---' => 'mostlycloudy', - ); - -my %GDSNightWeatherIconMap = ( - 'bedeckt' => 'overcast', - 'bewölkt' => 'mostlycloudy_night', - 'Dunst oder flacher Nebel' => 'haze_night', - 'gefrierender Nebel' => 'icy', - 'gering bewölkt' => 'partlycloudy_night', - 'Gewitter' => 'thunderstorm', - 'Glatteisbildung' => 'icy', - 'Graupelschauer' => 'snow', - 'Hagelschauer' => 'snow', - 'heiter' => 'partlycloudy_night', - 'in Wolken' => 'mostlycloudy_night', - 'kein signifikantes Wetter' => 'na', - 'kräftiger Graupelschauer' => 'heavysnow', - 'kräftiger Hagelschauer' => 'heavysnow', - 'kräftiger Regen' => 'heavyrain', - 'kräftiger Regenschauer' => 'scatteredshowers_night', - 'kräftiger Schneefall' => 'heavysnow', - 'kräftiger Schneeregen' => 'rainsnow', - 'kräftiger Schneeregenschauer' => 'rainsnow', - 'kräftiger Schneeschauer' => 'heavysnow', - 'leicht bewölkt' => 'partlycloudy_night', - 'leichter Regen' => 'mist', - 'leichter Schneefall' => 'snow', - 'leichter Schneeregen' => 'rainsnow', - 'Nebel' => 'fog', - 'Regen' => 'rain', - 'Regenschauer' => 'scatteredshowers_night', - 'Sandsturm' => 'dust', - 'Schneefall' => 'snow', - 'Schneefegen' => 'snow', - 'Schneeregen' => 'rainsnow', - 'Schneeregenschauer' => 'rainsnow', - 'Schneeschauer' => 'snow', - 'schweres Gewitter' => 'thunderstorm', - 'stark bewölkt' => 'mostlycloudy_night', - 'starkes Gewitter' => 'thunderstorm', - 'wolkenlos' => 'sunny_night', - '---' => 'mostlycloudy_night', - ); - -# icon parameters -use constant ICONHIGHT => 120; -use constant ICONWIDTH => 175; -use constant ICONSCALE => 0.5; - -sub GDSIsDay($$) { -# check if it is day at given time -# -# @param: time -# @param: altitude, see documentation of module SUNRISE_EL - my ($time, $altitude) = @_; - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time); - my $t = ($hour*60 + $min) + $sec; - - my (undef, $srHour, $srMin, $srSec, undef) = GetTimeSpec(sunrise_abs_dat($time, $altitude)); - my $sunrise = ($srHour*60 + $srMin) + $srSec; - - my (undef, $ssHour, $ssMin, $ssSec, undef) = GetTimeSpec(sunset_abs_dat($time, $altitude)); - my $sunset = ($ssHour*60 + $ssMin) + $ssSec; - - return $t >= $sunrise && $t <= $sunset; -} - -sub GDSIconIMGTag($;$) { -# get FHEM weather icon -# -# @param: weather description -# @param: time of weather description or 1 for night, optional, defaults to daytime icons - my $width = int(ICONSCALE*ICONWIDTH); - my ($weather, $time) = @_; - my $icon; - if (!defined($time) || (defined($time) && $time > 1 && GDSIsDay($time, "REAL"))) { - $icon = $GDSDayWeatherIconMap{$weather}; - } else { - $icon = $GDSNightWeatherIconMap{$weather}; - } - if (defined($icon)) { - my $url= FW_IconURL("weather/$icon"); - my $style= " width=$width"; - return "\"$icon\""; - } else { - return ""; - } -} - -sub GDSAsHtmlV($;$) { -# create forecast in a vertical HTML table -# -# @param: device name -# @param: number of icons, optional, default 8 - my ($d,$items) = @_; - $d = "" if(!$d); - $items = $items? $items - 1 : 7; - return "$d is not a GDS instance
" - if(!$defs{$d} || $defs{$d}{TYPE} ne "GDS"); - - my $width = int(ICONSCALE*ICONWIDTH); - - my $ret = sprintf('', $width); - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "c_weather", "?"), time_str2num(ReadingsTimestamp($d, "c_weather", TimeNow()))), - ReadingsVal($d, "c_weather", "?"), - ReadingsVal($d, "c_temperature", "?"), - ReadingsVal($d, "c_windSpeed", "?"), ReadingsVal($d, "c_windDir", "?")); - - # get time of last forecast - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time_str2num(ReadingsTimestamp($d, "fc3_weather24", TimeNow()))); - - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - my $weekday = $i == 0? ($hour < 17? 'Spät' : 'Nachts') : ($i - 1)%2 == 0? ReadingsVal($d, "fc".$day."_weekday", "?").' früh' : ReadingsVal($d, "fc".$day."_weekday", "?").' spät'; - - if (($i - 1)%2 == 0) { - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?")), - $weekday, - ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_tMinAir", "?"), - ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } else { - if ($i == 0 && $hour >= 17) { - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), 1), - $weekday, - ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_tAvgAir".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } else { - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?")), - $weekday, - ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_tMaxAir", "?"), - ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } - } - } - - $ret .= "
%sAktuell: %s
%s°C
Wind %s km/h %s
%s%s: %s
min %s°C
%s
%s%s: %s
%s°C
%s
%s%s: %s
max %s°C
%s
"; - return $ret; -} - -sub GDSAsHtmlH($;$) { -# create forecast in a horizontal HTML table -# -# @param: device name -# @param: number of icons, optional, default 8 - my ($d, $items) = @_; - $d = "" if(!$d); - $items = $items? $items - 1 : 7; - return "$d is not a GDS instance
" - if(!$defs{$d} || $defs{$d}{TYPE} ne "GDS"); - - my $width = 110; - - my $ret = ''; - - # get time of last forecast - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time_str2num(ReadingsTimestamp($d, "fc3_weather24", TimeNow()))); - - # weekday / time - $ret .= sprintf(''); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - my $weekday = $i == 0? ($hour < 17? 'Spät' : 'Nachts') : ($i - 1)%2 == 0? ReadingsVal($d, "fc".$day."_weekday", "?").' früh' : ReadingsVal($d, "fc".$day."_weekday", "?").' spät'; - $ret .= sprintf('', $weekday); - } - $ret .= ''; - - # condition icon - $ret .= sprintf('', $width, GDSIconIMGTag(ReadingsVal($d, "c_weather", "?"), time_str2num(ReadingsTimestamp($d, "c_weather", TimeNow())))); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - $ret .= sprintf('', $width, GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), $i==0 && $hour >= 17? 1 : undef)); - } - $ret .= ''; - - # condition text - $ret .= sprintf('', ReadingsVal($d, "c_weather", "?")); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?")); - } - $ret .= ''; - - # temperature / min temperature - $ret .= sprintf('', ReadingsVal($d, "c_temperature", "?")); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - if (($i - 1)%2 == 0) { - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_tMinAir", "?")); - } else { - if ($i == 0 && $hour >= 17) { - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_tAvgAir".$timeLabel, "?")); - } else { - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_tMaxAir", "?")); - } - } - } - $ret .= ''; - - # wind - $ret .= sprintf('', ReadingsVal($d, "c_windSpeed", "?"), ReadingsVal($d, "c_windDir", "?")); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } - $ret .= "
Aktuell%s
%s%s
%s%s
%s°Cmin %s°C%s°Cmax %s°C
%s km/h %s%s
"; - - return $ret; -} - -sub GDSAsHtmlD($;$) { -# create forecast in a horizontal or vertical HTML table depending on the display orientation -# -# @param: device name -# @param: number of icons, optional, default 8 - my ($d,$i) = @_; - if($FW_ss) { - GDSAsHtmlV($d,$i); - } else { - GDSAsHtmlH($d,$i); - } -} - -1; - -#################################################################################################### -# -# Documentation -# -#################################################################################################### -# -# Changelog: -# -# 2013-08-07 initial release -# -# 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-09 added more logging -# fixed missing error message if WARNCELLID does not exist -# update commandref -# -# 2013-08-10 added some more tolerance on text inputs -# modi switched from GetLogList to Log3 -# -# 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 caplist -# -# 2013-08-13 fixed some minor bugs to prevent annoying console messages -# added support for fhem installtions running on windows-based systems -# -# 2013-11-03 added error handling for malformed XML files from GDS -# -# 2014-02-04 added ShutdownFn -# changed FTP Timeout -# -# 2014-02-26 added attribute gdsPassiveFtp -# -# 2014-05-07 added readings a_onset_local & a_expires_local -# -# 2014-05-22 added reading a_sent_local -# -# 2014-05-23 added set clear alerts|all -# fixed some typos in docu and help -# -# 2014-10-15 added attr disable -# -# 2015-01-03 added multiple alerts handling -# -# 2015-01-30 changed use own FWEXT instead of HTTPSRV -# -# 2015-04-07 fixed a_X_valid calculation: use onset, too -# -# 2015-09-24 fixed prevent fhem crash on empty conditions file -# -# 2015-10-05 ------- Start rewrite for new data structures provided by DWD -# -# 2015-10-06 changed remove Coro Support -# remove $useFTP - always use http internally -# use LWP::Parallel::UserAgent for nonblocking transfers -# add Archive::ZIP for alert files transfer and unzip -# apt-get install libarchive-zip-perl -# -# 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 -# make text file retrieval more generic -# public first publication in ./contrib/55_GDS.2015 for testing -# -# 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 -# -# 2015-10-09 changed removed createIndexFile(), no longer needed since 2015-01-30 -# added forecast retrieval -# added weblink generator -# added more "set clear ..." commands -# done lots and lots of code cleanup -# -# feature MAKE retrieveFile() NONBLOCKING (experimental) :-) -# -#################################################################################################### -# -# 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 -# -#################################################################################################### - -=pod -=begin html - - -

GDS

-
    - - Prerequesits -
      - -
      - Module uses following additional Perl modules:

      - Net::FTP, List::MoreUtils, XML::Simple, Text::CSV

      - If not already installed in your environment, please install them using appropriate commands from your environment. - -
    -

    - - - Define -
      - -
      - define <name> GDS <username> <password>
      -
      - This module provides connection to GDS service generated by DWD
      -
      - It also provides three additional functions GDSAsHtmlV, GDSAsHtmlH and GDSAsHtmlD. - The first function returns the HTML code for a vertically arranged weather forecast. - The second function returns the HTML code for a horizontally arranged weather forecast. - The third function dynamically picks the orientation depending on whether a smallscreen style is set (vertical layout) or not (horizontal layout). - The attributes gdsSetCond and gdsSetForecast must be configured for the functions to work. - Each of these functions accepts an additional parameter to limit the number of icons to display (1...8). - If the attribute gdsSetForecast is not configured this parameter should be set to 1.
      -
      - Example: define MyForecastWeblink weblink htmlCode { GDSAsHtml("MyWeather") } where "MyWeather" is the name of your GDS device. -
    -

    - - - Set-Commands
    -
      - -
      - set <name> clear alerts|all -

      -
        -
      • alerts: Delete all a_* readings
      • -
      • all: Delete all a_*, c_*, g_* and fc_* readings
      • -
      -
      - - set <name> conditions <stationName> -

      -
        Retrieve current conditions at selected station. Data will be updated periodically.
      -
      - - set <name> forecasts <region>/<stationName> -

      -
        Retrieve forecasts for today and the following 3 days for 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 lists and CAP data
      -
      - - set <name> update -

      -
        Update conditions and forecasts readings at selected station and restart update-timer
      -
      - -
    • condition readings generated by SET use prefix "c_"
    • -
    • forecast readings generated by SET use prefix "fcd_" and a postfix of "hh" with d=relative day (0=today) and hh=last hour of forecast (exclusive)
    • -
    • readings generated by SET will be updated automatically every 20 minutes
    • - -
    -

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

      -
        Retrieve alert message for selected region from previously read alert file (see rereadcfg)
      -
      - - get <name> conditions <stationName> -

      -
        Retrieve current conditions at selected station
      -
      - - get <name> forecasts <region> -

      -
        Retrieve forecasts for today and the following 3 days for selected region as text
      -
      - - get <name> conditionsmap <region> -

      -
        Retrieve map (imagefile) showing current conditions at selected station
      -
      - - get <name> forecastsmap <stationName> -

      -
        Retrieve map (imagefile) showing forecasts for selected region
      -
      - - get <name> headlines -

      -
        Returns a string, containing all alert headlines separated by |
      -
      - - get <name> help -

      -
        Show a help text with available commands
      -
      - - get <name> list capstations|data|stations -

      -
        -
      • capstations: Retrieve list showing all defined warning regions. You can find your WARNCELLID with this list.
      • -
      • data: List current conditions for all available stations in one single table
      • -
      • stations: List all available stations that provide conditions data
      • -
      -
      - - get <name> radarmap <region> -

      -
        Retrieve map (imagefile) containig radar view from selected region
      -
      - - 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 -
          -
          -
        • report type VHDL30 = regular report, issued daily
        • -
        • report type VHDL31 = regular report, issued before weekend or national holiday
        • -
        • report type VHDL32 = preliminary report, issued on special conditions
        • -
        • report type VHDL33 = cancel report, issued if necessary to cancel VHDL32
        • -
        -
      -
      - - get <name> warningssmap <region> -

      -
        Retrieve map (imagefile) containig current warnings for selected region marked with symbols
      -

      - All downloaded mapfiles can be found inside "GDS Files" area in left navigation bar. - -
    -

    - - - Attributes

    -
      -
    • do_not_notify
    • -
    • readingFnAttributes
    • -
      -
    • disable - if set, gds will not try to connect to internet
    • -
    • gdsAll - defines filter for "all data" from alert message
    • -
    • gdsDebug - defines filter for debug informations
    • -
    • gdsSetCond - defines conditions area to be used after system restart
    • -
    • gdsSetForecast - defines forecasts region/station to be used after system restart
    • -
    • gdsLong - show long text fields "description" and "instruction" from alert message in readings
    • -
    • gdsPolygon - show polygon data from alert message in a reading
    • -
    • gdsShowMapFilesMenu - if set to 0, the "GDS Files" menu in the left navigation bar will not be shown
    • -
      -
    • gdsPassiveFtp - set to 1 to use passive FTP transfer
    • -
    • gdsFwName - define firewall hostname in format <hostname>:<port>
    • -
    • gdsFwType - define firewall type in a value 0..7 please refer to cpan documentation for further informations regarding firewall settings.
    • -
    -

    - - Generated Readings/Events: -

    -
      -
    • _<readingName> - debug informations
    • -
    • a_X_<readingName> - weather data from CAP alert messages. Readings will NOT be updated automatically
      - a_ readings contain a set of alert inforamtions, X represents a numeric set identifier starting with 0
      - that will be increased for every valid alert message in selected area
    • -
    • a_count - number of currently valid alert messages, can be used for own loop iterations on alert messages
    • -
    • a_valid - returns 1 if at least one of decoded alert messages is valid
    • -
    • c_<readingName> - weather data from SET weather conditions. Readings will be updated every 20 minutes.
    • -
    • fc?_<readingName>?? - weather data from SET weather forecasts, prefix by relative day and postfixed by last hour. Readings will be updated every 20 minutes.
      -
        -
      • 0_weather06 and ?_weather12 (with ? greater 0) is the weather in the morning
      • -
      • 0_weather12 is the weather at noon
      • -
      • 0_weather18 and ?_weather24 (with ? greater 0) is the weather in the afternoon
      • -
      • 0_weather24 is the weather at midnight
      • -
      • 0_windGust06 and ?_windGust12 (with ? greater 0) is the wind in the morning
      • -
      • 0_windGust12 is the wind at noon
      • -
      • 0_windGust18 and ?_windGust24 (with ? greater 0) is the wind in the afternoon
      • -
      • 0_windGust24 is the wind at midnight
      • -
      • ?_tMinAir is minimum temperature in the morning
      • -
      • 0_tAvgAir12 is the average temperature at noon
      • -
      • ?_tMaxAir is the maximum temperature in the afternoon
      • -
      • 0_tAvgAir24 is the average temperature at midnight
      • -
      -
    • -
    • 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, Text::CSV

      - If not already installed in your environment, please install them using appropriate commands from your environment.
    • -

      -
    • Have fun!

    • - -
    - -
- -=end html -=begin html_DE - - -

GDS

-
    -Sorry, keine deutsche Dokumentation vorhanden.

    -Die englische Doku gibt es hier: GDS
    -
-=end html_DE -=cut diff --git a/fhem/contrib/55_GDS.2015/GDSweblink.pm b/fhem/contrib/55_GDS.2015/GDSweblink.pm deleted file mode 100644 index 3de025430..000000000 --- a/fhem/contrib/55_GDS.2015/GDSweblink.pm +++ /dev/null @@ -1,319 +0,0 @@ -# $Id$ -package main; - -use strict; -use warnings; -use POSIX; - -#################################################################################################### -# -# create weblinks -# provided and maintained by jensb -# -#################################################################################################### - -# weather description to icon name mapping -my %GDSDayWeatherIconMap = ( - 'bedeckt' => 'overcast', - 'bewölkt' => 'mostlycloudy', - 'Dunst oder flacher Nebel' => 'haze', - 'gefrierender Nebel' => 'icy', - 'gering bewölkt' => 'partlycloudy', - 'Gewitter' => 'thunderstorm', - 'Glatteisbildung' => 'icy', - 'Graupelschauer' => 'snow', - 'Hagelschauer' => 'snow', - 'heiter' => 'partlycloudy', - 'in Wolken' => 'mostlycloudy', - 'kein signifikantes Wetter' => 'na', - 'kräftiger Graupelschauer' => 'heavysnow', - 'kräftiger Hagelschauer' => 'heavysnow', - 'kräftiger Regen' => 'heavyrain', - 'kräftiger Regenschauer' => 'scatteredshowers', - 'kräftiger Schneefall' => 'heavysnow', - 'kräftiger Schneeregen' => 'rainsnow', - 'kräftiger Schneeregenschauer' => 'rainsnow', - 'kräftiger Schneeschauer' => 'heavysnow', - 'leicht bewölkt' => 'partlycloudy', - 'leichter Regen' => 'mist', - 'leichter Schneefall' => 'snow', - 'leichter Schneeregen' => 'rainsnow', - 'Nebel' => 'fog', - 'Regen' => 'rain', - 'Regenschauer' => 'scatteredshowers', - 'Sandsturm' => 'dust', - 'Schneefall' => 'snow', - 'Schneefegen' => 'snow', - 'Schneeregen' => 'rainsnow', - 'Schneeregenschauer' => 'rainsnow', - 'Schneeschauer' => 'snow', - 'schweres Gewitter' => 'thunderstorm', - 'stark bewölkt' => 'mostlycloudy', - 'starkes Gewitter' => 'thunderstorm', - 'wolkenlos' => 'sunny', - '---' => 'mostlycloudy', - ); - -my %GDSNightWeatherIconMap = ( - 'bedeckt' => 'overcast', - 'bewölkt' => 'mostlycloudy_night', - 'Dunst oder flacher Nebel' => 'haze_night', - 'gefrierender Nebel' => 'icy', - 'gering bewölkt' => 'partlycloudy_night', - 'Gewitter' => 'thunderstorm', - 'Glatteisbildung' => 'icy', - 'Graupelschauer' => 'snow', - 'Hagelschauer' => 'snow', - 'heiter' => 'partlycloudy_night', - 'in Wolken' => 'mostlycloudy_night', - 'kein signifikantes Wetter' => 'na', - 'kräftiger Graupelschauer' => 'heavysnow', - 'kräftiger Hagelschauer' => 'heavysnow', - 'kräftiger Regen' => 'heavyrain', - 'kräftiger Regenschauer' => 'scatteredshowers_night', - 'kräftiger Schneefall' => 'heavysnow', - 'kräftiger Schneeregen' => 'rainsnow', - 'kräftiger Schneeregenschauer' => 'rainsnow', - 'kräftiger Schneeschauer' => 'heavysnow', - 'leicht bewölkt' => 'partlycloudy_night', - 'leichter Regen' => 'mist', - 'leichter Schneefall' => 'snow', - 'leichter Schneeregen' => 'rainsnow', - 'Nebel' => 'fog', - 'Regen' => 'rain', - 'Regenschauer' => 'scatteredshowers_night', - 'Sandsturm' => 'dust', - 'Schneefall' => 'snow', - 'Schneefegen' => 'snow', - 'Schneeregen' => 'rainsnow', - 'Schneeregenschauer' => 'rainsnow', - 'Schneeschauer' => 'snow', - 'schweres Gewitter' => 'thunderstorm', - 'stark bewölkt' => 'mostlycloudy_night', - 'starkes Gewitter' => 'thunderstorm', - 'wolkenlos' => 'sunny_night', - '---' => 'mostlycloudy_night', - ); - -# icon parameters -use constant ICONHIGHT => 120; -use constant ICONWIDTH => 175; -use constant ICONSCALE => 0.5; - -sub GDSIsDay($$) { -# check if it is day at given time -# -# @param: time -# @param: altitude, see documentation of module SUNRISE_EL - my ($time, $altitude) = @_; - - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time); - my $t = ($hour*60 + $min) + $sec; - - my (undef, $srHour, $srMin, $srSec, undef) = GetTimeSpec(sunrise_abs_dat($time, $altitude)); - my $sunrise = ($srHour*60 + $srMin) + $srSec; - - my (undef, $ssHour, $ssMin, $ssSec, undef) = GetTimeSpec(sunset_abs_dat($time, $altitude)); - my $sunset = ($ssHour*60 + $ssMin) + $ssSec; - - return $t >= $sunrise && $t <= $sunset; -} - -sub GDSIconIMGTag($;$) { -# get FHEM weather icon -# -# @param: weather description -# @param: time of weather description or 1 for night, optional, defaults to daytime icons - my $width = int(ICONSCALE*ICONWIDTH); - my ($weather, $time) = @_; - my $icon; - if (!defined($time) || (defined($time) && $time > 1 && GDSIsDay($time, "REAL"))) { - $icon = $GDSDayWeatherIconMap{$weather}; - } else { - $icon = $GDSNightWeatherIconMap{$weather}; - } - if (defined($icon)) { - my $url= FW_IconURL("weather/$icon"); - my $style= " width=$width"; - return "\"$icon\""; - } else { - return ""; - } -} - -sub GDSAsHtmlV($;$) { -# create forecast in a vertical HTML table -# -# @param: device name -# @param: number of icons, optional, default 8 - my ($d,$items) = @_; - $d = "" if(!$d); - $items = $items? $items - 1 : 7; - return "$d is not a GDS instance
" - if(!$defs{$d} || $defs{$d}{TYPE} ne "GDS"); - - my $width = int(ICONSCALE*ICONWIDTH); - - my $ret = sprintf('', $width); - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "c_weather", "?"), time_str2num(ReadingsTimestamp($d, "c_weather", TimeNow()))), - ReadingsVal($d, "c_weather", "?"), - ReadingsVal($d, "c_temperature", "?"), - ReadingsVal($d, "c_windSpeed", "?"), ReadingsVal($d, "c_windDir", "?")); - - # get time of last forecast - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time_str2num(ReadingsTimestamp($d, "fc3_weather24", TimeNow()))); - - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - my $weekday = $i == 0? ($hour < 17? 'Spät' : 'Nachts') : ($i - 1)%2 == 0? ReadingsVal($d, "fc".$day."_weekday", "?").' früh' : ReadingsVal($d, "fc".$day."_weekday", "?").' spät'; - - if (($i - 1)%2 == 0) { - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?")), - $weekday, - ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_tMinAir", "?"), - ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } else { - if ($i == 0 && $hour >= 17) { - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), 1), - $weekday, - ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_tAvgAir".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } else { - $ret .= sprintf('', - $width, - GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?")), - $weekday, - ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?"), - ReadingsVal($d, "fc".$day."_tMaxAir", "?"), - ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } - } - } - - $ret .= "
%sAktuell: %s
%s°C
Wind %s km/h %s
%s%s: %s
min %s°C
%s
%s%s: %s
%s°C
%s
%s%s: %s
max %s°C
%s
"; - return $ret; -} - -sub GDSAsHtmlH($;$) { -# create forecast in a horizontal HTML table -# -# @param: device name -# @param: number of icons, optional, default 8 - my ($d, $items) = @_; - $d = "" if(!$d); - $items = $items? $items - 1 : 7; - return "$d is not a GDS instance
" - if(!$defs{$d} || $defs{$d}{TYPE} ne "GDS"); - - my $width = 110; - - my $ret = ''; - - # get time of last forecast - my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time_str2num(ReadingsTimestamp($d, "fc3_weather24", TimeNow()))); - - # weekday / time - $ret .= sprintf(''); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - my $weekday = $i == 0? ($hour < 17? 'Spät' : 'Nachts') : ($i - 1)%2 == 0? ReadingsVal($d, "fc".$day."_weekday", "").' früh' : ReadingsVal($d, "fc".$day."_weekday", "").' spät'; - $ret .= sprintf('', $weekday); - } - $ret .= ''; - - # condition icon - $ret .= sprintf('', $width, GDSIconIMGTag(ReadingsVal($d, "c_weather", "na"), time_str2num(ReadingsTimestamp($d, "c_weather", TimeNow())))); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - $ret .= sprintf('', $width, GDSIconIMGTag(ReadingsVal($d, "fc".$day."_weather".$timeLabel, "na"), $i==0 && $hour >= 17? 1 : undef)); - } - $ret .= ''; - - # condition text - $ret .= sprintf('', ReadingsVal($d, "c_weather", "?")); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_weather".$timeLabel, "?")); - } - $ret .= ''; - - # temperature / min temperature - $ret .= sprintf('', ReadingsVal($d, "c_temperature", "?")); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - if (($i - 1)%2 == 0) { - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_tMinAir", "?")); - } else { - if ($i == 0 && $hour >= 17) { - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_tAvgAir".$timeLabel, "?")); - } else { - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_tMaxAir", "?")); - } - } - } - $ret .= ''; - - # wind - $ret .= sprintf('', ReadingsVal($d, "c_windSpeed", "?"), ReadingsVal($d, "c_windDir", "?")); - for(my $i=0; $i<$items; $i++) { - my $day = int(($i + 1)/2); - my $timeLabel = $i == 0? ($hour < 17? '18' : '24') : ($i - 1)%2 == 0? '12' : '24'; - $ret .= sprintf('', ReadingsVal($d, "fc".$day."_windGust".$timeLabel, "")); - } - $ret .= "
Aktuell%s
%s%s
%s%s
%s°Cmin %s°C%s°Cmax %s°C
%s km/h %s%s
"; - - return $ret; -} - -sub GDSAsHtmlD($;$) { -# create forecast in a horizontal or vertical HTML table -# depending on the display orientation -# @param: device name -# @param: number of icons, optional, default 8 - - my ($d,$i) = @_; - if(defined($FW_ss) && $FW_ss) { - GDSAsHtmlV($d,$i); - } else { - GDSAsHtmlH($d,$i); - } -} - -1; - -=pod -=begin html - - -

gdsUtils

-
    -
  • This module provides three additional functions:
    - GDSAsHtmlV, GDSAsHtmlH and GDSAsHtmlD.
    - The first function returns the HTML code for a vertically arranged weather forecast.
    - The second function returns the HTML code for a horizontally arranged weather forecast.
    - The third function dynamically picks the orientation depending on whether a
    - smallscreen style is set (vertical layout) or not (horizontal layout).
    - The attributes gdsSetCond and gdsSetForecast must be configured for the functions to work.
    - Each of these functions accepts an additional parameter to limit the number of icons to display (1...8).
    - If the attribute gdsSetForecast is not configured this parameter should be set to 1.
    -
    - Example: define MyForecastWeblink weblink htmlCode { GDSAsHtml("MyWeather") }
    - where "MyWeather" is the name of your GDS device.
    -
  • -
- -=end html -=cut