From 8b3bed8d6caea9a9c55b6704d755a89a8c9f0b0c Mon Sep 17 00:00:00 2001 From: borisneubert <> Date: Sun, 16 Sep 2012 16:31:41 +0000 Subject: [PATCH] switched 59_Weather from Google to Yahoo API (thanks to Erwin Menschhorn) tag img replaced tag gif in 02_RSS layout definition git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@1853 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- FHEM/02_RSS.pm | 56 ++++++--- FHEM/59_Weather.pm | 264 ++++++++++++++++++++++++++++++------------- docs/commandref.html | 14 ++- 3 files changed, 234 insertions(+), 100 deletions(-) diff --git a/FHEM/02_RSS.pm b/FHEM/02_RSS.pm index 334bd2b15..dd92bbcdf 100644 --- a/FHEM/02_RSS.pm +++ b/FHEM/02_RSS.pm @@ -5,7 +5,7 @@ # e-mail: omega at online dot de # ############################################## -# $Id: $ +# $Id $ package main; use strict; @@ -248,21 +248,41 @@ RSS_itemDate { } - - - sub -RSS_itemGif { - my ($S,$x,$y,$host,$filename,%params)= @_; - return if($host eq ""); - return if($filename eq ""); +RSS_itemImg { + my ($S,$x,$y,$scale,$imgtype,$srctype,$arg,%params)= @_; + return if($arg eq ""); + my $I; + if($srctype eq "url") { + my $data = GetFileFromURL($arg,3,undef,1); + if($imgtype eq "gif") { + $I= GD::Image->newFromGifData($data); + } elsif($imgtype eq "png") { + $I= GD::Image->newFromPngData($data); + } elsif($imgtype eq "jpeg") { + $I= GD::Image->newFromJpegData($data); + } else { + return; + } + } elsif($srctype eq "file") { + if($imgtype eq "gif") { + $I= GD::Image->newFromGif($arg); + } elsif($imgtype eq "png") { + $I= GD::Image->newFromPng($arg); + } elsif($imgtype eq "jpeg") { + $I= GD::Image->newFromJpeg($arg); + } else { + return; + } + } else { + return; + } ($x,$y)= RSS_xy($S,$x,$y); - my $data = GetFileFromURL("http://$host$filename"); - return unless(defined($data)); - my $I= GD::Image->newFromGifData($data); my ($width,$height)= $I->getBounds(); - Log 5, "RSS placing $host$filename ($width x $height) at ($x,$y)"; - $S->copy($I,$x,$y,0,0,$width,$height); + my ($swidth,$sheight)= (int($scale*$width), int($scale*$height)); + #Debug "RSS placing $arg ($swidth x $sheight) at ($x,$y)"; + Log 5, "RSS placing $arg ($swidth x $sheight) at ($x,$y)"; + $S->copyResampled($I,$x,$y,0,0,$swidth,$sheight,$width,$height); } @@ -278,7 +298,7 @@ RSS_evalLayout($$@) { $params{pt}= 12; $params{rgb}= "ffffff"; - my ($x,$y,$text,$host,$filename,$format); + my ($x,$y,$scale,$text,$imgtype,$srctype,$arg,$format); my $cont= ""; foreach my $line (@layout) { @@ -315,10 +335,10 @@ RSS_evalLayout($$@) { } elsif($cmd eq "date") { ($x,$y)= split("[ \t]+", $def, 2); RSS_itemDate($S,$x,$y,%params); - } elsif($cmd eq "gif") { - ($x,$y,$host,$filename)= split("[ \t]+", $def,4); - my $fn= AnalyzePerlCommand(undef, $filename); - RSS_itemGif($S,$x,$y,$host,$fn,%params); + } elsif($cmd eq "img") { + ($x,$y,$scale,$imgtype,$srctype,$arg)= split("[ \t]+", $def,6); + my $arg= AnalyzePerlCommand(undef, $arg); + RSS_itemImg($S,$x,$y,$scale,$imgtype,$srctype,$arg,%params); } else { Log 1, "$name: Illegal command $cmd in layout definition."; } diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index 58a001047..a6de4ce44 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -1,24 +1,23 @@ # # # 59_Weather.pm -# written by Dr. Boris Neubert 2009-06-01 +# maintainer: Dr. Boris Neubert 2009-06-01 # e-mail: omega at online dot de +# Port to Yahoo by Erwin Menschhorn 2012-08-30 +# e-mail emenschhorn at gmail dot com # ############################################## # $Id$ package main; - - use strict; use warnings; use Time::HiRes qw(gettimeofday); -my $UseWeatherGoogle= 0; # if you want Weather:Google back please set this to 1 and uncomment below. -## use Weather::Google; +# +# uses the Yahoo! Weather API: http://developer.yahoo.com/weather/ +# -# taken from Daniel "Possum" LeWarne's Google::Weather module -# http://cpansearch.perl.org/src/POSSUM/Weather-Google-0.05/lib/Weather/Google.pm # Mapping of current supported encodings my %DEFAULT_ENCODINGS = ( @@ -40,15 +39,63 @@ my %DEFAULT_ENCODINGS = ( 'zh-TW' => 'utf-8', ); + +# Mapping / translation of current weather codes 0-47 +my @YahooCodes_us = ( + 'tornado', 'tropical storm', 'hurricane', 'severe thunderstorms', 'thunderstorms', 'mixed rain and snow', + 'mixed rain and sleet', 'mixed snow and sleet', 'freezing drizzle', 'drizzle', 'freezing rain' ,'showers', + 'showers', 'snow flurries', 'light snow showers', 'blowing snow', 'snow', 'hail', + 'sleet', 'dust', 'foggy', 'haze', 'smoky', 'blustery', + 'windy', 'cold', 'cloudy', + 'mostly cloudy', # night + 'mostly cloudy', # day + 'partly cloudy', # night + 'partly cloudy', # day + 'clear', #night + 'sunny', + 'fair', #night + 'fair', #day + 'mixed rain and hail', + 'hot', 'isolated thunderstorms', 'scattered thunderstorms', 'scattered thunderstorms', 'scattered showers', 'heavy snow', + 'scattered snow showers', 'heavy snow', 'partly cloudy', 'thundershowers', 'snow showers', 'isolated thundershowers'); + +my @YahooCodes_de = ( + 'Tornado', 'schwerer Sturm', 'Sturm', 'schwere Gewitter', 'Gewitter', 'Regen und Schnee', + 'Regen und Schnee', 'Schnee und Regen', 'Eisregen', 'Graupelschauer', 'gefrierender Regen' ,'Regen', + 'Regen', 'Schneegestöber', 'leichter Schneeschauer', 'Schneeverwehungen', 'Schnee', 'Hagel', + 'Schnee und Regen', 'Dunst', 'neblig', 'Staub oder Rauch', 'Smog', 'blustery', + 'windig', 'kalt', 'wolkig', + 'überwiegend wolkig', # night + 'überwiegend wolkig', # day + 'teilweise wolkig', # night + 'teilweise wolkig', # day + 'klar', # night + 'sonnig', + 'bewölkt', # night + 'bewölkt', # day + 'Regen und Hagel', + 'heiss', 'einzelne Gewitter', 'vereinzelt Gewitter', 'vereinzelt Gewitter', 'vereinzelt Regen', 'heftiger Schneefall', + 'vereinzelt Schneeschauer', 'heftiger Schneefall', 'teilweise wolkig', 'Gewitterregen', 'Schneeschauer', 'vereinzelt Gewitter'); + +my @directions_de = ('N', 'NNO', 'NO', 'ONO', 'O', 'OSO', 'SO', 'SSO', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW'); + +my %wdayXlate = ('Mon' => 'Mo.', 'Tue' => 'Di.', 'Wed'=> 'Mi.', 'Thu' => 'Do.', 'Fri' => 'Fr.', 'Sat' => 'Sa.', 'Sun' => 'So.'); + +my @iconlist = ( + 'storm', 'storm', 'storm', 'thunderstorm', 'thunderstorm', 'rainsnow', + 'sleet', 'snow', 'drizzle', 'drizzle', 'icy' ,'chance_of_rain', + 'chance_of_rain', 'snowflurries', 'chance_of_snow', 'heavysnow', 'snow', 'heavyrain', + 'sleet', 'dust', 'fog', 'haze', 'smoke', 'flurries', + 'windy', 'icy', 'cloudy', 'mostlycloudy_night', 'mostlycloudy', 'partly_cloudy_night', + 'partly_cloudy', 'clear', 'sunny', 'mostly_clear_night', 'overcast', 'heavyrain', + 'clear', 'scatteredthunderstorms', 'scatteredthunderstorms', 'scatteredthunderstorms', 'scatteredshowers', 'heavysnow', + 'chance_of_snow', 'heavysnow', 'partly_cloudy', 'heavyrain', 'chance_of_snow', 'scatteredshowers'); + ##################################### sub Weather_Initialize($) { my ($hash) = @_; -# Provider -# $hash->{Clients} = undef; - -# Consumer $hash->{DefFn} = "Weather_Define"; $hash->{UndefFn} = "Weather_Undef"; $hash->{GetFn} = "Weather_Get"; @@ -68,16 +115,22 @@ sub latin1_to_utf8($) { ################################### -sub temperature_in_c { +sub temperature_in_c($$) { my ($temperature, $unitsystem)= @_; return $unitsystem ne "SI" ? int(($temperature-32)*5/9+0.5) : $temperature; } -sub wind_in_km_per_h { +sub wind_in_km_per_h($$) { my ($wind, $unitsystem)= @_; return $unitsystem ne "SI" ? int(1.609344*$wind+0.5) : $wind; } +sub degrees_to_direction($) { + my ($degrees) = @_; + my $mod = int((($degrees + 11.25) % 360) / 22.5); + return $directions_de[$mod]; +} + ################################### sub Weather_UpdateReading($$$$) { @@ -112,36 +165,111 @@ sub Weather_UpdateReading($$$$) { return 1; } -################################### -sub Weather_RetrieveDataDirectly($) + +################################### +sub Weather_RetrieveData($) { my ($hash)= @_; - my $location= $hash->{LOCATION}; - #$location =~ s/([^\w()’*~!.-])/sprintf '%%%02x', ord $1/eg; - my $lang= $hash->{LANG}; + + my $location= $hash->{LOCATION}; # WOEID [WHERE-ON-EARTH-ID], go to http://weather.yahoo.com to find out + my $units= $hash->{UNITS}; my $fc = undef; - my $xml = GetFileFromURL("http://www.google.com/ig/api?weather=" . $location . "&hl=" . $lang); + my $xml = GetFileFromURL("http://weather.yahooapis.com/forecastrss?w=" . $location . "&u=" . $units, 3, undef, 1); return 0 if( ! defined $xml || $xml eq ""); + foreach my $l (split("<",$xml)) { #Log 1, "DEBUG WEATHER: line=\"$l\""; next if($l eq ""); # skip empty lines $l =~ s/(\/|\?)?>$//; # strip off /> and > my ($tag,$value)= split(" ", $l, 2); # split tag data=..... at the first blank - #Log 1, "DEBUG WEATHER: tag=\"$tag\" value=\"$value\""; - $fc= 0 if($tag eq "current_conditions"); - $fc++ if($tag eq "forecast_conditions"); - next if(!defined($value) || ($value !~ /^data=/)); + next if(!defined($tag) || ($tag !~ /^yweather:/)); + $fc= 0 if($tag eq "yweather:condition"); + $fc++ if($tag eq "yweather:forecast"); my $prefix= $fc ? "fc" . $fc ."_" : ""; - my $key= $tag; - $value=~ s/^data=\"(.*)\"$/$1/; # extract DATA from data="DATA" - if($DEFAULT_ENCODINGS{$lang} eq "latin1") { - $value= latin1_to_utf8($value); # latin1 -> UTF-8 + + ### location + if ($tag eq "yweather:location" ) { + $value =~/city="(.*?)" .*country="(.*?)".*/; + my $loc = ""; + $loc = $1 if (defined($1)); + $loc .= ", $2" if (defined($2)); + readingsUpdate($hash, "city", $loc); } - #Log 1, "DEBUG WEATHER: prefix=\"$prefix\" tag=\"$tag\" value=\"$value\""; - Weather_UpdateReading($hash,$prefix,$key,$value); + + ### current condition and forecast + if (($tag eq "yweather:condition" ) || ($tag eq "yweather:forecast" )) { + my $code = (($value =~/code="([0-9]*?)".*/) ? $1 : undef); + if (defined($code)) { + readingsUpdate($hash, $prefix . "code", $code); + my $text = $YahooCodes_de[$code]; + if ($text) { readingsUpdate($hash, $prefix . "condition", $text); } + #### add icon logic here - generate from code + $text = $iconlist[$code]; + readingsUpdate($hash, $prefix . "icon", $text) if ($text); + } + } + + ### current condition + if ($tag eq "yweather:condition" ) { + my $temp = (($value =~/temp="([0-9.]*?)".*/) ? $1 : undef); + if ($temp) { + readingsUpdate($hash, "temperature", $temp); + readingsUpdate($hash, "temp_c", $temp); # compatibility + $temp = ( $temp * 9 / 5 ) + 32; # Celsius to Fahrenheit + readingsUpdate($hash, "temp_f", $temp); # compatibility + } + + my $datum = (($value =~/date=".*? ([0-9].*)".*/) ? $1 : undef); + readingsUpdate($hash, "current_date_time", $datum) if (defined($1)); + + my $day = (($value =~/date="(.*?), .*/) ? $1 : undef); + if ($day) { + my $day_de = $wdayXlate{$day}; + readingsUpdate($hash, "day_of_week", $day_de); + } + } + + ### forecast + if ($tag eq "yweather:forecast" ) { + my $low_c = (($value =~/low="([0-9.]*?)".*/) ? $1 : undef); + if ($low_c) { readingsUpdate($hash, $prefix . "low_c", $low_c); } + my $high_c = (($value =~/high="([0-9.]*?)".*/) ? $1 : undef); + if ($high_c) { readingsUpdate($hash, $prefix . "high_c", $high_c); } + my $day1 = (($value =~/day="(.*?)" .*/) ? $1 : undef); # forecast + if ($day1) { + my $day1_de = $wdayXlate{$day1}; + readingsUpdate($hash, $prefix . "day_of_week", $day1_de); + } + } + + ### humidiy / Pressure + if ($tag eq "yweather:atmosphere" ) { + $value =~/humidity="([0-9.]*?)" .*visibility="([0-9.]*?|\s*?)" .*pressure="([0-9.]*?)" .*rising="([0-9.]*?)" .*/; + + if ($1) { readingsUpdate($hash, "humidity", $1); } + my $vis = (($2 eq "") ? " " : $2); # clear visibility field + readingsUpdate($hash, "visibility", $vis); + if ($3) { readingsUpdate($hash, "pressure", $3); } + if ($4) { readingsUpdate($hash, "pressure-trend", $4); } + } + + ### wind + if ($tag eq "yweather:wind" ) { + $value =~/chill="([0-9.]*?)" .*direction="([0-9.]*?)" .*speed="([0-9.]*?)" .*/; + readingsUpdate($hash, "wind_chill", $1) if (defined($1)); + readingsUpdate($hash, "wind_direction", $2) if (defined($2)); + my $windspeed= defined($3) ? int($3) : ""; + readingsUpdate($hash, "wind_speed", $windspeed); + readingsUpdate($hash, "wind", $windspeed); # duplicate for compatibility + if (defined($2) & defined($3)) { + my $wdir = degrees_to_direction($2); + readingsUpdate($hash, "wind_condition", "Wind: $wdir mit $windspeed km/h"); # compatibility + } + } } -} +} #end sub + ################################### sub Weather_RetrieveDataViaWeatherGoogle($) @@ -201,14 +329,9 @@ sub Weather_GetUpdate($) InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Weather_GetUpdate", $hash, 1); } - readingsBeginUpdate($hash); - if($UseWeatherGoogle) { - Weather_RetrieveDataViaWeatherGoogle($hash); - } else { - Weather_RetrieveDataDirectly($hash); - } + Weather_RetrieveData($hash); my $temperature= $hash->{READINGS}{temperature}{VAL}; my $humidity= $hash->{READINGS}{humidity}{VAL}; @@ -291,7 +414,8 @@ sub Weather_Define($$) { $hash->{LOCATION} = $location; $hash->{INTERVAL} = $interval; - $hash->{LANG} = $lang; + $hash->{LANG} = $lang; + $hash->{UNITS} = "c"; # hardcoded to use degrees centigrade (Celsius) $hash->{READINGS}{current_date_time}{TIME}= TimeNow(); $hash->{READINGS}{current_date_time}{VAL}= "none"; @@ -315,36 +439,23 @@ sub Weather_Undef($$) { ##################################### +# Icon Parameter + +use constant ICONHIGHT => 120; +use constant ICONWIDTH => 175; +use constant ICONSCALE => 0.5; + +##################################### sub -WeatherIconIMGTag($$$) { +WeatherIconIMGTag($) { - use constant GOOGLEURL => "http://www.google.de"; - use constant SIZE => "50%"; - - my ($icon,$uselocal,$isday)= @_; - - my $url; - my $style= ""; + my $width= int(ICONSCALE*ICONWIDTH); + my ($icon)= @_; + my $url= FW_IconURL("weather/$icon"); + my $style= " width=$width"; + return "\"$icon\""; - if($uselocal) { - # strip off path and extension - $icon =~ s,^/ig/images/weather/(.*)\.gif$,$1,; - - if($isday) { - $icon= "weather/${icon}" - } else { - $icon= "weather/${icon}_night" - } - - $url= "fhem/icons/$icon"; - $style= " height=".SIZE." width=".SIZE; - } else { - $url= GOOGLEURL . $icon; - } - - return ""; - } ##################################### @@ -357,24 +468,23 @@ WeatherAsHtml($) return "$d is not a Weather instance
" if(!$defs{$d} || $defs{$d}{TYPE} ne "Weather"); - my $uselocal= AttrVal($d,"localicons",0); - my $isday; - if(exists &isday) { - $isday = isday(); - } else { - $isday = 1; #($hour>6 && $hour<19); - } - - my $ret = ""; - $ret .= sprintf('', - WeatherIconIMGTag(ReadingsVal($d, "icon", ""),$uselocal,$isday), + my $width= int(ICONSCALE*ICONWIDTH); + + my $ret = sprintf("
%s%s
temp %s, hum %s, %s
", $width); + $ret .= sprintf('', + $width, + WeatherIconIMGTag(ReadingsVal($d, "icon", "")), ReadingsVal($d, "condition", ""), ReadingsVal($d, "temp_c", ""), ReadingsVal($d, "humidity", ""), ReadingsVal($d, "wind_condition", "")); - for(my $i=1; $i<=4; $i++) { - $ret .= sprintf('', - WeatherIconIMGTag(ReadingsVal($d, "fc${i}_icon", ""),$uselocal,$isday), + for(my $i=1; $i<=2; $i++) { + # Yahoo provides only 2 days. + #next if (ReadingsVal($d, "fc${i}_code", "") eq ""); # MH skip non existent entries + + $ret .= sprintf('', + $width, + WeatherIconIMGTag(ReadingsVal($d, "fc${i}_icon", "")), ReadingsVal($d, "fc${i}_day_of_week", ""), ReadingsVal($d, "fc${i}_condition", ""), ReadingsVal($d, "fc${i}_low_c", ""), ReadingsVal($d, "fc${i}_high_c", "")); diff --git a/docs/commandref.html b/docs/commandref.html index 808a77029..9ca77e467 100644 --- a/docs/commandref.html +++ b/docs/commandref.html @@ -6228,9 +6228,13 @@ Attributes:
  • time <x> <y>
    Renders the current time in HH:MM format.

  • seconds <x> <y> <format>
    Renders the curent seconds. Maybe usefull for a RSS Clock. With option colon a :

  • date <x> <y>
    Renders the current date in DD:MM:YYY format.

  • -
  • gif <x> <y> <hostname> <filename>
    Renders the GIF picture - that is downloaded from the given host. You can use - { <perl special> } for <filename>. See below for example.

  • +
  • img <x> <y> <s> <imgtype> <srctype> <arg>
    Renders a picture at the + position (<x>, <y>). The <imgtype> is one of gif, jpeg, png. + The picture is scaled by the factor <s> (a decimal value). If <srctype> is file, the picture + is loaded from the filename <arg>, if <srctype> is url, the picture + is loaded from the URL <arg>. You can use + { <perl special> } for <arg>. See below for example. + Notice: do not load the image from URL that is served by fhem as it leads to a deadlock.

  • @@ -6241,8 +6245,8 @@ Attributes:
    pt 48 # font size in points
    time 0.10 0.90
    pt 24
    - text 0.10 0.95 { ReadingsVal("MyWeather","temperature","?"). "°C" }
    - gif 20 530 www.google.com:80 { ReadingsVal("e.ext.Weather","icon","") }
    + text 0.10 0.95 { ReadingsVal("MyWeather","temperature","?"). "°C" }
    + img 20 530 0.5 png file { "/usr/share/fhem/www/images/weather/" . ReadingsVal("MyWeather","icon","") . ".png" }
    %s%s
    %s°C %s%%
    %s
    %s%s: %s
    min %s max %s
    %s%s: %s
    min %s°C max %s°C