59_Weather: included caching in YahooWeatherAPI

git-svn-id: https://svn.fhem.de/fhem/trunk/fhem@11249 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
borisneubert 2016-04-16 17:03:47 +00:00
parent 86b521104e
commit c5563891b5
3 changed files with 136 additions and 101 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Add changes at the top of the list. Keep it in ASCII, and 80-char wide.
# Do not insert empty lines here, update check depends on it. # Do not insert empty lines here, update check depends on it.
- change: 59_Weather: included caching in YahooWeatherAPI
- change: 49_SSCam: behavior of "set ... on" changed, Attr "recextend" added - change: 49_SSCam: behavior of "set ... on" changed, Attr "recextend" added
please have a look at commandref and Wiki please have a look at commandref and Wiki
- bugfix: 49_SSCam: setstate-warning if FHEM is restarted and SVS not - bugfix: 49_SSCam: setstate-warning if FHEM is restarted and SVS not

View File

@ -100,21 +100,6 @@ sub degrees_to_direction($@) {
return $directions_txt_i18n[$mod]; return $directions_txt_i18n[$mod];
} }
sub F_to_C($) {
my ($F)= @_;
return(int(($F-32)*5/9+0.5));
}
sub inHg_to_hPa($) {
my ($inHg)= @_;
return int($inHg/33.86390+0.5);
}
sub miles_to_km($) {
my ($miles)= @_;
return int(1.609347219*$miles+0.5);
}
################################### ###################################
sub Weather_RetrieveData($$) { sub Weather_RetrieveData($$) {
my ($name, $blocking) = @_; my ($name, $blocking) = @_;
@ -132,7 +117,30 @@ sub Weather_RetrieveData($$) {
hash => $hash, hash => $hash,
); );
YahooWeatherAPI_RetrieveData(\%args); my $maxage= $hash->{fhem}{allowCache} ? 600 : 0; # use cached data if allowed
$hash->{fhem}{allowCache}= 1;
YahooWeatherAPI_RetrieveDataWithCache($maxage, \%args);
}
sub Weather_ReturnWithError($$$$$) {
my ($hash, $doTrigger, $err, $pubDate, $pubDateComment)= @_;
my $name= $hash->{NAME};
$hash->{fhem}{allowCache}= 0; # do not use cache on next try
Log3 $hash, 3, "$name: $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "lastError", $err);
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment) if(defined($pubDateComment));
readingsBulkUpdate($hash, "pubDateRemote", $pubDate) if(defined($pubDate));
readingsBulkUpdate($hash, "validity", "stale");
readingsEndUpdate($hash, $doTrigger);
my $next= 60; # $next= $hash->{INTERVAL};
Weather_RearmTimer($hash, gettimeofday()+$next);
return;
} }
sub Weather_RetrieveDataFinished($$$) { sub Weather_RetrieveDataFinished($$$) {
@ -143,75 +151,22 @@ sub Weather_RetrieveDataFinished($$$) {
my $name= $hash->{NAME}; my $name= $hash->{NAME};
my $doTrigger= $argsRef->{blocking} ? 0 : 1; my $doTrigger= $argsRef->{blocking} ? 0 : 1;
# the next 4 stanzas need to be rewritten, they are too repetitive for my taste
# check for error from retrieving data # check for error from retrieving data
if($err) { return Weather_ReturnWithError($hash, $doTrigger, $err, undef, undef) if($err);
Log3 $hash, 3, "$name: $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "lastError", $err);
readingsBulkUpdate($hash, "validity", "stale");
readingsEndUpdate($hash, $doTrigger);
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
return;
}
# decode JSON data from Weather Channel # decode JSON data from Weather Channel
my $data; my $data;
($err, $data)= YahooWeatherAPI_JSONReturnChannelData($response); ($err, $data)= YahooWeatherAPI_JSONReturnChannelData($response);
if($err) { return Weather_ReturnWithError($hash, $doTrigger, $err, undef, undef) if($err);
Log3 $hash, 3, "$name: $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "lastError", $err);
readingsBulkUpdate($hash, "validity", "stale");
readingsEndUpdate($hash, $doTrigger);
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
return;
}
# check if up-to-date # check if up-to-date
my ($pubDateComment, $pubDate, $pubDateTs)= YahooWeatherAPI_pubDate($data); my ($pubDateComment, $pubDate, $pubDateTs)= YahooWeatherAPI_pubDate($data);
if(!defined($pubDateTs)) { return Weather_ReturnWithError($hash, $doTrigger, $pubDateComment, $pubDate, $pubDateComment)
Log3 $hash, 3, "$name: $pubDateComment"; unless(defined($pubDateTs));
readingsBeginUpdate($hash); my $ts= defined($hash->{READINGS}{pubDateTs}) ? $hash->{READINGS}{pubDateTs}{VAL} : 0;
readingsBulkUpdate($hash, "lastError", $pubDateComment); return Weather_ReturnWithError($hash, $doTrigger, "stale data received", $pubDate, $pubDateComment)
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment); if($ts> $pubDateTs);
readingsBulkUpdate($hash, "pubDateRemote", $pubDate);
readingsBulkUpdate($hash, "validity", "stale");
readingsEndUpdate($hash, $doTrigger);
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
return;
} else {
my $ts= defined($hash->{READINGS}{pubDateTs}) ? $hash->{READINGS}{pubDateTs}{VAL} : 0;
if($ts> $pubDateTs) {
$err= "stale data received";
Log3 $hash, 3, "$name: $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "lastError", $err);
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment);
readingsBulkUpdate($hash, "pubDateRemote", $pubDate);
readingsBulkUpdate($hash, "validity", "stale");
readingsEndUpdate($hash, $doTrigger);
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
return;
}
}
# units
my $units= YahooWeatherAPI_units($data); # units hash reference
if($units->{temperature} ne "C") {
$err= "wrong units received";
Log3 $hash, 3, "$name: $err";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "lastError", $err);
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment);
readingsBulkUpdate($hash, "pubDateRemote", $pubDate);
readingsBulkUpdate($hash, "validity", "stale");
readingsEndUpdate($hash, $doTrigger);
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
return;
}
# #
# from here on we assume that $data is complete and correct # from here on we assume that $data is complete and correct
@ -249,21 +204,27 @@ sub Weather_RetrieveDataFinished($$$) {
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
# delete some unused readings
delete($hash->{READINGS}{temp_f}) if(defined($hash->{READINGS}{temp_f}));
delete($hash->{READINGS}{unit_distance}) if(defined($hash->{READINGS}{unit_distance}));
delete($hash->{READINGS}{unit_speed}) if(defined($hash->{READINGS}{unit_speed}));
delete($hash->{READINGS}{unit_pressuree}) if(defined($hash->{READINGS}{unit_pressuree}));
delete($hash->{READINGS}{unit_temperature}) if(defined($hash->{READINGS}{unit_temperature}));
# convert to metric units as far as required
my $isConverted= YahooWeatherAPI_ConvertChannelData($data);
# housekeeping information # housekeeping information
readingsBulkUpdate($hash, "lastError", ""); readingsBulkUpdate($hash, "lastError", "");
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment); readingsBulkUpdate($hash, "pubDateComment", $pubDateComment);
readingsBulkUpdate($hash, "pubDate", $pubDate); readingsBulkUpdate($hash, "pubDate", $pubDate);
readingsBulkUpdate($hash, "pubDateRemote", $pubDate); readingsBulkUpdate($hash, "pubDateRemote", $pubDate);
readingsBulkUpdate($hash, "pubDateTs", $pubDateTs); readingsBulkUpdate($hash, "pubDateTs", $pubDateTs);
readingsBulkUpdate($hash, "isConverted", $isConverted);
readingsBulkUpdate($hash, "validity", "up-to-date"); readingsBulkUpdate($hash, "validity", "up-to-date");
# units
readingsBulkUpdate($hash, "unit_distance", $units->{distance});
readingsBulkUpdate($hash, "unit_speed", $units->{speed});
readingsBulkUpdate($hash, "unit_pressuree", $units->{pressure});
readingsBulkUpdate($hash, "unit_temperature", $units->{temperature});
# description # description
readingsBulkUpdate($hash, "description", $data->{description}); readingsBulkUpdate($hash, "description", $data->{description});
@ -278,22 +239,16 @@ sub Weather_RetrieveDataFinished($$$) {
my $windspeed= int($data->{wind}{speed}+0.5); my $windspeed= int($data->{wind}{speed}+0.5);
readingsBulkUpdate($hash, "wind", $windspeed); readingsBulkUpdate($hash, "wind", $windspeed);
readingsBulkUpdate($hash, "wind_speed", $windspeed); readingsBulkUpdate($hash, "wind_speed", $windspeed);
# wind_chill comes in °F readingsBulkUpdate($hash, "wind_chill", $data->{wind}{chill});
readingsBulkUpdate($hash, "wind_chill", F_to_C($data->{wind}{chill}));
my $winddir= $data->{wind}{direction}; my $winddir= $data->{wind}{direction};
readingsBulkUpdate($hash, "wind_direction", $winddir); readingsBulkUpdate($hash, "wind_direction", $winddir);
my $wdir= degrees_to_direction($winddir, @directions_txt_i18n); my $wdir= degrees_to_direction($winddir, @directions_txt_i18n);
readingsBulkUpdate($hash, "wind_condition", "Wind: $wdir $windspeed km/h"); readingsBulkUpdate($hash, "wind_condition", "Wind: $wdir $windspeed km/h");
# delete the temp_f reading
delete($hash->{READINGS}{temp_f}) if(defined($hash->{READINGS}{temp_f}));
# atmosphere # atmosphere
my $humidity= $data->{atmosphere}{humidity}; my $humidity= $data->{atmosphere}{humidity};
readingsBulkUpdate($hash, "humidity", $humidity); readingsBulkUpdate($hash, "humidity", $humidity);
# pressure is in inHg my $pressure= $data->{atmosphere}{pressure};
my $pressure= inHg_to_hPa($data->{atmosphere}{pressure});
readingsBulkUpdate($hash, "pressure", $pressure); readingsBulkUpdate($hash, "pressure", $pressure);
readingsBulkUpdate($hash, "visibility", int($data->{atmosphere}{visibility}+0.5)); readingsBulkUpdate($hash, "visibility", int($data->{atmosphere}{visibility}+0.5));
my $pressure_trend= $data->{atmosphere}{rising}; my $pressure_trend= $data->{atmosphere}{rising};
@ -421,8 +376,7 @@ sub Weather_Notify($$) {
Weather_DisarmTimer($hash); Weather_DisarmTimer($hash);
my $delay= 10+int(rand(20)); my $delay= 10+int(rand(20));
# delay removed until further notice #$delay= 3; # delay removed until further notice
$delay= 3;
Log3 $hash, 5, "Weather $name: FHEM initialization or rereadcfg triggered update, delay $delay seconds."; Log3 $hash, 5, "Weather $name: FHEM initialization or rereadcfg triggered update, delay $delay seconds.";
Weather_RearmTimer($hash, gettimeofday()+$delay) ; Weather_RearmTimer($hash, gettimeofday()+$delay) ;
@ -461,6 +415,8 @@ sub Weather_Define($$) {
$hash->{READINGS}{current_date_time}{TIME}= TimeNow(); $hash->{READINGS}{current_date_time}{TIME}= TimeNow();
$hash->{READINGS}{current_date_time}{VAL}= "none"; $hash->{READINGS}{current_date_time}{VAL}= "none";
$hash->{fhem}{allowCache}= 1;
Weather_GetUpdate($hash) if($init_done); Weather_GetUpdate($hash) if($init_done);
return undef; return undef;

View File

@ -131,7 +131,30 @@ my @YahooCodes_pl = (
'gorąco', 'gdzieniegdzie burze', 'burze', 'burze', 'przelotne opady śniegu', 'duże opady śniegu', 'gorąco', 'gdzieniegdzie burze', 'burze', 'burze', 'przelotne opady śniegu', 'duże opady śniegu',
'ciężkie opady śniegu', 'dużo śniegu', 'częściowe zachmurzenie', 'burze z deszczem', 'opady śniegu', 'przejściowo burze'); 'ciężkie opady śniegu', 'dużo śniegu', 'częściowe zachmurzenie', 'burze z deszczem', 'opady śniegu', 'przejściowo burze');
###################################
# Cache
my %YahooWeatherAPI_CachedData= ();
my %YahooWeatherAPI_CachedDataTs= ();
###################################
sub F_to_C($) {
my ($F)= @_;
return(int(($F-32)*5/9+0.5));
}
sub inHg_to_hPa($) {
my ($inHg)= @_;
return int($inHg/33.86390+0.5);
}
sub miles_to_km($) {
my ($miles)= @_;
return int(1.609347219*$miles+0.5);
}
###################################
# call: YahooWeatherAPI_RetrieveData(%%args) # call: YahooWeatherAPI_RetrieveData(%%args)
# #
@ -144,9 +167,33 @@ my @YahooCodes_pl = (
# #
sub YahooWeatherAPI_RetrieveData($) { sub YahooWeatherAPI_RetrieveData($) {
my ($argsRef)= @_; my ($argsRef)= @_;
YahooWeatherAPI_RetrieveDataWithCache(0, $argsRef);
}
sub YahooWeatherAPI_RetrieveDataWithCache($$) {
my ($maxage, $argsRef)= @_;
my $woeid= $argsRef->{woeid}; my $woeid= $argsRef->{woeid};
Log3 undef, 5, "YahooWeatherAPI: retrieve weather for $woeid.";
# retrieve data from cache
my $ts= $YahooWeatherAPI_CachedDataTs{$woeid};
if(defined($ts)) {
my $now= time();
my $age= $now- $ts;
if($age< $maxage) {
Log3 undef, 5, "YahooWeatherAPI: data is cached, age $age seconds < $maxage seconds.";
$argsRef->{callbackFnRef}($argsRef, "", $YahooWeatherAPI_CachedData{$woeid});
return;
} else {
Log3 undef, 5, "YahooWeatherAPI: cache is expired, age $age seconds > $maxage seconds.";
}
} else {
Log3 undef, 5, "YahooWeatherAPI: no data in cache.";
}
my $format= $argsRef->{format}; my $format= $argsRef->{format};
my $blocking= $argsRef->{blocking}; my $blocking= $argsRef->{blocking};
my $callbackFnRef= $argsRef->{callbackFnRef}; my $callbackFnRef= $argsRef->{callbackFnRef};
@ -157,14 +204,14 @@ sub YahooWeatherAPI_RetrieveData($) {
if ($blocking) { if ($blocking) {
# do not use noshutdown => 0 in parameters # do not use noshutdown => 0 in parameters
my $response = HttpUtils_BlockingGet({ url => $url, timeout => 5 }); my $response = HttpUtils_BlockingGet({ url => $url, timeout => 15 });
my %param= (argsRef => $argsRef); my %param= (argsRef => $argsRef);
YahooWeatherAPI_RetrieveDataFinished(\%param, undef, $response); YahooWeatherAPI_RetrieveDataFinished(\%param, undef, $response);
} else { } else {
# do not use noshutdown => 0 in parameters # do not use noshutdown => 0 in parameters
HttpUtils_NonblockingGet({ HttpUtils_NonblockingGet({
url => $url, url => $url,
timeout => 5, timeout => 15,
argsRef => $argsRef, argsRef => $argsRef,
callback => \&YahooWeatherAPI_RetrieveDataFinished, callback => \&YahooWeatherAPI_RetrieveDataFinished,
}); });
@ -172,10 +219,16 @@ sub YahooWeatherAPI_RetrieveData($) {
} }
sub YahooWeatherAPI_RetrieveDataFinished($$$) { sub YahooWeatherAPI_RetrieveDataFinished($$$) {
my ($paramRef, $err, $xml) = @_; my ($paramRef, $err, $response) = @_;
my $argsRef= $paramRef->{argsRef}; my $argsRef= $paramRef->{argsRef};
#Debug "Finished retrieving Yahoo Weather data for " . $argsRef->{hash}->{NAME}; #Debug "Finished retrieving Yahoo Weather data for " . $argsRef->{hash}->{NAME};
$argsRef->{callbackFnRef}($argsRef, $err, $xml); if(!$err) {
my $woeid= $argsRef->{woeid};
$YahooWeatherAPI_CachedDataTs{$woeid}= time();
$YahooWeatherAPI_CachedData{$woeid}= $response;
Log3 undef, 5, "YahooWeatherAPI: caching data.";
}
$argsRef->{callbackFnRef}($argsRef, $err, $response);
} }
@ -184,6 +237,7 @@ sub YahooWeatherAPI_JSONReturnChannelData($) {
my ($response)= @_; my ($response)= @_;
return("empty response", undef) unless($response); return("empty response", undef) unless($response);
#Debug "Decoding response: $response"; #Debug "Decoding response: $response";
#Debug "response: " . Dumper($response);
my $data; my $data;
eval { $data= decode_json($response) }; eval { $data= decode_json($response) };
return($@, undef) if($@); return($@, undef) if($@);
@ -193,10 +247,34 @@ sub YahooWeatherAPI_JSONReturnChannelData($) {
#Debug "$count result(s)."; #Debug "$count result(s).";
return("$count result(s) retrieved", undef) unless($count == 1); return("$count result(s) retrieved", undef) unless($count == 1);
my $channel= $query->{results}{channel}; my $channel= $query->{results}{channel};
#Debug "Result: " . Dumper($channel);
return(undef, $channel); return(undef, $channel);
} }
sub YahooWeatherAPI_ConvertChannelData($) {
my ($data)= @_; # hash reference
$data->{wind}{chill}= F_to_C($data->{wind}{chill}); # wrongly always °F
$data->{atmosphere}{pressure}= inHg_to_hPa($data->{atmosphere}{pressure}); # wrongly always in inHg
my $units= YahooWeatherAPI_units($data); # units hash reference
return 0 if($units->{temperature} eq "C");
$data->{wind}{speed}= miles_to_km($data->{wind}{speed});
$data->{atmosphere}{visibility}= miles_to_km($data->{atmosphere}{visibility});
my $item= $data->{item};
$item->{condition}{temp}= F_to_C($item->{condition}{temp});
my $forecast= $item->{forecast};
foreach my $fc (@{$forecast}) {
$fc->{low}= F_to_C($fc->{low});
$fc->{high}= F_to_C($fc->{high});
}
return 1;
}
sub YahooWeatherAPI_ParseDateTime($) { sub YahooWeatherAPI_ParseDateTime($) {