2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-03-10 09:16:53 +00:00

non-blocking retrieval of data in 57_Weather.pm

git-svn-id: https://svn.fhem.de/fhem/trunk@6112 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
borisneubert 2014-06-14 17:41:36 +00:00
parent af3dabe67f
commit 5e9ad4dcf8
2 changed files with 186 additions and 146 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.
- feature: non-blocking retrieval of data in 57_Weather.pm (Boris & herrmannj)
- feature: new modules 37_SHC.pm and 37_SHCdev.pm added (rr2000)
- ad-hoc: (betateilchen)
reverted 10_CUL_HM.pm to previous version 6096

View File

@ -30,6 +30,7 @@ use warnings;
use Time::HiRes qw(gettimeofday);
use HttpUtils;
use vars qw($FW_ss);
use Blocking;
#
# uses the Yahoo! Weather API: http://developer.yahoo.com/weather/
@ -207,160 +208,202 @@ sub Weather_UpdateReading($$$$) {
return 1;
}
###################################
sub Weather_RetrieveData($)
sub Weather_RetrieveData($$)
{
my ($hash)= @_;
my ($name, $blocking) = @_;
my $hash = $defs{$name};
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://weather.yahooapis.com/forecastrss?w=" . $location . "&u=" . $units, 3, undef, 1);
return 0 if( ! defined $xml || $xml eq "");
my $lang= $hash->{LANG};
my @YahooCodes_i18n;
my %wdays_txt_i18n;
my @directions_txt_i18n;
my %pressure_trend_txt_i18n;
if($lang eq "de") {
@YahooCodes_i18n= @YahooCodes_de;
%wdays_txt_i18n= %wdays_txt_de;
@directions_txt_i18n= @directions_txt_de;
%pressure_trend_txt_i18n= %pressure_trend_txt_de;
} elsif($lang eq "nl") {
@YahooCodes_i18n= @YahooCodes_nl;
%wdays_txt_i18n= %wdays_txt_nl;
@directions_txt_i18n= @directions_txt_nl;
%pressure_trend_txt_i18n= %pressure_trend_txt_nl;
} else {
@YahooCodes_i18n= @YahooCodes_en;
%wdays_txt_i18n= %wdays_txt_en;
@directions_txt_i18n= @directions_txt_en;
%pressure_trend_txt_i18n= %pressure_trend_txt_en;
}
foreach my $l (split("<",$xml)) {
#Debug "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
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 $url = "http://weather.yahooapis.com/forecastrss?w=" . $location . "&u=" . $units;
### location
if ($tag eq "yweather:location" ) {
$value =~/city="(.*?)" .*country="(.*?)".*/;
my $loc = "";
$loc = $1 if (defined($1));
$loc .= ", $2" if (defined($2));
readingsBulkUpdate($hash, "city", $loc);
}
### current condition and forecast
if (($tag eq "yweather:condition" ) || ($tag eq "yweather:forecast" )) {
my $code = (($value =~/code="([0-9]*?)".*/) ? $1 : undef);
if(defined($code)) {
readingsBulkUpdate($hash, $prefix . "code", $code);
my $text = $YahooCodes_i18n[$code];
if ($text) { readingsBulkUpdate($hash, $prefix . "condition", $text); }
#### add icon logic here - generate from code
$text = $iconlist[$code];
readingsBulkUpdate($hash, $prefix . "icon", $text) if ($text);
}
}
### current condition
if ($tag eq "yweather:condition" ) {
my $temp = (($value =~/temp="(-?[0-9.]*?)".*/) ? $1 : undef);
if(defined($temp)) {
readingsBulkUpdate($hash, "temperature", $temp);
readingsBulkUpdate($hash, "temp_c", $temp); # compatibility
$temp = int(( $temp * 9 / 5 ) + 32.5); # Celsius to Fahrenheit
readingsBulkUpdate($hash, "temp_f", $temp); # compatibility
}
my $datum = (($value =~/date=".*? ([0-9].*)".*/) ? $1 : undef);
readingsBulkUpdate($hash, "current_date_time", $datum) if (defined($1));
my $day = (($value =~/date="(.*?), .*/) ? $1 : undef);
if(defined($day)) {
readingsBulkUpdate($hash, "day_of_week", $wdays_txt_i18n{$day});
}
}
### forecast
if ($tag eq "yweather:forecast" ) {
my $low_c = (($value =~/low="(-?[0-9.]*?)".*/) ? $1 : undef);
if(defined($low_c)) { readingsBulkUpdate($hash, $prefix . "low_c", $low_c); }
my $high_c = (($value =~/high="(-?[0-9.]*?)".*/) ? $1 : undef);
if(defined($high_c)) { readingsBulkUpdate($hash, $prefix . "high_c", $high_c); }
my $day1 = (($value =~/day="(.*?)" .*/) ? $1 : undef); # forecast
if(defined($day1)) {
readingsBulkUpdate($hash, $prefix . "day_of_week", $wdays_txt_i18n{$day1});
}
}
### humidiy / Pressure
if ($tag eq "yweather:atmosphere" ) {
$value =~/humidity="([0-9.]*?)" .*visibility="([0-9.]*?|\s*?)" .*pressure="([0-9.]*?)" .*rising="([0-9.]*?)" .*/;
if ($1) { readingsBulkUpdate($hash, "humidity", $1); }
my $vis = (($2 eq "") ? " " : int($2+0.5)); # clear visibility field
readingsBulkUpdate($hash, "visibility", $vis);
if ($3) { readingsBulkUpdate($hash, "pressure", int($3+0.5)); }
if ($4) {
readingsBulkUpdate($hash, "pressure_trend", $4);
readingsBulkUpdate($hash, "pressure_trend_txt", $pressure_trend_txt_i18n{$4});
readingsBulkUpdate($hash, "pressure_trend_sym", $pressure_trend_sym{$4});
}
}
### wind
if ($tag eq "yweather:wind" ) {
$value =~/chill="(-?[0-9.]*?)" .*direction="([0-9.]*?)" .*speed="([0-9.]*?)" .*/;
readingsBulkUpdate($hash, "wind_chill", $1) if (defined($1));
readingsBulkUpdate($hash, "wind_direction", $2) if (defined($2));
my $windspeed= defined($3) ? int($3+0.5) : "";
readingsBulkUpdate($hash, "wind_speed", $windspeed);
readingsBulkUpdate($hash, "wind", $windspeed); # duplicate for compatibility
if (defined($2) & defined($3)) {
my $wdir = degrees_to_direction($2,@directions_txt_i18n);
readingsBulkUpdate($hash, "wind_condition", "Wind: $wdir $windspeed km/h"); # compatibility
}
}
if ($blocking) {
my $response = GetFileFromURL($url, 5, undef, 0);
my %param = (hash => $hash, doTrigger => 0);
Weather_RetrieveDataFinished(\%param, undef, $response);
}
} #end sub
else {
HttpUtils_NonblockingGet(
{
url => $url,
timeout => 5,
noshutdown => 0,
hash => $hash,
doTrigger => 1,
callback => \&Weather_RetrieveDataFinished,
}
);
}
}
sub Weather_RetrieveDataFinished($$$)
{
my ( $param, $err, $xml ) = @_;
my $hash = $param->{hash};
my $doTrigger = $param->{doTrigger};
my $name = $hash->{NAME};
my $urlResult;
if (defined($xml) && $xml ne "") {
my $fc = undef;
my $lang= $hash->{LANG};
my @YahooCodes_i18n;
my %wdays_txt_i18n;
my @directions_txt_i18n;
my %pressure_trend_txt_i18n;
if($lang eq "de") {
@YahooCodes_i18n= @YahooCodes_de;
%wdays_txt_i18n= %wdays_txt_de;
@directions_txt_i18n= @directions_txt_de;
%pressure_trend_txt_i18n= %pressure_trend_txt_de;
} elsif($lang eq "nl") {
@YahooCodes_i18n= @YahooCodes_nl;
%wdays_txt_i18n= %wdays_txt_nl;
@directions_txt_i18n= @directions_txt_nl;
%pressure_trend_txt_i18n= %pressure_trend_txt_nl;
} else {
@YahooCodes_i18n= @YahooCodes_en;
%wdays_txt_i18n= %wdays_txt_en;
@directions_txt_i18n= @directions_txt_en;
%pressure_trend_txt_i18n= %pressure_trend_txt_en;
}
foreach my $l (split("<",$xml)) {
#Debug "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
next if(!defined($tag) || ($tag !~ /^yweather:/));
$fc= 0 if($tag eq "yweather:condition");
$fc++ if($tag eq "yweather:forecast");
my $prefix= $fc ? "fc" . $fc ."_" : "";
### location
if ($tag eq "yweather:location" ) {
$value =~/city="(.*?)" .*country="(.*?)".*/;
my $loc = "";
$loc = $1 if (defined($1));
$loc .= ", $2" if (defined($2));
$urlResult->{"readings"}->{"city"} = $loc;
}
### current condition and forecast
if (($tag eq "yweather:condition" ) || ($tag eq "yweather:forecast" )) {
my $code = (($value =~/code="([0-9]*?)".*/) ? $1 : undef);
if(defined($code)) {
$urlResult->{"readings"}->{$prefix . "code"} = $code;
my $text = $YahooCodes_i18n[$code];
if ($text) {
$urlResult->{"readings"}->{$prefix . "condition"} = $text;
}
#### add icon logic here - generate from code
$text = $iconlist[$code];
$urlResult->{"readings"}->{$prefix . "icon"} = $text if ($text);
}
}
### current condition
if ($tag eq "yweather:condition" ) {
my $temp = (($value =~/temp="(-?[0-9.]*?)".*/) ? $1 : undef);
if(defined($temp)) {
$urlResult->{"readings"}->{"temperature"} = $temp;
$urlResult->{"readings"}->{"temp_c"} = $temp;
$temp = int(( $temp * 9 / 5 ) + 32.5); # Celsius to Fahrenheit
$urlResult->{"readings"}->{"temp_f"} = $temp;
}
my $datum = (($value =~/date=".*? ([0-9].*)".*/) ? $1 : undef);
$urlResult->{"readings"}->{"current_date_time"} = $datum if (defined($1));
my $day = (($value =~/date="(.*?), .*/) ? $1 : undef);
if(defined($day)) {
$urlResult->{"readings"}->{"day_of_week"} = $wdays_txt_i18n{$day};
}
}
### forecast
if ($tag eq "yweather:forecast" ) {
my $low_c = (($value =~/low="(-?[0-9.]*?)".*/) ? $1 : undef);
if(defined($low_c)) { $urlResult->{"readings"}->{$prefix . "low_c"} = $low_c; }
my $high_c = (($value =~/high="(-?[0-9.]*?)".*/) ? $1 : undef);
if(defined($low_c)) { $urlResult->{"readings"}->{$prefix . "high_c"} = $high_c; }
my $day1 = (($value =~/day="(.*?)" .*/) ? $1 : undef); # forecast
if(defined($day1)) {
$urlResult->{"readings"}->{$prefix . "day_of_week"} = $wdays_txt_i18n{$day1};
}
}
### humidiy / Pressure
if ($tag eq "yweather:atmosphere" ) {
$value =~/humidity="([0-9.]*?)" .*visibility="([0-9.]*?|\s*?)" .*pressure="([0-9.]*?)" .*rising="([0-9.]*?)" .*/;
if ($1) { $urlResult->{"readings"}->{"humidity"} = $1; }
my $vis = (($2 eq "") ? " " : int($2+0.5)); # clear visibility field
$urlResult->{"readings"}->{"visibility"} = $vis;
if ($3) { $urlResult->{"readings"}->{"pressure"} = int($3+0.5); }
if ($4) {
$urlResult->{"readings"}->{"pressure_trend"} = $4;
$urlResult->{"readings"}->{"pressure_trend_txt"} = $pressure_trend_txt_i18n{$4};
$urlResult->{"readings"}->{"pressure_trend_sym"} = $pressure_trend_sym{$4};
}
}
### wind
if ($tag eq "yweather:wind" ) {
$value =~/chill="(-?[0-9.]*?)" .*direction="([0-9.]*?)" .*speed="([0-9.]*?)" .*/;
$urlResult->{"readings"}->{"wind_chill"} = $1 if (defined($1));
$urlResult->{"readings"}->{"wind_direction"} = $2 if defined($2);
my $windspeed= defined($3) ? int($3+0.5) : "";
$urlResult->{"readings"}->{"wind_speed"} = $windspeed;
$urlResult->{"readings"}->{"wind"} = $windspeed;# duplicate for compatibility
if (defined($2) & defined($3)) {
my $wdir = degrees_to_direction($2,@directions_txt_i18n);
$urlResult->{"readings"}->{"wind_condition"} = "Wind: $wdir $windspeed km/h"; # compatibility
}
}
}
}
if (exists($urlResult->{readings})) {
readingsBeginUpdate($hash);
while ( (my $key, my $value) = each %{$urlResult->{readings}} )
{
readingsBulkUpdate($hash, $key, $value);
}
my $temperature= $hash->{READINGS}{temperature}{VAL};
my $humidity= $hash->{READINGS}{humidity}{VAL};
my $wind= $hash->{READINGS}{wind}{VAL};
my $val= "T: $temperature H: $humidity W: $wind";
Log3 $hash, 4, "Weather ". $hash->{NAME} . ": $val";
readingsBulkUpdate($hash, "state", $val);
readingsEndUpdate($hash, $doTrigger ? 1 : 0);
}
}
###################################
sub Weather_GetUpdate($)
sub Weather_GetUpdateLocal($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
if(!$hash->{LOCAL}) {
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Weather_GetUpdate", $hash, 1);
}
Weather_RetrieveData($name, 1);
readingsBeginUpdate($hash);
return 1;
}
Weather_RetrieveData($hash);
sub Weather_GetUpdateTimer($)
{
my ($hash) = @_;
my $name = $hash->{NAME};
Weather_RetrieveData($name, 0);
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Weather_GetUpdateTimer", $hash, 1);
my $temperature= $hash->{READINGS}{temperature}{VAL};
my $humidity= $hash->{READINGS}{humidity}{VAL};
my $wind= $hash->{READINGS}{wind}{VAL};
my $val= "T: $temperature H: $humidity W: $wind";
Log3 $hash, 4, "Weather ". $hash->{NAME} . ": $val";
readingsBulkUpdate($hash, "state", $val);
readingsEndUpdate($hash, defined($hash->{LOCAL} ? 0 : 1)); # DoTrigger, because sub is called by a timer instead of dispatch
return 1;
}
@ -374,9 +417,7 @@ sub Weather_Get($@) {
return "argument is missing" if(int(@a) != 2);
$hash->{LOCAL} = 1;
Weather_GetUpdate($hash);
delete $hash->{LOCAL};
Weather_GetUpdateLocal($hash);
my $reading= $a[1];
my $value;
@ -400,7 +441,7 @@ sub Weather_Set($@) {
# usage check
if((@a == 2) && ($a[1] eq "update")) {
RemoveInternalTimer($hash);
Weather_GetUpdate($hash);
Weather_GetUpdateTimer($hash);
return undef;
} else {
return "Unknown argument $cmd, choose one of update";
@ -438,11 +479,9 @@ sub Weather_Define($$) {
$hash->{READINGS}{current_date_time}{TIME}= TimeNow();
$hash->{READINGS}{current_date_time}{VAL}= "none";
$hash->{LOCAL} = 1;
Weather_GetUpdate($hash);
delete $hash->{LOCAL};
Weather_GetUpdateLocal($hash);
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Weather_GetUpdate", $hash, 0);
InternalTimer(gettimeofday()+$hash->{INTERVAL}, "Weather_GetUpdateTimer", $hash, 0);
return undef;
}