#################################################################################################### # $Id$ # # 59_PROPLANTA.pm # # (c) 2014 Torsten Poitzsch < torsten . poitzsch at gmx . de > # # Weather forecast values for 12 days are captured from www.proplanta.de # inspired by 23_KOSTALPIKO.pm # # Copyright notice # # This script 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. # # The GNU General Public License can be found at # http://www.gnu.org/copyleft/gpl.html. # A copy is found in the text file GPL.txt and important notices to the license # from the author is found in LICENSE.txt distributed with these scripts. # # This script 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. # # This copyright notice MUST APPEAR in all copies of the script! # #################################################################################################### ############################################### # parser for the weather data package MyProplantaParser; use base qw(HTML::Parser); use Time::HiRes qw(usleep nanosleep); our @texte = (); my $lookupTag = "span|b"; my $curTag = ""; my $curReadingName = ""; my $curRowID = ""; my $curCol = 0; our $startDay = 0; my $curTextPos = 0; my $curReadingType = 0; # 1 = span|b Text, 2 = readingName, 3 = Tag-Type # Tag-Types: # 1 = Number Col 3 # 2 = Number Col 2-5 # 3 = Number Col 2|4|6|8 # 4 = Intensity-Text Col 2-5 # 5 = Time Col 2-5 # 6 = Time Col 3 # 7 = alternative text of image Col 2-5 (weather state) # 8 = MinMaxNummer Col 3 # 9 = Date Col 2-5 # 10 = alternative text of Col 3 (Wind direction) my @knownNoneIDs = ( ["Temperatur", "temperature", 1] ,["relative Feuchte", "humidity", 1] ,["Sichtweite", "visibility", 1] ,["Windrichtung", "windDir", 10] ,["Windgeschwindigkeit", "wind", 1] ,["Luftdruck", "pressure", 1] ,["Taupunkt", "dewPoint", 1] ,["Uhrzeit", "obsTime", 6] ,["Höhe der", "cloudBase", 8] ); # 1 = Tag-ID, 2 = readingName, 3 = Tag-Type (see above) my @knownIDs = ( ["DATUM", "date", 9] ,["TMAX", "tempMax", 2] ,["TMIN", "tempMin", 2] ,["NW", "chOfRainDay", 2] ,["NW_Nacht", "chOfRainNight", 2] ,["BF", "frost", 4] ,["VERDUNST", "evapor", 4] ,["TAUBILDUNG", "dew", 4] ,["SD", "sun", 2] ,["UV", "uv", 2] ,["GS", "rad", 3] ,["WETTER_ID", "weather", 7] ,["WETTER_ID_MORGENS", "weatherMorning", 7] ,["WETTER_ID_TAGSUEBER", "weatherDay", 7] ,["WETTER_ID_ABENDS", "weatherEvening", 7] ,["WETTER_ID_NACHT", "weatherNight", 7] ,["T_0", "temp00", 2] ,["T_3", "temp03", 2] ,["T_6", "temp06", 2] ,["T_9", "temp09", 2] ,["T_12", "temp12", 2] ,["T_15", "temp15", 2] ,["T_18", "temp18", 2] ,["T_21", "temp21", 2] ,["NW_0", "chOfRain00", 2] ,["NW_3", "chOfRain03", 2] ,["NW_6", "chOfRain06", 2] ,["NW_9", "chOfRain09", 2] ,["NW_12", "chOfRain12", 2] ,["NW_15", "chOfRain15", 2] ,["NW_18", "chOfRain18", 2] ,["NW_21", "chOfRain21", 2] ,["NS_0", "rain00", 2] ,["NS_3", "rain03", 2] ,["NS_6", "rain06", 2] ,["NS_9", "rain09", 2] ,["NS_12", "rain12", 2] ,["NS_15", "rain15", 2] ,["NS_18", "rain18", 2] ,["NS_21", "rain21", 2] ,["BD_0", "cloud00", 2] ,["BD_3", "cloud03", 2] ,["BD_6", "cloud06", 2] ,["BD_9", "cloud09", 2] ,["BD_12", "cloud12", 2] ,["BD_15", "cloud15", 2] ,["BD_18", "cloud18", 2] ,["BD_21", "cloud21", 2] ,["MA", "moonRise", 5] ,["MU", "moonSet", 5] ); my %intensity = ( "keine" => 0 ,"nein" => 0 ,"gering" => 1 ,"leicht" => 1 ,"ja" => 1 ,"mäßig" => 2 ,"stark" => 3 ); my %winddir = ( "Nord" => 0 ,"Nord-Nordost" => 23 ,"Nordost" => 45 ,"Ost-Nordost" => 68 ,"Ost" => 90 ,"Ost-Südost" => 113 ,"Südost" => 135 ,"Süd-Südost" => 158 ,"Süd" => 180 ,"Süd-Südwest" => 203 ,"Südwest" => 225 ,"West-Südwest" => 248 ,"West" => 270 ,"West-Nordwest" => 203 ,"Nordwest" => 225 ,"Nord-Nordwest" => 248 ); sub get_wday($) { my ($date) = @_; my @wday_txt = qw(So Mo Di Mi Do Fr Sa); my @th=localtime $date; return $wday_txt [$th[6]]; } # here HTML::text/start/end are overridden sub text { my ( $self, $text ) = @_; my $found = 0; my $readingName; # Wait 1ms to reduce CPU load and hence blocking of FHEM by it (workaround until a better solution is available) usleep (1000); if ( $curTag =~ $lookupTag ) { $curTextPos++; $text =~ s/^\s+//; # trim string $text =~ s/\s+$//; $text =~ s/0/0/g; # replace 0 $text =~ s/1/1/g; # replace 1 $text =~ s/2/2/g; # replace 2 $text =~ s/3/3/g; # replace 3 $text =~ s/4/4/g; # replace 4 $text =~ s/5/5/g; # replace 5 $text =~ s/6/6/g; # replace 6 $text =~ s/7/7/g; # replace 7 $text =~ s/8/8/g; # replace 8 $text =~ s/9/9/g; # replace 9 # Tag-Type 0 = Check for readings without tag-ID (current readings) if ($curReadingType == 0) { if ($startDay == 0 && $curCol == 1 && $curTextPos == 1) { foreach my $r (@knownNoneIDs) { if ( $$r[0] eq $text ) { $curReadingName = $$r[1]; $curReadingType = $$r[2]; last; } } } } # Tag-Type 1 = Number Col 3 elsif ($curReadingType == 1) { if ( $curCol == 3 ) { $readingName = $curReadingName; if ( $text =~ m/([-,\+]?\d+[,\.]?\d*)/ ) { $text = $1; $text =~ tr/,/./; # komma durch punkt ersetzen } push( @texte, $readingName."|".$text ); $curReadingType = 0; } } # Tag-Type 2 = Number Col 2-5 elsif ($curReadingType == 2) { if ( 1 < $curCol && $curCol <= 5 ) { $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName; if ( $text =~ m/([-+]?\d+[,.]?\d*)/ ) { $text = $1; $text =~ tr/,/./; # komma durch punkt ersetzen } push( @texte, $readingName."|".$text ); } } # Tag-Type 3 = Number Col 2|4|6|8 elsif ($curReadingType == 3) { if ( 2 <= $curCol && $curCol <= 5 ) { if ( $curTextPos % 2 == 1 ) { $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName; $text =~ tr/,/./; # komma durch punkt ersetzen push( @texte, $readingName."|".$text ); } } } # Tag-Type 4 = Intensity-Text Col 2-5 elsif ($curReadingType == 4) { if ( 2 <= $curCol && $curCol <= 5 ) { $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName; $text = $intensity{$text} if defined $intensity{$text}; push( @texte, $readingName . "|" . $text ); } } # Tag-Type 5 = Time Col 2-5 elsif ($curReadingType == 5) { if ( 2 <= $curCol && $curCol <= 5 ) { $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName; if ( $text =~ m/([012-]?[-0-9][.:][-0-5][-0-9])/ ) { $text = $1; $text =~ tr/./:/; # Punkt durch Doppelpunkt ersetzen } push( @texte, $readingName."|".$text ); } } # Tag-Type 6 = Time Col 3 elsif ($curReadingType == 6) { if ( $curCol == 3 ) { $readingName = $curReadingName; if ( $text =~ m/([012-]?[-0-9][.:][-0-5][-0-9])/ ) { $text = $1; $text =~ tr/./:/; # Punkt durch Doppelpunkt ersetzen } push( @texte, $readingName."|".$text ); } } # Tag-Type 8 = MinMaxNumber Col 3 elsif ($curReadingType == 8) { if ( $curCol == 3 ) { $readingName = $curReadingName; if ( $text =~ m/(\d+)\s*-\s*(\d+)/ ) { push( @texte, $readingName."Min|".$1 ); push( @texte, $readingName."Max|".$2 ); } else { push( @texte, $readingName."Min|-" ); push( @texte, $readingName."Max|-" ); } } } # Tag-Type 9 = Date Col 2-5 elsif ($curReadingType == 9) { if ( 1 < $curCol && $curCol <= 5 ) { $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName; push( @texte, $readingName."|".$text ); } } } } sub start { my ( $self, $tagname, $attr, $attrseq, $origtext ) = @_; $curTag = $tagname; if ( $tagname eq "tr" ) { $curReadingType = 0; $curCol = 0; $curTextPos = 0; if ( defined( $attr->{id} ) ) { foreach my $r (@knownIDs) { if ( $$r[0] eq $attr->{id} ) { $curReadingName = $$r[1]; $curReadingType = $$r[2]; last; } } } } elsif ($tagname eq "td") { $curCol++; $curTextPos = 0; } #wetterstate and icon elsif ($tagname eq "img" && $curReadingType == 7) { if ( 2 <= $curCol && $curCol <= 5 ) { # process alternative text $readingName = "fc".($startDay+$curCol-2)."_".$curReadingName; $text = $attr->{alt}; $text =~ s/Wetterzustand: //; $text =~ s/ö/oe/; $text =~ s/ä/ae/; $text =~ s/ü/ue/; $text =~ s/ß/ss/; push( @texte, $readingName . "|" . $text ); # Image URL push( @texte, $readingName."Icon" . "|" . $attr->{src} ); } } #wind direction elsif ($tagname eq "img" && $curReadingType == 10 && $curCol == 3 ) { # process alternative text $readingName = $curReadingName; $text = $attr->{alt}; $text =~ s/Windrichtung: //; $text = $winddir{$text} if defined $winddir{$text}; # $text =~ s/ö/oe/; # $text =~ s/ä/ae/; # $text =~ s/ü/ue/; # $text =~ s/ß/ss/; push( @texte, $readingName . "|" . $text ); # Image URL push( @texte, $readingName."Icon" . "|" . $attr->{src} ); } } sub end { my ( $self, $tagname, $attr, $attrseq, $origtext ) = @_; $curTag = ""; if ( $tagname eq "tr" ) { $curReadingType = 0 }; } ############################################## package main; use strict; use feature qw/say switch/; use warnings; my $missingModul; eval "use LWP::UserAgent;1" or $missingModul .= "LWP::UserAgent "; eval "use HTTP::Request;1" or $missingModul .= "HTTP::Request "; eval "use HTML::Parser;1" or $missingModul .= "HTML::Parser "; eval "use MIME::Base64;1" or $missingModul .= "MIME::Base64 "; require 'Blocking.pm'; require 'HttpUtils.pm'; use vars qw($readingFnAttributes); use vars qw(%defs); my $MODUL = "PROPLANTA"; my %url_template_1 =( "de" => "http://www.proplanta.de/Wetter/LOKALERORT-Wetter.html" , "at" => "http://www.proplanta.de/Agrarwetter-Oesterreich/LOKALERORT/" , "ch" => "http://www.proplanta.de/Agrarwetter-Schweiz/LOKALERORT/" , "fr" => "http://www.proplanta.de/Agrarwetter-Frankreich/LOKALERORT/" , "it" => "http://www.proplanta.de/Agrarwetter-Italien/LOKALERORT/" ); my %url_template_2 = ( "de" => "http://www.proplanta.de/Wetter/profi-wetter.php?SITEID=60&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter&wT=" , "at" => "http://www.proplanta.de/Wetter-Oesterreich/profi-wetter-at.php?SITEID=70&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter&wT=" , "ch" => "http://www.proplanta.de/Wetter-Schweiz/profi-wetter-ch.php?SITEID=80&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter&wT=" , "fr" => "http://www.proplanta.de/Wetter-Frankreich/profi-wetter-fr.php?SITEID=50&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter-Frankreich&wT=" , "it" => "http://www.proplanta.de/Wetter-Italien/profi-wetter-it.php?SITEID=40&PLZ=LOKALERORT&STADT=LOKALERORT&WETTERaufrufen=stadt&Wtp=&SUCHE=Wetter-Italien&wT=" ); ######################################## sub PROPLANTA_Log($$$) { my ( $hash, $loglevel, $text ) = @_; my $xline = ( caller(0) )[2]; my $xsubroutine = ( caller(1) )[3]; my $sub = ( split( ':', $xsubroutine ) )[2]; $sub =~ s/PROPLANTA_//; my $instName = ( ref($hash) eq "HASH" ) ? $hash->{NAME} : $hash; Log3 $instName, $loglevel, "$MODUL $instName: $sub.$xline " . $text; } ################################### sub PROPLANTA_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "PROPLANTA_Define"; $hash->{UndefFn} = "PROPLANTA_Undef"; $hash->{SetFn} = "PROPLANTA_Set"; $hash->{AttrList} = "INTERVAL URL disable:0,1 forecastDays:4,7,11,14 " . $readingFnAttributes; } ################################### sub PROPLANTA_Define($$) { my ( $hash, $def ) = @_; my $name = $hash->{NAME}; my $lang = ""; my @a = split( "[ \t][ \t]*", $def ); return "Error: Perl moduls ".$missingModul."are missing on this system" if $missingModul; return "Wrong syntax: use define PROPLANTA [City] [CountryCode]" if int(@a) > 4; $lang = "de" if int(@a) == 3; $lang = lc( $a[3] ) if int(@a) == 4; if ( $lang ne "") { # {my $test="http://www.proplanta.de/Wetter/LOKALERORT-Wetter.html";; $test =~ s/LOKALERORT/München/g;; return $test;;} return "Wrong country code '$lang': use " . join(" | ", keys( %url_template_1 ) ) unless defined( $url_template_1{$lang} ); my $URL = $url_template_1{$lang}; $URL =~ s/LOKALERORT/$a[2]/g; $hash->{URL} = $URL; $URL = $url_template_2{$lang}; $URL =~ s/LOKALERORT/$a[2]/g; $hash->{URL2} = $URL; } $hash->{STATE} = "Initializing"; $hash->{fhem}{LOCAL} = 0; $hash->{INTERVAL} = 3600; $hash->{fhem}{modulVersion} = '$Date$'; RemoveInternalTimer($hash); #Get first data after 32 seconds InternalTimer( gettimeofday() + 32, "PROPLANTA_Start", $hash, 0 ); return undef; } ##################################### sub PROPLANTA_Undef($$) { my ( $hash, $arg ) = @_; RemoveInternalTimer( $hash ); BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) ); return undef; } ##################################### sub PROPLANTA_Set($@) { my ( $hash, @a ) = @_; my $name = $hash->{NAME}; my $reUINT = '^([\\+]?\\d+)$'; my $usage = "Unknown argument $a[1], choose one of update:noArg "; return $usage if ( @a < 2 ); my $cmd = lc( $a[1] ); given ($cmd) { when ("?") { return $usage; } when ("update") { PROPLANTA_Log $hash, 3, "set command: " . $a[1]; $hash->{fhem}{LOCAL} = 1; PROPLANTA_Start($hash); $hash->{fhem}{LOCAL} = 0; } default { return $usage; } } return; } ##################################### # acquires the html page sub PROPLANTA_HtmlAcquire($$) { my ($hash, $URL) = @_; my $name = $hash->{NAME}; return unless (defined($hash->{NAME})); PROPLANTA_Log $hash, 4, "Start capturing of $URL"; my $err_log = ""; my $agent = LWP::UserAgent->new( env_proxy => 1, keep_alive => 1, protocols_allowed => ['http'], timeout => 10 , agent => "Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" ); my $request = HTTP::Request->new( GET => $URL ); my $response = $agent->request($request); $err_log = "Can't get $URL -- " . $response->status_line unless $response->is_success; if ( $err_log ne "" ) { readingsSingleUpdate($hash, "lastConnection", $response->status_line, 1); PROPLANTA_Log $hash, 1, "Error: $err_log"; return "Error|Error " . $response->status_line; } PROPLANTA_Log $hash, 4, length($response->content)." characters captured"; return $response->content; } ##################################### sub PROPLANTA_Start($) { my ($hash) = @_; my $name = $hash->{NAME}; return unless (defined($hash->{NAME})); $hash->{INTERVAL} = AttrVal( $name, "INTERVAL", $hash->{INTERVAL} ); if($hash->{INTERVAL} > 0) { # reset timer if interval is defined RemoveInternalTimer( $hash ); InternalTimer(gettimeofday() + $hash->{INTERVAL}, "PROPLANTA_Start", $hash, 1 ); return undef if AttrVal($name, "disable", 0 ) == 1 && !$hash->{fhem}{LOCAL}; } if ( AttrVal( $name, 'URL', '') eq '' && not defined( $hash->{URL} ) ) { PROPLANTA_Log $hash, 3, "missing URL"; return; } # "Set update"-action will kill a running update child process if (defined ($hash->{helper}{RUNNING_PID}) && $hash->{fhem}{LOCAL}) { BlockingKill($hash->{helper}{RUNNING_PID}); delete( $hash->{helper}{RUNNING_PID} ); PROPLANTA_Log $hash, 4, "Killing old forked process"; } unless (defined ($hash->{helper}{RUNNING_PID})) { $hash->{helper}{RUNNING_PID} = BlockingCall( "PROPLANTA_Run", # callback worker task $name, # name of the device "PROPLANTA_Done", # callback result method 120, # timeout seconds "PROPLANTA_Aborted", # callback for abortion $hash ); # parameter for abortion PROPLANTA_Log $hash, 4, "Start forked process to capture html"; } else { PROPLANTA_Log $hash, 1, "Could not start forked process, old process still running"; } } ##################################### sub PROPLANTA_Run($) { setpriority( 0, 0, 10); my ($name) = @_; my $ptext=$name; my $URL; return unless ( defined($name) ); my $hash = $defs{$name}; return unless (defined($hash->{NAME})); my $readingStartTime = time(); my $fcDays = AttrVal( $name, 'forecastDays', 14 ); my $attrURL = AttrVal( $name, 'URL', "" ); if ($attrURL eq "") { $URL = $hash->{URL}; } else { $URL = $attrURL; } # acquire the html-page my $response = PROPLANTA_HtmlAcquire($hash,$URL); if ($response =~ /^Error\|/) { $ptext .= "|".$response; } else { PROPLANTA_Log $hash, 4, "Start HTML parsing of captured page"; my $parser = MyProplantaParser->new; $parser->report_tags(qw(tr td span b img)); @MyProplantaParser::texte = (); $MyProplantaParser::startDay = 0; # parsing the complete html-page-response, needs some time $parser->parse($response); # add next periods if ($attrURL eq "") { $URL = $hash->{URL2}; my @URL_days = (4, 7, 11); unshift @URL_days, 0 if @MyProplantaParser::texte == 0; foreach (@URL_days) { last unless $_ < $fcDays; $response = PROPLANTA_HtmlAcquire($hash,$URL . $_); $MyProplantaParser::startDay = $_; if ($response !~ /^Error\|/) { PROPLANTA_Log $hash, 4, "Start HTML parsing of captured page"; $parser->parse($response); } } } PROPLANTA_Log $hash, 4, "Found terms: " . @MyProplantaParser::texte; # pack the results in a single string if (@MyProplantaParser::texte > 0) { $ptext .= "|". join('|', @MyProplantaParser::texte); } PROPLANTA_Log $hash, 5, "Parsed string: " . $ptext; } $ptext .= "|durationFetchReadings|"; $ptext .= sprintf "%.2f", time() - $readingStartTime; return $ptext; } ##################################### # asyncronous callback by blocking sub PROPLANTA_Done($) { my ($string) = @_; return unless ( defined($string) ); # all term are separated by "|" , the first is the name of the instance my ( $name, %values ) = split( "\\|", $string ); my $hash = $defs{$name}; return unless ( defined($hash->{NAME}) ); PROPLANTA_Log $hash, 4, "Forked process successfully finished"; # delete the marker for RUNNING_PID process delete( $hash->{helper}{RUNNING_PID} ); # Wetterdaten speichern readingsBeginUpdate($hash); if ( defined $values{Error} ) { readingsBulkUpdate( $hash, "lastConnection", $values{Error} ); } else { my $x = 0; my $fcDays = AttrVal( $name, 'forecastDays', 14 ); while (my ($rName, $rValue) = each(%values) ) { if ($fcDays < 14 && $rName =~ /^fc(\d+)_/) { next unless $1 < $fcDays; } readingsBulkUpdate( $hash, $rName, $rValue ); PROPLANTA_Log $hash, 5, "reading:$rName value:$rValue"; } if (keys %values > 0) { my $newState; if (defined $values{fc0_tempMin} && defined $values{fc0_tempMax}) { $newState = "Tmin: " . $values{fc0_tempMin} . " Tmax: " . $values{fc0_tempMax}; # Achtung! Nach Mitternacht fehlen für 1 h die aktuellen Werte $newState .= " T: " . $values{temperature} . " H: " . $values{humidity} . " W: " . $values{wind} . " P: " . $values{pressure} if defined $values{temperature} && defined $values{humidity} && defined $values{wind} && defined $values{pressure}; } else { $newState = "Error: Could not capture all data. Please check URL or city name."; } readingsBulkUpdate($hash, "state", $newState); readingsBulkUpdate( $hash, "lastConnection", keys( %values )." values captured in ".$values{durationFetchReadings}." s" ); PROPLANTA_Log $hash, 4, keys( %values )." values captured"; } else { readingsBulkUpdate( $hash, "lastConnection", "no data found" ); PROPLANTA_Log $hash, 1, "No data found. Check city name or URL."; } } readingsEndUpdate( $hash, 1 ); } ##################################### sub PROPLANTA_Aborted($) { my ($hash) = @_; delete( $hash->{helper}{RUNNING_PID} ); PROPLANTA_Log $hash, 4, "Forked process timed out"; } ##### noch nicht fertig ########### sub ##################################### PROPLANTA_Html($) { my ($d) = @_; $d = "" if(!$d); return "$d is not a PROPLANTA instance
" if(!$defs{$d} || $defs{$d}{TYPE} ne "PROPLANTA"); my $uselocal= 0; #AttrVal($d,"localicons",0); my $isday; if ( exists &isday) { $isday = isday(); } else { $isday = 1; #($hour>6 && $hour<19); } my $ret = ""; $ret .= sprintf '', $defs{$d}{DEF}; $ret .= sprintf '', $defs{$d}{DEF}; $ret .= ""; # define MyForecast weblink htmlCode { PROPLANTA_Html("ProPlanta_Wetter") } for(my $i=0; $i<=2; $i++) { $ret .= sprintf('', ReadingsVal($d, "fc".$i."_date", ""), ReadingsVal($d, "fc".$i."_weatherMorning", ""), ReadingsVal($d, "fc".$i."_weatherMorningIcon", ""), ReadingsVal($d, "fc".$i."_weatherDay", ""), ReadingsVal($d, "fc".$i."_weatherDayIcon", ""), ReadingsVal($d, "fc".$i."_weatherEvening", ""), ReadingsVal($d, "fc".$i."_weatherEveningIcon", ""), ReadingsVal($d, "fc".$i."_weatherNight", ""), ReadingsVal($d, "fc".$i."_weatherNightIcon", ""), ReadingsVal($d, "fc".$i."_tempMin", ""), ReadingsVal($d, "fc".$i."_tempMax", ""), ReadingsVal($d, "fc".$i."_chOfRainDay", ""), ReadingsVal($d, "fc".$i."_frost", "") ? "ja" : "nein" ); } # for(my $i=0; $i<=4; $i++) { # $ret .= sprintf('', # WWOIconIMGTag(ReadingsVal($d, "fc${i}_weatherDayIcon", ""),$uselocal,$isday), # ReadingsVal($d, "fc${i}_date", ""), # ReadingsVal($d, "fc${i}_weatherDay", ""), # ReadingsVal($d, "fc${i}_tempMinC", ""), ReadingsVal($d, "fc${i}_tempMaxC", ""), # } $ret .= "
%s
TagmorgenstagsueberabendsnachtsminmaxRegen tagsFrost
%s%s
%s
%s
%s
%s°C%s°C%s %%%s
%s%s: %s
min %s °C max %s °C
wind: %s km/h %s
precip: %s mm
"; return $ret; } ##################################### 1; =pod =begin html

PROPLANTA

    The module extracts weather data from www.proplanta.de.
    The website provides a forecast for 12 days, for the first 7 days in a 3-hours-interval.
    This modul causes a high CPU load. It is recommended to reduce the number of captured forecast days.
    It uses the perl moduls HTTP::Request, LWP::UserAgent and HTML::Parse.

    Define

      define <name> PROPLANTA [City] [CountryCode]
      Example:
      define wetter PROPLANTA Bern ch
      define wetter PROPLANTA Wittingen+(Niedersachsen)
       
    • [City]
      Optional. The city must be selectable on www.proplanta.de.
      Please pay attention to the Capital letters in the city names. Spaces within the name are replaced by a + (plus).

    • [CountryCode]
      Optional. Possible values: de (default), at, ch, fr, it

    • The function PROPLANTA_Html creates a HTML code for a 3 day weather forecast.
      Example:
      define HTMLForecast weblink htmlCode { PROPLANTA_Html("ProPlanta_Wetter") }


    Set

    • set <name> update
      The weather data are immediately polled from the website.


    Attributes

    • forecastDays <4-14>
      Number of days for which the forecast data shall be fetched. Default is 14 days (incl. today).

    • INTERVAL <seconds>
      Poll interval for weather data in seconds (default 3600 = 1 hour)

    • URL <internet address>
      URL to extract information from. Overwrites the values in the 'define' term.

    • readingFnAttributes

    Forecast readings

    • fc0|1|2|3|...|13_... - forecast values for today|tommorrow|in 2|3|...|13 days
    • fc0_...00|03|06|09|12|15|18|21 - forecast values for today at 00|03|06|09|12|15|18|21 o'clock
    • fc0_chOfRainDay|Night - chance of rain today by day|night in %
    • fc0_chOfRain15 - chance of rain today at 15:00 in %
    • fc0_cloud15 - cloud coverage today at 15:00 in %
    • fc0_dew - dew formation today (0=none, 1=small, 2=medium, 3=strong)
    • fc0_evapor - evaporation today (0=none, 1=small, 2=medium, 3=strong)
    • fc0_frost - ground frost today (0=no, 1=yes)
    • fc0_moonRise|Set - moon rise|set today
    • fc0_rad - global radiation today
    • fc0_rain15 - amount of rainfall today at 15:00 o'clock in mm
    • fc0_sun - relative sun shine duration today in % (between sun rise and set)
    • fc0_tempMin|Max - minimal|maximal temperature today in °C
    • fc0_temp15 - temperatur today at 15:00 o'clock in °C
    • fc0_uv - UV-Index today
    • fc0_weatherMorning|Day|Evening|Night - weather situation today morning|during day|in the evening|during night
    • fc0_weatherDayIcon - icon of weather situation today by day
    • etc.

=end html =begin html_DE

PROPLANTA

    Das Modul extrahiert Wetterdaten von der Website www.proplanta.de.
    Es stellt eine Vorhersage für 12 Tage zur Verfügung - während der ersten 7 Tage im 3-Stunden-Intervall.
    Dieses Modul erzeugt eine hohe CPU-Last. Es wird deshalb empfohlen, die auszulesenden Vorhersagetage zu reduzieren.
    Es nutzt die Perl-Module HTTP::Request, LWP::UserAgent und HTML::Parse.

    Define

      define <Name> PROPLANTA [Stadt] [Ländercode]
      Beispiel:
      define wetter PROPLANTA Bern ch
      define wetter PROPLANTA Wittingen+(Niedersachsen)
       
    • [Stadt]
      Optional. Die Stadt muss auf www.proplanta.de auswählbar sein.
      Wichtig!! Auf die großen Anfangsbuchstaben achten. Leerzeichen im Stadtnamen werden durch ein + (Plus) ersetzt.

    • [Ländercode]
      Optional. Mögliche Werte: de (Standard), at, ch, fr, it

    • Über die Funktion PROPLANTA_Html wird ein HTML-Code für eine 3-Tages-Vorhersage erzeugt.
      Beispiel:
      define Vorschau weblink htmlCode {PROPLANTA_Html("Wetter")}


    Set

    • set <name> update
      Startet sofort ein neues Auslesen der Wetterdaten.

    Attribute

    • forecastDays <4-14>
      Anzahl Tage, für die die Vorhersage ausgelesen werden soll. Standard ist 14 Tage (inkl. heute).

    • INTERVAL <Abfrageinterval>
      Abfrageinterval in Sekunden (Standard 3600 = 1 Stunde)

    • URL <Internetadresse>
      Internetadresse, von der die Daten ausgelesen werden (überschreibt die Werte im 'define'-Term)

    • readingFnAttributes


    Vorhersagewerte

    • fc0|1|2|3...|13_... - Vorhersagewerte für heute|morgen|übermorgen|in 3|...|13 Tagen
    • fc0_...00|03|06|09|12|15|18|21 - Vorhersagewerte für heute um 00|03|06|09|12|15|18|21 Uhr
    • fc0_chOfRainDay|Night - heutiges Niederschlagsrisiko tagsüber|nachts in %
    • fc1_chOfRain15 - morgiges Niederschlagsrisiko um 15:00 Uhr in %
    • fc2_cloud15 - Wolkenbedeckungsgrad übermorgen um 15:00 Uhr in %
    • fc0_dew - Taubildung heute (0=keine, 1=leicht, 2=mäßig, 3=stark)
    • fc0_evapor - Verdunstung heute (0=keine, 1=gering, 2=mäßig, 3=stark)
    • fc0_frost - Bodenfrost heute (0=nein, 1=ja)
    • fc1_moonRise|Set - Mondauf|untergang morgen
    • fc0_rad - Globalstrahlung heute
    • fc0_rain15 - Niederschlagsmenge heute um 15:00 Uhr in mm
    • fc0_sun - relative Sonnenscheindauer heute in % (zwischen Sonnenauf- und -untergang)
    • fc0_tempMin|Max - Minimal|Maximaltemperatur heute in °C
    • fc0_temp15 - Temperatur heute um 15:00 Uhr in °C
    • fc0_uv - UV-Index heute
    • fc0_weatherMorning|Day|Evening|Night - Wetterzustand heute morgen|tagsüber|abends|nachts
    • fc0_weatherDayIcon - Icon Wetterzustand heute tagsüber
    • etc.

    Aktuelle Werte

    • cloudBaseMin|Max - Höhe der minimalen|maximalen Wolkenuntergrenze in m
    • dewPoint - Taupunkt in °C
    • humidity - relative Feuchtigkeit in %
    • obs_time - Uhrzeit der Wetterbeobachtung
    • pressure - Luftdruck in hPa
    • temperature - Temperature in °C
    • visibility - Sichtweite in km
    • wind - Windgeschwindigkeit in km/h
    • windDir - Windrichtung in °


=end html_DE =cut