2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-03 16:56:54 +00:00

59_Weather: included caching in YahooWeatherAPI

git-svn-id: https://svn.fhem.de/fhem/trunk@11249 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
borisneubert 2016-04-16 17:03:47 +00:00
parent 422f12057c
commit e02d2fc17f
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.
# 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
please have a look at commandref and Wiki
- 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];
}
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($$) {
my ($name, $blocking) = @_;
@ -132,7 +117,30 @@ sub Weather_RetrieveData($$) {
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($$$) {
@ -143,75 +151,22 @@ sub Weather_RetrieveDataFinished($$$) {
my $name= $hash->{NAME};
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
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;
}
return Weather_ReturnWithError($hash, $doTrigger, $err, undef, undef) if($err);
# decode JSON data from Weather Channel
my $data;
($err, $data)= YahooWeatherAPI_JSONReturnChannelData($response);
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;
}
return Weather_ReturnWithError($hash, $doTrigger, $err, undef, undef) if($err);
# check if up-to-date
my ($pubDateComment, $pubDate, $pubDateTs)= YahooWeatherAPI_pubDate($data);
if(!defined($pubDateTs)) {
Log3 $hash, 3, "$name: $pubDateComment";
readingsBeginUpdate($hash);
readingsBulkUpdate($hash, "lastError", $pubDateComment);
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment);
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;
}
}
return Weather_ReturnWithError($hash, $doTrigger, $pubDateComment, $pubDate, $pubDateComment)
unless(defined($pubDateTs));
my $ts= defined($hash->{READINGS}{pubDateTs}) ? $hash->{READINGS}{pubDateTs}{VAL} : 0;
return Weather_ReturnWithError($hash, $doTrigger, "stale data received", $pubDate, $pubDateComment)
if($ts> $pubDateTs);
# 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
@ -249,21 +204,27 @@ sub Weather_RetrieveDataFinished($$$) {
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
readingsBulkUpdate($hash, "lastError", "");
readingsBulkUpdate($hash, "pubDateComment", $pubDateComment);
readingsBulkUpdate($hash, "pubDate", $pubDate);
readingsBulkUpdate($hash, "pubDateRemote", $pubDate);
readingsBulkUpdate($hash, "pubDateTs", $pubDateTs);
readingsBulkUpdate($hash, "isConverted", $isConverted);
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
readingsBulkUpdate($hash, "description", $data->{description});
@ -278,22 +239,16 @@ sub Weather_RetrieveDataFinished($$$) {
my $windspeed= int($data->{wind}{speed}+0.5);
readingsBulkUpdate($hash, "wind", $windspeed);
readingsBulkUpdate($hash, "wind_speed", $windspeed);
# wind_chill comes in °F
readingsBulkUpdate($hash, "wind_chill", F_to_C($data->{wind}{chill}));
readingsBulkUpdate($hash, "wind_chill", $data->{wind}{chill});
my $winddir= $data->{wind}{direction};
readingsBulkUpdate($hash, "wind_direction", $winddir);
my $wdir= degrees_to_direction($winddir, @directions_txt_i18n);
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
my $humidity= $data->{atmosphere}{humidity};
readingsBulkUpdate($hash, "humidity", $humidity);
# pressure is in inHg
my $pressure= inHg_to_hPa($data->{atmosphere}{pressure});
my $pressure= $data->{atmosphere}{pressure};
readingsBulkUpdate($hash, "pressure", $pressure);
readingsBulkUpdate($hash, "visibility", int($data->{atmosphere}{visibility}+0.5));
my $pressure_trend= $data->{atmosphere}{rising};
@ -421,8 +376,7 @@ sub Weather_Notify($$) {
Weather_DisarmTimer($hash);
my $delay= 10+int(rand(20));
# delay removed until further notice
$delay= 3;
#$delay= 3; # delay removed until further notice
Log3 $hash, 5, "Weather $name: FHEM initialization or rereadcfg triggered update, delay $delay seconds.";
Weather_RearmTimer($hash, gettimeofday()+$delay) ;
@ -461,6 +415,8 @@ sub Weather_Define($$) {
$hash->{READINGS}{current_date_time}{TIME}= TimeNow();
$hash->{READINGS}{current_date_time}{VAL}= "none";
$hash->{fhem}{allowCache}= 1;
Weather_GetUpdate($hash) if($init_done);
return undef;

View File

@ -131,7 +131,30 @@ my @YahooCodes_pl = (
'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');
###################################
# 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)
#
@ -144,9 +167,33 @@ my @YahooCodes_pl = (
#
sub YahooWeatherAPI_RetrieveData($) {
my ($argsRef)= @_;
YahooWeatherAPI_RetrieveDataWithCache(0, $argsRef);
}
sub YahooWeatherAPI_RetrieveDataWithCache($$) {
my ($maxage, $argsRef)= @_;
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 $blocking= $argsRef->{blocking};
my $callbackFnRef= $argsRef->{callbackFnRef};
@ -157,14 +204,14 @@ sub YahooWeatherAPI_RetrieveData($) {
if ($blocking) {
# 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);
YahooWeatherAPI_RetrieveDataFinished(\%param, undef, $response);
} else {
# do not use noshutdown => 0 in parameters
HttpUtils_NonblockingGet({
url => $url,
timeout => 5,
timeout => 15,
argsRef => $argsRef,
callback => \&YahooWeatherAPI_RetrieveDataFinished,
});
@ -172,10 +219,16 @@ sub YahooWeatherAPI_RetrieveData($) {
}
sub YahooWeatherAPI_RetrieveDataFinished($$$) {
my ($paramRef, $err, $xml) = @_;
my ($paramRef, $err, $response) = @_;
my $argsRef= $paramRef->{argsRef};
#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)= @_;
return("empty response", undef) unless($response);
#Debug "Decoding response: $response";
#Debug "response: " . Dumper($response);
my $data;
eval { $data= decode_json($response) };
return($@, undef) if($@);
@ -193,10 +247,34 @@ sub YahooWeatherAPI_JSONReturnChannelData($) {
#Debug "$count result(s).";
return("$count result(s) retrieved", undef) unless($count == 1);
my $channel= $query->{results}{channel};
#Debug "Result: " . Dumper($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($) {