first commit
This commit is contained in:
commit
209cde6abb
976
59_Weather.pm
Executable file
976
59_Weather.pm
Executable file
@ -0,0 +1,976 @@
|
||||
# $Id: 59_Weather.pm 16644 2018-04-22 08:07:35Z neubert $
|
||||
##############################################################################
|
||||
#
|
||||
# 59_Weather.pm
|
||||
# Copyright by Dr. Boris Neubert
|
||||
# e-mail: omega at online dot de
|
||||
#
|
||||
# This file is part of fhem.
|
||||
#
|
||||
# Fhem 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.
|
||||
#
|
||||
# Fhem 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Time::HiRes qw(gettimeofday);
|
||||
use HttpUtils;
|
||||
use vars qw($FW_ss);
|
||||
|
||||
# use Data::Dumper; # for Debug only
|
||||
|
||||
my %pressure_trend_txt_en = ( 0 => "steady", 1 => "rising", 2 => "falling" );
|
||||
my %pressure_trend_txt_de = ( 0 => "gleichbleibend", 1 => "steigend", 2 => "fallend" );
|
||||
my %pressure_trend_txt_nl = ( 0 => "stabiel", 1 => "stijgend", 2 => "dalend" );
|
||||
my %pressure_trend_txt_fr = ( 0 => "stable", 1 => "croissant", 2 => "décroissant" );
|
||||
my %pressure_trend_txt_pl = ( 0 => "stabilne", 1 => "rośnie", 2 => "spada" );
|
||||
my %pressure_trend_txt_it = ( 0 => "stabile", 1 => "in aumento", 2 => "in diminuzione" );
|
||||
my %pressure_trend_sym = ( 0 => "=", 1 => "+", 2 => "-" );
|
||||
|
||||
my @directions_txt_en = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
|
||||
my @directions_txt_de = ('N', 'NNO', 'NO', 'ONO', 'O', 'OSO', 'SO', 'SSO', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
|
||||
my @directions_txt_nl = ('N', 'NNO', 'NO', 'ONO', 'O', 'OZO', 'ZO', 'ZZO', 'Z', 'ZZW', 'ZW', 'WZW', 'W', 'WNW', 'NW', 'NNW');
|
||||
my @directions_txt_fr = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSO', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO');
|
||||
my @directions_txt_pl = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW');
|
||||
my @directions_txt_it = ('N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSO', 'SO', 'OSO', 'O', 'ONO', 'NO', 'NNO');
|
||||
|
||||
my %wdays_txt_en = ('Mon' => 'Mon', 'Tue' => 'Tue', 'Wed'=> 'Wed', 'Thu' => 'Thu', 'Fri' => 'Fri', 'Sat' => 'Sat', 'Sun' => 'Sun');
|
||||
my %wdays_txt_de = ('Mon' => 'Mo', 'Tue' => 'Di', 'Wed'=> 'Mi', 'Thu' => 'Do', 'Fri' => 'Fr', 'Sat' => 'Sa', 'Sun' => 'So');
|
||||
my %wdays_txt_nl = ('Mon' => 'Maa', 'Tue' => 'Din', 'Wed'=> 'Woe', 'Thu' => 'Don', 'Fri' => 'Vri', 'Sat' => 'Zat', 'Sun' => 'Zon');
|
||||
my %wdays_txt_fr= ('Mon' => 'Lun', 'Tue' => 'Mar', 'Wed'=> 'Mer', 'Thu' => 'Jeu', 'Fri' => 'Ven', 'Sat' => 'Sam', 'Sun' => 'Dim');
|
||||
my %wdays_txt_pl = ('Mon' => 'Pon', 'Tue' => 'Wt', 'Wed'=> 'Śr', 'Thu' => 'Czw', 'Fri' => 'Pt', 'Sat' => 'Sob', 'Sun' => 'Nie');
|
||||
my %wdays_txt_it = ('Mon' => 'Lun', 'Tue' => 'Mar', 'Wed'=> 'Mer', 'Thu' => 'Gio', 'Fri' => 'Ven', 'Sat' => 'Sab', 'Sun' => 'Dom');
|
||||
|
||||
my %status_items_txt_en = ( 0 => "Wind", 1 => "Humidity", 2 => "Temperature", 3 => "Right Now", 4 => "Weather forecast for " );
|
||||
my %status_items_txt_de = ( 0 => "Wind", 1 => "Feuchtigkeit", 2 => "Temperatur", 3 => "Jetzt Sofort", 4 => "Wettervorhersage für " );
|
||||
my %status_items_txt_nl = ( 0 => "Wind", 1 => "Vochtigheid", 2 => "Temperatuur", 3 => "Direct", 4 => "Weersvoorspelling voor " );
|
||||
my %status_items_txt_fr = ( 0 => "Vent", 1 => "Humidité", 2 => "Température", 3 => "Maintenant", 4 => "Prévisions météo pour " );
|
||||
my %status_items_txt_pl = ( 0 => "Wiatr", 1 => "Wilgotność", 2 => "Temperatura", 3 => "Teraz", 4 => "Prognoza pogody w " );
|
||||
my %status_items_txt_it = ( 0 => "Vento", 1 => "Umidità", 2 => "Temperatura", 3 => "Adesso", 4 => "Previsioni del tempo per " );
|
||||
|
||||
my %wdays_txt_i18n;
|
||||
my @directions_txt_i18n;
|
||||
my %pressure_trend_txt_i18n;
|
||||
my %status_items_txt_i18n;
|
||||
|
||||
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', 'sleet',
|
||||
'sleet', 'dust', 'fog', 'haze', 'smoke', 'flurries',
|
||||
'windy', 'icy', 'cloudy', 'mostlycloudy_night', 'mostlycloudy', 'partly_cloudy_night',
|
||||
'partly_cloudy', 'sunny', 'sunny', 'mostly_clear_night', 'mostly_sunny', 'heavyrain',
|
||||
'sunny', 'scatteredthunderstorms', 'scatteredthunderstorms', 'scatteredthunderstorms', 'scatteredshowers', 'heavysnow',
|
||||
'chance_of_snow', 'heavysnow', 'partly_cloudy', 'heavyrain', 'chance_of_snow', 'scatteredshowers');
|
||||
|
||||
###################################
|
||||
sub Weather_LanguageInitialize($) {
|
||||
|
||||
my ($lang) = @_;
|
||||
|
||||
if($lang eq "de") {
|
||||
%wdays_txt_i18n= %wdays_txt_de;
|
||||
@directions_txt_i18n= @directions_txt_de;
|
||||
%pressure_trend_txt_i18n= %pressure_trend_txt_de;
|
||||
%status_items_txt_i18n= %status_items_txt_de;
|
||||
} elsif($lang eq "nl") {
|
||||
%wdays_txt_i18n= %wdays_txt_nl;
|
||||
@directions_txt_i18n= @directions_txt_nl;
|
||||
%pressure_trend_txt_i18n= %pressure_trend_txt_nl;
|
||||
%status_items_txt_i18n= %status_items_txt_nl;
|
||||
} elsif($lang eq "fr") {
|
||||
%wdays_txt_i18n= %wdays_txt_fr;
|
||||
@directions_txt_i18n= @directions_txt_fr;
|
||||
%pressure_trend_txt_i18n= %pressure_trend_txt_fr;
|
||||
%status_items_txt_i18n= %status_items_txt_fr;
|
||||
} elsif($lang eq "pl") {
|
||||
%wdays_txt_i18n= %wdays_txt_pl;
|
||||
@directions_txt_i18n= @directions_txt_pl;
|
||||
%pressure_trend_txt_i18n= %pressure_trend_txt_pl;
|
||||
%status_items_txt_i18n= %status_items_txt_pl;
|
||||
} elsif($lang eq "it") {
|
||||
%wdays_txt_i18n= %wdays_txt_it;
|
||||
@directions_txt_i18n= @directions_txt_it;
|
||||
%pressure_trend_txt_i18n= %pressure_trend_txt_it;
|
||||
%status_items_txt_i18n= %status_items_txt_it;
|
||||
} else {
|
||||
%wdays_txt_i18n= %wdays_txt_en;
|
||||
@directions_txt_i18n= @directions_txt_en;
|
||||
%pressure_trend_txt_i18n= %pressure_trend_txt_en;
|
||||
%status_items_txt_i18n= %status_items_txt_en;
|
||||
}
|
||||
}
|
||||
|
||||
###################################
|
||||
sub Weather_DebugCodes($) {
|
||||
|
||||
my ($lang)= @_;
|
||||
my @YahooCodes_i18n= YahooWeatherAPI_getYahooCodes($lang);
|
||||
|
||||
Debug "Weather Code List, see http://developer.yahoo.com/weather/#codes";
|
||||
for(my $c= 0; $c<= 47; $c++) {
|
||||
Debug sprintf("%2d %30s %30s", $c, $iconlist[$c], $YahooCodes_i18n[$c]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#####################################
|
||||
sub Weather_Initialize($) {
|
||||
|
||||
my ($hash) = @_;
|
||||
|
||||
$hash->{DefFn} = "Weather_Define";
|
||||
$hash->{UndefFn} = "Weather_Undef";
|
||||
$hash->{GetFn} = "Weather_Get";
|
||||
$hash->{SetFn} = "Weather_Set";
|
||||
$hash->{AttrList}= "disable " . $readingFnAttributes;
|
||||
$hash->{NotifyFn}= "Weather_Notify";
|
||||
|
||||
#Weather_DebugCodes('de');
|
||||
}
|
||||
|
||||
###################################
|
||||
|
||||
sub degrees_to_direction($@) {
|
||||
my ($degrees,@directions_txt_i18n) = @_;
|
||||
my $mod = int((($degrees + 11.25) % 360) / 22.5);
|
||||
return $directions_txt_i18n[$mod];
|
||||
}
|
||||
|
||||
###################################
|
||||
# sub Weather_RetrieveData($$) {
|
||||
# my ($name, $blocking) = @_;
|
||||
# my $hash = $defs{$name};
|
||||
#
|
||||
# # WOEID [WHERE-ON-EARTH-ID], go to http://weather.yahoo.com to find out
|
||||
# my $location= $hash->{LOCATION};
|
||||
# my $units= $hash->{UNITS};
|
||||
#
|
||||
# my %args= (
|
||||
# woeid => $location,
|
||||
# format => "json",
|
||||
# blocking => $blocking,
|
||||
# callbackFnRef => \&Weather_RetrieveDataFinished,
|
||||
# hash => $hash,
|
||||
# );
|
||||
#
|
||||
# # this needs to be finalized to use the APIOPTIONS
|
||||
# my $maxage= $hash->{fhem}{allowCache} ? 600 : 0; # use cached data if allowed
|
||||
# $hash->{fhem}{allowCache}= 1;
|
||||
# YahooWeatherAPI_RetrieveDataWithCache($maxage, \%args);
|
||||
#
|
||||
#
|
||||
# $hash->{fhem}{api}->setRetrieveData;
|
||||
# }
|
||||
|
||||
sub Weather_ReturnWithError($$) {
|
||||
my ($hash, $responseRef)= @_;
|
||||
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);
|
||||
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate($hash, 'lastError', $responseRef->{status});
|
||||
|
||||
foreach my $r (keys %{$responseRef} ) {
|
||||
readingsBulkUpdate($hash, $r, $responseRef->{$r}) if ($r ne 'status');
|
||||
}
|
||||
readingsBulkUpdate($hash, 'state', 'API Maintainer: ' . $responseRef->{apiMaintainer} . ' ErrorMsg: ' . $responseRef->{status});
|
||||
readingsEndUpdate($hash, 1);
|
||||
|
||||
my $next= 60; # $next= $hash->{INTERVAL};
|
||||
Weather_RearmTimer($hash, gettimeofday()+$next);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# sub Weather_RetrieveDataFinished($$$) {
|
||||
#
|
||||
# my ($argsRef, $err, $response)= @_;
|
||||
#
|
||||
# my $hash= $argsRef->{hash};
|
||||
# my $name= $hash->{NAME};
|
||||
# my $doTrigger= $argsRef->{blocking} ? 0 : 1;
|
||||
#
|
||||
# # check for error from retrieving data
|
||||
# return Weather_ReturnWithError($hash, $doTrigger, $err, undef, undef) if($err);
|
||||
#
|
||||
# # decode JSON data from Weather Channel
|
||||
# my $data;
|
||||
# ($err, $data)= YahooWeatherAPI_JSONReturnChannelData($response);
|
||||
# return Weather_ReturnWithError($hash, $doTrigger, $err, undef, undef) if($err);
|
||||
#
|
||||
# # check if up-to-date
|
||||
# my ($pubDateComment, $pubDate, $pubDateTs)= YahooWeatherAPI_pubDate($data);
|
||||
# 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);
|
||||
#
|
||||
#
|
||||
# #
|
||||
# # from here on we assume that $data is complete and correct
|
||||
# #
|
||||
# my $lang= $hash->{LANG};
|
||||
#
|
||||
# my @YahooCodes_i18n= YahooWeatherAPI_getYahooCodes($lang);
|
||||
#
|
||||
# my $item= $data->{item};
|
||||
#
|
||||
# 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");
|
||||
#
|
||||
# # description
|
||||
# readingsBulkUpdate($hash, "description", $data->{description});
|
||||
#
|
||||
# # location
|
||||
# readingsBulkUpdate($hash, "city", $data->{location}{city});
|
||||
# readingsBulkUpdate($hash, "region", $data->{location}{region});
|
||||
# readingsBulkUpdate($hash, "country", $data->{location}{country});
|
||||
# readingsBulkUpdate($hash, "lat", $item->{lat});
|
||||
# readingsBulkUpdate($hash, "long", $item->{long});
|
||||
#
|
||||
# # wind
|
||||
# my $windspeed= int($data->{wind}{speed}+0.5);
|
||||
# readingsBulkUpdate($hash, "wind", $windspeed);
|
||||
# readingsBulkUpdate($hash, "wind_speed", $windspeed);
|
||||
# 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");
|
||||
#
|
||||
# # atmosphere
|
||||
# my $humidity= $data->{atmosphere}{humidity};
|
||||
# readingsBulkUpdate($hash, "humidity", $humidity);
|
||||
# my $pressure= $data->{atmosphere}{pressure};
|
||||
# readingsBulkUpdate($hash, "pressure", $pressure);
|
||||
# readingsBulkUpdate($hash, "visibility", int($data->{atmosphere}{visibility}+0.5));
|
||||
# my $pressure_trend= $data->{atmosphere}{rising};
|
||||
# readingsBulkUpdate($hash, "pressure_trend", $pressure_trend);
|
||||
# readingsBulkUpdate($hash, "pressure_trend_txt", $pressure_trend_txt_i18n{$pressure_trend});
|
||||
# readingsBulkUpdate($hash, "pressure_trend_sym", $pressure_trend_sym{$pressure_trend});
|
||||
#
|
||||
# # condition
|
||||
# my $date= $item->{condition}{date};
|
||||
# readingsBulkUpdate($hash, "current_date_time", $date);
|
||||
# readingsBulkUpdate($hash, "day_of_week", $wdays_txt_i18n{substr($date,0,3)});
|
||||
# my $code= $item->{condition}{code};
|
||||
# readingsBulkUpdate($hash, "code", $code);
|
||||
# readingsBulkUpdate($hash, "condition", $YahooCodes_i18n[$code]);
|
||||
# readingsBulkUpdate($hash, "icon", $iconlist[$code]);
|
||||
# my $temp= $item->{condition}{temp};
|
||||
# readingsBulkUpdate($hash, "temp_c", $temp);
|
||||
# readingsBulkUpdate($hash, "temperature", $temp);
|
||||
#
|
||||
# # forecast
|
||||
# my $forecast= $item->{forecast};
|
||||
# my $i= 0;
|
||||
# foreach my $fc (@{$forecast}) {
|
||||
# $i++;
|
||||
# my $f= "fc" . $i ."_";
|
||||
# readingsBulkUpdate($hash, $f . "day_of_week", $wdays_txt_i18n{$fc->{day}});
|
||||
# readingsBulkUpdate($hash, $f . "date", $fc->{date});
|
||||
# readingsBulkUpdate($hash, $f . "low_c", $fc->{low});
|
||||
# readingsBulkUpdate($hash, $f . "high_c", $fc->{high});
|
||||
# my $fccode= $fc->{code};
|
||||
# readingsBulkUpdate($hash, $f . "code", $fccode);
|
||||
# readingsBulkUpdate($hash, $f . "condition", $YahooCodes_i18n[$fccode]);
|
||||
# readingsBulkUpdate($hash, $f . "icon", $iconlist[$fccode]);
|
||||
# }
|
||||
#
|
||||
# #my $val= "T:$temp°C " . substr($status_items_txt_i18n{1}, 0, 1) .":$humidity% " . substr($status_items_txt_i18n{0}, 0, 1) . ":$windspeed km/h P:$pressure mbar";
|
||||
# my $val= "T: $temp H: $humidity W: $windspeed P: $pressure";
|
||||
# Log3 $hash, 4, "$name: $val";
|
||||
# readingsBulkUpdate($hash, "state", $val);
|
||||
#
|
||||
# readingsEndUpdate($hash, $doTrigger);
|
||||
#
|
||||
# Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
|
||||
# return;
|
||||
#
|
||||
# }
|
||||
|
||||
sub Weather_RetrieveCallbackFn($) {
|
||||
|
||||
my $hash = shift;
|
||||
my $name = $hash->{NAME};
|
||||
my $responseRef = $hash->{fhem}{api}->getWeather;
|
||||
|
||||
if ( $responseRef->{status} eq 'ok' ) {
|
||||
Weather_WriteReadings($hash,$responseRef);
|
||||
}
|
||||
else {
|
||||
Weather_ReturnWithError($hash,$responseRef);
|
||||
}
|
||||
}
|
||||
|
||||
sub Weather_WriteReadings($$) {
|
||||
my ($hash,$dataRef) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
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}));
|
||||
|
||||
# housekeeping information
|
||||
readingsBulkUpdate($hash, 'lastError', '');
|
||||
foreach my $r (keys %{$dataRef} ) {
|
||||
readingsBulkUpdate($hash, $r, $dataRef->{$r}) if ($r ne 'status' and $r ne 'current' and $r ne 'forcast');
|
||||
}
|
||||
readingsBulkUpdate($hash, "validity", "up-to-date");
|
||||
|
||||
|
||||
# current
|
||||
while( my ($r,$v) = each %{$dataRef->{current}} ) {
|
||||
readingsBulkUpdate($hash, $r, $v);
|
||||
}
|
||||
readingsBulkUpdate($hash, 'icon', $iconlist[$dataRef->{current}{code}]);
|
||||
if ( defined($dataRef->{current}{wind_direction})
|
||||
and defined($dataRef->{current}{wind_speed} ) )
|
||||
{
|
||||
my $wdir= degrees_to_direction($dataRef->{current}{wind_direction}, @directions_txt_i18n);
|
||||
readingsBulkUpdate($hash, 'wind_condition', 'Wind: ' . $wdir . ' ' . $dataRef->{current}{wind_speed} . ' km/h');
|
||||
}
|
||||
|
||||
# forecast
|
||||
if ( ref( $dataRef->{forcast} ) eq 'HASH' ) {
|
||||
## Forcast for hourly
|
||||
if ( ref( $dataRef->{forcast}{hourly} ) eq "ARRAY"
|
||||
and scalar( @{ $dataRef->{forcast}{daily} } ) > 0 )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
## Forcast for Daily
|
||||
if ( ref( $dataRef->{forcast}{daily} ) eq "ARRAY"
|
||||
and scalar( @{ $dataRef->{forcast}{daily} } ) > 0 )
|
||||
{
|
||||
my $i= 0;
|
||||
foreach my $fc (@{$dataRef->{forcast}{daily}}) {
|
||||
$i++;
|
||||
my $f= "fc" . $i ."_";
|
||||
|
||||
while( my ($r,$v) = each %{$fc} ) {
|
||||
readingsBulkUpdate($hash, $f.$r, $v);
|
||||
}
|
||||
# readingsBulkUpdate($hash, $f . "day_of_week", $wdays_txt_i18n{$fc->{day}});
|
||||
readingsBulkUpdate($hash, $f . 'icon', $iconlist[$dataRef->{forcast}{daily}[$i-1]{code}]);
|
||||
if ( defined($dataRef->{forcast}{daily}[$i-1]{wind_direction})
|
||||
and defined($dataRef->{forcast}{daily}[$i-1]{wind_speed}) )
|
||||
{
|
||||
my $wdir= degrees_to_direction($dataRef->{forcast}{daily}[$i-1]{wind_direction}, @directions_txt_i18n);
|
||||
readingsBulkUpdate($hash, $f . 'wind_condition', 'Wind: ' . $wdir . ' ' . $dataRef->{forcast}{daily}[$i-1]{wind_speed} . ' km/h');
|
||||
}
|
||||
# readingsBulkUpdate($hash, $f . 'day_of_week', $wdays_txt_i18n{substr($dataRef->{forcast}[$i-1]{date},0,3)});
|
||||
}
|
||||
}
|
||||
}
|
||||
my $val= 'T:' . $dataRef->{current}{temperature} . '°C'
|
||||
.' ' . substr($status_items_txt_i18n{1}, 0, 1) . ':' . $dataRef->{current}{humidity} . '%'
|
||||
.' ' . substr($status_items_txt_i18n{0}, 0, 1) . ':' . $dataRef->{current}{wind} . 'km/h'
|
||||
.' P:' . $dataRef->{current}{pressure} . 'mbar';
|
||||
|
||||
Log3 $hash, 4, "$name: $val";
|
||||
readingsBulkUpdate($hash, 'state', $val);
|
||||
|
||||
readingsEndUpdate($hash, 1);
|
||||
|
||||
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
###################################
|
||||
sub Weather_GetUpdate($) {
|
||||
|
||||
my ($hash) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
|
||||
if($attr{$name} && $attr{$name}{disable}) {
|
||||
Log3 $hash, 5, "Weather $name: retrieval of weather data is disabled by attribute.";
|
||||
readingsBeginUpdate($hash);
|
||||
readingsBulkUpdate($hash, "pubDateComment", "disabled by attribute");
|
||||
readingsBulkUpdate($hash, "validity", "stale");
|
||||
readingsEndUpdate($hash, 1);
|
||||
Weather_RearmTimer($hash, gettimeofday()+$hash->{INTERVAL});
|
||||
} else {
|
||||
# Weather_RetrieveData($name, 0);
|
||||
$hash->{fhem}{api}->setRetrieveData;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
###################################
|
||||
sub Weather_Get($@) {
|
||||
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
return "argument is missing" if(int(@a) != 2);
|
||||
|
||||
my $reading= $a[1];
|
||||
my $value;
|
||||
|
||||
if(defined($hash->{READINGS}{$reading})) {
|
||||
$value= $hash->{READINGS}{$reading}{VAL};
|
||||
} else {
|
||||
my $rt= "";
|
||||
if(defined($hash->{READINGS})) {
|
||||
$rt= join(" ", sort keys %{$hash->{READINGS}});
|
||||
}
|
||||
return "Unknown reading $reading, choose one of " . $rt;
|
||||
}
|
||||
|
||||
return "$a[0] $reading => $value";
|
||||
}
|
||||
|
||||
###################################
|
||||
sub Weather_Set($@) {
|
||||
my ($hash, @a) = @_;
|
||||
|
||||
my $cmd= $a[1];
|
||||
|
||||
# usage check
|
||||
if((@a == 2) && ($a[1] eq "update")) {
|
||||
Weather_DisarmTimer($hash);
|
||||
Weather_GetUpdate($hash);
|
||||
return undef;
|
||||
} else {
|
||||
return "Unknown argument $cmd, choose one of update";
|
||||
}
|
||||
}
|
||||
|
||||
###################################
|
||||
sub Weather_RearmTimer($$) {
|
||||
|
||||
my ($hash, $t) = @_;
|
||||
InternalTimer($t, "Weather_GetUpdate", $hash, 0) ;
|
||||
|
||||
}
|
||||
|
||||
sub Weather_DisarmTimer($) {
|
||||
|
||||
my ($hash)= @_;
|
||||
RemoveInternalTimer($hash);
|
||||
}
|
||||
|
||||
sub Weather_Notify($$) {
|
||||
my ($hash,$dev) = @_;
|
||||
my $name = $hash->{NAME};
|
||||
my $type = $hash->{TYPE};
|
||||
|
||||
return if($dev->{NAME} ne "global");
|
||||
return if(!grep(m/^INITIALIZED|REREADCFG$/, @{$dev->{CHANGED}}));
|
||||
|
||||
# return if($attr{$name} && $attr{$name}{disable});
|
||||
|
||||
# update weather after initialization or change of configuration
|
||||
# wait 10 to 29 seconds to avoid congestion due to concurrent activities
|
||||
Weather_DisarmTimer($hash);
|
||||
my $delay= 10+int(rand(20));
|
||||
|
||||
#$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) ;
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub Weather_Define($$) {
|
||||
|
||||
my ($hash, $def) = @_;
|
||||
|
||||
# define <name> Weather <location> [interval]
|
||||
# define MyWeather Weather "Maintal,HE" 3600
|
||||
|
||||
# define <name> Weather location=<location> [API=<API>] [interval=<interval>] [lang=<lang>]
|
||||
|
||||
my $name;
|
||||
my $API="DarkSkyAPI,cachemaxage:600";
|
||||
my $location;
|
||||
my $apikey;
|
||||
my $interval = 3600;
|
||||
my $lang;
|
||||
|
||||
if($def =~ /=/) {
|
||||
|
||||
my $usage= "syntax: define <name> Weather location=<location> [API=<API>] [lang=<lang>]";
|
||||
|
||||
my ($arrayref, $hashref)= parseParams($def);
|
||||
my @a= @{$arrayref};
|
||||
my %h= %{$hashref};
|
||||
|
||||
return $usage unless(scalar @a == 2);
|
||||
$name= $a[0];
|
||||
|
||||
#return $usage unless exists $h{location};
|
||||
$location= $h{location};
|
||||
$apikey = $h{apikey} if exists $h{apikey};
|
||||
$lang= $h{lang} if exists $h{lang};
|
||||
$interval= $h{interval} if exists $h{interval};
|
||||
$API = $h{API} if exists $h{API};
|
||||
|
||||
} else {
|
||||
my @a = split("[ \t][ \t]*", $def);
|
||||
|
||||
return "syntax: define <name> Weather <location> [interval [en|de|nl|fr|pl|it]]"
|
||||
if(int(@a) < 3 && int(@a) > 5);
|
||||
|
||||
$name = $a[0];
|
||||
$location = $a[2];
|
||||
if(int(@a)>=4) { $interval= $a[3]; }
|
||||
if(int(@a)==5) { $lang= $a[4]; }
|
||||
|
||||
}
|
||||
|
||||
my ($api,$apioptions)= split(',', $API, 2);
|
||||
$apioptions= "" unless(defined($apioptions));
|
||||
eval {
|
||||
require "$api.pm";
|
||||
};
|
||||
return "$name: cannot load API $api: $@" if($@);
|
||||
|
||||
$hash->{NOTIFYDEV} = "global";
|
||||
$hash->{fhem}{interfaces}= "temperature;humidity;wind";
|
||||
$hash->{LOCATION} = ( defined($location) ? $location : AttrVal( 'global', 'latitude', 'error' ).','.AttrVal( 'global', 'longitude', 'error' ) );
|
||||
$hash->{INTERVAL} = $interval;
|
||||
$hash->{LANG} = ( defined($lang) ? $lang : lc(AttrVal('global','language','de')) );
|
||||
$hash->{API} = $api;
|
||||
$hash->{APIKEY} = $apikey;
|
||||
$hash->{APIOPTIONS} = $apioptions;
|
||||
#$hash->{UNITS} = "c"; # hardcoded to use degrees centigrade (Celsius)
|
||||
$hash->{READINGS}{current_date_time}{TIME}= TimeNow();
|
||||
$hash->{READINGS}{current_date_time}{VAL}= "none";
|
||||
|
||||
$hash->{fhem}{allowCache}= 1;
|
||||
|
||||
readingsSingleUpdate($hash,'state','Initialized',1);
|
||||
Weather_LanguageInitialize($hash->{LANG});
|
||||
|
||||
my $apistring = $api . '::Weather';
|
||||
# $hash->{fhem}{api} = $api . '::Weather'->new( { hash => $hash, apikey => $hash->{APIKEY}, location => $hash->{LOCATION}, cachemaxage => 600, language => $hash->{LANG} } );
|
||||
$hash->{fhem}{api} = $apistring->new( { hash => $hash, apikey => $hash->{APIKEY}, location => $hash->{LOCATION}, cachemaxage => 600, language => $hash->{LANG} } );
|
||||
|
||||
Weather_GetUpdate($hash) if($init_done);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
sub Weather_Undef($$) {
|
||||
|
||||
my ($hash, $arg) = @_;
|
||||
|
||||
RemoveInternalTimer($hash);
|
||||
return undef;
|
||||
}
|
||||
|
||||
#####################################
|
||||
|
||||
# Icon Parameter
|
||||
|
||||
use constant ICONHIGHT => 120;
|
||||
use constant ICONWIDTH => 175;
|
||||
use constant ICONSCALE => 0.5;
|
||||
|
||||
#####################################
|
||||
|
||||
sub
|
||||
WeatherIconIMGTag($) {
|
||||
|
||||
my $width= int(ICONSCALE*ICONWIDTH);
|
||||
my ($icon)= @_;
|
||||
my $url= FW_IconURL("weather/$icon");
|
||||
my $style= " width=$width";
|
||||
return "<img src=\"$url\"$style alt=\"$icon\">";
|
||||
|
||||
}
|
||||
|
||||
#####################################
|
||||
|
||||
sub
|
||||
WeatherAsHtmlV($;$)
|
||||
{
|
||||
|
||||
my ($d,$items) = @_;
|
||||
$d = "<none>" if(!$d);
|
||||
$items = 10 if( !$items );
|
||||
return "$d is not a Weather instance<br>"
|
||||
if(!$defs{$d} || $defs{$d}{TYPE} ne "Weather");
|
||||
|
||||
my $width= int(ICONSCALE*ICONWIDTH);
|
||||
|
||||
my $ret = '<table class="weather">';
|
||||
$ret .= sprintf('<tr><td class="weatherIcon" width=%d>%s</td><td class="weatherValue">%s<br>%s°C %s%%<br>%s</td></tr>',
|
||||
$width,
|
||||
WeatherIconIMGTag(ReadingsVal($d, "icon", "")),
|
||||
ReadingsVal($d, "condition", ""),
|
||||
ReadingsVal($d, "temp_c", ""), ReadingsVal($d, "humidity", ""),
|
||||
ReadingsVal($d, "wind_condition", ""));
|
||||
|
||||
for(my $i=1; $i<$items; $i++) {
|
||||
$ret .= sprintf('<tr><td class="weatherIcon" width=%d>%s</td><td class="weatherValue"><span class="weatherDay">%s: %s</span><br><span class="weatherMin">min %s°C</span> <span class="weatherMax">max %s°C</span></td></tr>',
|
||||
$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", ""));
|
||||
}
|
||||
|
||||
$ret .= "</table>";
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub
|
||||
WeatherAsHtml($;$)
|
||||
{
|
||||
my ($d,$i) = @_;
|
||||
WeatherAsHtmlV($d,$i);
|
||||
}
|
||||
|
||||
sub
|
||||
WeatherAsHtmlH($;$)
|
||||
{
|
||||
|
||||
my ($d,$items) = @_;
|
||||
$d = "<none>" if(!$d);
|
||||
$items = 10 if( !$items );
|
||||
return "$d is not a Weather instance<br>"
|
||||
if(!$defs{$d} || $defs{$d}{TYPE} ne "Weather");
|
||||
|
||||
my $width= int(ICONSCALE*ICONWIDTH);
|
||||
|
||||
|
||||
|
||||
my $format= '<td><table border=1><tr><td class="weatherIcon" width=%d>%s</td></tr><tr><td class="weatherValue">%s</td></tr><tr><td class="weatherValue">%s°C %s%%</td></tr><tr><td class="weatherValue">%s</td></tr></table></td>';
|
||||
|
||||
my $ret = '<table class="weather">';
|
||||
|
||||
# icons
|
||||
$ret .= sprintf('<tr><td class="weatherIcon" width=%d>%s</td>', $width, WeatherIconIMGTag(ReadingsVal($d, "icon", "")));
|
||||
for(my $i=1; $i<$items; $i++) {
|
||||
$ret .= sprintf('<td class="weatherIcon" width=%d>%s</td>', $width, WeatherIconIMGTag(ReadingsVal($d, "fc${i}_icon", "")));
|
||||
}
|
||||
$ret .= '</tr>';
|
||||
|
||||
# condition
|
||||
$ret .= sprintf('<tr><td class="weatherDay">%s</td>', ReadingsVal($d, "condition", ""));
|
||||
for(my $i=1; $i<$items; $i++) {
|
||||
$ret .= sprintf('<td class="weatherDay">%s: %s</td>', ReadingsVal($d, "fc${i}_day_of_week", ""),
|
||||
ReadingsVal($d, "fc${i}_condition", ""));
|
||||
}
|
||||
$ret .= '</tr>';
|
||||
|
||||
# temp/hum | min
|
||||
$ret .= sprintf('<tr><td class="weatherMin">%s°C %s%%</td>', ReadingsVal($d, "temp_c", ""), ReadingsVal($d, "humidity", ""));
|
||||
for(my $i=1; $i<$items; $i++) {
|
||||
$ret .= sprintf('<td class="weatherMin">min %s°C</td>', ReadingsVal($d, "fc${i}_low_c", ""));
|
||||
}
|
||||
$ret .= '</tr>';
|
||||
|
||||
# wind | max
|
||||
$ret .= sprintf('<tr><td class="weatherMax">%s</td>', ReadingsVal($d, "wind_condition", ""));
|
||||
for(my $i=1; $i<$items; $i++) {
|
||||
$ret .= sprintf('<td class="weatherMax">max %s°C</td>', ReadingsVal($d, "fc${i}_high_c", ""));
|
||||
}
|
||||
$ret .= "</tr></table>";
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub
|
||||
WeatherAsHtmlD($;$)
|
||||
{
|
||||
my ($d,$i) = @_;
|
||||
if($FW_ss) {
|
||||
WeatherAsHtmlV($d,$i);
|
||||
} else {
|
||||
WeatherAsHtmlH($d,$i);
|
||||
}
|
||||
}
|
||||
|
||||
#####################################
|
||||
|
||||
|
||||
1;
|
||||
|
||||
=pod
|
||||
=item device
|
||||
=item summary provides current weather condition and forecast (source: Yahoo Weather API)
|
||||
=item summary_DE stellt Wetterbericht und -vorhersage bereit (Quelle: Yahoo Weather API)
|
||||
=begin html
|
||||
|
||||
<a name="Weather"></a>
|
||||
<h3>Weather</h3>
|
||||
<ul>
|
||||
You need the JSON perl module. Use <code>apt-get install libjson-perl</code> on Debian and derivatives.<br><br>
|
||||
|
||||
<a name="Weatherdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> Weather <location> [<interval> [<language>]]</code><br>
|
||||
<br>
|
||||
Defines a virtual device for weather forecasts.<br><br>
|
||||
|
||||
A Weather device periodically gathers current and forecast weather conditions
|
||||
from the Yahoo Weather API.<br><br>
|
||||
|
||||
The parameter <code>location</code> is the WOEID (WHERE-ON-EARTH-ID), go to
|
||||
<a href="http://weather.yahoo.com">http://weather.yahoo.com</a> to find it out for your location.<br><br>
|
||||
|
||||
The optional parameter <code>interval</code> is the time between subsequent updates
|
||||
in seconds. It defaults to 3600 (1 hour).<br><br>
|
||||
|
||||
The optional language parameter may be one of
|
||||
<code>de</code>,
|
||||
<code>en</code>,
|
||||
<code>pl</code>,
|
||||
<code>fr</code>,
|
||||
<code>nl</code>,
|
||||
<code>it</code>,
|
||||
|
||||
It determines the natural language in which the forecast information appears.
|
||||
It defaults to <code>en</code>. If you want to set the language you also have to set the interval.<br><br>
|
||||
|
||||
Examples:
|
||||
<pre>
|
||||
define MyWeather Weather 673513
|
||||
define Forecast Weather 673513 1800
|
||||
</pre>
|
||||
|
||||
The module provides four additional functions <code>WeatherAsHtml</code>, <code>WeatherAsHtmlV</code>, <code>WeatherAsHtmlH</code> and
|
||||
<code>WeatherAsHtmlD</code>. The former two functions are identical: they return the HTML code for a
|
||||
vertically arranged weather forecast. The third function returns the HTML code for a horizontally arranged weather forecast. The
|
||||
latter function dynamically picks the orientation depending on wether a smallscreen style is set (vertical layout) or not (horizontal layout). Each version accepts an additional paramter to limit the numer of icons to display.<br><br>
|
||||
Example:
|
||||
<pre>
|
||||
define MyWeatherWeblink weblink htmlCode { WeatherAsHtmlH("MyWeather") }
|
||||
</pre>
|
||||
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Weatherset"></a>
|
||||
<b>Set </b>
|
||||
<ul>
|
||||
<code>set <name> update</code><br><br>
|
||||
|
||||
Forces the retrieval of the weather data. The next automatic retrieval is scheduled to occur
|
||||
<code>interval</code> seconds later.<br><br>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Weatherget"></a>
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
<code>get <name> <reading></code><br><br>
|
||||
|
||||
Valid readings and their meaning (? can be one of 1, 2, 3, 4, 5 and stands
|
||||
for today, tomorrow, etc.):<br>
|
||||
<table>
|
||||
<tr><td>city</td><td>name of town returned for location</td></tr>
|
||||
<tr><td>code</td><td>current condition code</td></tr>
|
||||
<tr><td>condition</td><td>current condition</td></tr>
|
||||
<tr><td>current_date_time</td><td>last update of forecast on server</td></tr>
|
||||
<tr><td>fc?_code</td><td>forecast condition code</td></tr>
|
||||
<tr><td>fc?_condition</td><td>forecast condition</td></tr>
|
||||
<tr><td>fc?_day_of_week</td><td>day of week for day +?</td></tr>
|
||||
<tr><td>fc?_high_c</td><td>forecasted daily high in degrees centigrade</td></tr>
|
||||
<tr><td>fc?_icon</td><td>forecast icon</td></tr>
|
||||
<tr><td>fc?_low_c</td><td>forecasted daily low in degrees centigrade</td></tr>
|
||||
<tr><td>humidity</td><td>current humidity in %</td></tr>
|
||||
<tr><td>icon</td><td>relative path for current icon</td></tr>
|
||||
<tr><td>pressure</td><td>air pressure in hPa</td></tr>
|
||||
<tr><td>pressure_trend</td><td>air pressure trend (0= steady, 1= rising, 2= falling)</td></tr>
|
||||
<tr><td>pressure_trend_txt</td><td>textual representation of air pressure trend</td></tr>
|
||||
<tr><td>pressure_trend_sym</td><td>symbolic representation of air pressure trend</td></tr>
|
||||
<tr><td>temperature</td><td>current temperature in degrees centigrade</td></tr>
|
||||
<tr><td>temp_c</td><td>current temperature in degrees centigrade</td></tr>
|
||||
<tr><td>temp_f</td><td>current temperature in degrees Fahrenheit</td></tr>
|
||||
<tr><td>visibility</td><td>visibility in km</td></tr>
|
||||
<tr><td>wind</td><td>wind speed in km/h</td></tr>
|
||||
<tr><td>wind_chill</td><td>wind chill in degrees centigrade</td></tr>
|
||||
<tr><td>wind_condition</td><td>wind direction and speed</td></tr>
|
||||
<tr><td>wind_direction</td><td>direction wind comes from in degrees (0 = north wind)</td></tr>
|
||||
<tr><td>wind_speed</td><td>same as wind</td></tr>
|
||||
</table>
|
||||
<br>
|
||||
The following readings help to identify whether a workaround has kicked in to avoid the retrieval of
|
||||
stale data from the remote server:
|
||||
<table>
|
||||
<tr><td>pubDate</td><td>publication time of forecast for current set of readings</td></tr>
|
||||
<tr><td>pubDateRemote</td><td>publication time of forecast as seen on remote server</td></tr>
|
||||
<tr><td>validity</td><td>stale, if publication time as seen on remote server is before that of current set of readings</td></tr>
|
||||
</table>
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Weatherattr"></a>
|
||||
<b>Attributes</b>
|
||||
<ul>
|
||||
<li>disable: disables the retrieval of weather data - the timer runs according to schedule,
|
||||
though no data is requested from the API.</li>
|
||||
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
|
||||
=end html
|
||||
=begin html_DE
|
||||
|
||||
<a name="Weather"></a>
|
||||
<h3>Weather</h3>
|
||||
<ul>
|
||||
Es wird das Perl-Modul JSON benötigt. Mit <code>apt-get install libjson-perl</code> kann es unter Debian und Derivaten installiert werden.<br><br>
|
||||
|
||||
<a name="Weatherdefine"></a>
|
||||
<b>Define</b>
|
||||
<ul>
|
||||
<code>define <name> Weather <location> [<interval> [<language>]]</code><br>
|
||||
<br>
|
||||
Bezechnet ein virtuelles Gerät für Wettervorhersagen.<br><br>
|
||||
|
||||
Eine solche virtuelle Wetterstation sammelt periodisch aktuelle und zukünftige Wetterdaten aus der Yahoo-Wetter-API.<br><br>
|
||||
|
||||
Der Parameter <code>location</code> entspricht der sechsstelligen WOEID (WHERE-ON-EARTH-ID). Die WOEID für den eigenen Standort kann auf <a href="http://weather.yahoo.com">http://weather.yahoo.com</a> gefunden werden.<br><br>
|
||||
|
||||
Der optionale Parameter <code>interval</code> gibt die Dauer in Sekunden zwischen den einzelnen Aktualisierungen der Wetterdaten an. Der Standardwert ist 3600 (1 Stunde). Wird kein Wert angegeben, gilt der Standardwert.<br><br>
|
||||
|
||||
Der optionale Parameter für die möglichen Sprachen darf einen der folgende Werte annehmen: <code>de</code>, <code>en</code>, <code>pl</code>, <code>fr</code> oder <code>nl</code>. Er bezeichnet die natürliche Sprache, in der die Wetterinformationen dargestellt werden. Der Standardwert ist <code>en</code>. Wird für die Sprache kein Wert angegeben, gilt der Standardwert. Wird allerdings der Parameter für die Sprache gesetzt, muss ebenfalls ein Wert für das Abfrageintervall gesetzt werden.<br><br>
|
||||
|
||||
|
||||
Beispiele:
|
||||
<pre>
|
||||
define MyWeather Weather 673513
|
||||
define Forecast Weather 673513 1800
|
||||
</pre>
|
||||
|
||||
Das Modul unterstützt zusätzlich vier verschiedene Funktionen <code>WeatherAsHtml</code>, <code>WeatherAsHtmlV</code>, <code>WeatherAsHtmlH</code> und <code>WeatherAsHtmlD</code>. Die ersten beiden Funktionen sind identisch: sie erzeugen den HTML-Code für eine vertikale Darstellung des Wetterberichtes. Die dritte Funktion liefert den HTML-Code für eine horizontale Darstellung des Wetterberichtes. Die letztgenannte Funktion wählt automatisch eine Ausrichtung, die abhängig davon ist, ob ein Smallcreen Style ausgewählt ist (vertikale Darstellung) oder nicht (horizontale Darstellung). Alle vier Funnktionen akzeptieren einen zusätzlichen optionalen Paramter um die Anzahl der darzustellenden Icons anzugeben.<br><br>
|
||||
Beispiel:
|
||||
<pre>
|
||||
define MyWeatherWeblink weblink htmlCode { WeatherAsHtmlH("MyWeather") }
|
||||
</pre>
|
||||
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Weatherset"></a>
|
||||
<b>Set </b>
|
||||
<ul>
|
||||
<code>set <name> update</code><br><br>
|
||||
|
||||
Erzwingt eine Abfrage der Wetterdaten. Die darauffolgende Abfrage wird gemäß dem eingestellten Intervall <code>interval</code> Sekunden später durchgeführt.<br><br>
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Weatherget"></a>
|
||||
<b>Get</b>
|
||||
<ul>
|
||||
<code>get <name> <reading></code><br><br>
|
||||
|
||||
Gültige ausgelesene Daten (readings) und ihre Bedeutung (das ? kann einen der Werte 1, 2, 3 , 4 oder 5 annehmen und steht für heute, morgen, übermorgen etc.):<br><br>
|
||||
<table>
|
||||
<tr><td>city</td><td>Name der Stadt, der aufgrund der WOEID übermittelt wird</td></tr>
|
||||
<tr><td>code</td><td>Code für die aktuellen Wetterverhältnisse</td></tr>
|
||||
<tr><td>condition</td><td>aktuelle Wetterverhältnisse</td></tr>
|
||||
<tr><td>current_date_time</td><td>Zeitstempel der letzten Aktualisierung der Wetterdaten vom Server</td></tr>
|
||||
<tr><td>fc?_code</td><td>Code für die vorhergesagten Wetterverhältnisse</td></tr>
|
||||
<tr><td>fc?_condition</td><td>vorhergesagte Wetterverhältnisse</td></tr>
|
||||
<tr><td>fc?_day_of_week</td><td>Wochentag des Tages, der durch ? dargestellt wird</td></tr>
|
||||
<tr><td>fc?_high_c</td><td>vorhergesagte maximale Tagestemperatur in Grad Celsius</td></tr>
|
||||
<tr><td>fc?_icon</td><td>Icon für Vorhersage</td></tr>
|
||||
<tr><td>fc?_low_c</td><td>vorhergesagte niedrigste Tagestemperatur in Grad Celsius</td></tr>
|
||||
<tr><td>humidity</td><td>gegenwärtige Luftfeuchtgkeit in %</td></tr>
|
||||
<tr><td>icon</td><td>relativer Pfad für das aktuelle Icon</td></tr>
|
||||
<tr><td>pressure</td><td>Luftdruck in hPa</td></tr>
|
||||
<tr><td>pressure_trend</td><td>Luftdrucktendenz (0= gleichbleibend, 1= steigend, 2= fallend)</td></tr>
|
||||
<tr><td>pressure_trend_txt</td><td>textliche Darstellung der Luftdrucktendenz</td></tr>
|
||||
<tr><td>pressure_trend_sym</td><td>symbolische Darstellung der Luftdrucktendenz</td></tr>
|
||||
<tr><td>temperature</td><td>gegenwärtige Temperatur in Grad Celsius</td></tr>
|
||||
<tr><td>temp_c</td><td>gegenwärtige Temperatur in Grad Celsius</td></tr>
|
||||
<tr><td>temp_f</td><td>gegenwärtige Temperatur in Grad Celsius</td></tr>
|
||||
<tr><td>visibility</td><td>Sichtweite in km</td></tr>
|
||||
<tr><td>wind</td><td>Windgeschwindigkeit in km/h</td></tr>
|
||||
<tr><td>wind_chill</td><td>gefühlte Temperatur in Grad Celsius</td></tr>
|
||||
<tr><td>wind_condition</td><td>Windrichtung und -geschwindigkeit</td></tr>
|
||||
<tr><td>wind_direction</td><td>Gradangabe der Windrichtung (0 = Nordwind)</td></tr>
|
||||
<tr><td>wind_speed</td><td>Windgeschwindigkeit in km/h (mit wind identisch)</td></tr>
|
||||
</table>
|
||||
<br>
|
||||
Die folgenden Daten helfen zu identifizieren, ob ein Workaround angeschlagen hat, der die Verwendung von
|
||||
veralteten Daten auf dem entfernten Server verhindert:
|
||||
<table>
|
||||
<tr><td>pubDate</td><td>Veröffentlichungszeitpunkt der Wettervorhersage in den aktuellen Daten (readings)</td></tr>
|
||||
<tr><td>pubDateRemote</td><td>Veröffentlichungszeitpunkt der Wettervorhersage auf dem entfernten Server</td></tr>
|
||||
<tr><td>validity</td><td>stale, wenn der Veröffentlichungszeitpunkt auf dem entfernten Server vor dem Zeitpunkt der aktuellen Daten (readings) liegt</td></tr>
|
||||
</table>
|
||||
|
||||
</ul>
|
||||
<br>
|
||||
|
||||
<a name="Weatherattr"></a>
|
||||
<b>Attribute</b>
|
||||
<ul>
|
||||
<li>disable: stellt die Abfrage der Wetterdaten ab - der Timer läft gemäß Plan doch es werden keine Daten vom
|
||||
API angefordert.</li>
|
||||
<li><a href="#readingFnAttributes">readingFnAttributes</a></li>
|
||||
</ul>
|
||||
<br>
|
||||
</ul>
|
||||
|
||||
=end html_DE
|
||||
=cut
|
406
DarkSkyAPI.pm
Normal file
406
DarkSkyAPI.pm
Normal file
@ -0,0 +1,406 @@
|
||||
###############################################################################
|
||||
#
|
||||
# Developed with Kate
|
||||
#
|
||||
# (c) 2019 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
|
||||
# All rights reserved
|
||||
#
|
||||
# Special thanks goes to:
|
||||
#
|
||||
#
|
||||
# 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
|
||||
# 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 textfile 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.
|
||||
#
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
package DarkSkyAPI::Weather;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use POSIX;
|
||||
use HttpUtils;
|
||||
|
||||
my $missingModul = '';
|
||||
eval "use JSON;1"
|
||||
or $missingModul .=
|
||||
"JSON "; # apt-get install libperl-JSON on Debian and derivatives
|
||||
eval "use Encode qw(encode_utf8);1" or $missingModul .= "Encode ";
|
||||
|
||||
# use Data::Dumper; # for Debug only
|
||||
## API URL
|
||||
use constant URL => 'https://api.darksky.net/forecast/';
|
||||
|
||||
my %codes = (
|
||||
'clear-day' => 32,
|
||||
'clear-night' => 31,
|
||||
'rain' => 11,
|
||||
'snow' => 16,
|
||||
'sleet' => 18,
|
||||
'wind' => 24,
|
||||
'fog' => 20,
|
||||
'cloudy' => 26,
|
||||
'partly-cloudy-day' => 30,
|
||||
'partly-cloudy-night' => 29,
|
||||
'hail' => 17,
|
||||
'thunderstorm' => 4,
|
||||
'tornado' => 0,
|
||||
);
|
||||
|
||||
sub new {
|
||||
### geliefert wird ein Hash
|
||||
my ( $class, $argsRef ) = @_;
|
||||
|
||||
my $self = {
|
||||
devHash => $argsRef->{hash},
|
||||
key => ( defined( $argsRef->{apikey} ) ? $argsRef->{apikey} : 'none' ),
|
||||
cachemaxage => $argsRef->{cachemaxage},
|
||||
lang => $argsRef->{language},
|
||||
lat => ( split( ',', $argsRef->{location} ) )[0],
|
||||
long => ( split( ',', $argsRef->{location} ) )[1],
|
||||
fetchTime => 0,
|
||||
};
|
||||
|
||||
$self->{cached} = _CreateForcastRef($self);
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub setFetchTime {
|
||||
my $self = shift;
|
||||
|
||||
$self->{fetchTime} = time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub setRetrieveData {
|
||||
my $self = shift;
|
||||
|
||||
_RetrieveDataFromDarkSky($self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub getFetchTime {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{fetchTime};
|
||||
}
|
||||
|
||||
sub getWeather {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{cached};
|
||||
}
|
||||
|
||||
sub _RetrieveDataFromDarkSky($) {
|
||||
my $self = shift;
|
||||
|
||||
# retrieve data from cache
|
||||
if ( (time() - $self->{fetchTime}) < $self->{cachemaxage} ) {
|
||||
return _CallWeatherCallbackFn($self);
|
||||
}
|
||||
|
||||
my $paramRef = {
|
||||
timeout => 15,
|
||||
self => $self,
|
||||
callback => \&_RetrieveDataFinished,
|
||||
};
|
||||
|
||||
if ( $self->{lat} eq 'error'
|
||||
or $self->{long} eq 'error'
|
||||
or $self->{key} eq 'none'
|
||||
or $missingModul )
|
||||
{
|
||||
_RetrieveDataFinished( $paramRef,
|
||||
'The given location is invalid. (wrong latitude or longitude?) put both as an attribute in the global device or set define option location=[LAT],[LONG]',
|
||||
undef )
|
||||
if ( $self->{lat} eq 'error' or $self->{long} eq 'error' );
|
||||
|
||||
_RetrieveDataFinished( $paramRef, 'No given api key. (define myWeather Weather apikey=[KEY])', undef )
|
||||
if ( $self->{key} eq 'none' );
|
||||
|
||||
_RetrieveDataFinished( $paramRef,
|
||||
'Perl modul ' . $missingModul . ' is missing.', undef )
|
||||
if ($missingModul);
|
||||
}
|
||||
else {
|
||||
$paramRef->{url} =
|
||||
URL
|
||||
. $self->{key} . '/'
|
||||
. $self->{lat} . ','
|
||||
. $self->{long}
|
||||
. '?lang='
|
||||
. $self->{lang}
|
||||
. '&units=auto';
|
||||
|
||||
main::HttpUtils_NonblockingGet($paramRef);
|
||||
}
|
||||
}
|
||||
|
||||
sub _RetrieveDataFinished($$$) {
|
||||
my ( $paramRef, $err, $response ) = @_;
|
||||
my $self = $paramRef->{self};
|
||||
|
||||
if ( !$err ) {
|
||||
$self->{cached}->{status} = 'ok';
|
||||
$self->{cached}->{validity} = 'up-to-date',
|
||||
$self->{fetchTime} = time();
|
||||
_ProcessingRetrieveData( $self, $response );
|
||||
}
|
||||
else {
|
||||
$self->{fetchTime} = time() if ( not defined( $self->{fetchTime} ) );
|
||||
_ErrorHandling( $self, $err );
|
||||
_ProcessingRetrieveData( $self, $response );
|
||||
}
|
||||
}
|
||||
|
||||
sub _ProcessingRetrieveData($$) {
|
||||
my ( $self, $response ) = @_;
|
||||
|
||||
if ( $self->{cached}->{status} eq 'ok' and defined($response) )
|
||||
{
|
||||
my $data = eval { decode_json($response) };
|
||||
|
||||
if ($@) {
|
||||
_ErrorHandling( $self, 'DarkSky Weather decode JSON err ' . $@ );
|
||||
}
|
||||
elsif ( defined( $data->{code} ) and defined( $data->{error} ) ) {
|
||||
_ErrorHandling( $self, 'Code: ' . $data->{code} . ' Error: ' . $data->{error} );
|
||||
}
|
||||
else {
|
||||
# print Dumper $data; ## für Debugging
|
||||
|
||||
$self->{cached}->{current_date_time} = strftime("%a,%e %b %Y %H:%M %p",localtime( $self->{fetchTime} )),
|
||||
$self->{cached}->{timezone} = $data->{timezone};
|
||||
$self->{cached}->{license} = $data->{flags}->{'meteoalarm-license'};
|
||||
$self->{cached}->{current} = {
|
||||
'temperature' => int(sprintf("%.1f",$data->{currently}->{temperature}) + 0.5),
|
||||
'temp_c' => int(sprintf("%.1f",$data->{currently}->{temperature}) + 0.5),
|
||||
'dewPoint' => int(sprintf("%.1f",$data->{currently}->{dewPoint}) + 0.5),
|
||||
'humidity' => $data->{currently}->{humidity} * 100,
|
||||
'condition' =>
|
||||
encode_utf8( $data->{currently}->{summary} ),
|
||||
'pressure' => $data->{currently}->{pressure},
|
||||
'wind' => $data->{currently}->{windSpeed},
|
||||
'wind_speed' => $data->{currently}->{windSpeed},
|
||||
'wind_direction' => $data->{currently}->{windBearing},
|
||||
'windGust' => $data->{currently}->{windGust},
|
||||
'cloudCover' => $data->{currently}->{cloudCover},
|
||||
'uvIndex' => $data->{currently}->{uvIndex},
|
||||
'visibility' => $data->{currently}->{visibility},
|
||||
'ozone' => $data->{currently}->{ozone},
|
||||
'code' => $codes{ $data->{currently}->{icon} },
|
||||
'iconAPI' => $data->{currently}->{icon},
|
||||
'pubDate' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime( $data->{currently}->{'time'} )
|
||||
),
|
||||
'precipProbability' =>
|
||||
$data->{currently}->{precipProbability},
|
||||
'apparentTemperature' =>
|
||||
int(sprintf("%.1f",$data->{currently}->{apparentTemperature}) + 0.5),
|
||||
'precipIntensity' =>
|
||||
$data->{currently}->{precipIntensity},
|
||||
};
|
||||
|
||||
my $i = 0;
|
||||
if ( ref($data->{daily}->{data}) eq "ARRAY"
|
||||
and scalar(@{ $data->{daily}->{data}}) > 0 )
|
||||
{
|
||||
foreach (@{$data->{daily}->{data}}) {
|
||||
push (@{$self->{cached}->{forcast}->{daily}},
|
||||
{
|
||||
'date' => strftime(
|
||||
"%a, %d.%m.%Y",
|
||||
localtime( $data->{daily}->{data}[$i]{'time'} )
|
||||
),
|
||||
'day_of_week' => strftime(
|
||||
"%a",
|
||||
localtime( $data->{daily}->{data}[$i]{'time'} )
|
||||
),
|
||||
'low_c' => int(sprintf("%.1f",$data->{daily}->{data}[$i]{temperatureLow}) + 0.5),
|
||||
'high_c' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{temperatureHigh}) + 0.5),
|
||||
'tempMin' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{temperatureMin}) + 0.5),
|
||||
'tempMinTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{temperatureMinTime}
|
||||
)
|
||||
),
|
||||
'tempMax' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{temperatureMax}) + 0.5),
|
||||
'tempMaxTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{temperatureMaxTime}
|
||||
)
|
||||
),
|
||||
'tempLow' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{temperatureLow}) + 0.5),
|
||||
'tempLowTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{temperatureLowTime}
|
||||
)
|
||||
),
|
||||
'tempHigh' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{temperatureHigh}) + 0.5),
|
||||
'tempHighTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{temperatureHighTime}
|
||||
)
|
||||
),
|
||||
'apparentTempLow' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{apparentTemperatureLow}) + 0.5),
|
||||
'apparentTempLowTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]
|
||||
{apparentTemperatureLowTime}
|
||||
)
|
||||
),
|
||||
'apparentTempHigh' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{apparentTemperatureHigh}) + 0.5),
|
||||
'apparentTempHighTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]
|
||||
{apparentTemperatureHighTime}
|
||||
)
|
||||
),
|
||||
'apparenttempMin' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{apparentTemperatureMin}) + 0.5),
|
||||
'apparenttempMinTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]
|
||||
{apparentTemperatureMinTime}
|
||||
)
|
||||
),
|
||||
'apparenttempMax' =>
|
||||
int(sprintf("%.1f",$data->{daily}->{data}[$i]{apparentTemperatureMax}) + 0.5),
|
||||
'apparenttempMaxTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]
|
||||
{apparentTemperatureMaxTime}
|
||||
)
|
||||
),
|
||||
'code' => $codes{ $data->{daily}->{data}[$i]{icon} },
|
||||
'iconAPI' => $data->{daily}->{data}[$i]{icon},
|
||||
'condition' =>
|
||||
encode_utf8( $data->{daily}->{data}[$i]{summary} ),
|
||||
'ozone' => $data->{daily}->{data}[$i]{ozone},
|
||||
'uvIndex' => $data->{daily}->{data}[$i]{uvIndex},
|
||||
'uvIndexTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{uvIndexTime}
|
||||
)
|
||||
),
|
||||
'precipIntensity' =>
|
||||
$data->{daily}->{data}[$i]{precipIntensity},
|
||||
'precipIntensityMax' =>
|
||||
$data->{daily}->{data}[$i]{precipIntensityMax},
|
||||
'precipIntensityMaxTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]
|
||||
{precipIntensityMaxTime}
|
||||
)
|
||||
),
|
||||
'dewPoint' => $data->{daily}->{data}[$i]{dewPoint},
|
||||
'humidity' => $data->{daily}->{data}[$i]{humidity} * 100,
|
||||
'cloudCover' => $data->{daily}->{data}[$i]{cloudCover},
|
||||
'precipType' => $data->{daily}->{data}[$i]{precipType},
|
||||
|
||||
'wind_direction' =>
|
||||
$data->{daily}->{data}[$i]{windBearing},
|
||||
'wind' => $data->{daily}->{data}[$i]{windSpeed},
|
||||
'wind_speed' => $data->{daily}->{data}[$i]{windSpeed},
|
||||
'windGust' => $data->{daily}->{data}[$i]{windGust},
|
||||
'windGustTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{windGustTime}
|
||||
)
|
||||
),
|
||||
'moonPhase' => $data->{daily}->{data}[$i]{moonPhase},
|
||||
'sunsetTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{sunsetTime}
|
||||
)
|
||||
),
|
||||
'sunriseTime' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime(
|
||||
$data->{daily}->{data}[$i]{sunriseTime}
|
||||
)
|
||||
),
|
||||
|
||||
'precipProbability' =>
|
||||
$data->{daily}->{data}[$i]{precipProbability},
|
||||
'pressure' => $data->{daily}->{data}[$i]{pressure},
|
||||
'visibility' => $data->{daily}->{data}[$i]{visibility},
|
||||
});
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
## Aufruf der callbackFn
|
||||
_CallWeatherCallbackFn($self);
|
||||
}
|
||||
|
||||
sub _CallWeatherCallbackFn($) {
|
||||
my $self = shift;
|
||||
|
||||
# ## Aufruf der callbackFn
|
||||
main::Weather_RetrieveCallbackFn( $self->{devHash} );
|
||||
}
|
||||
|
||||
sub _ErrorHandling($$) {
|
||||
my ($self,$err) = @_;
|
||||
|
||||
$self->{cached}->{current_date_time} = strftime("%a,%e %b %Y %H:%M %p",localtime( $self->{fetchTime} )),
|
||||
$self->{cached}->{status} = $err;
|
||||
$self->{cached}->{validity} = 'stale';
|
||||
}
|
||||
|
||||
sub _CreateForcastRef($) {
|
||||
my $self = shift;
|
||||
|
||||
my $forcastRef = (
|
||||
{
|
||||
lat => $self->{lat},
|
||||
long => $self->{long},
|
||||
apiMaintainer => 'Leon Gaultier (<a href=https://forum.fhem.de/index.php?action=profile;u=13684>CoolTux</a>)',
|
||||
}
|
||||
);
|
||||
|
||||
return $forcastRef;
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
1;
|
57
DarkSkyJSON.txt
Normal file
57
DarkSkyJSON.txt
Normal file
File diff suppressed because one or more lines are too long
1152
HashRef_Strukture_fuer_Weather.txt
Normal file
1152
HashRef_Strukture_fuer_Weather.txt
Normal file
File diff suppressed because it is too large
Load Diff
268
OpenWeatherMapAPI.pm
Normal file
268
OpenWeatherMapAPI.pm
Normal file
@ -0,0 +1,268 @@
|
||||
###############################################################################
|
||||
#
|
||||
# Developed with Kate
|
||||
#
|
||||
# (c) 2019 Copyright: Marko Oldenburg (leongaultier at gmail dot com)
|
||||
# All rights reserved
|
||||
#
|
||||
# Special thanks goes to:
|
||||
#
|
||||
#
|
||||
# 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
|
||||
# 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 textfile 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.
|
||||
#
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
### Beispielaufruf
|
||||
# https://api.openweathermap.org/data/2.5/weather?lat=[lat]&lon=[long]&APPID=[API] Current
|
||||
# https://api.openweathermap.org/data/2.5/forcast?lat=[lat]&lon=[long]&APPID=[API] Forcast
|
||||
# https://openweathermap.org/weather-conditions Icons und Conditions ID's
|
||||
|
||||
package OpenWeatherMapAPI::Weather;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use POSIX;
|
||||
use HttpUtils;
|
||||
|
||||
my $missingModul = '';
|
||||
eval "use JSON;1"
|
||||
or $missingModul .=
|
||||
"JSON "; # apt-get install libperl-JSON on Debian and derivatives
|
||||
eval "use Encode qw(encode_utf8);1" or $missingModul .= "Encode ";
|
||||
|
||||
use Data::Dumper; # for Debug only
|
||||
## API URL
|
||||
use constant URL => 'https://api.openweathermap.org/data/2.5/';
|
||||
## URL . 'weather?' for current data
|
||||
## URL . 'forcast?' for forcast data
|
||||
|
||||
my %codes = (
|
||||
200 => 45, 201 => 45, 202 => 45, 210 => 4, 211 => 4, 212 => 3, 221 => 4, 230 => 45, 231 => 45, 232 => 45,
|
||||
300 => 9, 301 => 9, 302 => 9, 310 => 9, 311 => 9, 312 => 9, 313 => 9, 314 => 9, 321 => 9,
|
||||
500 => 35, 501 => 35, 502 => 35, 503 => 35, 504 => 35, 511 => 35, 520 => 35, 521 => 35, 522 => 35, 531 => 35,
|
||||
);
|
||||
|
||||
sub new {
|
||||
### geliefert wird ein Hash
|
||||
my ( $class, $argsRef ) = @_;
|
||||
|
||||
my $self = {
|
||||
devHash => $argsRef->{hash},
|
||||
key => ( defined( $argsRef->{apikey} ) ? $argsRef->{apikey} : 'none' ),
|
||||
cachemaxage => $argsRef->{cachemaxage},
|
||||
lang => $argsRef->{language},
|
||||
lat => ( split( ',', $argsRef->{location} ) )[0],
|
||||
long => ( split( ',', $argsRef->{location} ) )[1],
|
||||
fetchTime => 0,
|
||||
endpoint => 'none',
|
||||
};
|
||||
|
||||
$self->{cached} = _CreateForcastRef($self);
|
||||
|
||||
bless $self, $class;
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub setFetchTime {
|
||||
my $self = shift;
|
||||
|
||||
$self->{fetchTime} = time();
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub setRetrieveData {
|
||||
my $self = shift;
|
||||
|
||||
_RetrieveDataFromOpenWeatherMap($self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sub getFetchTime {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{fetchTime};
|
||||
}
|
||||
|
||||
sub getWeather {
|
||||
my $self = shift;
|
||||
|
||||
return $self->{cached};
|
||||
}
|
||||
|
||||
sub _RetrieveDataFromOpenWeatherMap($) {
|
||||
my $self = shift;
|
||||
|
||||
# retrieve data from cache
|
||||
if ( (time() - $self->{fetchTime}) < $self->{cachemaxage} ) {
|
||||
return _CallWeatherCallbackFn($self);
|
||||
}
|
||||
|
||||
my $paramRef = {
|
||||
timeout => 15,
|
||||
self => $self,
|
||||
endpoint => ( $self->{endpoint} eq 'none' ? 'weather' : 'forcast' ),
|
||||
callback => \&_RetrieveDataFinished,
|
||||
};
|
||||
|
||||
$self->{endpoint} = $paramRef->{endpoint};
|
||||
|
||||
if ( $self->{lat} eq 'error'
|
||||
or $self->{long} eq 'error'
|
||||
or $self->{key} eq 'none'
|
||||
or $missingModul )
|
||||
{
|
||||
_RetrieveDataFinished( $paramRef,
|
||||
'The given location is invalid. (wrong latitude or longitude?) put both as an attribute in the global device or set define option location=[LAT],[LONG]',
|
||||
undef )
|
||||
if ( $self->{lat} eq 'error' or $self->{long} eq 'error' );
|
||||
|
||||
_RetrieveDataFinished( $paramRef, 'No given api key. (define myWeather Weather apikey=[KEY])', undef )
|
||||
if ( $self->{key} eq 'none' );
|
||||
|
||||
_RetrieveDataFinished( $paramRef,
|
||||
'Perl modul ' . $missingModul . ' is missing.', undef )
|
||||
if ($missingModul);
|
||||
}
|
||||
else {
|
||||
$paramRef->{url} =
|
||||
URL
|
||||
. $paramRef->{endpoint} . '?'
|
||||
. 'lat='
|
||||
. $self->{lat} . '&'
|
||||
. 'lon='
|
||||
. $self->{long} . '&'
|
||||
. 'APPID='
|
||||
. $self->{key} . '&'
|
||||
. 'lang='
|
||||
. $self->{lang};
|
||||
|
||||
main::HttpUtils_NonblockingGet($paramRef);
|
||||
}
|
||||
}
|
||||
|
||||
sub _RetrieveDataFinished($$$) {
|
||||
my ( $paramRef, $err, $response ) = @_;
|
||||
my $self = $paramRef->{self};
|
||||
|
||||
if ( !$err ) {
|
||||
$self->{cached}->{status} = 'ok';
|
||||
$self->{cached}->{validity} = 'up-to-date',
|
||||
$self->{fetchTime} = time();
|
||||
_ProcessingRetrieveData( $self, $response );
|
||||
}
|
||||
else {
|
||||
$self->{fetchTime} = time() if ( not defined( $self->{fetchTime} ) );
|
||||
_ErrorHandling( $self, $err );
|
||||
_ProcessingRetrieveData( $self, $response );
|
||||
}
|
||||
|
||||
$self->{endpoint} = $paramRef->{endpoint};
|
||||
}
|
||||
|
||||
sub _ProcessingRetrieveData($$) {
|
||||
my ( $self, $response ) = @_;
|
||||
|
||||
if ( $self->{cached}->{status} eq 'ok' and defined($response) )
|
||||
{
|
||||
my $data = eval { decode_json($response) };
|
||||
#print 'Dumper1: ' . Dumper $data;
|
||||
|
||||
if ($@) {
|
||||
_ErrorHandling( $self, 'OpenWeatherMap Weather decode JSON err ' . $@ );
|
||||
}
|
||||
elsif ( defined($data->{cod}) and defined($data->{message}) ) {
|
||||
print 'Dumper2: ' . Dumper $data;
|
||||
_ErrorHandling( $self, $data->{cod} . ': ' . $data->{message} );
|
||||
}
|
||||
else {
|
||||
# print Dumper $data; ## für Debugging
|
||||
return if ( $self->{endpoint} eq 'forcast' );
|
||||
|
||||
|
||||
$self->{cached}->{current_date_time} = strftime("%a,%e %b %Y %H:%M %p",localtime( $self->{fetchTime} )),
|
||||
$self->{cached}->{country} = $data->{sys}->{country};
|
||||
$self->{cached}->{city} = $data->{name};
|
||||
$self->{cached}->{current} = {
|
||||
'temperature' => int(sprintf("%.1f",($data->{main}->{temp} - 273.15 )) + 0.5),
|
||||
'temp_c' => int(sprintf("%.1f",($data->{main}->{temp} - 273.15 )) + 0.5),
|
||||
'low_c' => int(sprintf("%.1f",($data->{main}->{temp_min} - 273.15 )) + 0.5),
|
||||
'high_c' => int(sprintf("%.1f",($data->{main}->{temp_max} - 273.15 )) + 0.5),
|
||||
'tempLow' => int(sprintf("%.1f",($data->{main}->{temp_min} - 273.15 )) + 0.5),
|
||||
'tempHigh' => int(sprintf("%.1f",($data->{main}->{temp_max} - 273.15 )) + 0.5),
|
||||
'humidity' => $data->{main}->{humidity},
|
||||
'condition' =>
|
||||
encode_utf8( $data->{weather}[0]{description} ),
|
||||
'pressure' => $data->{main}->{pressure},
|
||||
'wind' => $data->{wind}->{speed},
|
||||
'wind_speed' => $data->{wind}->{speed},
|
||||
'wind_direction' => $data->{wind}->{deg},
|
||||
'cloudCover' => $data->{clouds}->{all},
|
||||
'visibility' => $data->{visibility},
|
||||
# 'code' => $codes{ $data->{weather}[0]{icon} },
|
||||
'iconAPI' => $data->{weather}[0]{icon},
|
||||
'sunsetTime' => strftime("%a,%e %b %Y %H:%M %p",localtime($data->{sys}->{sunset})),
|
||||
'sunriseTime' => strftime("%a,%e %b %Y %H:%M %p",localtime($data->{sys}->{sunrise})),
|
||||
'pubDate' => strftime(
|
||||
"%a,%e %b %Y %H:%M %p",
|
||||
localtime( $data->{dt} )
|
||||
),
|
||||
} if ( $self->{endpoint} eq 'weather' );
|
||||
}
|
||||
}
|
||||
|
||||
# $self->{cached} = $forcastRef; Vorsicht
|
||||
|
||||
_RetrieveDataFromOpenWeatherMap($self) if ( $self->{endpoint} eq 'weather' );
|
||||
$self->{endpoint} = 'none' if ( $self->{endpoint} eq 'forcast' );
|
||||
|
||||
_CallWeatherCallbackFn($self);
|
||||
}
|
||||
|
||||
sub _CallWeatherCallbackFn($) {
|
||||
my $self = shift;
|
||||
|
||||
# ## Aufruf der callbackFn
|
||||
main::Weather_RetrieveCallbackFn( $self->{devHash} );
|
||||
}
|
||||
|
||||
sub _ErrorHandling($$) {
|
||||
my ($self,$err) = @_;
|
||||
|
||||
$self->{cached}->{current_date_time} = strftime("%a,%e %b %Y %H:%M %p",localtime( $self->{fetchTime} )),
|
||||
$self->{cached}->{status} = $err;
|
||||
$self->{cached}->{validity} = 'stale';
|
||||
}
|
||||
|
||||
sub _CreateForcastRef($) {
|
||||
my $self = shift;
|
||||
|
||||
my $forcastRef = (
|
||||
{
|
||||
lat => $self->{lat},
|
||||
long => $self->{long},
|
||||
apiMaintainer => 'Leon Gaultier (<a href=https://forum.fhem.de/index.php?action=profile;u=13684>CoolTux</a>)',
|
||||
}
|
||||
);
|
||||
|
||||
return $forcastRef;
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
1;
|
468
OpenWeatherMapJSON.txt
Normal file
468
OpenWeatherMapJSON.txt
Normal file
@ -0,0 +1,468 @@
|
||||
https://api.openweathermap.org/data/2.5/forecast?lat=52.&lon=13.&APPID=4
|
||||
|
||||
https://api.openweathermap.org/data/2.5/forecast?lat=52.&lon=13.&APPID=4
|
||||
|
||||
|
||||
|
||||
{
|
||||
'dt' => 1546973400,
|
||||
'weather' => [
|
||||
{
|
||||
'description' => 'moderate rain',
|
||||
'id' => 501,
|
||||
'main' => 'Rain',
|
||||
'icon' => '10n'
|
||||
}
|
||||
],
|
||||
'main' => {
|
||||
'pressure' => 998,
|
||||
'temp_max' => '277.15',
|
||||
'humidity' => 86,
|
||||
'temp_min' => '276.15',
|
||||
'temp' => '276.53'
|
||||
},
|
||||
'visibility' => 10000,
|
||||
'wind' => {
|
||||
'deg' => 290,
|
||||
'speed' => '6.2'
|
||||
},
|
||||
'sys' => {
|
||||
'type' => 1,
|
||||
'message' => '0.0368',
|
||||
'country' => 'DE',
|
||||
'sunrise' => 1546931687,
|
||||
'id' => 1275,
|
||||
'sunset' => 1546960424
|
||||
},
|
||||
'coord' => {
|
||||
'lon' => '13.',
|
||||
'lat' => '52.'
|
||||
},
|
||||
'base' => 'stations',
|
||||
'name' => 'Stahnsdorf',
|
||||
'cod' => 200,
|
||||
'clouds' => {
|
||||
'all' => 75
|
||||
},
|
||||
'id' => 2829657
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
{"cod":429, "message": "Your account is temporary blocked due to exceeding of requests limitation of your subscription type. Please choose the proper subscription http://openweathermap.org/price"}
|
||||
|
||||
|
||||
|
||||
http://api.openweathermap.org/data/2.5/weather? current
|
||||
|
||||
{"coord":{"lon":1,"lat":5},
|
||||
"weather":[{"id":520,"main":"Rain","description":"light intensity shower rain","icon":"09d"}],
|
||||
"base":"stations",
|
||||
"main":{"temp":278.47,"pressure":997,"humidity":87,"temp_min":278.15,"temp_max":279.15},
|
||||
"visibility":10000,
|
||||
"wind":{"speed":9.8,"deg":250,"gust":14.9},
|
||||
"clouds":{"all":75},
|
||||
"dt":1546944780,
|
||||
"sys":{"type":1,"id":1275,"message":0.0243,"country":"DE","sunrise":1546931697,"sunset":1546960397},
|
||||
"id":2829657,
|
||||
"name":"Stahnsdorf",
|
||||
"cod":200}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
http://api.openweathermap.org/data/2.5/forecast? forcast
|
||||
|
||||
{"cod":"200","message":0.0054,"cnt":40,
|
||||
|
||||
|
||||
"list":[
|
||||
{"dt":1546948800,
|
||||
"main":{"temp":278.35,"temp_min":278.034,"temp_max":278.35,"pressure":1003.32,"sea_level":1010.26,"grnd_level":1003.32,"humidity":98,"temp_kf":0.32},
|
||||
"weather":[{"id":501,"main":"Rain","description":"moderate rain","icon":"10d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":11.17,"deg":269.502},
|
||||
"rain":{"3h":4.225},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-08 12:00:00"},
|
||||
|
||||
|
||||
{"dt":1546959600,
|
||||
"main":{"temp":278.87,"temp_min":278.629,"temp_max":278.87,"pressure":1003.39,"sea_level":1010.29,"grnd_level":1003.39,"humidity":96,"temp_kf":0.24},
|
||||
weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":10.2,"deg":295.502},
|
||||
"rain":{"3h":1.125},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-08 15:00:00"},
|
||||
|
||||
|
||||
{"dt":1546970400,
|
||||
"main":{"temp":277.13,"temp_min":276.974,"temp_max":277.13,"pressure":1004.37,"sea_level":1011.35,"grnd_level":1004.37,"humidity":98,"temp_kf":0.16},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":88},
|
||||
"wind":{"speed":8.79,"deg":310.001},
|
||||
"rain":{"3h":0.37},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-08 18:00:00"},
|
||||
|
||||
|
||||
{"dt":1546981200,
|
||||
"main":{"temp":274.9,"temp_min":274.816,"temp_max":274.9,"pressure":1004.8,"sea_level":1011.86,"grnd_level":1004.8,"humidity":98,"temp_kf":0.08},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":9.44,"deg":300.004},
|
||||
"rain":{"3h":0.5075},
|
||||
"snow":{"3h":0.13},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-08 21:00:00"},
|
||||
|
||||
|
||||
{"dt":1546992000,
|
||||
"main":{"temp":274.496,"temp_min":274.496,"temp_max":274.496,"pressure":1004.53,"sea_level":1011.62,"grnd_level":1004.53,"humidity":98,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":88},
|
||||
"wind":{"speed":8.63,"deg":311.004},
|
||||
"rain":{"3h":0.1275},
|
||||
"snow":{"3h":0.97875},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-09 00:00:00"},
|
||||
|
||||
|
||||
{"dt":1547002800,
|
||||
"main":{"temp":274.199,"temp_min":274.199,"temp_max":274.199,"pressure":1005.85,"sea_level":1013,"grnd_level":1005.85,"humidity":95,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":8.96,"deg":328.001},
|
||||
"rain":{"3h":0.055000000000001},
|
||||
"snow":{"3h":1.89},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-09 03:00:00"},
|
||||
|
||||
|
||||
{"dt":1547013600,
|
||||
"main":{"temp":274.112,"temp_min":274.112,"temp_max":274.112,"pressure":1007.93,"sea_level":1015.04,"grnd_level":1007.93,"humidity":96,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":8.89,"deg":339.005},
|
||||
"rain":{"3h":0.0099999999999998},
|
||||
"snow":{"3h":1.8625},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-09 06:00:00"},
|
||||
|
||||
|
||||
{"dt":1547024400,
|
||||
"main":{"temp":273.999,"temp_min":273.999,"temp_max":273.999,"pressure":1010.13,"sea_level":1017.22,"grnd_level":1010.13,"humidity":97,"temp_kf":0},
|
||||
"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":8.24,"deg":340.002},
|
||||
"rain":{},
|
||||
"snow":{"3h":0.88375},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-09 09:00:00"},
|
||||
|
||||
|
||||
{"dt":1547035200,
|
||||
"main":{"temp":274.568,"temp_min":274.568,"temp_max":274.568,"pressure":1011.45,"sea_level":1018.57,"grnd_level":1011.45,"humidity":95,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":8.46,"deg":345},
|
||||
"rain":{"3h":0.215},
|
||||
"snow":{"3h":1.1525},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-09 12:00:00"},
|
||||
|
||||
|
||||
{"dt":1547046000,
|
||||
"main":{"temp":274.905,"temp_min":274.905,"temp_max":274.905,"pressure":1013.5,"sea_level":1020.67,"grnd_level":1013.5,"humidity":93,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":8.26,"deg":355.5},
|
||||
"rain":{"3h":0.605},
|
||||
"snow":{"3h":0.58},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-09 15:00:00"},
|
||||
|
||||
|
||||
{"dt":1547056800,
|
||||
"main":{"temp":275.292,"temp_min":275.292,"temp_max":275.292,"pressure":1016.15,"sea_level":1023.39,"grnd_level":1016.15,"humidity":96,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":7.89,"deg":2.50183},
|
||||
"rain":{"3h":0.81},
|
||||
"snow":{"3h":0.05},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-09 18:00:00"},
|
||||
|
||||
|
||||
{"dt":1547067600,
|
||||
"main":{"temp":275.616,"temp_min":275.616,"temp_max":275.616,"pressure":1018.97,"sea_level":1026.15,"grnd_level":1018.97,"humidity":96,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":6.91,
|
||||
"deg":7.00064},
|
||||
"rain":{"3h":0.825},
|
||||
"snow":{},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-09 21:00:00"},
|
||||
|
||||
|
||||
{"dt":1547078400,
|
||||
"main":{"temp":275.176,"temp_min":275.176,"temp_max":275.176,"pressure":1021.42,"sea_level":1028.56,"grnd_level":1021.42,"humidity":95,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":6.07,"deg":9.00732},
|
||||
"rain":{"3h":0.3},
|
||||
"snow":{},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-10 00:00:00"},
|
||||
|
||||
|
||||
{"dt":1547089200,
|
||||
"main":{"temp":274.73,"temp_min":274.73,"temp_max":274.73,"pressure":1022.76,"sea_level":1030.03,"grnd_level":1022.76,"humidity":95,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":6.26,"deg":0.500885},
|
||||
"rain":{"3h":0.22},
|
||||
"snow":{"3h":0.0725},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-10 03:00:00"},
|
||||
|
||||
|
||||
{"dt":1547100000,
|
||||
"main":{"temp":274.447,"temp_min":274.447,"temp_max":274.447,"pressure":1024.21,"sea_level":1031.46,"grnd_level":1024.21,"humidity":97,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":88},
|
||||
"wind":{"speed":6.56,"deg":1.50931},
|
||||
"rain":{"3h":0.13},
|
||||
"snow":{"3h":0.19},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-10 06:00:00"},
|
||||
|
||||
|
||||
{"dt":1547110800,
|
||||
"main":{"temp":274.451,"temp_min":274.451,"temp_max":274.451,"pressure":1026.46,"sea_level":1033.64,"grnd_level":1026.46,"humidity":95,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],
|
||||
"clouds":{"all":76},
|
||||
"wind":{"speed":6.06,"deg":6.50006},
|
||||
"rain":{"3h":0.14},"snow":{"3h":0.3975},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-10 09:00:00"},
|
||||
|
||||
|
||||
{"dt":1547121600,
|
||||
"main":{"temp":275.178,"temp_min":275.178,"temp_max":275.178,"pressure":1027.64,"sea_level":1034.83,"grnd_level":1027.64,"humidity":97,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":{"all":56},"wind":{"speed":5.36,"deg":5.50165},
|
||||
"rain":{"3h":0.09},
|
||||
"snow":{"3h":0.145},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-10 12:00:00"},
|
||||
|
||||
|
||||
{"dt":1547132400,
|
||||
"main":{"temp":274.055,"temp_min":274.055,"temp_max":274.055,"pressure":1028.63,"sea_level":1035.76,"grnd_level":1028.63,"humidity":93,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],
|
||||
"clouds":{"all":56},
|
||||
"wind":{"speed":4.32,"deg":355.007},
|
||||
"rain":{"3h":0.0099999999999998},
|
||||
"snow":{"3h":0.0075000000000003},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-10 15:00:00"},
|
||||
|
||||
|
||||
{"dt":1547143200,
|
||||
"main":{"temp":272.575,"temp_min":272.575,"temp_max":272.575,"pressure":1029.83,"sea_level":1037.09,"grnd_level":1029.83,"humidity":96,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":56},
|
||||
"wind":{"speed":4,"deg":347.001},
|
||||
"rain":{"3h":0.0099999999999998},
|
||||
"snow":{"3h":0.035},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-10 18:00:00"},
|
||||
|
||||
|
||||
{"dt":1547154000,
|
||||
"main":{"temp":271.138,"temp_min":271.138,"temp_max":271.138,"pressure":1030.29,"sea_level":1037.62,"grnd_level":1030.29,"humidity":97,"temp_kf":0},
|
||||
"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13n"}],
|
||||
"clouds":{"all":48},
|
||||
"wind":{"speed":3.51,"deg":327.003},
|
||||
"rain":{},
|
||||
"snow":{"3h":0.035},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-10 21:00:00"},
|
||||
|
||||
|
||||
|
||||
{"dt":1547164800,
|
||||
"main":{"temp":270.23,"temp_min":270.23,"temp_max":270.23,"pressure":1030.28,"sea_level":1037.69,"grnd_level":1030.28,"humidity":94,"temp_kf":0},
|
||||
"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],
|
||||
"clouds":{"all":44},
|
||||
"wind":{"speed":2.96,"deg":298.001},
|
||||
"rain":{},
|
||||
"snow":{"3h":0.0175},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-11 00:00:00"},
|
||||
|
||||
|
||||
{"dt":1547175600,
|
||||
"main":{"temp":270.201,"temp_min":270.201,"temp_max":270.201,"pressure":1029.39,"sea_level":1036.71,"grnd_level":1029.39,"humidity":94,"temp_kf":0}
|
||||
,"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],
|
||||
"clouds":{"all":76},
|
||||
"wind":{"speed":3.36,"deg":276.002},
|
||||
"rain":{},
|
||||
"snow":{},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-11 03:00:00"},
|
||||
|
||||
|
||||
{"dt":1547186400,
|
||||
"main":{"temp":270.741,"temp_min":270.741,"temp_max":270.741,"pressure":1027.81,"sea_level":1035.1,"grnd_level":1027.81,"humidity":93,"temp_kf":0},
|
||||
"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],
|
||||
"clouds":{"all":80},
|
||||
"wind":{"speed":3.66,"deg":248.501},
|
||||
"rain":{},
|
||||
"snow":{},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-11 06:00:00"},
|
||||
|
||||
|
||||
{"dt":1547197200,
|
||||
"main":{"temp":271.997,"temp_min":271.997,"temp_max":271.997,"pressure":1026.11,"sea_level":1033.37,"grnd_level":1026.11,"humidity":96,"temp_kf":0},
|
||||
"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13d"}],
|
||||
"clouds":{"all":76},
|
||||
"wind":{"speed":5.02,"deg":229.002},
|
||||
"rain":{},
|
||||
"snow":{"3h":0.1475},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-11 09:00:00"},
|
||||
|
||||
|
||||
{"dt":1547208000,
|
||||
"main":{"temp":273.695,"temp_min":273.695,"temp_max":273.695,"pressure":1023.62,"sea_level":1030.8,"grnd_level":1023.62,"humidity":98,"temp_kf":0},
|
||||
"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":6.47,"deg":236.003},
|
||||
"rain":{},
|
||||
"snow":{"3h":0.625},
|
||||
"sys":{"pod":"d"},
|
||||
"dt_txt":"2019-01-11 12:00:00"},
|
||||
|
||||
|
||||
{"dt":1547218800,
|
||||
"main":{"temp":275.073,"temp_min":275.073,"temp_max":275.073,"pressure":1020.92,"sea_level":1028.15,"grnd_level":1020.92,"humidity":93,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":5.95,"deg":249.003},
|
||||
"rain":{"3h":0.25},
|
||||
"snow":{"3h":0.1625},
|
||||
"sys":{"pod":"d"},"dt_txt":"2019-01-11 15:00:00"},
|
||||
|
||||
|
||||
{"dt":1547229600,
|
||||
"main":{"temp":276.411,"temp_min":276.411,"temp_max":276.411,"pressure":1019.61,"sea_level":1026.67,"grnd_level":1019.61,"humidity":93,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":92},
|
||||
"wind":{"speed":6.07,"deg":275.502},
|
||||
"rain":{"3h":0.65},
|
||||
"snow":{},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-11 18:00:00"},
|
||||
|
||||
|
||||
{"dt":1547240400,
|
||||
"main":{"temp":276.543,"temp_min":276.543,"temp_max":276.543,"pressure":1019.21,"sea_level":1026.27,"grnd_level":1019.21,"humidity":94,"temp_kf":0},
|
||||
"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10n"}],
|
||||
"clouds":{"all":48},
|
||||
"wind":{"speed":6.56,"deg":289.5},
|
||||
"rain":{"3h":0.2},
|
||||
"snow":{},
|
||||
"sys":{"pod":"n"},
|
||||
"dt_txt":"2019-01-11 21:00:00"}
|
||||
],
|
||||
|
||||
"city":{"id":2829657,"name":"","coord":{"lat":52.3833,"lon":13.2167},"country":"DE","population":12964}}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
https://api.openweathermap.org/data/2.5/forecast?lat=5&lon=1&APPID=
|
||||
|
||||
https://api.openweathermap.org/data/2.5/forecast?lat=5&lon=1&APPID=
|
||||
|
||||
|
||||
|
||||
{
|
||||
'dt' => 1546973400,
|
||||
'weather' => [
|
||||
{
|
||||
'description' => 'moderate rain',
|
||||
'id' => 501,
|
||||
'main' => 'Rain',
|
||||
'icon' => '10n'
|
||||
}
|
||||
],
|
||||
'main' => {
|
||||
'pressure' => 998,
|
||||
'temp_max' => '277.15',
|
||||
'humidity' => 86,
|
||||
'temp_min' => '276.15',
|
||||
'temp' => '276.53'
|
||||
},
|
||||
'visibility' => 10000,
|
||||
'wind' => {
|
||||
'deg' => 290,
|
||||
'speed' => '6.2'
|
||||
},
|
||||
'sys' => {
|
||||
'type' => 1,
|
||||
'message' => '0.0368',
|
||||
'country' => 'DE',
|
||||
'sunrise' => 1546931687,
|
||||
'id' => 1275,
|
||||
'sunset' => 1546960424
|
||||
},
|
||||
'coord' => {
|
||||
'lon' => '13.2',
|
||||
'lat' => '52.39'
|
||||
},
|
||||
'base' => 'stations',
|
||||
'name' => '',
|
||||
'cod' => 200,
|
||||
'clouds' => {
|
||||
'all' => 75
|
||||
},
|
||||
'id' => 2829657
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
{"cod":429, "message": "Your account is temporary blocked due to exceeding of requests limitation of your subscription type. Please choose the proper subscription http://openweathermap.org/price"}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
374
YahooWeatherAPI.pm
Normal file
374
YahooWeatherAPI.pm
Normal file
@ -0,0 +1,374 @@
|
||||
# $Id: YahooWeatherAPI.pm 16641 2018-04-21 12:28:38Z neubert $
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# YahooWeatherAPI.pm
|
||||
# Copyright by Dr. Boris Neubert
|
||||
# e-mail: omega at online dot de
|
||||
#
|
||||
# This file is part of fhem.
|
||||
#
|
||||
# Fhem 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.
|
||||
#
|
||||
# Fhem 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.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with fhem. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
package main;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use HttpUtils;
|
||||
use JSON; # apt-get install libperl-JSON on Debian and derivatives
|
||||
#use Data::Dumper; # for Debug only
|
||||
|
||||
# Yahoo! Weather API: http://developer.yahoo.com/weather/
|
||||
|
||||
use constant URL => "https://query.yahooapis.com/v1/public/yql?q=select%%20*%%20from%%20weather.forecast%%20where%%20woeid=%s%%20and%%20u=%%27c%%27&format=%s&env=store%%3A%%2F%%2Fdatatables.org%%2Falltableswithkeys";
|
||||
|
||||
|
||||
# Mapping / translation of current weather codes 0-47
|
||||
my @YahooCodes_en = (
|
||||
'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',
|
||||
'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', 'Orkan', 'schwere Gewitter', 'Gewitter', 'Regen und Schnee',
|
||||
'Regen und Graupel', 'Schnee und Graupel', 'Eisregen', 'Nieselregen', 'gefrierender Regen' ,'Schauer',
|
||||
'Schauer', 'Schneetreiben', 'leichte Schneeschauer', 'Schneeverwehungen', 'Schnee', 'Hagel',
|
||||
'Graupel', 'Staub', 'Nebel', 'Dunst', 'Smog', 'Sturm',
|
||||
'windig', 'kalt', 'wolkig',
|
||||
'überwiegend wolkig', # night
|
||||
'überwiegend wolkig', # day
|
||||
'teilweise wolkig', # night
|
||||
'teilweise wolkig', # day
|
||||
'klar', # night
|
||||
'sonnig',
|
||||
'heiter', # night
|
||||
'heiter', # day
|
||||
'Regen und Hagel',
|
||||
'heiß', 'einzelne Gewitter', 'vereinzelt Gewitter', 'vereinzelt Gewitter', 'vereinzelt Schauer', 'starker Schneefall',
|
||||
'vereinzelt Schneeschauer', 'starker Schneefall', 'teilweise wolkig', 'Gewitterregen', 'Schneeschauer', 'vereinzelt Gewitter');
|
||||
|
||||
my @YahooCodes_nl = (
|
||||
'tornado', 'zware storm', 'orkaan', 'hevig onweer', 'onweer',
|
||||
'regen en sneeuw',
|
||||
'regen en ijzel', 'sneeuw en ijzel', 'aanvriezende motregen',
|
||||
'motregen', 'aanvriezende regen' ,'buien',
|
||||
'buien', 'sneeuw windstoten', 'lichte sneeuwbuien',
|
||||
'stuifsneeuw', 'sneeuw', 'hagel',
|
||||
'ijzel', 'stof', 'mist', 'waas', 'smog', 'onstuimig',
|
||||
'winderig', 'koud', 'bewolkt',
|
||||
'overwegend bewolkt', # night
|
||||
'overwegend bewolkt', # day
|
||||
'gedeeltelijk bewolkt', # night
|
||||
'gedeeltelijk bewolkt', # day
|
||||
'helder', #night
|
||||
'zonnig',
|
||||
'mooi', #night
|
||||
'mooi', #day
|
||||
'regen en hagel',
|
||||
'heet', 'plaatselijk onweer', 'af en toe onweer', 'af en toe onweer', 'af en toe regenbuien', 'hevige sneeuwval',
|
||||
'af en toe sneeuwbuien', 'hevige sneeuwval', 'deels bewolkt',
|
||||
'onweersbuien', 'sneeuwbuien', 'af en toe onweersbuien');
|
||||
|
||||
my @YahooCodes_fr = (
|
||||
'tornade', 'tempête tropicale', 'ouragan', 'tempête sévère', 'orage', 'pluie et neige',
|
||||
'pluie et grésil', 'neige et grésil', 'bruine verglassante', 'bruine', 'pluie verglassante' ,'averse',
|
||||
'averses', 'tourbillon de neige', 'légères averses de neige', 'rafale de neige', 'neige', 'grêle',
|
||||
'giboulées', 'poussières', 'brouillard', 'brume', 'enfumé', 'orageux',
|
||||
'venteux', 'froid', 'nuageux',
|
||||
'couverte', # night
|
||||
'couvert', # day
|
||||
'partiellement couverte', # night
|
||||
'partiellement couvert', # day
|
||||
'clair',
|
||||
'ensoleillé',
|
||||
'douce', #night
|
||||
'agréable', #day
|
||||
'pluie et grêle',
|
||||
'chaud', 'orages isolés', 'tempêtes éparses', 'orages épars', 'averses éparses', 'tempête de neige',
|
||||
'chûtes de neiges éparses', 'tempêtes de neige', 'partielement nuageux', 'averses orageuses', 'chûte de neige', 'chûtes de neige isolées');
|
||||
|
||||
my @YahooCodes_pl = (
|
||||
'tornado', 'burza tropikalna', 'huragan', 'porywiste burze', 'burze', 'deszcz ze śniegiem',
|
||||
'deszcz i deszcz ze śniegiem', 'śnieg i deszcz ze śniegiem', 'marznąca mżawka', 'mżawka', 'marznący deszcz' ,'deszcz',
|
||||
'deszcz', 'przelotne opady śniegu', 'lekkie opady śniegu', 'zamieć śnieżna', 'śnieg', 'grad',
|
||||
'deszcz ze śniegiem', 'pył', 'mgła', 'mgła', 'smog', 'przenikliwie',
|
||||
'wietrznie', 'zimno', 'pochmurno',
|
||||
'pochmurno', # night
|
||||
'pochmurno', # day
|
||||
'częściowe zachmurzenie', # night
|
||||
'częściowe zachmurzenie', # day
|
||||
'czyste niebo',
|
||||
'słonecznie',
|
||||
'ładna noc', #night
|
||||
'ładny dzień', #day
|
||||
'deszcz z gradem',
|
||||
'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');
|
||||
|
||||
my @YahooCodes_it = (
|
||||
'tromba d\'aria', 'tempesta tropicale', 'uragano', 'temporali di grande intensità', 'temporali', 'pioggia mista e neve',
|
||||
'pioggia mista e nevischio', 'neve mista e nevischio', 'pioggia gelata', 'pioggia leggera', 'grandine' ,'rovesci',
|
||||
'piogge', 'raffiche di neve', 'deboli nevicate', 'bufera di neve', 'neve', 'grandine',
|
||||
'nevischio', 'pulviscolo', 'nebbia', 'foschia', 'smog', 'ventoso',
|
||||
'ventoso', 'freddo', 'nuvoloso',
|
||||
'parzialmente nuvoloso', # night
|
||||
'parzialmente nuvoloso', # day
|
||||
'parzialmente nuvoloso', # night
|
||||
'parzialmente nuvoloso', # day
|
||||
'sereno',
|
||||
'soleggiato',
|
||||
'bel tempo', #night
|
||||
'bel tempo', #day
|
||||
'pioggia mista a grandine',
|
||||
'caldo', 'temporali isolati', 'temporali sparsi', 'temporali sparsi', 'piogge sparse', 'forti nevicate',
|
||||
'nevicate sparse', 'forti nevicate', 'parzialmente nuvoloso', 'rovesci temporaleschi', 'rovesci di neve', 'temporali isolati');
|
||||
|
||||
|
||||
###################################
|
||||
|
||||
# Cache
|
||||
my %YahooWeatherAPI_CachedData= ();
|
||||
my %YahooWeatherAPI_CachedDataTs= ();
|
||||
|
||||
###################################
|
||||
|
||||
#
|
||||
# there is a bug in the Yahoo Weather API that gets all units wrong
|
||||
# these routines fix that
|
||||
|
||||
|
||||
sub value_to_C($) {
|
||||
my ($F)= @_;
|
||||
return(int(($F-32)*5/9+0.5));
|
||||
}
|
||||
|
||||
sub value_to_hPa($) {
|
||||
my ($inHg)= @_;
|
||||
return int($inHg/33.86390+0.5);
|
||||
}
|
||||
|
||||
sub value_to_km($) {
|
||||
my ($value)= @_;
|
||||
return int($value/1.609347219+0.5);
|
||||
}
|
||||
|
||||
###################################
|
||||
|
||||
# call: YahooWeatherAPI_RetrieveData(%%args)
|
||||
#
|
||||
# the args hash reference must contain at least
|
||||
# woeid => WOEID [WHERE-ON-EARTH-ID], go to http://weather.yahoo.com to find out
|
||||
# format => xml or json
|
||||
# blocking => 0 or 1
|
||||
# callbackFnRef => reference to callback function with arguments ($argsRef, $err, $result)
|
||||
# the args hash reference is returned as first argument of the callbackFn
|
||||
#
|
||||
|
||||
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};
|
||||
|
||||
my $url = sprintf(URL, $woeid, $format);
|
||||
|
||||
#Debug "Retrieve Yahoo Weather data for " . $argsRef->{hash}->{NAME};
|
||||
|
||||
if ($blocking) {
|
||||
# do not use noshutdown => 0 in parameters
|
||||
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 => 15,
|
||||
argsRef => $argsRef,
|
||||
callback => \&YahooWeatherAPI_RetrieveDataFinished,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sub YahooWeatherAPI_RetrieveDataFinished($$$) {
|
||||
my ($paramRef, $err, $response) = @_;
|
||||
my $argsRef= $paramRef->{argsRef};
|
||||
#Debug "Finished retrieving Yahoo Weather data for " . $argsRef->{hash}->{NAME};
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
# this decodes a JSON result and returns the Weather Channel hash reference
|
||||
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($@);
|
||||
my $query= $data->{query};
|
||||
#Debug Dumper($query);
|
||||
my $count= $query->{count};
|
||||
#Debug "$count result(s).";
|
||||
return("$count result(s) retrieved", undef) unless($count == 1);
|
||||
my $channel= $query->{results}{channel};
|
||||
return(undef, $channel);
|
||||
}
|
||||
|
||||
sub YahooWeatherAPI_ConvertChannelData($) {
|
||||
my ($data)= @_; # hash reference
|
||||
|
||||
$data->{wind}{chill}= value_to_C($data->{wind}{chill}); # # API delivers wrong value
|
||||
$data->{atmosphere}{pressure}= value_to_hPa($data->{atmosphere}{pressure}); # API delivers wrong value
|
||||
|
||||
|
||||
my $units= YahooWeatherAPI_units($data); # units hash reference
|
||||
|
||||
$data->{wind}{speed}= value_to_km($data->{wind}{speed}); # API delivers km
|
||||
$data->{atmosphere}{visibility}= value_to_km($data->{atmosphere}{visibility}); # API delivers km
|
||||
|
||||
return 0 if($units->{temperature} eq "C");
|
||||
|
||||
my $item= $data->{item};
|
||||
$item->{condition}{temp}= value_to_C($item->{condition}{temp});
|
||||
|
||||
my $forecast= $item->{forecast};
|
||||
foreach my $fc (@{$forecast}) {
|
||||
$fc->{low}= value_to_C($fc->{low});
|
||||
$fc->{high}= value_to_C($fc->{high});
|
||||
}
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
|
||||
sub YahooWeatherAPI_ParseDateTime($) {
|
||||
|
||||
my ($value)= @_; ### "Fri, 13 Nov 2015 8:00 am CET"
|
||||
|
||||
my @months= qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
|
||||
my %monthindex;
|
||||
@monthindex{@months} = (0..$#months);
|
||||
|
||||
if($value =~ '^(\w{3}), (\d{1,2}) (\w{3}) (\d{4}) (\d{1,2}):(\d{2}) (\w{2}) (\w{3,4})$') {
|
||||
my ($wd, $d, $mon, $y, $h, $n, $p, $tz)= ($1,$2,$3,$4,$5,$6,$7,$8);
|
||||
# 12 AM= 0, 12 PM= 12
|
||||
$h+=12 if($h==12); if($p eq "PM") { $h= ($h+12) % 24 } else { $h%= 12 };
|
||||
my $m= $monthindex{$mon};
|
||||
return undef unless defined($m);
|
||||
#main::Debug "###### $value -> $wd $d $m $y $h:$n $tz";
|
||||
# $mday= 1..
|
||||
# $month= 0..11
|
||||
# $year is year-1900
|
||||
# we ignore the time zone as it probably never changes for a weather device an assume
|
||||
# local time zone
|
||||
return fhemTimeLocal(0, $n, $h, $d, $m, $y-1900);
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
sub YahooWeatherAPI_pubDate($) {
|
||||
|
||||
my ($channel)= @_;
|
||||
|
||||
### pubDate Fri, 13 Nov 2015 8:00 am CET
|
||||
if(!defined($channel->{item}{pubDate})) {
|
||||
return("no pubDate received", "", undef);
|
||||
};
|
||||
my $pubDate= $channel->{item}{pubDate};
|
||||
my $ts= YahooWeatherAPI_ParseDateTime($pubDate);
|
||||
if(defined($ts)) {
|
||||
return("okay", $pubDate, $ts);
|
||||
} else {
|
||||
return("could not parse pubDate $pubDate", $pubDate, undef);
|
||||
}
|
||||
}
|
||||
|
||||
sub YahooWeatherAPI_units($) {
|
||||
|
||||
my ($channel)= @_;
|
||||
return $channel->{units};
|
||||
}
|
||||
|
||||
sub YahooWeatherAPI_getYahooCodes($) {
|
||||
|
||||
my ($lang)= @_;
|
||||
|
||||
if($lang eq "de") {
|
||||
return @YahooCodes_de;
|
||||
} elsif($lang eq "nl") {
|
||||
return @YahooCodes_nl;
|
||||
} elsif($lang eq "fr") {
|
||||
return @YahooCodes_fr;
|
||||
} elsif($lang eq "pl") {
|
||||
return @YahooCodes_pl;
|
||||
} elsif($lang eq "it") {
|
||||
return @YahooCodes_it;
|
||||
} else {
|
||||
return @YahooCodes_en;
|
||||
}
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
|
||||
1;
|
Loading…
Reference in New Issue
Block a user