From be8ece6238136927eef47ff0f7e85cd6ddb57a83 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 12 Feb 2022 12:56:04 +0100 Subject: [PATCH 01/24] add Onecall to Endpoint for daily output [Ticket: no] --- OpenWeatherMapAPI.pm | 757 +++++++++++++++++++++++++++---------------- 1 file changed, 479 insertions(+), 278 deletions(-) diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 74043fc..b78dde4 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -1,9 +1,9 @@ # $Id: $ ############################################################################### # -# Developed with Kate +# Developed with VSCodium and richterger perl plugin. # -# (c) 2019 Copyright: Marko Oldenburg (leongaultier at gmail dot com) +# (c) 2019-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) # All rights reserved # # Special thanks goes to: @@ -30,6 +30,7 @@ ### Beispielaufruf # https://api.openweathermap.org/data/2.5/weather?lat=[lat]&lon=[long]&APPID=[API] Current # https://api.openweathermap.org/data/2.5/forecast?lat=[lat]&lon=[long]&APPID=[API] Forecast +# https://api.openweathermap.org/data/2.5/onecall?lat=[lat]&lon=[long]&APPID=[API] Forecast # https://openweathermap.org/weather-conditions Icons und Conditions ID's package OpenWeatherMapAPI; @@ -38,7 +39,7 @@ use warnings; use FHEM::Meta; FHEM::Meta::Load(__PACKAGE__); -use version 0.50; our $VERSION = $main::packages{OpenWeatherMapAPI}{META}{version}; +use version 0.50; our $VERSION = $::packages{OpenWeatherMapAPI}{META}{version}; package OpenWeatherMapAPI::Weather; use strict; @@ -46,6 +47,7 @@ use warnings; use POSIX; use HttpUtils; +use experimental qw /switch/; # use Data::Dumper; @@ -55,15 +57,11 @@ eval { require JSON::MaybeXS; import JSON::MaybeXS qw( decode_json encode_json ); 1; -}; - -if ($@) { - $@ = undef; +} or do { # try to use JSON wrapper # for chance of better performance eval { - # JSON preference order local $ENV{PERL_JSON_BACKEND} = 'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP' @@ -72,10 +70,7 @@ if ($@) { require JSON; import JSON qw( decode_json encode_json ); 1; - }; - - if ($@) { - $@ = undef; + } or do { # In rare cases, Cpanel::JSON::XS may # be installed but JSON|JSON::MaybeXS not ... @@ -83,10 +78,7 @@ if ($@) { require Cpanel::JSON::XS; import Cpanel::JSON::XS qw(decode_json encode_json); 1; - }; - - if ($@) { - $@ = undef; + } or do { # In rare cases, JSON::XS may # be installed but JSON not ... @@ -94,10 +86,7 @@ if ($@) { require JSON::XS; import JSON::XS qw(decode_json encode_json); 1; - }; - - if ($@) { - $@ = undef; + } or do { # Fallback to built-in JSON which SHOULD # be available since 5.014 ... @@ -105,27 +94,29 @@ if ($@) { require JSON::PP; import JSON::PP qw(decode_json encode_json); 1; - }; - - if ($@) { - $@ = undef; + } or do { # Fallback to JSON::backportPP in really rare cases require JSON::backportPP; import JSON::backportPP qw(decode_json encode_json); 1; - } - } - } - } -} + }; + }; + }; + }; +}; my $missingModul = ''; -eval "use Encode qw(encode_utf8);1" or $missingModul .= "Encode "; +## no critic (Conditional "use" statement. Use "require" to conditionally include a module (Modules::ProhibitConditionalUseStatements)) +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/'; +eval { use Readonly; 1 } + or $missingModul .= 'Readonly '; # apt install libreadonly-perl +## use critic + +Readonly my $URL => 'https://api.openweathermap.org/data/2.5/'; ## URL . 'weather?' for current data ## URL . 'forecast?' for forecast data @@ -194,7 +185,7 @@ sub new { my $self = { devName => $argsRef->{devName}, key => ( - ( defined( $argsRef->{apikey} ) and $argsRef->{apikey} ) + ( defined( $argsRef->{apikey} ) && $argsRef->{apikey} ) ? $argsRef->{apikey} : 'none' ), @@ -272,9 +263,10 @@ sub _RetrieveDataFromOpenWeatherMap { my $self = shift; # retrieve data from cache - if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} - and $self->{cached}->{lat} == $self->{lat} - and $self->{cached}->{long} == $self->{long} ) + if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} + && $self->{cached}->{lat} == $self->{lat} + && $self->{cached}->{long} == $self->{long} + && $self->{endpoint} eq 'none' ) { return _CallWeatherCallbackFn($self); } @@ -287,22 +279,24 @@ sub _RetrieveDataFromOpenWeatherMap { my $paramRef = { timeout => 15, self => $self, - endpoint => ( $self->{endpoint} eq 'none' ? 'weather' : 'forecast' ), + endpoint => $self->{endpoint} eq 'none' ? 'weather' + : $self->{endpoint} eq 'forecast' ? 'onecall' + : 'forecast', callback => \&_RetrieveDataFinished, }; $self->{endpoint} = $paramRef->{endpoint}; if ( $self->{lat} eq 'error' - or $self->{long} eq 'error' - or $self->{key} eq 'none' - or $missingModul ) + || $self->{long} eq 'error' + || $self->{key} eq 'none' + || $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' ); + ) if ( $self->{lat} eq 'error' || $self->{long} eq 'error' ); _RetrieveDataFinished( $paramRef, 'No given api key. (define myWeather Weather apikey=[KEY])', @@ -315,7 +309,7 @@ sub _RetrieveDataFromOpenWeatherMap { } else { $paramRef->{url} = - URL + $URL . $paramRef->{endpoint} . '?' . 'lat=' . $self->{lat} . '&' . 'lon=' . $self->{long} . '&' @@ -323,8 +317,10 @@ sub _RetrieveDataFromOpenWeatherMap { . $self->{key} . '&' . 'lang=' . $self->{lang}; - main::HttpUtils_NonblockingGet($paramRef); + ::HttpUtils_NonblockingGet($paramRef); } + + return; } sub _RetrieveDataFinished { @@ -334,8 +330,9 @@ sub _RetrieveDataFinished { my $self = $paramRef->{self}; if ( !$err ) { - $self->{cached}->{status} = 'ok'; - $self->{cached}->{validity} = 'up-to-date', $self->{fetchTime} = time(); + $self->{cached}->{status} = 'ok'; + $self->{cached}->{validity} = 'up-to-date'; + $self->{fetchTime} = time(); _ProcessingRetrieveData( $self, $response ); } else { @@ -343,28 +340,30 @@ sub _RetrieveDataFinished { _ErrorHandling( $self, $err ); _ProcessingRetrieveData( $self, $response ); } + + return; } sub _ProcessingRetrieveData { my $self = shift; my $response = shift; - if ( $self->{cached}->{status} eq 'ok' - and defined($response) - and $response ) + if ( $self->{cached}->{status} eq 'ok' + && defined($response) + && $response ) { - if ( $response =~ m/^{.*}$/ ) { + if ( $response =~ m/^{.*}$/x ) { my $data = eval { decode_json($response) }; if ($@) { _ErrorHandling( $self, 'OpenWeatherMap Weather decode JSON err ' . $@ ); } - elsif ( defined( $data->{cod} ) - and $data->{cod} - and $data->{cod} != 200 - and defined( $data->{message} ) - and $data->{message} ) + elsif (defined( $data->{cod} ) + && $data->{cod} + && $data->{cod} != 200 + && defined( $data->{message} ) + && $data->{message} ) { _ErrorHandling( $self, $data->{cod} . ': ' . $data->{message} ); } @@ -376,217 +375,409 @@ sub _ProcessingRetrieveData { strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); - if ( $self->{endpoint} eq 'weather' ) { - $self->{cached}->{country} = $data->{sys}->{country}; - $self->{cached}->{city} = encode_utf8( $data->{name} ); - $self->{cached}->{license}{text} = 'none'; - $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 - ), - 'tempFeelsLike_c' => int( - sprintf( "%.1f", - ( $data->{main}->{feels_like} - 273.15 ) ) + 0.5 - ), - 'humidity' => $data->{main}->{humidity}, - 'condition' => - encode_utf8( $data->{weather}->[0]->{description} ), - 'pressure' => int( - sprintf( "%.1f", $data->{main}->{pressure} ) + 0.5 - ), - 'wind' => int( - sprintf( "%.1f", ( $data->{wind}->{speed} * 3.6 ) ) - + 0.5 - ), - 'wind_speed' => int( - sprintf( "%.1f", ( $data->{wind}->{speed} * 3.6 ) ) - + 0.5 - ), - 'wind_gust' => int( - sprintf( "%.1f", ( $data->{wind}->{gust} * 3.6 ) ) - + 0.5 - ), - 'wind_direction' => $data->{wind}->{deg}, - 'cloudCover' => $data->{clouds}->{all}, - 'code' => $codes{ $data->{weather}->[0]->{id} }, - 'iconAPI' => $data->{weather}->[0]->{icon}, - 'sunsetTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{sys}->{sunset} ) - ), - 'sunriseTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{sys}->{sunrise} ) - ), - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{dt} ) - ), - }; + given ( $self->{endpoint} ) { + when ('weather') { + $self->{cached}->{country} = $data->{sys}->{country}; + $self->{cached}->{city} = encode_utf8( $data->{name} ); + $self->{cached}->{license}{text} = 'none'; + $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 + ), + 'tempFeelsLike_c' => int( + sprintf( "%.1f", + ( $data->{main}->{feels_like} - 273.15 ) ) + + 0.5 + ), + 'humidity' => $data->{main}->{humidity}, + 'condition' => encode_utf8( + $data->{weather}->[0]->{description} + ), + 'pressure' => int( + sprintf( "%.1f", $data->{main}->{pressure} ) + + 0.5 + ), + 'wind' => int( + sprintf( "%.1f", + ( $data->{wind}->{speed} * 3.6 ) ) + 0.5 + ), + 'wind_speed' => int( + sprintf( "%.1f", + ( $data->{wind}->{speed} * 3.6 ) ) + 0.5 + ), + 'wind_gust' => int( + sprintf( "%.1f", + ( $data->{wind}->{gust} * 3.6 ) ) + 0.5 + ), + 'wind_direction' => $data->{wind}->{deg}, + 'cloudCover' => $data->{clouds}->{all}, + 'code' => $codes{ $data->{weather}->[0]->{id} }, + 'iconAPI' => $data->{weather}->[0]->{icon}, + 'sunsetTime' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{sys}->{sunset} ) + ), + 'sunriseTime' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{sys}->{sunrise} ) + ), + 'pubDate' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{dt} ) + ), + }; - $self->{cached}->{current}->{'visibility'} = - int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ) - if ( exists $data->{visibility} ); - } + $self->{cached}->{current}->{'visibility'} = + int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ) + if ( exists $data->{visibility} ); + } - if ( $self->{endpoint} eq 'forecast' ) { - if ( ref( $data->{list} ) eq "ARRAY" - and scalar( @{ $data->{list} } ) > 0 ) - { - ## löschen des alten Datensatzes - delete $self->{cached}->{forecast}; + when ('forecast') { + if ( ref( $data->{list} ) eq "ARRAY" + && scalar( @{ $data->{list} } ) > 0 ) + { + ## löschen des alten Datensatzes + delete $self->{cached}->{forecast}; - my $i = 0; - for ( @{ $data->{list} } ) { - push( - @{ $self->{cached}->{forecast}->{hourly} }, - { - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - ( $data->{list}->[$i]->{dt} ) - 3600 - ) - ), - 'day_of_week' => strftime( - "%a, %H:%M", - localtime( - ( $data->{list}->[$i]->{dt} ) - 3600 - ) - ), - 'temperature' => int( - sprintf( - "%.1f", - ( + my $i = 0; + for ( @{ $data->{list} } ) { + push( + @{ $self->{cached}->{forecast}->{hourly} }, + { + 'pubDate' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + ( $data->{list}->[$i]->{dt} ) - + 3600 + ) + ), + 'day_of_week' => strftime( + "%a, %H:%M", + localtime( + ( $data->{list}->[$i]->{dt} ) - + 3600 + ) + ), + 'temperature' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{main} + ->{temp} - 273.15 + ) + ) + 0.5 + ), + 'temp_c' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{main} + ->{temp} - 273.15 + ) + ) + 0.5 + ), + 'low_c' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{main} + ->{temp_min} - 273.15 + ) + ) + 0.5 + ), + 'high_c' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{main} + ->{temp_max} - 273.15 + ) + ) + 0.5 + ), + 'tempLow' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{main} + ->{temp_min} - 273.15 + ) + ) + 0.5 + ), + 'tempHigh' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{main} + ->{temp_max} - 273.15 + ) + ) + 0.5 + ), + 'humidity' => + $data->{list}->[$i]->{main} + ->{humidity}, + 'condition' => encode_utf8( + $data->{list}->[$i]->{weather}->[0] + ->{description} + ), + 'pressure' => int( + sprintf( "%.1f", $data->{list}->[$i]->{main} - ->{temp} - 273.15 - ) - ) + 0.5 - ), - 'temp_c' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{main} - ->{temp} - 273.15 - ) - ) + 0.5 - ), - 'low_c' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{main} - ->{temp_min} - 273.15 - ) - ) + 0.5 - ), - 'high_c' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{main} - ->{temp_max} - 273.15 - ) - ) + 0.5 - ), - 'tempLow' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{main} - ->{temp_min} - 273.15 - ) - ) + 0.5 - ), - 'tempHigh' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{main} - ->{temp_max} - 273.15 - ) - ) + 0.5 - ), - 'humidity' => - $data->{list}->[$i]->{main}->{humidity}, - 'condition' => encode_utf8( - $data->{list}->[$i]->{weather}->[0] - ->{description} - ), - 'pressure' => int( - sprintf( "%.1f", - $data->{list}->[$i]->{main} - ->{pressure} ) + 0.5 - ), - 'wind' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{wind} - ->{speed} * 3.6 - ) - ) + 0.5 - ), - 'wind_speed' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{wind} - ->{speed} * 3.6 - ) - ) + 0.5 - ), - 'wind_gust' => int( - sprintf( - "%.1f", - ( - $data->{list}->[$i]->{wind} - ->{gust} * 3.6 - ) - ) + 0.5 - ), - 'cloudCover' => - $data->{list}->[$i]->{clouds}->{all}, - 'code' => $codes{ - $data->{list}->[$i]->{weather}->[0] - ->{id} + ->{pressure} ) + 0.5 + ), + 'wind' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{wind} + ->{speed} * 3.6 + ) + ) + 0.5 + ), + 'wind_speed' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{wind} + ->{speed} * 3.6 + ) + ) + 0.5 + ), + 'wind_gust' => int( + sprintf( + "%.1f", + ( + $data->{list}->[$i]->{wind} + ->{gust} * 3.6 + ) + ) + 0.5 + ), + 'cloudCover' => + $data->{list}->[$i]->{clouds}->{all}, + 'code' => $codes{ + $data->{list}->[$i]->{weather}->[0] + ->{id} + }, + 'iconAPI' => + $data->{list}->[$i]->{weather}->[0] + ->{icon}, + 'rain1h' => + $data->{list}->[$i]->{rain}->{'1h'}, + 'rain3h' => + $data->{list}->[$i]->{rain}->{'3h'}, + 'snow1h' => + $data->{list}->[$i]->{snow}->{'1h'}, + 'snow3h' => + $data->{list}->[$i]->{snow}->{'3h'}, }, - 'iconAPI' => - $data->{list}->[$i]->{weather}->[0] - ->{icon}, - 'rain1h' => - $data->{list}->[$i]->{rain}->{'1h'}, - 'rain3h' => - $data->{list}->[$i]->{rain}->{'3h'}, - 'snow1h' => - $data->{list}->[$i]->{snow}->{'1h'}, - 'snow3h' => - $data->{list}->[$i]->{snow}->{'3h'}, - }, - ); + ); - $i++; + $i++; + } + } + } + + when ('onecall') { + if ( ref( $data->{daily} ) eq "ARRAY" + && scalar( @{ $data->{daily} } ) > 0 ) + { + my $i = 0; + for ( @{ $data->{daily} } ) { + push( + @{ $self->{cached}->{forecast}->{daily} }, + { + 'pubDate' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + ( $data->{daily}->[$i]->{dt} ) + - 3600 + ) + ), + 'day_of_week' => strftime( + "%a, %H:%M", + localtime( + ( $data->{daily}->[$i]->{dt} ) + - 3600 + ) + ), + 'sunrise' => strftime( + "%H:%M", + localtime( + ( + $data->{daily}->[$i] + ->{sunrise} + ) - 3600 + ) + ), + 'sunset' => strftime( + "%a, %H:%M", + localtime( + ( + $data->{daily}->[$i] + ->{sunset} + ) - 3600 + ) + ), + 'moonrise' => strftime( + "%a, %H:%M", + localtime( + ( + $data->{daily}->[$i] + ->{moonrise} + ) - 3600 + ) + ), + 'moonset' => strftime( + "%a, %H:%M", + localtime( + ( + $data->{daily}->[$i] + ->{moonset} + ) - 3600 + ) + ), + 'temperature' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{day} - 273.15 + ) + ) + 0.5 + ), + 'temp_c' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{day} - 273.15 + ) + ) + 0.5 + ), + 'low_c' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{min} - 273.15 + ) + ) + 0.5 + ), + 'high_c' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{max} - 273.15 + ) + ) + 0.5 + ), + 'tempLow' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{min} - 273.15 + ) + ) + 0.5 + ), + 'tempHigh' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{max} - 273.15 + ) + ) + 0.5 + ), + 'humidity' => + $data->{daily}->[$i]->{humidity}, + 'condition' => encode_utf8( + $data->{daily}->[$i]->{weather} + ->[0]->{description} + ), + 'pressure' => int( + sprintf( "%.1f", + $data->{daily}->[$i]->{pressure} + ) + 0.5 + ), + 'wind' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{wind_speed} * 3.6 + ) + ) + 0.5 + ), + 'wind_speed' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{wind_speed} * 3.6 + ) + ) + 0.5 + ), + 'wind_gust' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{wind_gust} * 3.6 + ) + ) + 0.5 + ), + 'wind_direction' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{wind_deg} + ) + ) + ), + 'cloudCover' => + $data->{daily}->[$i]->{clouds}, + 'code' => $codes{ + $data->{daily}->[$i]->{weather} + ->[0]->{id} + }, + 'iconAPI' => + $data->{daily}->[$i]->{weather}->[0] + ->{icon}, + 'rain' => $data->{daily}->[$i]->{rain}, + 'snow' => $data->{daily}->[$i]->{snow}, + 'uvi' => $data->{daily}->[$i]->{uvi}, + }, + ); + + $i++; + } } } } @@ -595,12 +786,15 @@ sub _ProcessingRetrieveData { else { _ErrorHandling( $self, 'OpenWeatherMap ' . $response ); } } - $self->{endpoint} = 'none' if ( $self->{endpoint} eq 'forecast' ); + $self->{endpoint} = 'none' if ( $self->{endpoint} eq 'onecall' ); _RetrieveDataFromOpenWeatherMap($self) - if ( $self->{endpoint} eq 'weather' ); + if ( $self->{endpoint} eq 'weather' + || $self->{endpoint} eq 'forecast' ); _CallWeatherCallbackFn($self) if ( $self->{endpoint} eq 'none' ); + + return; } sub _CallWeatherCallbackFn { @@ -608,7 +802,9 @@ sub _CallWeatherCallbackFn { # print 'Dumperausgabe: ' . Dumper $self; ### Aufruf der callbackFn - main::Weather_RetrieveCallbackFn( $self->{devName} ); + ::Weather_RetrieveCallbackFn( $self->{devName} ); + + return; } sub _ErrorHandling { @@ -616,9 +812,11 @@ sub _ErrorHandling { my $err = shift; $self->{cached}->{current_date_time} = - strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ), - $self->{cached}->{status} = $err; + strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); + $self->{cached}->{status} = $err; $self->{cached}->{validity} = 'stale'; + + return; } sub _CreateForecastRef { @@ -639,20 +837,21 @@ sub _CreateForecastRef { } sub strftimeWrapper { - my $string = POSIX::strftime(@_); + my @data = @_; + my $string = POSIX::strftime(@data); - $string =~ s/\xe4/ä/g; - $string =~ s/\xc4/Ä/g; - $string =~ s/\xf6/ö/g; - $string =~ s/\xd6/Ö/g; - $string =~ s/\xfc/ü/g; - $string =~ s/\xdc/Ü/g; - $string =~ s/\xdf/ß/g; - $string =~ s/\xdf/ß/g; - $string =~ s/\xe1/á/g; - $string =~ s/\xe9/é/g; - $string =~ s/\xc1/Á/g; - $string =~ s/\xc9/É/g; + $string =~ s/\xe4/ä/xg; + $string =~ s/\xc4/Ä/xg; + $string =~ s/\xf6/ö/xg; + $string =~ s/\xd6/Ö/xg; + $string =~ s/\xfc/ü/xg; + $string =~ s/\xdc/Ü/xg; + $string =~ s/\xdf/ß/xg; + $string =~ s/\xdf/ß/xg; + $string =~ s/\xe1/á/xg; + $string =~ s/\xe9/é/xg; + $string =~ s/\xc1/Á/xg; + $string =~ s/\xc9/É/xg; return $string; } @@ -673,9 +872,9 @@ sub strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v1.0.3", + "version": "v1.2.0", "author": [ - "Marko Oldenburg " + "Marko Oldenburg " ], "x_fhem_maintainer": [ "CoolTux" @@ -707,3 +906,5 @@ sub strftimeWrapper { =end :application/json;q=META.json =cut + +__END__ -- 2.47.2 From 714c95d6d04c7c46d27d42693dc2c5100aa5e6a5 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 17 Nov 2022 19:58:56 +0100 Subject: [PATCH 02/24] new API Call and Data --- OpenWeatherMapAPI.pm | 97 +-- fhem-2022-11.log | 1523 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1573 insertions(+), 47 deletions(-) create mode 100644 fhem-2022-11.log diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index b78dde4..7fb5876 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -280,8 +280,8 @@ sub _RetrieveDataFromOpenWeatherMap { timeout => 15, self => $self, endpoint => $self->{endpoint} eq 'none' ? 'weather' - : $self->{endpoint} eq 'forecast' ? 'onecall' - : 'forecast', + : $self->{endpoint} eq 'weather' ? 'onecall' + : 'weather', callback => \&_RetrieveDataFinished, }; @@ -380,7 +380,7 @@ sub _ProcessingRetrieveData { $self->{cached}->{country} = $data->{sys}->{country}; $self->{cached}->{city} = encode_utf8( $data->{name} ); $self->{cached}->{license}{text} = 'none'; - $self->{cached}->{current} = { + $self->{cached}->{current} = { 'temperature' => int( sprintf( "%.1f", ( $data->{main}->{temp} - 273.15 ) ) + 0.5 @@ -435,6 +435,7 @@ sub _ProcessingRetrieveData { ( $data->{wind}->{gust} * 3.6 ) ) + 0.5 ), 'wind_direction' => $data->{wind}->{deg}, + 'rain_1h' => $data->{rain}->{'1h'}, 'cloudCover' => $data->{clouds}->{all}, 'code' => $codes{ $data->{weather}->[0]->{id} }, 'iconAPI' => $data->{weather}->[0]->{icon}, @@ -457,38 +458,38 @@ sub _ProcessingRetrieveData { if ( exists $data->{visibility} ); } - when ('forecast') { - if ( ref( $data->{list} ) eq "ARRAY" - && scalar( @{ $data->{list} } ) > 0 ) + when ('onecall') { + if ( ref( $data->{hourly} ) eq "ARRAY" + && scalar( @{ $data->{hourly} } ) > 0 ) { ## löschen des alten Datensatzes delete $self->{cached}->{forecast}; my $i = 0; - for ( @{ $data->{list} } ) { + for ( @{ $data->{hourly} } ) { push( @{ $self->{cached}->{forecast}->{hourly} }, { 'pubDate' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( - ( $data->{list}->[$i]->{dt} ) - - 3600 + ( $data->{hourly}->[$i]->{dt} ) + - 3600 ) ), 'day_of_week' => strftime( "%a, %H:%M", localtime( - ( $data->{list}->[$i]->{dt} ) - - 3600 + ( $data->{hourly}->[$i]->{dt} ) + - 3600 ) ), 'temperature' => int( sprintf( "%.1f", ( - $data->{list}->[$i]->{main} - ->{temp} - 273.15 + $data->{hourly}->[$i] + ->{main}->{temp} - 273.15 ) ) + 0.5 ), @@ -496,8 +497,8 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{main} - ->{temp} - 273.15 + $data->{hourly}->[$i] + ->{main}->{temp} - 273.15 ) ) + 0.5 ), @@ -505,8 +506,9 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{main} - ->{temp_min} - 273.15 + $data->{hourly}->[$i] + ->{main}->{temp_min} - + 273.15 ) ) + 0.5 ), @@ -514,8 +516,9 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{main} - ->{temp_max} - 273.15 + $data->{hourly}->[$i] + ->{main}->{temp_max} - + 273.15 ) ) + 0.5 ), @@ -523,8 +526,9 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{main} - ->{temp_min} - 273.15 + $data->{hourly}->[$i] + ->{main}->{temp_min} - + 273.15 ) ) + 0.5 ), @@ -532,29 +536,30 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{main} - ->{temp_max} - 273.15 + $data->{hourly}->[$i] + ->{main}->{temp_max} - + 273.15 ) ) + 0.5 ), 'humidity' => - $data->{list}->[$i]->{main} + $data->{hourly}->[$i]->{main} ->{humidity}, 'condition' => encode_utf8( - $data->{list}->[$i]->{weather}->[0] - ->{description} + $data->{hourly}->[$i]->{weather} + ->[0]->{description} ), 'pressure' => int( sprintf( "%.1f", - $data->{list}->[$i]->{main} + $data->{hourly}->[$i]->{main} ->{pressure} ) + 0.5 ), 'wind' => int( sprintf( "%.1f", ( - $data->{list}->[$i]->{wind} - ->{speed} * 3.6 + $data->{hourly}->[$i] + ->{wind}->{speed} * 3.6 ) ) + 0.5 ), @@ -562,8 +567,8 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{wind} - ->{speed} * 3.6 + $data->{hourly}->[$i] + ->{wind}->{speed} * 3.6 ) ) + 0.5 ), @@ -571,37 +576,36 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", ( - $data->{list}->[$i]->{wind} - ->{gust} * 3.6 + $data->{hourly}->[$i] + ->{wind}->{gust} * 3.6 ) ) + 0.5 ), 'cloudCover' => - $data->{list}->[$i]->{clouds}->{all}, + $data->{hourly}->[$i]->{clouds} + ->{all}, 'code' => $codes{ - $data->{list}->[$i]->{weather}->[0] - ->{id} + $data->{hourly}->[$i]->{weather} + ->[0]->{id} }, 'iconAPI' => - $data->{list}->[$i]->{weather}->[0] + $data->{hourly}->[$i]->{weather}->[0] ->{icon}, 'rain1h' => - $data->{list}->[$i]->{rain}->{'1h'}, + $data->{hourly}->[$i]->{rain}->{'1h'}, 'rain3h' => - $data->{list}->[$i]->{rain}->{'3h'}, + $data->{hourly}->[$i]->{rain}->{'3h'}, 'snow1h' => - $data->{list}->[$i]->{snow}->{'1h'}, + $data->{hourly}->[$i]->{snow}->{'1h'}, 'snow3h' => - $data->{list}->[$i]->{snow}->{'3h'}, + $data->{hourly}->[$i]->{snow}->{'3h'}, }, ); $i++; } } - } - when ('onecall') { if ( ref( $data->{daily} ) eq "ARRAY" && scalar( @{ $data->{daily} } ) > 0 ) { @@ -789,8 +793,7 @@ sub _ProcessingRetrieveData { $self->{endpoint} = 'none' if ( $self->{endpoint} eq 'onecall' ); _RetrieveDataFromOpenWeatherMap($self) - if ( $self->{endpoint} eq 'weather' - || $self->{endpoint} eq 'forecast' ); + if ( $self->{endpoint} eq 'weather' ); _CallWeatherCallbackFn($self) if ( $self->{endpoint} eq 'none' ); @@ -824,8 +827,8 @@ sub _CreateForecastRef { my $forecastRef = ( { - lat => $self->{lat}, - long => $self->{long}, + lat => $self->{lat}, + long => $self->{long}, apiMaintainer => 'Marko Oldenburg (CoolTux)', apiVersion => diff --git a/fhem-2022-11.log b/fhem-2022-11.log new file mode 100644 index 0000000..3dba360 --- /dev/null +++ b/fhem-2022-11.log @@ -0,0 +1,1523 @@ +!!!DEBUG!!! Endpoint ist: weather!!!DEBUG!!! Response DATA: $VAR1 = { + 'id' => 2935132, + 'main' => { + 'grnd_level' => 996, + 'temp_min' => '275.94', + 'temp' => '276.64', + 'feels_like' => '271.67', + 'pressure' => 1002, + 'temp_max' => '277.57', + 'humidity' => 90, + 'sea_level' => 1002 + }, + 'cod' => 200, + 'coord' => { + 'lat' => '52.3901', + 'lon' => '13.1968' + }, + 'timezone' => 3600, + 'weather' => [ + { + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804, + 'icon' => '04n' + } + ], + 'wind' => { + 'gust' => '12.2', + 'speed' => '7.14', + 'deg' => 103 + }, + 'name' => 'Dreilinden', + 'clouds' => { + 'all' => 100 + }, + 'sys' => { + 'id' => 2038087, + 'sunrise' => 1668666709, + 'sunset' => 1668697964, + 'type' => 2, + 'country' => 'DE' + }, + 'dt' => 1668708257, + 'visibility' => 10000, + 'base' => 'stations' + }; + +!!!DEBUG!!! Endpoint ist: onecall!!!DEBUG!!! Response DATA: $VAR1 = { + 'timezone' => 'Europe/Berlin', + 'timezone_offset' => 3600, + 'daily' => [ + { + 'sunset' => 1668697964, + 'moonrise' => 0, + 'humidity' => 76, + 'clouds' => 100, + 'wind_gust' => '13.89', + 'pressure' => 1003, + 'dt' => 1668679200, + 'moonset' => 1668690780, + 'feels_like' => { + 'morn' => '272.6', + 'eve' => '272.24', + 'night' => '271.21', + 'day' => '273.02' + }, + 'dew_point' => '274.06', + 'sunrise' => 1668666709, + 'weather' => [ + { + 'main' => 'Rain', + 'description' => 'Leichter Regen', + 'id' => 500, + 'icon' => '10d' + } + ], + 'rain' => '1.54', + 'temp' => { + 'morn' => '277.24', + 'eve' => '277.13', + 'max' => '278.68', + 'night' => '275.86', + 'min' => '275.86', + 'day' => '277.91' + }, + 'uvi' => '0.61', + 'wind_speed' => '7.97', + 'moon_phase' => '0.78', + 'pop' => '0.73', + 'wind_deg' => 109 + }, + { + 'moon_phase' => '0.81', + 'uvi' => '0.57', + 'wind_speed' => '6.3', + 'temp' => { + 'night' => '271.21', + 'eve' => '274.04', + 'morn' => '274.75', + 'max' => '276.16', + 'day' => '274.87', + 'min' => '271.21' + }, + 'wind_deg' => 80, + 'pop' => '0.64', + 'dew_point' => '266.1', + 'sunrise' => 1668753214, + 'feels_like' => { + 'day' => '269.75', + 'night' => '268.07', + 'eve' => '269.54', + 'morn' => '269.65' + }, + 'moonset' => 1668777900, + 'weather' => [ + { + 'icon' => '04d', + 'id' => 804, + 'main' => 'Clouds', + 'description' => 'Bedeckt' + } + ], + 'wind_gust' => 13, + 'clouds' => 88, + 'pressure' => 1008, + 'dt' => 1668765600, + 'sunset' => 1668784284, + 'humidity' => 52, + 'moonrise' => 1668726960 + }, + { + 'dew_point' => '266.22', + 'sunrise' => 1668839718, + 'feels_like' => { + 'eve' => '269.78', + 'morn' => '267.78', + 'day' => '273.08', + 'night' => '267.39' + }, + 'moonset' => 1668865020, + 'weather' => [ + { + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804, + 'icon' => '04d' + } + ], + 'moon_phase' => '0.84', + 'wind_speed' => '2.23', + 'uvi' => '0.68', + 'temp' => { + 'min' => '270.22', + 'day' => '273.08', + 'max' => '274.08', + 'morn' => '270.31', + 'eve' => '271.57', + 'night' => '270.35' + }, + 'wind_deg' => 83, + 'pop' => 0, + 'sunset' => 1668870607, + 'humidity' => 60, + 'moonrise' => 1668817920, + 'wind_gust' => '3.93', + 'clouds' => 98, + 'pressure' => 1015, + 'dt' => 1668852000 + }, + { + 'wind_deg' => 163, + 'pop' => 0, + 'moon_phase' => '0.87', + 'wind_speed' => '3.12', + 'uvi' => '0.63', + 'temp' => { + 'night' => '273.94', + 'eve' => '275.16', + 'morn' => '269.75', + 'max' => '275.87', + 'day' => '272.53', + 'min' => '269.57' + }, + 'weather' => [ + { + 'id' => 800, + 'description' => 'Klarer Himmel', + 'main' => 'Clear', + 'icon' => '01d' + } + ], + 'dew_point' => '267.18', + 'sunrise' => 1668926222, + 'feels_like' => { + 'night' => '270.5', + 'day' => '269.52', + 'morn' => '266.49', + 'eve' => '272.45' + }, + 'moonset' => 1668952080, + 'dt' => 1668938400, + 'clouds' => 8, + 'pressure' => 1010, + 'wind_gust' => '7.87', + 'humidity' => 67, + 'moonrise' => 1668908880, + 'sunset' => 1668956932 + }, + { + 'sunrise' => 1669012725, + 'dew_point' => '275.1', + 'moonset' => 1669039200, + 'feels_like' => { + 'day' => '274.08', + 'night' => '271.6', + 'eve' => '274.02', + 'morn' => '271.39' + }, + 'weather' => [ + { + 'id' => 804, + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'icon' => '04d' + } + ], + 'moon_phase' => '0.91', + 'temp' => { + 'day' => '276.85', + 'min' => '274.31', + 'night' => '274.53', + 'max' => '277.63', + 'morn' => '274.31', + 'eve' => '276.43' + }, + 'wind_speed' => '3.04', + 'uvi' => '0.58', + 'pop' => '0.02', + 'wind_deg' => 171, + 'sunset' => 1669043260, + 'moonrise' => 1669000020, + 'humidity' => 88, + 'clouds' => 100, + 'pressure' => 1009, + 'wind_gust' => '9.03', + 'dt' => 1669024800 + }, + { + 'sunset' => 1669129590, + 'humidity' => 82, + 'moonrise' => 1669091460, + 'clouds' => 48, + 'wind_gust' => '7.23', + 'pressure' => 1005, + 'dt' => 1669111200, + 'dew_point' => '272.56', + 'sunrise' => 1669099226, + 'feels_like' => { + 'eve' => '273.5', + 'morn' => '270.61', + 'day' => '271.81', + 'night' => '271.49' + }, + 'moonset' => 1669126500, + 'weather' => [ + { + 'id' => 802, + 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt", + 'main' => 'Clouds', + 'icon' => '03d' + } + ], + 'moon_phase' => '0.94', + 'wind_speed' => '3.77', + 'uvi' => '0.55', + 'temp' => { + 'day' => '275.39', + 'min' => '273.06', + 'night' => '274.03', + 'morn' => '273.73', + 'eve' => '276.24', + 'max' => '278.19' + }, + 'wind_deg' => 110, + 'pop' => 0 + }, + { + 'wind_gust' => '3.49', + 'clouds' => 100, + 'pressure' => 1007, + 'dt' => 1669197600, + 'sunset' => 1669215923, + 'humidity' => 78, + 'moonrise' => 1669183200, + 'moon_phase' => 0, + 'wind_speed' => '2.07', + 'uvi' => 1, + 'temp' => { + 'morn' => '273.32', + 'eve' => '275.73', + 'max' => '276.61', + 'night' => '274.34', + 'min' => '273.32', + 'day' => '274.89' + }, + 'wind_deg' => 103, + 'pop' => '0.02', + 'dew_point' => '271.43', + 'sunrise' => 1669185726, + 'feels_like' => { + 'night' => '274.34', + 'day' => '272.93', + 'eve' => '275.73', + 'morn' => '270.81' + }, + 'moonset' => 1669214100, + 'weather' => [ + { + 'icon' => '04d', + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804 + } + ] + }, + { + 'moonrise' => 1669275060, + 'humidity' => 79, + 'sunset' => 1669302259, + 'dt' => 1669284000, + 'pressure' => 1013, + 'clouds' => 73, + 'wind_gust' => '2.19', + 'weather' => [ + { + 'icon' => '04d', + 'id' => 803, + 'main' => 'Clouds', + 'description' => "\x{dc}berwiegend bew\x{f6}lkt" + } + ], + 'moonset' => 1669302240, + 'feels_like' => { + 'eve' => '275.96', + 'morn' => '274.91', + 'night' => '271.47', + 'day' => '274.51' + }, + 'dew_point' => '272.35', + 'sunrise' => 1669272225, + 'pop' => 0, + 'wind_deg' => 137, + 'temp' => { + 'night' => '273.77', + 'morn' => '274.91', + 'eve' => '275.96', + 'max' => '278.22', + 'day' => '275.67', + 'min' => '273.62' + }, + 'uvi' => 1, + 'wind_speed' => '1.96', + 'moon_phase' => '0.02' + } + ], + 'lon' => '13.1968', + 'hourly' => [ + { + 'feels_like' => '271.67', + 'dew_point' => '275.16', + 'weather' => [ + { + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804, + 'icon' => '04n' + } + ], + 'wind_speed' => '7.14', + 'uvi' => 0, + 'temp' => '276.64', + 'wind_deg' => 103, + 'pop' => '0.42', + 'humidity' => 90, + 'wind_gust' => '12.2', + 'clouds' => 100, + 'pressure' => 1002, + 'visibility' => 10000, + 'dt' => 1668708000 + }, + { + 'feels_like' => '271.74', + 'dew_point' => '274.82', + 'rain' => { + '1h' => '0.42' + }, + 'weather' => [ + { + 'icon' => '10n', + 'id' => 500, + 'description' => 'Leichter Regen', + 'main' => 'Rain' + } + ], + 'uvi' => 0, + 'wind_speed' => '6.88', + 'temp' => '276.62', + 'wind_deg' => 100, + 'pop' => '0.61', + 'humidity' => 88, + 'pressure' => 1002, + 'wind_gust' => '12.71', + 'clouds' => 100, + 'visibility' => 10000, + 'dt' => 1668711600 + }, + { + 'feels_like' => '271.59', + 'dew_point' => '274.76', + 'rain' => { + '1h' => '0.75' + }, + 'weather' => [ + { + 'icon' => '10n', + 'id' => 500, + 'description' => 'Leichter Regen', + 'main' => 'Rain' + } + ], + 'temp' => '276.4', + 'uvi' => 0, + 'wind_speed' => '6.56', + 'pop' => '0.66', + 'wind_deg' => 98, + 'humidity' => 89, + 'wind_gust' => '12.75', + 'clouds' => 100, + 'pressure' => 1002, + 'visibility' => 10000, + 'dt' => 1668715200 + }, + { + 'pop' => '0.73', + 'wind_deg' => 97, + 'temp' => '276.07', + 'uvi' => 0, + 'wind_speed' => '6.28', + 'weather' => [ + { + 'main' => 'Rain', + 'description' => 'Leichter Regen', + 'id' => 500, + 'icon' => '10n' + } + ], + 'rain' => { + '1h' => '0.22' + }, + 'feels_like' => '271.28', + 'dew_point' => '274.9', + 'visibility' => 7053, + 'dt' => 1668718800, + 'clouds' => 100, + 'wind_gust' => '12.69', + 'pressure' => 1003, + 'humidity' => 92 + }, + { + 'pop' => '0.72', + 'wind_deg' => 94, + 'temp' => '275.86', + 'uvi' => 0, + 'wind_speed' => '5.85', + 'rain' => { + '1h' => '0.15' + }, + 'weather' => [ + { + 'icon' => '10n', + 'description' => 'Leichter Regen', + 'main' => 'Rain', + 'id' => 500 + } + ], + 'feels_like' => '271.21', + 'dew_point' => '274.39', + 'visibility' => 10000, + 'dt' => 1668722400, + 'wind_gust' => '12.01', + 'clouds' => 100, + 'pressure' => 1003, + 'humidity' => 90 + }, + { + 'dew_point' => '273.5', + 'feels_like' => '271.19', + 'weather' => [ + { + 'id' => 804, + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'icon' => '04n' + } + ], + 'wind_speed' => '5.62', + 'uvi' => 0, + 'temp' => '275.76', + 'wind_deg' => 93, + 'pop' => '0.64', + 'humidity' => 85, + 'wind_gust' => '11.28', + 'clouds' => 100, + 'pressure' => 1003, + 'dt' => 1668726000, + 'visibility' => 10000 + }, + { + 'weather' => [ + { + 'icon' => '04n', + 'id' => 804, + 'main' => 'Clouds', + 'description' => 'Bedeckt' + } + ], + 'feels_like' => '271.25', + 'dew_point' => '272.46', + 'pop' => '0.6', + 'wind_deg' => 89, + 'temp' => '275.81', + 'wind_speed' => '5.63', + 'uvi' => 0, + 'humidity' => 79, + 'visibility' => 10000, + 'dt' => 1668729600, + 'wind_gust' => '10.97', + 'pressure' => 1003, + 'clouds' => 100 + }, + { + 'clouds' => 100, + 'wind_gust' => '11.12', + 'pressure' => 1003, + 'visibility' => 10000, + 'dt' => 1668733200, + 'humidity' => 74, + 'wind_speed' => '5.94', + 'uvi' => 0, + 'temp' => '275.68', + 'wind_deg' => 84, + 'pop' => 0, + 'feels_like' => '270.94', + 'dew_point' => '271.41', + 'weather' => [ + { + 'icon' => '04n', + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804 + } + ] + }, + { + 'humidity' => 72, + 'wind_gust' => '10.72', + 'clouds' => 100, + 'pressure' => 1004, + 'dt' => 1668736800, + 'visibility' => 10000, + 'dew_point' => '270.95', + 'feels_like' => '271.04', + 'weather' => [ + { + 'icon' => '04n', + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804 + } + ], + 'temp' => '275.45', + 'wind_speed' => '5.14', + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 89 + }, + { + 'weather' => [ + { + 'id' => 804, + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'icon' => '04n' + } + ], + 'feels_like' => '270.61', + 'dew_point' => '270.75', + 'wind_deg' => 84, + 'pop' => 0, + 'uvi' => 0, + 'wind_speed' => '5.34', + 'temp' => '275.19', + 'humidity' => 73, + 'visibility' => 10000, + 'dt' => 1668740400, + 'clouds' => 100, + 'wind_gust' => '10.26', + 'pressure' => 1004 + }, + { + 'pop' => 0, + 'wind_deg' => 77, + 'temp' => '274.75', + 'uvi' => 0, + 'wind_speed' => '6.17', + 'weather' => [ + { + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804, + 'icon' => '04n' + } + ], + 'dew_point' => '270.39', + 'feels_like' => '269.65', + 'dt' => 1668744000, + 'visibility' => 10000, + 'clouds' => 100, + 'wind_gust' => '11.93', + 'pressure' => 1004, + 'humidity' => 73 + }, + { + 'wind_deg' => 80, + 'pop' => 0, + 'uvi' => 0, + 'wind_speed' => '6.09', + 'temp' => '274.27', + 'weather' => [ + { + 'icon' => '04n', + 'id' => 804, + 'description' => 'Bedeckt', + 'main' => 'Clouds' + } + ], + 'dew_point' => '269.84', + 'feels_like' => '269.08', + 'dt' => 1668747600, + 'visibility' => 10000, + 'wind_gust' => '12.46', + 'clouds' => 100, + 'pressure' => 1005, + 'humidity' => 72 + }, + { + 'humidity' => 72, + 'clouds' => 100, + 'wind_gust' => '12.57', + 'pressure' => 1005, + 'visibility' => 10000, + 'dt' => 1668751200, + 'feels_like' => '268.51', + 'dew_point' => '269.29', + 'weather' => [ + { + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804, + 'icon' => '04n' + } + ], + 'wind_speed' => '6.08', + 'uvi' => 0, + 'temp' => '273.82', + 'wind_deg' => 80, + 'pop' => 0 + }, + { + 'humidity' => 69, + 'clouds' => 100, + 'wind_gust' => 13, + 'pressure' => 1006, + 'dt' => 1668754800, + 'visibility' => 10000, + 'dew_point' => '268.4', + 'feels_like' => '268.05', + 'weather' => [ + { + 'icon' => '04d', + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804 + } + ], + 'wind_speed' => '6.12', + 'uvi' => 0, + 'temp' => '273.47', + 'wind_deg' => 79, + 'pop' => 0 + }, + { + 'dew_point' => '267.74', + 'feels_like' => '268.16', + 'weather' => [ + { + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804, + 'icon' => '04d' + } + ], + 'temp' => '273.49', + 'wind_speed' => '5.94', + 'uvi' => '0.06', + 'pop' => 0, + 'wind_deg' => 80, + 'humidity' => 65, + 'clouds' => 99, + 'wind_gust' => '12.76', + 'pressure' => 1007, + 'dt' => 1668758400, + 'visibility' => 10000 + }, + { + 'wind_deg' => 80, + 'pop' => 0, + 'wind_speed' => '6.08', + 'uvi' => '0.16', + 'temp' => '274.01', + 'weather' => [ + { + 'icon' => '04d', + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804 + } + ], + 'dew_point' => '266.97', + 'feels_like' => '268.75', + 'dt' => 1668762000, + 'visibility' => 10000, + 'clouds' => 98, + 'wind_gust' => '12.35', + 'pressure' => 1008, + 'humidity' => 59 + }, + { + 'wind_deg' => 82, + 'pop' => 0, + 'uvi' => '0.47', + 'wind_speed' => '6.28', + 'temp' => '274.87', + 'weather' => [ + { + 'icon' => '04d', + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804 + } + ], + 'feels_like' => '269.75', + 'dew_point' => '266.1', + 'visibility' => 10000, + 'dt' => 1668765600, + 'pressure' => 1008, + 'wind_gust' => '10.99', + 'clouds' => 88, + 'humidity' => 52 + }, + { + 'weather' => [ + { + 'icon' => '04d', + 'main' => 'Clouds', + 'description' => "\x{dc}berwiegend bew\x{f6}lkt", + 'id' => 803 + } + ], + 'feels_like' => '270.78', + 'dew_point' => '265.68', + 'pop' => 0, + 'wind_deg' => 80, + 'temp' => '275.68', + 'uvi' => '0.57', + 'wind_speed' => '6.3', + 'humidity' => 48, + 'visibility' => 10000, + 'dt' => 1668769200, + 'wind_gust' => '9.62', + 'clouds' => 75, + 'pressure' => 1008 + }, + { + 'weather' => [ + { + 'id' => 803, + 'main' => 'Clouds', + 'description' => "\x{dc}berwiegend bew\x{f6}lkt", + 'icon' => '04d' + } + ], + 'feels_like' => '271.45', + 'dew_point' => '265.86', + 'pop' => 0, + 'wind_deg' => 79, + 'temp' => '276.11', + 'uvi' => '0.51', + 'wind_speed' => '6.01', + 'humidity' => 47, + 'visibility' => 10000, + 'dt' => 1668772800, + 'clouds' => 63, + 'wind_gust' => '8.61', + 'pressure' => 1009 + }, + { + 'visibility' => 10000, + 'dt' => 1668776400, + 'wind_gust' => '7.9', + 'clouds' => 2, + 'pressure' => 1009, + 'humidity' => 48, + 'pop' => 0, + 'wind_deg' => 78, + 'temp' => '276.16', + 'uvi' => '0.44', + 'wind_speed' => '5.55', + 'weather' => [ + { + 'main' => 'Clear', + 'description' => 'Klarer Himmel', + 'id' => 800, + 'icon' => '01d' + } + ], + 'feels_like' => '271.73', + 'dew_point' => '266.24' + }, + { + 'weather' => [ + { + 'icon' => '01d', + 'main' => 'Clear', + 'description' => 'Klarer Himmel', + 'id' => 800 + } + ], + 'feels_like' => '271.33', + 'dew_point' => '266.57', + 'pop' => 0, + 'wind_deg' => 76, + 'temp' => '275.71', + 'uvi' => '0.21', + 'wind_speed' => '5.21', + 'humidity' => 51, + 'visibility' => 10000, + 'dt' => 1668780000, + 'clouds' => 4, + 'wind_gust' => '8.05', + 'pressure' => 1009 + }, + { + 'weather' => [ + { + 'main' => 'Clear', + 'description' => 'Klarer Himmel', + 'id' => 800, + 'icon' => '01d' + } + ], + 'dew_point' => '266.69', + 'feels_like' => '270.22', + 'pop' => 0, + 'wind_deg' => 72, + 'temp' => '274.72', + 'uvi' => 0, + 'wind_speed' => '4.96', + 'humidity' => 55, + 'dt' => 1668783600, + 'visibility' => 10000, + 'wind_gust' => '9.04', + 'clouds' => 4, + 'pressure' => 1010 + }, + { + 'humidity' => 59, + 'pressure' => 1011, + 'wind_gust' => '9.44', + 'clouds' => 4, + 'visibility' => 10000, + 'dt' => 1668787200, + 'feels_like' => '269.54', + 'dew_point' => '266.84', + 'weather' => [ + { + 'icon' => '01n', + 'main' => 'Clear', + 'description' => 'Klarer Himmel', + 'id' => 800 + } + ], + 'wind_speed' => '4.66', + 'uvi' => 0, + 'temp' => '274.04', + 'wind_deg' => 71, + 'pop' => 0 + }, + { + 'humidity' => 61, + 'wind_gust' => '9.44', + 'clouds' => 4, + 'pressure' => 1011, + 'dt' => 1668790800, + 'visibility' => 10000, + 'dew_point' => '267.02', + 'feels_like' => '269.16', + 'weather' => [ + { + 'icon' => '01n', + 'id' => 800, + 'description' => 'Klarer Himmel', + 'main' => 'Clear' + } + ], + 'temp' => '273.61', + 'wind_speed' => '4.42', + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 69 + }, + { + 'wind_deg' => 70, + 'pop' => 0, + 'uvi' => 0, + 'wind_speed' => '4.1', + 'temp' => '273.15', + 'weather' => [ + { + 'main' => 'Clear', + 'description' => 'Klarer Himmel', + 'id' => 800, + 'icon' => '01n' + } + ], + 'dew_point' => '267.15', + 'feels_like' => '268.8', + 'dt' => 1668794400, + 'visibility' => 10000, + 'wind_gust' => '9.24', + 'clouds' => 4, + 'pressure' => 1012, + 'humidity' => 64 + }, + { + 'pop' => 0, + 'wind_deg' => 73, + 'temp' => '272.53', + 'wind_speed' => '3.54', + 'uvi' => 0, + 'weather' => [ + { + 'icon' => '01n', + 'id' => 800, + 'description' => 'Klarer Himmel', + 'main' => 'Clear' + } + ], + 'feels_like' => '268.45', + 'dew_point' => '267.17', + 'visibility' => 10000, + 'dt' => 1668798000, + 'clouds' => 5, + 'wind_gust' => '8.35', + 'pressure' => 1012, + 'humidity' => 67 + }, + { + 'weather' => [ + { + 'description' => 'Klarer Himmel', + 'main' => 'Clear', + 'id' => 800, + 'icon' => '01n' + } + ], + 'dew_point' => '267.15', + 'feels_like' => '268.23', + 'wind_deg' => 73, + 'pop' => 0, + 'uvi' => 0, + 'wind_speed' => '2.94', + 'temp' => '271.92', + 'humidity' => 70, + 'dt' => 1668801600, + 'visibility' => 10000, + 'clouds' => 3, + 'wind_gust' => '7.03', + 'pressure' => 1013 + }, + { + 'wind_speed' => '2.48', + 'uvi' => 0, + 'temp' => '271.4', + 'wind_deg' => 76, + 'pop' => 0, + 'feels_like' => '268.08', + 'dew_point' => '267.11', + 'weather' => [ + { + 'icon' => '01n', + 'id' => 800, + 'description' => 'Klarer Himmel', + 'main' => 'Clear' + } + ], + 'clouds' => 4, + 'wind_gust' => '5.24', + 'pressure' => 1013, + 'visibility' => 10000, + 'dt' => 1668805200, + 'humidity' => 73 + }, + { + 'humidity' => 74, + 'wind_gust' => '4.22', + 'clouds' => 22, + 'pressure' => 1013, + 'visibility' => 10000, + 'dt' => 1668808800, + 'feels_like' => '268.07', + 'dew_point' => '267.07', + 'weather' => [ + { + 'id' => 801, + 'description' => 'Ein paar Wolken', + 'main' => 'Clouds', + 'icon' => '02n' + } + ], + 'uvi' => 0, + 'wind_speed' => '2.29', + 'temp' => '271.21', + 'wind_deg' => 82, + 'pop' => 0 + }, + { + 'dew_point' => '267.06', + 'feels_like' => '268.01', + 'weather' => [ + { + 'icon' => '03n', + 'id' => 802, + 'main' => 'Clouds', + 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt" + } + ], + 'temp' => '271.1', + 'wind_speed' => '2.23', + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 83, + 'humidity' => 74, + 'wind_gust' => '3.93', + 'pressure' => 1014, + 'clouds' => 27, + 'dt' => 1668812400, + 'visibility' => 10000 + }, + { + 'humidity' => 76, + 'clouds' => 24, + 'wind_gust' => '3.47', + 'pressure' => 1014, + 'visibility' => 10000, + 'dt' => 1668816000, + 'feels_like' => '267.7', + 'dew_point' => '267.03', + 'weather' => [ + { + 'id' => 801, + 'main' => 'Clouds', + 'description' => 'Ein paar Wolken', + 'icon' => '02n' + } + ], + 'wind_speed' => '2.22', + 'uvi' => 0, + 'temp' => '270.82', + 'wind_deg' => 77, + 'pop' => 0 + }, + { + 'feels_like' => '267.7', + 'dew_point' => '266.98', + 'weather' => [ + { + 'description' => 'Klarer Himmel', + 'main' => 'Clear', + 'id' => 800, + 'icon' => '01n' + } + ], + 'temp' => '270.64', + 'wind_speed' => '2.05', + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 77, + 'humidity' => 76, + 'clouds' => 5, + 'wind_gust' => '3.07', + 'pressure' => 1015, + 'visibility' => 10000, + 'dt' => 1668819600 + }, + { + 'clouds' => 5, + 'wind_gust' => '2.83', + 'pressure' => 1015, + 'dt' => 1668823200, + 'visibility' => 10000, + 'humidity' => 77, + 'temp' => '270.51', + 'wind_speed' => 2, + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 76, + 'dew_point' => '266.93', + 'feels_like' => '267.62', + 'weather' => [ + { + 'icon' => '01n', + 'id' => 800, + 'description' => 'Klarer Himmel', + 'main' => 'Clear' + } + ] + }, + { + 'dew_point' => '266.86', + 'feels_like' => '267.66', + 'weather' => [ + { + 'id' => 800, + 'main' => 'Clear', + 'description' => 'Klarer Himmel', + 'icon' => '01n' + } + ], + 'wind_speed' => '1.88', + 'uvi' => 0, + 'temp' => '270.4', + 'wind_deg' => 83, + 'pop' => 0, + 'humidity' => 77, + 'wind_gust' => '2.57', + 'pressure' => 1015, + 'clouds' => 5, + 'dt' => 1668826800, + 'visibility' => 10000 + }, + { + 'humidity' => 77, + 'visibility' => 10000, + 'dt' => 1668830400, + 'wind_gust' => '2.32', + 'pressure' => 1015, + 'clouds' => 12, + 'weather' => [ + { + 'description' => 'Ein paar Wolken', + 'main' => 'Clouds', + 'id' => 801, + 'icon' => '02n' + } + ], + 'feels_like' => '267.78', + 'dew_point' => '266.78', + 'pop' => 0, + 'wind_deg' => 79, + 'temp' => '270.31', + 'uvi' => 0, + 'wind_speed' => '1.73' + }, + { + 'humidity' => 76, + 'visibility' => 10000, + 'dt' => 1668834000, + 'clouds' => 30, + 'wind_gust' => '2.17', + 'pressure' => 1015, + 'weather' => [ + { + 'id' => 802, + 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt", + 'main' => 'Clouds', + 'icon' => '03n' + } + ], + 'feels_like' => '268.16', + 'dew_point' => '266.72', + 'wind_deg' => 100, + 'pop' => 0, + 'uvi' => 0, + 'wind_speed' => '1.58', + 'temp' => '270.43' + }, + { + 'temp' => '270.22', + 'uvi' => 0, + 'wind_speed' => '1.41', + 'pop' => 0, + 'wind_deg' => 91, + 'dew_point' => '266.64', + 'feels_like' => '268.22', + 'weather' => [ + { + 'id' => 802, + 'main' => 'Clouds', + 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt", + 'icon' => '03n' + } + ], + 'clouds' => 41, + 'wind_gust' => '1.88', + 'pressure' => 1015, + 'dt' => 1668837600, + 'visibility' => 10000, + 'humidity' => 77 + }, + { + 'wind_speed' => '1.24', + 'uvi' => 0, + 'temp' => '270.4', + 'wind_deg' => 106, + 'pop' => 0, + 'feels_like' => '270.4', + 'dew_point' => '266.59', + 'weather' => [ + { + 'icon' => '04d', + 'id' => 804, + 'main' => 'Clouds', + 'description' => 'Bedeckt' + } + ], + 'wind_gust' => '1.75', + 'clouds' => 100, + 'pressure' => 1015, + 'visibility' => 10000, + 'dt' => 1668841200, + 'humidity' => 76 + }, + { + 'visibility' => 10000, + 'dt' => 1668844800, + 'wind_gust' => '1.9', + 'clouds' => 100, + 'pressure' => 1015, + 'humidity' => 71, + 'wind_deg' => 130, + 'pop' => 0, + 'uvi' => '0.15', + 'wind_speed' => '1.18', + 'temp' => '271.29', + 'weather' => [ + { + 'icon' => '04d', + 'id' => 804, + 'description' => 'Bedeckt', + 'main' => 'Clouds' + } + ], + 'feels_like' => '271.29', + 'dew_point' => '266.61' + }, + { + 'humidity' => 65, + 'dt' => 1668848400, + 'visibility' => 10000, + 'clouds' => 97, + 'wind_gust' => '1.69', + 'pressure' => 1015, + 'weather' => [ + { + 'icon' => '04d', + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804 + } + ], + 'dew_point' => '266.47', + 'feels_like' => '272.33', + 'pop' => 0, + 'wind_deg' => 134, + 'temp' => '272.33', + 'uvi' => '0.36', + 'wind_speed' => '1.25' + }, + { + 'clouds' => 98, + 'wind_gust' => '1.48', + 'pressure' => 1015, + 'visibility' => 10000, + 'dt' => 1668852000, + 'humidity' => 60, + 'temp' => '273.08', + 'wind_speed' => '1.18', + 'uvi' => '0.57', + 'pop' => 0, + 'wind_deg' => 140, + 'feels_like' => '273.08', + 'dew_point' => '266.22', + 'weather' => [ + { + 'id' => 804, + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'icon' => '04d' + } + ] + }, + { + 'feels_like' => '273.67', + 'dew_point' => '265.95', + 'weather' => [ + { + 'icon' => '04d', + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804 + } + ], + 'wind_speed' => '1.02', + 'uvi' => '0.68', + 'temp' => '273.67', + 'wind_deg' => 156, + 'pop' => 0, + 'humidity' => 56, + 'wind_gust' => '1.09', + 'clouds' => 98, + 'pressure' => 1015, + 'visibility' => 10000, + 'dt' => 1668855600 + }, + { + 'wind_deg' => 154, + 'pop' => 0, + 'wind_speed' => '0.98', + 'uvi' => '0.62', + 'temp' => '273.98', + 'weather' => [ + { + 'id' => 804, + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'icon' => '04d' + } + ], + 'feels_like' => '273.98', + 'dew_point' => '265.74', + 'visibility' => 10000, + 'dt' => 1668859200, + 'clouds' => 99, + 'wind_gust' => '1.17', + 'pressure' => 1015, + 'humidity' => 54 + }, + { + 'wind_speed' => '1.15', + 'uvi' => '0.41', + 'temp' => '274.08', + 'wind_deg' => 149, + 'pop' => 0, + 'dew_point' => '265.58', + 'feels_like' => '274.08', + 'weather' => [ + { + 'icon' => '04d', + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804 + } + ], + 'wind_gust' => '1.38', + 'clouds' => 100, + 'pressure' => 1014, + 'dt' => 1668862800, + 'visibility' => 10000, + 'humidity' => 53 + }, + { + 'feels_like' => '273.78', + 'dew_point' => '265.76', + 'weather' => [ + { + 'description' => 'Bedeckt', + 'main' => 'Clouds', + 'id' => 804, + 'icon' => '04d' + } + ], + 'temp' => '273.78', + 'uvi' => '0.19', + 'wind_speed' => '1.23', + 'pop' => 0, + 'wind_deg' => 149, + 'humidity' => 55, + 'clouds' => 99, + 'wind_gust' => '1.71', + 'pressure' => 1014, + 'visibility' => 10000, + 'dt' => 1668866400 + }, + { + 'temp' => '272.43', + 'uvi' => 0, + 'wind_speed' => '1.43', + 'pop' => 0, + 'wind_deg' => 153, + 'dew_point' => '265.65', + 'feels_like' => '270.69', + 'weather' => [ + { + 'main' => 'Clouds', + 'description' => 'Bedeckt', + 'id' => 804, + 'icon' => '04d' + } + ], + 'pressure' => 1014, + 'wind_gust' => '2.08', + 'clouds' => 86, + 'dt' => 1668870000, + 'visibility' => 10000, + 'humidity' => 60 + }, + { + 'wind_gust' => '1.95', + 'clouds' => 68, + 'pressure' => 1014, + 'dt' => 1668873600, + 'visibility' => 10000, + 'humidity' => 64, + 'temp' => '271.57', + 'wind_speed' => '1.39', + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 159, + 'dew_point' => '265.63', + 'feels_like' => '269.78', + 'weather' => [ + { + 'icon' => '04n', + 'id' => 803, + 'description' => "\x{dc}berwiegend bew\x{f6}lkt", + 'main' => 'Clouds' + } + ] + }, + { + 'feels_like' => '269.44', + 'dew_point' => '265.63', + 'weather' => [ + { + 'main' => 'Clouds', + 'description' => "\x{dc}berwiegend bew\x{f6}lkt", + 'id' => 803, + 'icon' => '04n' + } + ], + 'temp' => '271.2', + 'wind_speed' => '1.35', + 'uvi' => 0, + 'pop' => 0, + 'wind_deg' => 154, + 'humidity' => 66, + 'clouds' => 55, + 'wind_gust' => '1.89', + 'pressure' => 1014, + 'visibility' => 10000, + 'dt' => 1668877200 + } + ], + 'current' => { + 'wind_deg' => 103, + 'temp' => '276.64', + 'uvi' => 0, + 'wind_speed' => '7.14', + 'weather' => [ + { + 'icon' => '04n', + 'id' => 804, + 'main' => 'Clouds', + 'description' => 'Bedeckt' + } + ], + 'feels_like' => '271.67', + 'dew_point' => '275.16', + 'sunrise' => 1668666709, + 'visibility' => 10000, + 'dt' => 1668708257, + 'wind_gust' => '12.2', + 'pressure' => 1002, + 'clouds' => 100, + 'humidity' => 90, + 'sunset' => 1668697964 + }, + 'lat' => '52.3901', + 'alerts' => [ + { + 'tags' => [ + 'Extreme temperature value' + ], + 'end' => 1668769200, + 'description' => "There is a risk of frost (Level 1 of 2). +Minimum temperature: 0 - -3 \x{b0}C", + 'sender_name' => 'Deutscher Wetterdienst', + 'start' => 1668718800, + 'event' => 'frost' + } + ] + }; \ No newline at end of file -- 2.47.2 From ddefbac55a5c4e1c3f30fb3c9c453a785724c8ff Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sun, 20 Nov 2022 21:10:22 +0100 Subject: [PATCH 03/24] new OpenWeatherMapAPI onecall v3 Support --- 59_Weather.pm | 49 +- OpenWeatherMapAPI.pm | 190 ++++-- fhem-2022-11.log | 1523 ------------------------------------------ 3 files changed, 170 insertions(+), 1592 deletions(-) delete mode 100644 fhem-2022-11.log diff --git a/59_Weather.pm b/59_Weather.pm index ab2e6f4..c8c6731 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -269,8 +269,9 @@ sub Weather_Initialize($) { $hash->{SetFn} = 'Weather_Set'; $hash->{AttrList} = 'disable:0,1 ' - . 'forecast:hourly,daily,every,off ' + . 'forecast:multiple-strict,hourly,daily ' . 'forecastLimit ' + . 'alerts:0,1 ' . $readingFnAttributes; $hash->{NotifyFn} = 'Weather_Notify'; @@ -328,7 +329,12 @@ sub Weather_RetrieveCallbackFn($) { sub Weather_WriteReadings($$) { my ( $hash, $dataRef ) = @_; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; + my $hourly = ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1: 0 ); + my $daily = ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); + my $alerts = ( AttrVal( $name, 'forecast', '' ) =~ m{alerts}xms ? 1 : 0 ); + + readingsBeginUpdate($hash); # delete some unused readings @@ -381,16 +387,15 @@ sub Weather_WriteReadings($$) { } ### forecast - if ( ref( $dataRef->{forecast} ) eq 'HASH' - and AttrVal( $name, 'forecast', 'every' ) ne 'off' ) + if ( ref( $dataRef->{forecast} ) eq 'HASH' + and ($hourly or $daily) ) { ## hourly if ( defined( $dataRef->{forecast}->{hourly} ) and ref( $dataRef->{forecast}->{hourly} ) eq 'ARRAY' and scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0 - and ( AttrVal( $name, 'forecast', 'every' ) eq 'every' - or AttrVal( $name, 'forecast', 'hourly' ) eq 'hourly' ) + and $hourly ) { my $i = 0; @@ -445,8 +450,7 @@ sub Weather_WriteReadings($$) { defined( $dataRef->{forecast}->{daily} ) and ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY' and scalar( @{ $dataRef->{forecast}->{daily} } ) > 0 - and ( AttrVal( $name, 'forecast', 'every' ) eq 'every' - or AttrVal( $name, 'forecast', 'daily' ) eq 'daily' ) + and $daily ) { my $i = 0; @@ -496,6 +500,16 @@ sub Weather_WriteReadings($$) { } } + if ( ref( $dataRef->{alerts} ) eq 'HASH' + and $alerts ) + { + while ( my ( $r, $v ) = each %{ $dataRef->{alerts} } ) { + readingsBulkUpdate( $hash, $r, $v ) + if ( ref( $dataRef->{$r} ) ne 'HASH' + and ref( $dataRef->{$r} ) ne 'ARRAY' ); + } + } + my $val = 'T: ' . $dataRef->{current}->{temperature} . ' °C' . ' ' . substr( $status_items_txt_i18n{1}, 0, 1 ) . ': ' @@ -529,7 +543,7 @@ sub Weather_GetUpdate($) { Weather_RearmTimer( $hash, gettimeofday() + $hash->{INTERVAL} ); } else { - # Weather_RetrieveData($name, 0); + $hash->{fhem}->{api}->{exclude} = Weather_parseForcastAttr($hash); $hash->{fhem}->{api}->setRetrieveData; } @@ -537,6 +551,20 @@ sub Weather_GetUpdate($) { } ################################### +sub Weather_parseForcastAttr { + my $hash = shift; + my $name = $hash->{NAME}; + + my @exclude = qw 'alerts minutely hourly daily'; + my @forecast = split(',',AttrVal($name,'forcast','') . (AttrVal($name,'alerts',0) ? ',alerts' : '')); + my %exclude =(); + + @exclude{@exclude} = @exclude; + delete @exclude{@forecast}; + + return join(',',keys %exclude); +} + sub Weather_Get($@) { my ( $hash, @a ) = @_; @@ -712,6 +740,7 @@ sub Weather_Define($$) { location => $hash->{fhem}->{LOCATION}, apioptions => $hash->{APIOPTIONS}, language => $hash->{LANG} + exclude => Weather_parseForcastAttr($hash), } ); @@ -1373,7 +1402,7 @@ sub WeatherCheckOptions($@) { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.1.4", + "version": "v2.2.5", "author": [ "Marko Oldenburg " ], diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 7fb5876..eb9b224 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -118,7 +118,7 @@ eval { use Readonly; 1 } Readonly my $URL => 'https://api.openweathermap.org/data/2.5/'; ## URL . 'weather?' for current data -## URL . 'forecast?' for forecast data +## URL . 'onecall?' for forecast data my %codes = ( 200 => 45, @@ -194,6 +194,7 @@ sub new { long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, endpoint => 'none', + exclude => $argsRef->{exclude}, }; $self->{cachemaxage} = ( @@ -201,8 +202,9 @@ sub new { ? $apioptions->{cachemaxage} : 900 ); - $self->{cached} = _CreateForecastRef($self); + $self->{cached} = _CreateForecastRef($self); + bless $self, $class; return $self; } @@ -315,7 +317,8 @@ sub _RetrieveDataFromOpenWeatherMap { . $self->{long} . '&' . 'APPID=' . $self->{key} . '&' . 'lang=' - . $self->{lang}; + . $self->{lang} . '&' . 'exclude=' + . $self->{exclude}; ::HttpUtils_NonblockingGet($paramRef); } @@ -369,7 +372,8 @@ sub _ProcessingRetrieveData { } else { ### Debug - # print 'Response: ' . Dumper $data; + # print '!!! DEBUG !!! - Endpoint: ' . $self->{endpoint} . "\n"; + # print '!!! DEBUG !!! - Response: ' . Dumper $data; ###### Ab hier wird die ResponseHash Referenze für die Rückgabe zusammen gestellt $self->{cached}->{current_date_time} = strftimeWrapper( "%a, %e %b %Y %H:%M", @@ -489,7 +493,7 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{main}->{temp} - 273.15 + ->{temp} - 273.15 ) ) + 0.5 ), @@ -498,52 +502,12 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{main}->{temp} - 273.15 - ) - ) + 0.5 - ), - 'low_c' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{main}->{temp_min} - - 273.15 - ) - ) + 0.5 - ), - 'high_c' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{main}->{temp_max} - - 273.15 - ) - ) + 0.5 - ), - 'tempLow' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{main}->{temp_min} - - 273.15 - ) - ) + 0.5 - ), - 'tempHigh' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{main}->{temp_max} - - 273.15 + ->{temp} - 273.15 ) ) + 0.5 ), 'humidity' => - $data->{hourly}->[$i]->{main} + $data->{hourly}->[$i] ->{humidity}, 'condition' => encode_utf8( $data->{hourly}->[$i]->{weather} @@ -551,7 +515,7 @@ sub _ProcessingRetrieveData { ), 'pressure' => int( sprintf( "%.1f", - $data->{hourly}->[$i]->{main} + $data->{hourly}->[$i] ->{pressure} ) + 0.5 ), 'wind' => int( @@ -559,7 +523,7 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{wind}->{speed} * 3.6 + ->{wind_speed} * 3.6 ) ) + 0.5 ), @@ -568,7 +532,7 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{wind}->{speed} * 3.6 + ->{wind_speed} * 3.6 ) ) + 0.5 ), @@ -577,13 +541,13 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{wind}->{gust} * 3.6 + ->{wind_gust} * 3.6 ) ) + 0.5 ), 'cloudCover' => - $data->{hourly}->[$i]->{clouds} - ->{all}, + $data->{hourly}->[$i] + ->{clouds}, 'code' => $codes{ $data->{hourly}->[$i]->{weather} ->[0]->{id} @@ -592,13 +556,9 @@ sub _ProcessingRetrieveData { $data->{hourly}->[$i]->{weather}->[0] ->{icon}, 'rain1h' => - $data->{hourly}->[$i]->{rain}->{'1h'}, - 'rain3h' => - $data->{hourly}->[$i]->{rain}->{'3h'}, + $data->{hourly}->[$i]->{rain}->{'1h'}, 'snow1h' => - $data->{hourly}->[$i]->{snow}->{'1h'}, - 'snow3h' => - $data->{hourly}->[$i]->{snow}->{'3h'}, + $data->{hourly}->[$i]->{snow}->{'1h'}, }, ); @@ -673,6 +633,69 @@ sub _ProcessingRetrieveData { ) ) + 0.5 ), + 'temperature_morn' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{morn} - 273.15 + ) + ) + 0.5 + ), + 'temperature_eve' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{eve} - 273.15 + ) + ) + 0.5 + ), + 'temperature_night' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{temp}->{night} - 273.15 + ) + ) + 0.5 + ), + 'tempFeelsLike_morn' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{feels_like}->{morn} - 273.15 + ) + ) + 0.5 + ), + 'tempFeelsLike_eve' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{feels_like}->{eve} - 273.15 + ) + ) + 0.5 + ), + 'tempFeelsLike_night' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{feels_like}->{night} - 273.15 + ) + ) + 0.5 + ), + 'tempFeelsLike_day' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i] + ->{feels_like}->{day} - 273.15 + ) + ) + 0.5 + ), 'temp_c' => int( sprintf( "%.1f", @@ -718,6 +741,14 @@ sub _ProcessingRetrieveData { ) ) + 0.5 ), + 'dew_point' => int( + sprintf( + "%.1f", + ( + $data->{daily}->[$i]->{dew_point} - 273.15 + ) + ) + 0.5 + ), 'humidity' => $data->{daily}->[$i]->{humidity}, 'condition' => encode_utf8( @@ -783,6 +814,47 @@ sub _ProcessingRetrieveData { $i++; } } + + if ( ref( $data->{alerts} ) eq "ARRAY" + && scalar( @{ $data->{alerts} } ) > 0 ) + { + ## löschen des alten Datensatzes + delete $self->{cached}->{alerts}; + + my $i = 0; + for ( @{ $data->{alerts} } ) { + push( + @{ $self->{cached}->{alerts} }, + { + 'warn_'.$i.'_End' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + ( $data->{alerts}->[$i]->{end} ) + - 3600 + ) + ), + 'warn_'.$i.'_Start' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + ( $data->{alerts}->[$i]->{start} ) + - 3600 + ) + ), + 'warn_'.$i.'_Description' => encode_utf8( + $data->{alerts}->[$i]->{description} + ), + 'warn_'.$i.'_SenderName' => encode_utf8( + $data->{alerts}->[$i]->{sender_name} + ), + 'warn_'.$i.'_Event' => encode_utf8( + $data->{alerts}->[$i]->{event} + ), + }, + ); + + $i++; + } + } } } } diff --git a/fhem-2022-11.log b/fhem-2022-11.log deleted file mode 100644 index 3dba360..0000000 --- a/fhem-2022-11.log +++ /dev/null @@ -1,1523 +0,0 @@ -!!!DEBUG!!! Endpoint ist: weather!!!DEBUG!!! Response DATA: $VAR1 = { - 'id' => 2935132, - 'main' => { - 'grnd_level' => 996, - 'temp_min' => '275.94', - 'temp' => '276.64', - 'feels_like' => '271.67', - 'pressure' => 1002, - 'temp_max' => '277.57', - 'humidity' => 90, - 'sea_level' => 1002 - }, - 'cod' => 200, - 'coord' => { - 'lat' => '52.3901', - 'lon' => '13.1968' - }, - 'timezone' => 3600, - 'weather' => [ - { - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804, - 'icon' => '04n' - } - ], - 'wind' => { - 'gust' => '12.2', - 'speed' => '7.14', - 'deg' => 103 - }, - 'name' => 'Dreilinden', - 'clouds' => { - 'all' => 100 - }, - 'sys' => { - 'id' => 2038087, - 'sunrise' => 1668666709, - 'sunset' => 1668697964, - 'type' => 2, - 'country' => 'DE' - }, - 'dt' => 1668708257, - 'visibility' => 10000, - 'base' => 'stations' - }; - -!!!DEBUG!!! Endpoint ist: onecall!!!DEBUG!!! Response DATA: $VAR1 = { - 'timezone' => 'Europe/Berlin', - 'timezone_offset' => 3600, - 'daily' => [ - { - 'sunset' => 1668697964, - 'moonrise' => 0, - 'humidity' => 76, - 'clouds' => 100, - 'wind_gust' => '13.89', - 'pressure' => 1003, - 'dt' => 1668679200, - 'moonset' => 1668690780, - 'feels_like' => { - 'morn' => '272.6', - 'eve' => '272.24', - 'night' => '271.21', - 'day' => '273.02' - }, - 'dew_point' => '274.06', - 'sunrise' => 1668666709, - 'weather' => [ - { - 'main' => 'Rain', - 'description' => 'Leichter Regen', - 'id' => 500, - 'icon' => '10d' - } - ], - 'rain' => '1.54', - 'temp' => { - 'morn' => '277.24', - 'eve' => '277.13', - 'max' => '278.68', - 'night' => '275.86', - 'min' => '275.86', - 'day' => '277.91' - }, - 'uvi' => '0.61', - 'wind_speed' => '7.97', - 'moon_phase' => '0.78', - 'pop' => '0.73', - 'wind_deg' => 109 - }, - { - 'moon_phase' => '0.81', - 'uvi' => '0.57', - 'wind_speed' => '6.3', - 'temp' => { - 'night' => '271.21', - 'eve' => '274.04', - 'morn' => '274.75', - 'max' => '276.16', - 'day' => '274.87', - 'min' => '271.21' - }, - 'wind_deg' => 80, - 'pop' => '0.64', - 'dew_point' => '266.1', - 'sunrise' => 1668753214, - 'feels_like' => { - 'day' => '269.75', - 'night' => '268.07', - 'eve' => '269.54', - 'morn' => '269.65' - }, - 'moonset' => 1668777900, - 'weather' => [ - { - 'icon' => '04d', - 'id' => 804, - 'main' => 'Clouds', - 'description' => 'Bedeckt' - } - ], - 'wind_gust' => 13, - 'clouds' => 88, - 'pressure' => 1008, - 'dt' => 1668765600, - 'sunset' => 1668784284, - 'humidity' => 52, - 'moonrise' => 1668726960 - }, - { - 'dew_point' => '266.22', - 'sunrise' => 1668839718, - 'feels_like' => { - 'eve' => '269.78', - 'morn' => '267.78', - 'day' => '273.08', - 'night' => '267.39' - }, - 'moonset' => 1668865020, - 'weather' => [ - { - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804, - 'icon' => '04d' - } - ], - 'moon_phase' => '0.84', - 'wind_speed' => '2.23', - 'uvi' => '0.68', - 'temp' => { - 'min' => '270.22', - 'day' => '273.08', - 'max' => '274.08', - 'morn' => '270.31', - 'eve' => '271.57', - 'night' => '270.35' - }, - 'wind_deg' => 83, - 'pop' => 0, - 'sunset' => 1668870607, - 'humidity' => 60, - 'moonrise' => 1668817920, - 'wind_gust' => '3.93', - 'clouds' => 98, - 'pressure' => 1015, - 'dt' => 1668852000 - }, - { - 'wind_deg' => 163, - 'pop' => 0, - 'moon_phase' => '0.87', - 'wind_speed' => '3.12', - 'uvi' => '0.63', - 'temp' => { - 'night' => '273.94', - 'eve' => '275.16', - 'morn' => '269.75', - 'max' => '275.87', - 'day' => '272.53', - 'min' => '269.57' - }, - 'weather' => [ - { - 'id' => 800, - 'description' => 'Klarer Himmel', - 'main' => 'Clear', - 'icon' => '01d' - } - ], - 'dew_point' => '267.18', - 'sunrise' => 1668926222, - 'feels_like' => { - 'night' => '270.5', - 'day' => '269.52', - 'morn' => '266.49', - 'eve' => '272.45' - }, - 'moonset' => 1668952080, - 'dt' => 1668938400, - 'clouds' => 8, - 'pressure' => 1010, - 'wind_gust' => '7.87', - 'humidity' => 67, - 'moonrise' => 1668908880, - 'sunset' => 1668956932 - }, - { - 'sunrise' => 1669012725, - 'dew_point' => '275.1', - 'moonset' => 1669039200, - 'feels_like' => { - 'day' => '274.08', - 'night' => '271.6', - 'eve' => '274.02', - 'morn' => '271.39' - }, - 'weather' => [ - { - 'id' => 804, - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'icon' => '04d' - } - ], - 'moon_phase' => '0.91', - 'temp' => { - 'day' => '276.85', - 'min' => '274.31', - 'night' => '274.53', - 'max' => '277.63', - 'morn' => '274.31', - 'eve' => '276.43' - }, - 'wind_speed' => '3.04', - 'uvi' => '0.58', - 'pop' => '0.02', - 'wind_deg' => 171, - 'sunset' => 1669043260, - 'moonrise' => 1669000020, - 'humidity' => 88, - 'clouds' => 100, - 'pressure' => 1009, - 'wind_gust' => '9.03', - 'dt' => 1669024800 - }, - { - 'sunset' => 1669129590, - 'humidity' => 82, - 'moonrise' => 1669091460, - 'clouds' => 48, - 'wind_gust' => '7.23', - 'pressure' => 1005, - 'dt' => 1669111200, - 'dew_point' => '272.56', - 'sunrise' => 1669099226, - 'feels_like' => { - 'eve' => '273.5', - 'morn' => '270.61', - 'day' => '271.81', - 'night' => '271.49' - }, - 'moonset' => 1669126500, - 'weather' => [ - { - 'id' => 802, - 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt", - 'main' => 'Clouds', - 'icon' => '03d' - } - ], - 'moon_phase' => '0.94', - 'wind_speed' => '3.77', - 'uvi' => '0.55', - 'temp' => { - 'day' => '275.39', - 'min' => '273.06', - 'night' => '274.03', - 'morn' => '273.73', - 'eve' => '276.24', - 'max' => '278.19' - }, - 'wind_deg' => 110, - 'pop' => 0 - }, - { - 'wind_gust' => '3.49', - 'clouds' => 100, - 'pressure' => 1007, - 'dt' => 1669197600, - 'sunset' => 1669215923, - 'humidity' => 78, - 'moonrise' => 1669183200, - 'moon_phase' => 0, - 'wind_speed' => '2.07', - 'uvi' => 1, - 'temp' => { - 'morn' => '273.32', - 'eve' => '275.73', - 'max' => '276.61', - 'night' => '274.34', - 'min' => '273.32', - 'day' => '274.89' - }, - 'wind_deg' => 103, - 'pop' => '0.02', - 'dew_point' => '271.43', - 'sunrise' => 1669185726, - 'feels_like' => { - 'night' => '274.34', - 'day' => '272.93', - 'eve' => '275.73', - 'morn' => '270.81' - }, - 'moonset' => 1669214100, - 'weather' => [ - { - 'icon' => '04d', - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804 - } - ] - }, - { - 'moonrise' => 1669275060, - 'humidity' => 79, - 'sunset' => 1669302259, - 'dt' => 1669284000, - 'pressure' => 1013, - 'clouds' => 73, - 'wind_gust' => '2.19', - 'weather' => [ - { - 'icon' => '04d', - 'id' => 803, - 'main' => 'Clouds', - 'description' => "\x{dc}berwiegend bew\x{f6}lkt" - } - ], - 'moonset' => 1669302240, - 'feels_like' => { - 'eve' => '275.96', - 'morn' => '274.91', - 'night' => '271.47', - 'day' => '274.51' - }, - 'dew_point' => '272.35', - 'sunrise' => 1669272225, - 'pop' => 0, - 'wind_deg' => 137, - 'temp' => { - 'night' => '273.77', - 'morn' => '274.91', - 'eve' => '275.96', - 'max' => '278.22', - 'day' => '275.67', - 'min' => '273.62' - }, - 'uvi' => 1, - 'wind_speed' => '1.96', - 'moon_phase' => '0.02' - } - ], - 'lon' => '13.1968', - 'hourly' => [ - { - 'feels_like' => '271.67', - 'dew_point' => '275.16', - 'weather' => [ - { - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804, - 'icon' => '04n' - } - ], - 'wind_speed' => '7.14', - 'uvi' => 0, - 'temp' => '276.64', - 'wind_deg' => 103, - 'pop' => '0.42', - 'humidity' => 90, - 'wind_gust' => '12.2', - 'clouds' => 100, - 'pressure' => 1002, - 'visibility' => 10000, - 'dt' => 1668708000 - }, - { - 'feels_like' => '271.74', - 'dew_point' => '274.82', - 'rain' => { - '1h' => '0.42' - }, - 'weather' => [ - { - 'icon' => '10n', - 'id' => 500, - 'description' => 'Leichter Regen', - 'main' => 'Rain' - } - ], - 'uvi' => 0, - 'wind_speed' => '6.88', - 'temp' => '276.62', - 'wind_deg' => 100, - 'pop' => '0.61', - 'humidity' => 88, - 'pressure' => 1002, - 'wind_gust' => '12.71', - 'clouds' => 100, - 'visibility' => 10000, - 'dt' => 1668711600 - }, - { - 'feels_like' => '271.59', - 'dew_point' => '274.76', - 'rain' => { - '1h' => '0.75' - }, - 'weather' => [ - { - 'icon' => '10n', - 'id' => 500, - 'description' => 'Leichter Regen', - 'main' => 'Rain' - } - ], - 'temp' => '276.4', - 'uvi' => 0, - 'wind_speed' => '6.56', - 'pop' => '0.66', - 'wind_deg' => 98, - 'humidity' => 89, - 'wind_gust' => '12.75', - 'clouds' => 100, - 'pressure' => 1002, - 'visibility' => 10000, - 'dt' => 1668715200 - }, - { - 'pop' => '0.73', - 'wind_deg' => 97, - 'temp' => '276.07', - 'uvi' => 0, - 'wind_speed' => '6.28', - 'weather' => [ - { - 'main' => 'Rain', - 'description' => 'Leichter Regen', - 'id' => 500, - 'icon' => '10n' - } - ], - 'rain' => { - '1h' => '0.22' - }, - 'feels_like' => '271.28', - 'dew_point' => '274.9', - 'visibility' => 7053, - 'dt' => 1668718800, - 'clouds' => 100, - 'wind_gust' => '12.69', - 'pressure' => 1003, - 'humidity' => 92 - }, - { - 'pop' => '0.72', - 'wind_deg' => 94, - 'temp' => '275.86', - 'uvi' => 0, - 'wind_speed' => '5.85', - 'rain' => { - '1h' => '0.15' - }, - 'weather' => [ - { - 'icon' => '10n', - 'description' => 'Leichter Regen', - 'main' => 'Rain', - 'id' => 500 - } - ], - 'feels_like' => '271.21', - 'dew_point' => '274.39', - 'visibility' => 10000, - 'dt' => 1668722400, - 'wind_gust' => '12.01', - 'clouds' => 100, - 'pressure' => 1003, - 'humidity' => 90 - }, - { - 'dew_point' => '273.5', - 'feels_like' => '271.19', - 'weather' => [ - { - 'id' => 804, - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'icon' => '04n' - } - ], - 'wind_speed' => '5.62', - 'uvi' => 0, - 'temp' => '275.76', - 'wind_deg' => 93, - 'pop' => '0.64', - 'humidity' => 85, - 'wind_gust' => '11.28', - 'clouds' => 100, - 'pressure' => 1003, - 'dt' => 1668726000, - 'visibility' => 10000 - }, - { - 'weather' => [ - { - 'icon' => '04n', - 'id' => 804, - 'main' => 'Clouds', - 'description' => 'Bedeckt' - } - ], - 'feels_like' => '271.25', - 'dew_point' => '272.46', - 'pop' => '0.6', - 'wind_deg' => 89, - 'temp' => '275.81', - 'wind_speed' => '5.63', - 'uvi' => 0, - 'humidity' => 79, - 'visibility' => 10000, - 'dt' => 1668729600, - 'wind_gust' => '10.97', - 'pressure' => 1003, - 'clouds' => 100 - }, - { - 'clouds' => 100, - 'wind_gust' => '11.12', - 'pressure' => 1003, - 'visibility' => 10000, - 'dt' => 1668733200, - 'humidity' => 74, - 'wind_speed' => '5.94', - 'uvi' => 0, - 'temp' => '275.68', - 'wind_deg' => 84, - 'pop' => 0, - 'feels_like' => '270.94', - 'dew_point' => '271.41', - 'weather' => [ - { - 'icon' => '04n', - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804 - } - ] - }, - { - 'humidity' => 72, - 'wind_gust' => '10.72', - 'clouds' => 100, - 'pressure' => 1004, - 'dt' => 1668736800, - 'visibility' => 10000, - 'dew_point' => '270.95', - 'feels_like' => '271.04', - 'weather' => [ - { - 'icon' => '04n', - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804 - } - ], - 'temp' => '275.45', - 'wind_speed' => '5.14', - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 89 - }, - { - 'weather' => [ - { - 'id' => 804, - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'icon' => '04n' - } - ], - 'feels_like' => '270.61', - 'dew_point' => '270.75', - 'wind_deg' => 84, - 'pop' => 0, - 'uvi' => 0, - 'wind_speed' => '5.34', - 'temp' => '275.19', - 'humidity' => 73, - 'visibility' => 10000, - 'dt' => 1668740400, - 'clouds' => 100, - 'wind_gust' => '10.26', - 'pressure' => 1004 - }, - { - 'pop' => 0, - 'wind_deg' => 77, - 'temp' => '274.75', - 'uvi' => 0, - 'wind_speed' => '6.17', - 'weather' => [ - { - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804, - 'icon' => '04n' - } - ], - 'dew_point' => '270.39', - 'feels_like' => '269.65', - 'dt' => 1668744000, - 'visibility' => 10000, - 'clouds' => 100, - 'wind_gust' => '11.93', - 'pressure' => 1004, - 'humidity' => 73 - }, - { - 'wind_deg' => 80, - 'pop' => 0, - 'uvi' => 0, - 'wind_speed' => '6.09', - 'temp' => '274.27', - 'weather' => [ - { - 'icon' => '04n', - 'id' => 804, - 'description' => 'Bedeckt', - 'main' => 'Clouds' - } - ], - 'dew_point' => '269.84', - 'feels_like' => '269.08', - 'dt' => 1668747600, - 'visibility' => 10000, - 'wind_gust' => '12.46', - 'clouds' => 100, - 'pressure' => 1005, - 'humidity' => 72 - }, - { - 'humidity' => 72, - 'clouds' => 100, - 'wind_gust' => '12.57', - 'pressure' => 1005, - 'visibility' => 10000, - 'dt' => 1668751200, - 'feels_like' => '268.51', - 'dew_point' => '269.29', - 'weather' => [ - { - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804, - 'icon' => '04n' - } - ], - 'wind_speed' => '6.08', - 'uvi' => 0, - 'temp' => '273.82', - 'wind_deg' => 80, - 'pop' => 0 - }, - { - 'humidity' => 69, - 'clouds' => 100, - 'wind_gust' => 13, - 'pressure' => 1006, - 'dt' => 1668754800, - 'visibility' => 10000, - 'dew_point' => '268.4', - 'feels_like' => '268.05', - 'weather' => [ - { - 'icon' => '04d', - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804 - } - ], - 'wind_speed' => '6.12', - 'uvi' => 0, - 'temp' => '273.47', - 'wind_deg' => 79, - 'pop' => 0 - }, - { - 'dew_point' => '267.74', - 'feels_like' => '268.16', - 'weather' => [ - { - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804, - 'icon' => '04d' - } - ], - 'temp' => '273.49', - 'wind_speed' => '5.94', - 'uvi' => '0.06', - 'pop' => 0, - 'wind_deg' => 80, - 'humidity' => 65, - 'clouds' => 99, - 'wind_gust' => '12.76', - 'pressure' => 1007, - 'dt' => 1668758400, - 'visibility' => 10000 - }, - { - 'wind_deg' => 80, - 'pop' => 0, - 'wind_speed' => '6.08', - 'uvi' => '0.16', - 'temp' => '274.01', - 'weather' => [ - { - 'icon' => '04d', - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804 - } - ], - 'dew_point' => '266.97', - 'feels_like' => '268.75', - 'dt' => 1668762000, - 'visibility' => 10000, - 'clouds' => 98, - 'wind_gust' => '12.35', - 'pressure' => 1008, - 'humidity' => 59 - }, - { - 'wind_deg' => 82, - 'pop' => 0, - 'uvi' => '0.47', - 'wind_speed' => '6.28', - 'temp' => '274.87', - 'weather' => [ - { - 'icon' => '04d', - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804 - } - ], - 'feels_like' => '269.75', - 'dew_point' => '266.1', - 'visibility' => 10000, - 'dt' => 1668765600, - 'pressure' => 1008, - 'wind_gust' => '10.99', - 'clouds' => 88, - 'humidity' => 52 - }, - { - 'weather' => [ - { - 'icon' => '04d', - 'main' => 'Clouds', - 'description' => "\x{dc}berwiegend bew\x{f6}lkt", - 'id' => 803 - } - ], - 'feels_like' => '270.78', - 'dew_point' => '265.68', - 'pop' => 0, - 'wind_deg' => 80, - 'temp' => '275.68', - 'uvi' => '0.57', - 'wind_speed' => '6.3', - 'humidity' => 48, - 'visibility' => 10000, - 'dt' => 1668769200, - 'wind_gust' => '9.62', - 'clouds' => 75, - 'pressure' => 1008 - }, - { - 'weather' => [ - { - 'id' => 803, - 'main' => 'Clouds', - 'description' => "\x{dc}berwiegend bew\x{f6}lkt", - 'icon' => '04d' - } - ], - 'feels_like' => '271.45', - 'dew_point' => '265.86', - 'pop' => 0, - 'wind_deg' => 79, - 'temp' => '276.11', - 'uvi' => '0.51', - 'wind_speed' => '6.01', - 'humidity' => 47, - 'visibility' => 10000, - 'dt' => 1668772800, - 'clouds' => 63, - 'wind_gust' => '8.61', - 'pressure' => 1009 - }, - { - 'visibility' => 10000, - 'dt' => 1668776400, - 'wind_gust' => '7.9', - 'clouds' => 2, - 'pressure' => 1009, - 'humidity' => 48, - 'pop' => 0, - 'wind_deg' => 78, - 'temp' => '276.16', - 'uvi' => '0.44', - 'wind_speed' => '5.55', - 'weather' => [ - { - 'main' => 'Clear', - 'description' => 'Klarer Himmel', - 'id' => 800, - 'icon' => '01d' - } - ], - 'feels_like' => '271.73', - 'dew_point' => '266.24' - }, - { - 'weather' => [ - { - 'icon' => '01d', - 'main' => 'Clear', - 'description' => 'Klarer Himmel', - 'id' => 800 - } - ], - 'feels_like' => '271.33', - 'dew_point' => '266.57', - 'pop' => 0, - 'wind_deg' => 76, - 'temp' => '275.71', - 'uvi' => '0.21', - 'wind_speed' => '5.21', - 'humidity' => 51, - 'visibility' => 10000, - 'dt' => 1668780000, - 'clouds' => 4, - 'wind_gust' => '8.05', - 'pressure' => 1009 - }, - { - 'weather' => [ - { - 'main' => 'Clear', - 'description' => 'Klarer Himmel', - 'id' => 800, - 'icon' => '01d' - } - ], - 'dew_point' => '266.69', - 'feels_like' => '270.22', - 'pop' => 0, - 'wind_deg' => 72, - 'temp' => '274.72', - 'uvi' => 0, - 'wind_speed' => '4.96', - 'humidity' => 55, - 'dt' => 1668783600, - 'visibility' => 10000, - 'wind_gust' => '9.04', - 'clouds' => 4, - 'pressure' => 1010 - }, - { - 'humidity' => 59, - 'pressure' => 1011, - 'wind_gust' => '9.44', - 'clouds' => 4, - 'visibility' => 10000, - 'dt' => 1668787200, - 'feels_like' => '269.54', - 'dew_point' => '266.84', - 'weather' => [ - { - 'icon' => '01n', - 'main' => 'Clear', - 'description' => 'Klarer Himmel', - 'id' => 800 - } - ], - 'wind_speed' => '4.66', - 'uvi' => 0, - 'temp' => '274.04', - 'wind_deg' => 71, - 'pop' => 0 - }, - { - 'humidity' => 61, - 'wind_gust' => '9.44', - 'clouds' => 4, - 'pressure' => 1011, - 'dt' => 1668790800, - 'visibility' => 10000, - 'dew_point' => '267.02', - 'feels_like' => '269.16', - 'weather' => [ - { - 'icon' => '01n', - 'id' => 800, - 'description' => 'Klarer Himmel', - 'main' => 'Clear' - } - ], - 'temp' => '273.61', - 'wind_speed' => '4.42', - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 69 - }, - { - 'wind_deg' => 70, - 'pop' => 0, - 'uvi' => 0, - 'wind_speed' => '4.1', - 'temp' => '273.15', - 'weather' => [ - { - 'main' => 'Clear', - 'description' => 'Klarer Himmel', - 'id' => 800, - 'icon' => '01n' - } - ], - 'dew_point' => '267.15', - 'feels_like' => '268.8', - 'dt' => 1668794400, - 'visibility' => 10000, - 'wind_gust' => '9.24', - 'clouds' => 4, - 'pressure' => 1012, - 'humidity' => 64 - }, - { - 'pop' => 0, - 'wind_deg' => 73, - 'temp' => '272.53', - 'wind_speed' => '3.54', - 'uvi' => 0, - 'weather' => [ - { - 'icon' => '01n', - 'id' => 800, - 'description' => 'Klarer Himmel', - 'main' => 'Clear' - } - ], - 'feels_like' => '268.45', - 'dew_point' => '267.17', - 'visibility' => 10000, - 'dt' => 1668798000, - 'clouds' => 5, - 'wind_gust' => '8.35', - 'pressure' => 1012, - 'humidity' => 67 - }, - { - 'weather' => [ - { - 'description' => 'Klarer Himmel', - 'main' => 'Clear', - 'id' => 800, - 'icon' => '01n' - } - ], - 'dew_point' => '267.15', - 'feels_like' => '268.23', - 'wind_deg' => 73, - 'pop' => 0, - 'uvi' => 0, - 'wind_speed' => '2.94', - 'temp' => '271.92', - 'humidity' => 70, - 'dt' => 1668801600, - 'visibility' => 10000, - 'clouds' => 3, - 'wind_gust' => '7.03', - 'pressure' => 1013 - }, - { - 'wind_speed' => '2.48', - 'uvi' => 0, - 'temp' => '271.4', - 'wind_deg' => 76, - 'pop' => 0, - 'feels_like' => '268.08', - 'dew_point' => '267.11', - 'weather' => [ - { - 'icon' => '01n', - 'id' => 800, - 'description' => 'Klarer Himmel', - 'main' => 'Clear' - } - ], - 'clouds' => 4, - 'wind_gust' => '5.24', - 'pressure' => 1013, - 'visibility' => 10000, - 'dt' => 1668805200, - 'humidity' => 73 - }, - { - 'humidity' => 74, - 'wind_gust' => '4.22', - 'clouds' => 22, - 'pressure' => 1013, - 'visibility' => 10000, - 'dt' => 1668808800, - 'feels_like' => '268.07', - 'dew_point' => '267.07', - 'weather' => [ - { - 'id' => 801, - 'description' => 'Ein paar Wolken', - 'main' => 'Clouds', - 'icon' => '02n' - } - ], - 'uvi' => 0, - 'wind_speed' => '2.29', - 'temp' => '271.21', - 'wind_deg' => 82, - 'pop' => 0 - }, - { - 'dew_point' => '267.06', - 'feels_like' => '268.01', - 'weather' => [ - { - 'icon' => '03n', - 'id' => 802, - 'main' => 'Clouds', - 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt" - } - ], - 'temp' => '271.1', - 'wind_speed' => '2.23', - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 83, - 'humidity' => 74, - 'wind_gust' => '3.93', - 'pressure' => 1014, - 'clouds' => 27, - 'dt' => 1668812400, - 'visibility' => 10000 - }, - { - 'humidity' => 76, - 'clouds' => 24, - 'wind_gust' => '3.47', - 'pressure' => 1014, - 'visibility' => 10000, - 'dt' => 1668816000, - 'feels_like' => '267.7', - 'dew_point' => '267.03', - 'weather' => [ - { - 'id' => 801, - 'main' => 'Clouds', - 'description' => 'Ein paar Wolken', - 'icon' => '02n' - } - ], - 'wind_speed' => '2.22', - 'uvi' => 0, - 'temp' => '270.82', - 'wind_deg' => 77, - 'pop' => 0 - }, - { - 'feels_like' => '267.7', - 'dew_point' => '266.98', - 'weather' => [ - { - 'description' => 'Klarer Himmel', - 'main' => 'Clear', - 'id' => 800, - 'icon' => '01n' - } - ], - 'temp' => '270.64', - 'wind_speed' => '2.05', - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 77, - 'humidity' => 76, - 'clouds' => 5, - 'wind_gust' => '3.07', - 'pressure' => 1015, - 'visibility' => 10000, - 'dt' => 1668819600 - }, - { - 'clouds' => 5, - 'wind_gust' => '2.83', - 'pressure' => 1015, - 'dt' => 1668823200, - 'visibility' => 10000, - 'humidity' => 77, - 'temp' => '270.51', - 'wind_speed' => 2, - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 76, - 'dew_point' => '266.93', - 'feels_like' => '267.62', - 'weather' => [ - { - 'icon' => '01n', - 'id' => 800, - 'description' => 'Klarer Himmel', - 'main' => 'Clear' - } - ] - }, - { - 'dew_point' => '266.86', - 'feels_like' => '267.66', - 'weather' => [ - { - 'id' => 800, - 'main' => 'Clear', - 'description' => 'Klarer Himmel', - 'icon' => '01n' - } - ], - 'wind_speed' => '1.88', - 'uvi' => 0, - 'temp' => '270.4', - 'wind_deg' => 83, - 'pop' => 0, - 'humidity' => 77, - 'wind_gust' => '2.57', - 'pressure' => 1015, - 'clouds' => 5, - 'dt' => 1668826800, - 'visibility' => 10000 - }, - { - 'humidity' => 77, - 'visibility' => 10000, - 'dt' => 1668830400, - 'wind_gust' => '2.32', - 'pressure' => 1015, - 'clouds' => 12, - 'weather' => [ - { - 'description' => 'Ein paar Wolken', - 'main' => 'Clouds', - 'id' => 801, - 'icon' => '02n' - } - ], - 'feels_like' => '267.78', - 'dew_point' => '266.78', - 'pop' => 0, - 'wind_deg' => 79, - 'temp' => '270.31', - 'uvi' => 0, - 'wind_speed' => '1.73' - }, - { - 'humidity' => 76, - 'visibility' => 10000, - 'dt' => 1668834000, - 'clouds' => 30, - 'wind_gust' => '2.17', - 'pressure' => 1015, - 'weather' => [ - { - 'id' => 802, - 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt", - 'main' => 'Clouds', - 'icon' => '03n' - } - ], - 'feels_like' => '268.16', - 'dew_point' => '266.72', - 'wind_deg' => 100, - 'pop' => 0, - 'uvi' => 0, - 'wind_speed' => '1.58', - 'temp' => '270.43' - }, - { - 'temp' => '270.22', - 'uvi' => 0, - 'wind_speed' => '1.41', - 'pop' => 0, - 'wind_deg' => 91, - 'dew_point' => '266.64', - 'feels_like' => '268.22', - 'weather' => [ - { - 'id' => 802, - 'main' => 'Clouds', - 'description' => "M\x{e4}\x{df}ig bew\x{f6}lkt", - 'icon' => '03n' - } - ], - 'clouds' => 41, - 'wind_gust' => '1.88', - 'pressure' => 1015, - 'dt' => 1668837600, - 'visibility' => 10000, - 'humidity' => 77 - }, - { - 'wind_speed' => '1.24', - 'uvi' => 0, - 'temp' => '270.4', - 'wind_deg' => 106, - 'pop' => 0, - 'feels_like' => '270.4', - 'dew_point' => '266.59', - 'weather' => [ - { - 'icon' => '04d', - 'id' => 804, - 'main' => 'Clouds', - 'description' => 'Bedeckt' - } - ], - 'wind_gust' => '1.75', - 'clouds' => 100, - 'pressure' => 1015, - 'visibility' => 10000, - 'dt' => 1668841200, - 'humidity' => 76 - }, - { - 'visibility' => 10000, - 'dt' => 1668844800, - 'wind_gust' => '1.9', - 'clouds' => 100, - 'pressure' => 1015, - 'humidity' => 71, - 'wind_deg' => 130, - 'pop' => 0, - 'uvi' => '0.15', - 'wind_speed' => '1.18', - 'temp' => '271.29', - 'weather' => [ - { - 'icon' => '04d', - 'id' => 804, - 'description' => 'Bedeckt', - 'main' => 'Clouds' - } - ], - 'feels_like' => '271.29', - 'dew_point' => '266.61' - }, - { - 'humidity' => 65, - 'dt' => 1668848400, - 'visibility' => 10000, - 'clouds' => 97, - 'wind_gust' => '1.69', - 'pressure' => 1015, - 'weather' => [ - { - 'icon' => '04d', - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804 - } - ], - 'dew_point' => '266.47', - 'feels_like' => '272.33', - 'pop' => 0, - 'wind_deg' => 134, - 'temp' => '272.33', - 'uvi' => '0.36', - 'wind_speed' => '1.25' - }, - { - 'clouds' => 98, - 'wind_gust' => '1.48', - 'pressure' => 1015, - 'visibility' => 10000, - 'dt' => 1668852000, - 'humidity' => 60, - 'temp' => '273.08', - 'wind_speed' => '1.18', - 'uvi' => '0.57', - 'pop' => 0, - 'wind_deg' => 140, - 'feels_like' => '273.08', - 'dew_point' => '266.22', - 'weather' => [ - { - 'id' => 804, - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'icon' => '04d' - } - ] - }, - { - 'feels_like' => '273.67', - 'dew_point' => '265.95', - 'weather' => [ - { - 'icon' => '04d', - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804 - } - ], - 'wind_speed' => '1.02', - 'uvi' => '0.68', - 'temp' => '273.67', - 'wind_deg' => 156, - 'pop' => 0, - 'humidity' => 56, - 'wind_gust' => '1.09', - 'clouds' => 98, - 'pressure' => 1015, - 'visibility' => 10000, - 'dt' => 1668855600 - }, - { - 'wind_deg' => 154, - 'pop' => 0, - 'wind_speed' => '0.98', - 'uvi' => '0.62', - 'temp' => '273.98', - 'weather' => [ - { - 'id' => 804, - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'icon' => '04d' - } - ], - 'feels_like' => '273.98', - 'dew_point' => '265.74', - 'visibility' => 10000, - 'dt' => 1668859200, - 'clouds' => 99, - 'wind_gust' => '1.17', - 'pressure' => 1015, - 'humidity' => 54 - }, - { - 'wind_speed' => '1.15', - 'uvi' => '0.41', - 'temp' => '274.08', - 'wind_deg' => 149, - 'pop' => 0, - 'dew_point' => '265.58', - 'feels_like' => '274.08', - 'weather' => [ - { - 'icon' => '04d', - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804 - } - ], - 'wind_gust' => '1.38', - 'clouds' => 100, - 'pressure' => 1014, - 'dt' => 1668862800, - 'visibility' => 10000, - 'humidity' => 53 - }, - { - 'feels_like' => '273.78', - 'dew_point' => '265.76', - 'weather' => [ - { - 'description' => 'Bedeckt', - 'main' => 'Clouds', - 'id' => 804, - 'icon' => '04d' - } - ], - 'temp' => '273.78', - 'uvi' => '0.19', - 'wind_speed' => '1.23', - 'pop' => 0, - 'wind_deg' => 149, - 'humidity' => 55, - 'clouds' => 99, - 'wind_gust' => '1.71', - 'pressure' => 1014, - 'visibility' => 10000, - 'dt' => 1668866400 - }, - { - 'temp' => '272.43', - 'uvi' => 0, - 'wind_speed' => '1.43', - 'pop' => 0, - 'wind_deg' => 153, - 'dew_point' => '265.65', - 'feels_like' => '270.69', - 'weather' => [ - { - 'main' => 'Clouds', - 'description' => 'Bedeckt', - 'id' => 804, - 'icon' => '04d' - } - ], - 'pressure' => 1014, - 'wind_gust' => '2.08', - 'clouds' => 86, - 'dt' => 1668870000, - 'visibility' => 10000, - 'humidity' => 60 - }, - { - 'wind_gust' => '1.95', - 'clouds' => 68, - 'pressure' => 1014, - 'dt' => 1668873600, - 'visibility' => 10000, - 'humidity' => 64, - 'temp' => '271.57', - 'wind_speed' => '1.39', - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 159, - 'dew_point' => '265.63', - 'feels_like' => '269.78', - 'weather' => [ - { - 'icon' => '04n', - 'id' => 803, - 'description' => "\x{dc}berwiegend bew\x{f6}lkt", - 'main' => 'Clouds' - } - ] - }, - { - 'feels_like' => '269.44', - 'dew_point' => '265.63', - 'weather' => [ - { - 'main' => 'Clouds', - 'description' => "\x{dc}berwiegend bew\x{f6}lkt", - 'id' => 803, - 'icon' => '04n' - } - ], - 'temp' => '271.2', - 'wind_speed' => '1.35', - 'uvi' => 0, - 'pop' => 0, - 'wind_deg' => 154, - 'humidity' => 66, - 'clouds' => 55, - 'wind_gust' => '1.89', - 'pressure' => 1014, - 'visibility' => 10000, - 'dt' => 1668877200 - } - ], - 'current' => { - 'wind_deg' => 103, - 'temp' => '276.64', - 'uvi' => 0, - 'wind_speed' => '7.14', - 'weather' => [ - { - 'icon' => '04n', - 'id' => 804, - 'main' => 'Clouds', - 'description' => 'Bedeckt' - } - ], - 'feels_like' => '271.67', - 'dew_point' => '275.16', - 'sunrise' => 1668666709, - 'visibility' => 10000, - 'dt' => 1668708257, - 'wind_gust' => '12.2', - 'pressure' => 1002, - 'clouds' => 100, - 'humidity' => 90, - 'sunset' => 1668697964 - }, - 'lat' => '52.3901', - 'alerts' => [ - { - 'tags' => [ - 'Extreme temperature value' - ], - 'end' => 1668769200, - 'description' => "There is a risk of frost (Level 1 of 2). -Minimum temperature: 0 - -3 \x{b0}C", - 'sender_name' => 'Deutscher Wetterdienst', - 'start' => 1668718800, - 'event' => 'frost' - } - ] - }; \ No newline at end of file -- 2.47.2 From ee989a84bfc7a4e0d1db901a2f6c13a7b25caa89 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 24 Nov 2022 19:22:40 +0100 Subject: [PATCH 04/24] change OpenWeatherMapAPI Code and extend 59_Weather.pm Modul --- 59_Weather.pm | 500 ++++++++++++++++++++++++------------------- DarkSkyAPI.pm | 18 ++ OpenWeatherMapAPI.pm | 152 +++++++------ wundergroundAPI.pm | 18 ++ 4 files changed, 387 insertions(+), 301 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index c8c6731..cc80c32 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -32,10 +32,13 @@ package main; use strict; use warnings; use Time::HiRes qw(gettimeofday); -#use HttpUtils; +use experimental qw /switch/; +use Readonly; + use FHEM::Meta; use vars qw($FW_ss); + # use Data::Dumper; # for Debug only my %pressure_trend_txt_en = ( 0 => "steady", 1 => "rising", 2 => "falling" ); @@ -205,50 +208,60 @@ my @iconlist = ( ); ################################### -sub Weather_LanguageInitialize($) { - my ($lang) = @_; +sub Weather_LanguageInitialize { + my $lang = shift; - 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; + given ($lang) { + when ('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; + } + + when ('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; + } + + when ('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; + } + + when ('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; + } + + when ('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; + } + + default { + %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; + } } + + return; } ################################### -sub Weather_DebugCodes($) { - my ($lang) = @_; +sub Weather_DebugCodes { + my $lang = shift; + my @YahooCodes_i18n = YahooWeatherAPI_getYahooCodes($lang); Debug "Weather Code List, see http://developer.yahoo.com/weather/#codes"; @@ -257,37 +270,44 @@ sub Weather_DebugCodes($) { sprintf( "%2d %30s %30s", $c, $iconlist[$c], $YahooCodes_i18n[$c] ); } + return; } ##################################### -sub Weather_Initialize($) { - my ($hash) = @_; +sub Weather_Initialize { + my $hash = shift; - $hash->{DefFn} = 'Weather_Define'; - $hash->{UndefFn} = 'Weather_Undef'; - $hash->{GetFn} = 'Weather_Get'; - $hash->{SetFn} = 'Weather_Set'; - $hash->{AttrList} = + $hash->{DefFn} = \&Weather_Define; + $hash->{UndefFn} = \&Weather_Undef; + $hash->{GetFn} = \&Weather_Get; + $hash->{SetFn} = \&Weather_Set; + $hash->{AttrFn} = \&Weather_Attr; + $hash->{AttrList} = 'disable:0,1 ' . 'forecast:multiple-strict,hourly,daily ' . 'forecastLimit ' . 'alerts:0,1 ' . $readingFnAttributes; - $hash->{NotifyFn} = 'Weather_Notify'; + $hash->{NotifyFn} = \&Weather_Notify; + $hash->{parseParams} = 1; return FHEM::Meta::InitMod( __FILE__, $hash ); } ################################### -sub degrees_to_direction($@) { - my ( $degrees, @directions_txt_i18n ) = @_; +sub degrees_to_direction { + my $degrees = shift; + my $directions_txt_i18n = shift; + my $mod = int( ( ( $degrees + 11.25 ) % 360 ) / 22.5 ); - return $directions_txt_i18n[$mod]; + return $directions_txt_i18n->[$mod]; } -sub Weather_ReturnWithError($$) { - my ( $hash, $responseRef ) = @_; +sub Weather_ReturnWithError { + my $hash = shift; + my $responseRef = shift; + my $name = $hash->{NAME}; readingsBeginUpdate($hash); @@ -310,10 +330,10 @@ sub Weather_ReturnWithError($$) { return; } -sub Weather_RetrieveCallbackFn($) { - my $name = shift; +sub Weather_RetrieveCallbackFn { + my $name = shift; - return undef + return unless ( IsDevice($name) ); my $hash = $defs{$name}; @@ -325,10 +345,14 @@ sub Weather_RetrieveCallbackFn($) { else { Weather_ReturnWithError( $hash, $responseRef ); } + + return; } -sub Weather_WriteReadings($$) { - my ( $hash, $dataRef ) = @_; +sub Weather_WriteReadings { + my $hash = shift; + my $dataRef = shift; + my $name = $hash->{NAME}; my $hourly = ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1: 0 ); my $daily = ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); @@ -353,31 +377,31 @@ sub Weather_WriteReadings($$) { readingsBulkUpdate( $hash, 'lastError', '' ); foreach my $r ( keys %{$dataRef} ) { readingsBulkUpdate( $hash, $r, $dataRef->{$r} ) - if ( ref( $dataRef->{$r} ) ne 'HASH' - and ref( $dataRef->{$r} ) ne 'ARRAY' ); + if ( ref( $dataRef->{$r} ) ne 'HASH' + && ref( $dataRef->{$r} ) ne 'ARRAY' ); readingsBulkUpdate( $hash, '.license', $dataRef->{license}->{text} ); } ### current if ( defined( $dataRef->{current} ) - and ref( $dataRef->{current} ) eq 'HASH' ) + && ref( $dataRef->{current} ) eq 'HASH' ) { while ( my ( $r, $v ) = each %{ $dataRef->{current} } ) { readingsBulkUpdate( $hash, $r, $v ) - if ( ref( $dataRef->{$r} ) ne 'HASH' - and ref( $dataRef->{$r} ) ne 'ARRAY' ); + if ( ref( $dataRef->{$r} ) ne 'HASH' + && ref( $dataRef->{$r} ) ne 'ARRAY' ); } readingsBulkUpdate( $hash, 'icon', $iconlist[ $dataRef->{current}->{code} ] ); - if ( defined( $dataRef->{current}->{wind_direction} ) - and $dataRef->{current}->{wind_direction} - and defined( $dataRef->{current}->{wind_speed} ) - and $dataRef->{current}->{wind_speed} ) + if ( defined( $dataRef->{current}->{wind_direction} ) + && $dataRef->{current}->{wind_direction} + && defined( $dataRef->{current}->{wind_speed} ) + && $dataRef->{current}->{wind_speed} ) { my $wdir = degrees_to_direction( $dataRef->{current}->{wind_direction}, - @directions_txt_i18n ); + \@directions_txt_i18n ); readingsBulkUpdate( $hash, 'wind_condition', 'Wind: ' . $wdir . ' ' @@ -387,15 +411,15 @@ sub Weather_WriteReadings($$) { } ### forecast - if ( ref( $dataRef->{forecast} ) eq 'HASH' - and ($hourly or $daily) ) + if ( ref( $dataRef->{forecast} ) eq 'HASH' + && ($hourly || $daily) ) { ## hourly if ( - defined( $dataRef->{forecast}->{hourly} ) - and ref( $dataRef->{forecast}->{hourly} ) eq 'ARRAY' - and scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0 - and $hourly + defined( $dataRef->{forecast}->{hourly} ) + && ref( $dataRef->{forecast}->{hourly} ) eq 'ARRAY' + && scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0 + && $hourly ) { my $i = 0; @@ -406,8 +430,8 @@ sub Weather_WriteReadings($$) { while ( my ( $r, $v ) = each %{$fc} ) { readingsBulkUpdate( $hash, $f . $r, $v ) - if ( ref( $dataRef->{$r} ) ne 'HASH' - and ref( $dataRef->{$r} ) ne 'ARRAY' ); + if ( ref( $dataRef->{$r} ) ne 'HASH' + && ref( $dataRef->{$r} ) ne 'ARRAY' ); } readingsBulkUpdate( $hash, @@ -417,19 +441,19 @@ sub Weather_WriteReadings($$) { if ( defined( - $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} + $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} ) - and $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} - and defined( + && $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} + && defined( $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_speed} ) - and $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_speed} + && $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_speed} ) { my $wdir = degrees_to_direction( $dataRef->{forecast} ->{hourly}[ $i - 1 ]{wind_direction}, - @directions_txt_i18n + \@directions_txt_i18n ); readingsBulkUpdate( $hash, @@ -441,16 +465,16 @@ sub Weather_WriteReadings($$) { ); } - last if ( $i == $limit and $limit > 0 ); + last if ( $i == $limit && $limit > 0 ); } } ## daily if ( - defined( $dataRef->{forecast}->{daily} ) - and ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY' - and scalar( @{ $dataRef->{forecast}->{daily} } ) > 0 - and $daily + defined( $dataRef->{forecast}->{daily} ) + && ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY' + && scalar( @{ $dataRef->{forecast}->{daily} } ) > 0 + && $daily ) { my $i = 0; @@ -461,8 +485,8 @@ sub Weather_WriteReadings($$) { while ( my ( $r, $v ) = each %{$fc} ) { readingsBulkUpdate( $hash, $f . $r, $v ) - if ( ref( $dataRef->{$r} ) ne 'HASH' - and ref( $dataRef->{$r} ) ne 'ARRAY' ); + if ( ref( $dataRef->{$r} ) ne 'HASH' + && ref( $dataRef->{$r} ) ne 'ARRAY' ); } readingsBulkUpdate( $hash, @@ -472,18 +496,18 @@ sub Weather_WriteReadings($$) { if ( defined( - $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} + $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} ) - and $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} - and defined( + && $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} + && defined( $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_speed} ) - and $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_speed} + && $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_speed} ) { my $wdir = degrees_to_direction( $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction}, - @directions_txt_i18n + \@directions_txt_i18n ); readingsBulkUpdate( $hash, @@ -495,18 +519,18 @@ sub Weather_WriteReadings($$) { ); } - last if ( $i == $limit and $limit > 0 ); + last if ( $i == $limit && $limit > 0 ); } } } - if ( ref( $dataRef->{alerts} ) eq 'HASH' - and $alerts ) + if ( ref( $dataRef->{alerts} ) eq 'HASH' + && $alerts ) { while ( my ( $r, $v ) = each %{ $dataRef->{alerts} } ) { readingsBulkUpdate( $hash, $r, $v ) - if ( ref( $dataRef->{$r} ) ne 'HASH' - and ref( $dataRef->{$r} ) ne 'ARRAY' ); + if ( ref( $dataRef->{$r} ) ne 'HASH' + && ref( $dataRef->{$r} ) ne 'ARRAY' ); } } @@ -524,13 +548,15 @@ sub Weather_WriteReadings($$) { readingsEndUpdate( $hash, 1 ); Weather_RearmTimer( $hash, gettimeofday() + $hash->{INTERVAL} ); + return; } ################################### -sub Weather_GetUpdate($) { - my ($hash) = @_; +sub Weather_GetUpdate { + my $hash = shift; + my $name = $hash->{NAME}; if ( $attr{$name} && $attr{$name}->{disable} ) { @@ -543,34 +569,19 @@ sub Weather_GetUpdate($) { Weather_RearmTimer( $hash, gettimeofday() + $hash->{INTERVAL} ); } else { - $hash->{fhem}->{api}->{exclude} = Weather_parseForcastAttr($hash); $hash->{fhem}->{api}->setRetrieveData; } - return 1; + return; } ################################### -sub Weather_parseForcastAttr { - my $hash = shift; - my $name = $hash->{NAME}; - - my @exclude = qw 'alerts minutely hourly daily'; - my @forecast = split(',',AttrVal($name,'forcast','') . (AttrVal($name,'alerts',0) ? ',alerts' : '')); - my %exclude =(); +sub Weather_Get { + my $hash = shift // return; + my $aRef = shift // return; - @exclude{@exclude} = @exclude; - delete @exclude{@forecast}; - - return join(',',keys %exclude); -} - -sub Weather_Get($@) { - my ( $hash, @a ) = @_; - - return "argument is missing" if ( int(@a) != 2 ); - - my $reading = $a[1]; + my $name = shift @$aRef // return; + my $reading = shift @$aRef // return; my $value; if ( defined( $hash->{READINGS}->{$reading} ) ) { @@ -585,42 +596,50 @@ sub Weather_Get($@) { return "Unknown reading $reading, choose one of " . $rt; } - return "$a[0] $reading => $value"; + return "$name $reading => $value"; } ################################### -sub Weather_Set($@) { - my ( $hash, @a ) = @_; +sub Weather_Set { + my $hash = shift // return; + my $aRef = shift // return; - my $cmd = $a[1]; + my $name = shift @$aRef // return; + my $cmd = shift @$aRef + // return qq{"set $name" needs at least one argument}; # usage check - if ( ( @a == 2 ) && ( $a[1] eq "update" ) ) { + if ( scalar(@{$aRef}) == 0 + && $cmd eq 'update' ) + { Weather_DisarmTimer($hash); Weather_GetUpdate($hash); - return undef; + + return; } - elsif ( ( @a >= 2 ) && ( $a[1] eq "newLocation" ) ) { + elsif ( scalar(@{$aRef}) == 1 + && $cmd eq "newLocation" ) + { if ( $hash->{API} eq 'DarkSkyAPI' - or $hash->{API} eq 'OpenWeatherMapAPI' - or $hash->{API} eq 'wundergroundAPI' + || $hash->{API} eq 'OpenWeatherMapAPI' + || $hash->{API} eq 'wundergroundAPI' ) { my ($lat,$long); - ($lat,$long) = split(',',$a[2]) - if ( defined($a[2]) and $a[2] ); + ($lat,$long) = split(',',$aRef->[0]) + if ( defined($aRef->[0]) && $aRef->[0] ); ($lat,$long) = split(',',$hash->{fhem}->{LOCATION}) unless ( defined($lat) - and defined($long) - and $lat =~ /(-?\d+(\.\d+)?)/ - and $long =~ /(-?\d+(\.\d+)?)/ ); + && defined($long) + && $lat =~ m{(-?\d+(\.\d+)?)}xms + && $long =~ m{(-?\d+(\.\d+)?)}xms ); $hash->{fhem}->{api}->setLocation($lat,$long); Weather_DisarmTimer($hash); Weather_GetUpdate($hash); - return undef; + return; } - else { return 'this API is not ' . $a[1] .' supported' } + else { return 'this API is not ' . $aRef->[0] .' supported' } } else { return "Unknown argument $cmd, choose one of update:noArg newLocation"; @@ -628,27 +647,33 @@ sub Weather_Set($@) { } ################################### -sub Weather_RearmTimer($$) { - my ( $hash, $t ) = @_; +sub Weather_RearmTimer { + my $hash = shift; + my $t = shift; Log3( $hash, 4, "Weather $hash->{NAME}: Rearm new Timer" ); InternalTimer( $t, "Weather_GetUpdate", $hash, 0 ); - + + return; } -sub Weather_DisarmTimer($) { - my ($hash) = @_; +sub Weather_DisarmTimer { + my $hash = shift; RemoveInternalTimer($hash); + + return; } -sub Weather_Notify($$) { - my ( $hash, $dev ) = @_; - my $name = $hash->{NAME}; - my $type = $hash->{TYPE}; +sub Weather_Notify { + my $hash = shift; + my $dev = shift; + + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; return if ( $dev->{NAME} ne "global" ); - return if ( !grep( m/^INITIALIZED|REREADCFG$/, @{ $dev->{CHANGED} } ) ); + return if ( !grep { /^INITIALIZED|REREADCFG$/ } @{ $dev->{CHANGED} } ); # return if($attr{$name} && $attr{$name}->{disable}); @@ -665,14 +690,17 @@ sub Weather_Notify($$) { ### quick run GetUpdate then Demo Weather_GetUpdate($hash) - if ( defined( $hash->{APIKEY} ) and lc( $hash->{APIKEY} ) eq 'demo' ); + if ( defined( $hash->{APIKEY} ) && lc( $hash->{APIKEY} ) eq 'demo' ); - return undef; + return; } ##################################### -sub Weather_Define($$) { - my ( $hash, $def ) = @_; +sub Weather_Define { + my $hash = shift // return; + my $aRef = shift // return; + my $hRef = shift // undef; + return $@ unless ( FHEM::Meta::SetInternals($hash) ); use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); @@ -680,42 +708,33 @@ sub Weather_Define($$) { my $usage = "syntax: define Weather [API=] [apikey=] [location=] [interval=] [lang=]"; - # defaults - my $API = "DarkSkyAPI,cachemaxage:600"; - my $interval = 3600; - - # parse parameters - my ( $arrayref, $hashref ) = parseParams($def); - my @a = @{$arrayref}; - my %h = %{$hashref}; - # check minimum syntax - return $usage unless ( scalar @a == 2 ); - my $name = $a[0]; + return $usage unless ( scalar @{$aRef} == 2 ); + my $name = $aRef->[0]; - my $location = $h{location} if exists $h{location}; - my $apikey = $h{apikey} if exists $h{apikey}; - my $lang = $h{lang} if exists $h{lang}; - $interval = $h{interval} if exists $h{interval}; - $API = $h{API} if exists $h{API}; + my $location = $hRef->{location} // undef; + my $apikey = $hRef->{apikey} // undef; + my $lang = $hRef->{lang} // undef; + my $interval = $hRef->{interval} // 3600; + my $API = $hRef->{API} // "DarkSkyAPI,cachemaxage:600"; # evaluate API options my ( $api, $apioptions ) = split( ',', $API, 2 ); $apioptions = "" unless ( defined($apioptions) ); - eval { require "$api.pm"; }; + eval { require $api . '.pm'; }; return "$name: cannot load API $api: $@" if ($@); $hash->{NOTIFYDEV} = "global"; $hash->{fhem}->{interfaces} = "temperature;humidity;wind"; $hash->{fhem}->{LOCATION} = ( - ( defined($location) and $location ) + ( defined($location) && $location ) ? $location : AttrVal( 'global', 'latitude', 'error' ) . ',' . AttrVal( 'global', 'longitude', 'error' ) ); $hash->{INTERVAL} = $interval; $hash->{LANG} = ( - ( defined($lang) and $lang ) + ( defined($lang) && $lang ) ? $lang : lc( AttrVal( 'global', 'language', 'de' ) ) ); @@ -739,58 +758,89 @@ sub Weather_Define($$) { apikey => $hash->{APIKEY}, location => $hash->{fhem}->{LOCATION}, apioptions => $hash->{APIOPTIONS}, - language => $hash->{LANG} - exclude => Weather_parseForcastAttr($hash), + language => $hash->{LANG}, + forecast => AttrVal($name,'forecast',''), + alerts => AttrVal($name,'alerts',0) } ); - Weather_GetUpdate($hash) if ($init_done); + Weather_GetUpdate($hash) + if ($init_done); - return undef; + return; } ##################################### -sub Weather_Undef($$) { - my ( $hash, $arg ) = @_; +sub Weather_Undef { + my $hash = shift; + my $arg = shift; RemoveInternalTimer($hash); - return undef; + return; +} + +sub Weather_Attr { + my ( $cmd, $name, $attrName, $attrVal ) = @_; + my $hash = $defs{$name}; + + given ($attrName) { + when ('forecast') { + if ( $cmd eq 'set' ) { + $hash->{fhem}->{api}->setForecast($attrVal) + } + + $hash->{fhem}->{api}->setForecast(); + } + + when ('alerts') { + if ( $cmd eq 'set' ) { + $hash->{fhem}->{api}->setAlerts($attrVal); + } + + $hash->{fhem}->{api}->setAlerts(); + } + } + + return; } ##################################### # Icon Parameter -use constant ICONHIGHT => 120; -use constant ICONWIDTH => 175; -use constant ICONSCALE => 0.5; +Readonly my $ICONWIDTH => 175; +Readonly my $ICONSCALE => 0.5; ##################################### -sub WeatherIconIMGTag($) { - my $width = int( ICONSCALE * ICONWIDTH ); - my ($icon) = @_; - my $url = FW_IconURL("weather/$icon"); - my $style = " width=$width"; +sub WeatherIconIMGTag { + my $icon = shift; + + my $width = int( $ICONSCALE * $ICONWIDTH ); + my $url = FW_IconURL("weather/$icon"); + my $style = " width=$width"; + return "\"$icon\""; } ##################################### -sub WeatherAsHtmlV($;$$) { - my ( $d, $op1, $op2 ) = @_; +sub WeatherAsHtmlV { + my $d = shift; + my $op1 = shift; + my $op2 = shift; my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); my $h = $defs{$d}; - my $width = int( ICONSCALE * ICONWIDTH ); + my $width = int( $ICONSCALE * $ICONWIDTH ); my $ret = ''; my $fc; if ( defined($f) - and ( $f eq 'h' - or $f eq 'd' ) + && ( $f eq 'h' + || $f eq 'd' ) ) { $fc = ( $f eq 'd' ? 'fc' : 'hfc' ); @@ -799,7 +849,7 @@ sub WeatherAsHtmlV($;$$) { $fc = ( ( defined( $h->{READINGS}->{fc1_day_of_week} ) - and $h->{READINGS}->{fc1_day_of_week} + && $h->{READINGS}->{fc1_day_of_week} ) ? 'fc' : 'hfc' ); } @@ -816,7 +866,7 @@ sub WeatherAsHtmlV($;$$) { for ( my $i = 1 ; $i < $items ; $i++ ) { if ( defined( $h->{READINGS}->{"${fc}${i}_low_c"} ) - and $h->{READINGS}->{"${fc}${i}_low_c"} ) + && $h->{READINGS}->{"${fc}${i}_low_c"} ) { $ret .= sprintf( '', @@ -846,21 +896,25 @@ sub WeatherAsHtmlV($;$$) { return $ret; } -sub WeatherAsHtml($;$$) { - my ( $d, $op1, $op2 ) = @_; +sub WeatherAsHtml { + my $d = shift; + my $op1 = shift; + my $op2 = shift; my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); - WeatherAsHtmlV( $d, $f, $items ); + return WeatherAsHtmlV( $d, $f, $items ); } -sub WeatherAsHtmlH($;$$) { - my ( $d, $op1, $op2 ) = @_; +sub WeatherAsHtmlH { + my $d = shift; + my $op1 = shift; + my $op2 = shift; my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); my $h = $defs{$d}; - my $width = int( ICONSCALE * ICONWIDTH ); + my $width = int( $ICONSCALE * $ICONWIDTH ); my $format = ''; @@ -869,8 +923,8 @@ sub WeatherAsHtmlH($;$$) { my $fc; if ( defined($f) - and ( $f eq 'h' - or $f eq 'd' ) + && ( $f eq 'h' + || $f eq 'd' ) ) { $fc = ( $f eq 'd' ? 'fc' : 'hfc' ); @@ -879,7 +933,7 @@ sub WeatherAsHtmlH($;$$) { $fc = ( ( defined( $h->{READINGS}->{fc1_day_of_week} ) - and $h->{READINGS}->{fc1_day_of_week} + && $h->{READINGS}->{fc1_day_of_week} ) ? 'fc' : 'hfc' ); } @@ -914,7 +968,7 @@ sub WeatherAsHtmlH($;$$) { ); for ( my $i = 1 ; $i < $items ; $i++ ) { if ( defined( $h->{READINGS}->{"${fc}${i}_low_c"} ) - and $h->{READINGS}->{"${fc}${i}_low_c"} ) + && $h->{READINGS}->{"${fc}${i}_low_c"} ) { $ret .= sprintf( '', ReadingsVal( $d, "${fc}${i}_low_c", " - " ) ); @@ -932,7 +986,7 @@ sub WeatherAsHtmlH($;$$) { ReadingsVal( $d, "wind_condition", "" ) ); for ( my $i = 1 ; $i < $items ; $i++ ) { if ( defined( $h->{READINGS}->{"${fc}${i}_high_c"} ) - and $h->{READINGS}->{"${fc}${i}_high_c"} ) + && $h->{READINGS}->{"${fc}${i}_high_c"} ) { $ret .= sprintf( '', ReadingsVal( $d, "${fc}${i}_high_c", " - " ) ); @@ -944,8 +998,10 @@ sub WeatherAsHtmlH($;$$) { return $ret; } -sub WeatherAsHtmlD($;$$) { - my ( $d, $op1, $op2 ) = @_; +sub WeatherAsHtmlD { + my $d = shift; + my $op1 = shift; + my $op2 = shift; my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); @@ -955,19 +1011,23 @@ sub WeatherAsHtmlD($;$$) { else { WeatherAsHtmlH( $d, $f, $items ); } + + return; } -sub WeatherCheckOptions($@) { - my ( $d, $op1, $op2 ) = @_; +sub WeatherCheckOptions { + my $d = shift; + my $op1 = shift; + my $op2 = shift; my $items = $op2; my $f = $op1; - if ( defined($op1) and $op1 and $op1 =~ /[0-9]/g ) { $items = $op1; } - if ( defined($op2) and $op2 and $op2 =~ /[dh]/g ) { $f = $op2; } + if ( defined($op1) && $op1 && $op1 =~ m{[0-9]}xms ) { $items = $op1; } + if ( defined($op2) && $op2 && $op2 =~ m{[dh]}xms ) { $f = $op2; } - $f =~ tr/dh/./cd if ( defined $f and $f ); - $items =~ tr/0-9/./cd if ( defined($items) and $items ); + $f =~ tr/dh/./cd if ( defined $f && $f ); + $items =~ tr/0-9/./cd if ( defined($items) && $items ); $items = 6 if ( !$items ); @@ -1053,9 +1113,8 @@ sub WeatherCheckOptions($@) {
%s%s: %s
min %s°C max %s°C
%s
%s
%s
%s°C %s%%
%s
min %s°Cmax %s°C
- +
APIDarkSkyAPI
apioptionscachemaxage=<cachemaxage>
duration - in seconds to retrieve the forecast from the cache instead from the API
extend=hourly -
extends the number of hours forecast records to 149
apioptionscachemaxage:<cachemaxage>
duration + in seconds to retrieve the forecast from the cache instead from the API
location<latitude,longitude>
geographic coordinates in degrees of the location for which the weather is forecast; if missing, the values of the attributes @@ -1067,7 +1126,7 @@ sub WeatherCheckOptions($@) { -
APIOpenWeatherMapAPI
apioptionscachemaxage=<cachemaxage>
duration +
apioptionscachemaxage:<cachemaxage>
duration in seconds to retrieve the forecast from the cache instead from the API
location<latitude,longitude>
geographic coordinates in degrees of the location for which the @@ -1080,7 +1139,7 @@ sub WeatherCheckOptions($@) { -
APIwundergroundAPI
apioptionscachemaxage=<cachemaxage>
duration +
apioptionscachemaxage:<cachemaxage>
duration in seconds to retrieve the forecast from the cache instead from the API
stationId:ID-Num
Station ID of the station to be read.
location<latitude,longitude>
@@ -1247,10 +1306,9 @@ sub WeatherCheckOptions($@) { - + sondern aus dem Cache zurück geliefert wird.
APIDarkSkyAPI
apioptionscachemaxage=<cachemaxage>
Zeitdauer in +
apioptionscachemaxage:<cachemaxage>
Zeitdauer in Sekunden, innerhalb derer die Wettervorhersage nicht neu abgerufen - sondern aus dem Cache zurück geliefert wird.
extend=hourly -
erweitert die Anzahl der Datensätze für die Stundenvorhersage auf 149
location<latitude,longitude>
Geographische Breite und Länge des Ortes in Grad, für den das Wetter vorhergesagt wird. Bei fehlender Angabe werden die Werte aus den gleichnamigen Attributen @@ -1262,7 +1320,7 @@ sub WeatherCheckOptions($@) { -
APIOpenWeatherMapAPI
apioptionscachemaxage=<cachemaxage> Zeitdauer in +
apioptionscachemaxage:<cachemaxage> Zeitdauer in Sekunden, innerhalb derer die Wettervorhersage nicht neu abgerufen sondern aus dem Cache zurück geliefert wird.
location<latitude,longitude> Geographische Breite @@ -1276,7 +1334,7 @@ sub WeatherCheckOptions($@) { - diff --git a/DarkSkyAPI.pm b/DarkSkyAPI.pm index ab2ffc8..52afcce 100644 --- a/DarkSkyAPI.pm +++ b/DarkSkyAPI.pm @@ -157,6 +157,8 @@ sub new { lat => ( split( ',', $argsRef->{location} ) )[0], long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, + forecast => $argsRef->{forecast}, + alerts => $argsRef->{alerts}, }; $self->{cachemaxage} = ( @@ -189,6 +191,22 @@ sub parseApiOptions($) { return \%h; } +sub setAlerts { + my $self = shift; + my $alerts = shift // 0; + + $self->{alerts} = $alerts; + return; +} + +sub setForecast { + my $self = shift; + my $forecast = shift // ''; + + $self->{forecast} = $forecast; + return; +} + sub setFetchTime { my $self = shift; diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index eb9b224..d9a39db 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -116,7 +116,8 @@ eval { use Readonly; 1 } or $missingModul .= 'Readonly '; # apt install libreadonly-perl ## use critic -Readonly my $URL => 'https://api.openweathermap.org/data/2.5/'; +# Readonly my $URL => 'https://api.openweathermap.org/data/2.5/'; +Readonly my $URL => 'https://api.openweathermap.org/data/'; ## URL . 'weather?' for current data ## URL . 'onecall?' for forecast data @@ -180,7 +181,7 @@ my %codes = ( sub new { ### geliefert wird ein Hash my ( $class, $argsRef ) = @_; - my $apioptions = parseApiOptions( $argsRef->{apioptions} ); + my $apioptions = _parseApiOptions( $argsRef->{apioptions} ); my $self = { devName => $argsRef->{devName}, @@ -194,7 +195,8 @@ sub new { long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, endpoint => 'none', - exclude => $argsRef->{exclude}, + forecast => $argsRef->{forecast}, + alerts => $argsRef->{alerts}, }; $self->{cachemaxage} = ( @@ -203,13 +205,15 @@ sub new { : 900 ); - $self->{cached} = _CreateForecastRef($self); + $self->{apiversion} = ($apioptions->{version} ? $apioptions->{version} : '2.5'); + + $self->{cached} = _CreateForecastRef($self); bless $self, $class; return $self; } -sub parseApiOptions { +sub _parseApiOptions { my $apioptions = shift; my @params; @@ -226,27 +230,45 @@ sub parseApiOptions { return \%h; } +sub setAlerts { + my $self = shift; + my $alerts = shift // 0; + + $self->{alerts} = $alerts; + return; +} + +sub setForecast { + my $self = shift; + my $forecast = shift // ''; + + $self->{forecast} = $forecast; + return; +} + sub setFetchTime { my $self = shift; $self->{fetchTime} = time(); - return 0; + return; } sub setRetrieveData { my $self = shift; _RetrieveDataFromOpenWeatherMap($self); - return 0; + return; } sub setLocation { - my ( $self, $lat, $long ) = @_; + my $self = shift; + my $lat = shift; + my $long = shift; $self->{lat} = $lat; $self->{long} = $long; - return 0; + return; } sub getFetchTime { @@ -312,13 +334,15 @@ sub _RetrieveDataFromOpenWeatherMap { else { $paramRef->{url} = $URL + . $self->{apiversion} . '/' . $paramRef->{endpoint} . '?' . 'lat=' . $self->{lat} . '&' . 'lon=' . $self->{long} . '&' . 'APPID=' - . $self->{key} . '&' . 'lang=' + . $self->{key} . '&' . 'units=' + . 'metric' . '&' . 'lang=' . $self->{lang} . '&' . 'exclude=' - . $self->{exclude}; + . _createExcludeString($self->{forecast},$self->{alerts}); ::HttpUtils_NonblockingGet($paramRef); } @@ -326,6 +350,20 @@ sub _RetrieveDataFromOpenWeatherMap { return; } +sub _createExcludeString { + my $forecast = shift; + my $alerts = shift; + + my @exclude = qw/alerts minutely hourly daily/; + my @forecast = split(',',$forecast); + my @alerts = ( $alerts ? ',alerts' : '' ); + + my %in_forecast = map {$_ => 1} @forecast,@alerts; + my @diff = grep {not $in_forecast{$_}} @exclude; + + return join(',',@alerts); +} + sub _RetrieveDataFinished { my $paramRef = shift; my $err = shift; @@ -387,36 +425,31 @@ sub _ProcessingRetrieveData { $self->{cached}->{current} = { 'temperature' => int( sprintf( "%.1f", - ( $data->{main}->{temp} - 273.15 ) ) + 0.5 + $data->{main}->{temp}) + 0.5 ), 'temp_c' => int( sprintf( "%.1f", - ( $data->{main}->{temp} - 273.15 ) ) + 0.5 + $data->{main}->{temp}) + 0.5 ), 'low_c' => int( sprintf( "%.1f", - ( $data->{main}->{temp_min} - 273.15 ) ) + - 0.5 + $data->{main}->{temp_min}) + 0.5 ), 'high_c' => int( sprintf( "%.1f", - ( $data->{main}->{temp_max} - 273.15 ) ) + - 0.5 + $data->{main}->{temp_max}) + 0.5 ), 'tempLow' => int( sprintf( "%.1f", - ( $data->{main}->{temp_min} - 273.15 ) ) + - 0.5 + $data->{main}->{temp_min}) + 0.5 ), 'tempHigh' => int( sprintf( "%.1f", - ( $data->{main}->{temp_max} - 273.15 ) ) + - 0.5 + $data->{main}->{temp_max}) + 0.5 ), 'tempFeelsLike_c' => int( sprintf( "%.1f", - ( $data->{main}->{feels_like} - 273.15 ) ) - + 0.5 + $data->{main}->{feels_like}) + 0.5 ), 'humidity' => $data->{main}->{humidity}, 'condition' => encode_utf8( @@ -493,7 +526,7 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{temp} - 273.15 + ->{temp} ) ) + 0.5 ), @@ -502,7 +535,7 @@ sub _ProcessingRetrieveData { "%.1f", ( $data->{hourly}->[$i] - ->{temp} - 273.15 + ->{temp} ) ) + 0.5 ), @@ -627,126 +660,85 @@ sub _ProcessingRetrieveData { 'temperature' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{day} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{day} ) + 0.5 ), 'temperature_morn' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{morn} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{morn} ) + 0.5 ), 'temperature_eve' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{eve} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{eve} ) + 0.5 ), 'temperature_night' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{night} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{night} ) + 0.5 ), 'tempFeelsLike_morn' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{feels_like}->{morn} - 273.15 - ) + $data->{daily}->[$i]->{feels_like}->{morn} ) + 0.5 ), 'tempFeelsLike_eve' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{feels_like}->{eve} - 273.15 - ) + $data->{daily}->[$i]->{feels_like}->{eve} ) + 0.5 ), 'tempFeelsLike_night' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{feels_like}->{night} - 273.15 - ) + $data->{daily}->[$i]->{feels_like}->{night} ) + 0.5 ), 'tempFeelsLike_day' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{feels_like}->{day} - 273.15 - ) + $data->{daily}->[$i]->{feels_like}->{day} ) + 0.5 ), 'temp_c' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{day} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{day} ) + 0.5 ), 'low_c' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{min} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{min} ) + 0.5 ), 'high_c' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{max} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{max} ) + 0.5 ), 'tempLow' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{min} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{min} ) + 0.5 ), 'tempHigh' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i] - ->{temp}->{max} - 273.15 - ) + $data->{daily}->[$i]->{temp}->{max} ) + 0.5 ), 'dew_point' => int( sprintf( "%.1f", - ( - $data->{daily}->[$i]->{dew_point} - 273.15 - ) + $data->{daily}->[$i]->{dew_point} ) + 0.5 ), 'humidity' => diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 726b4eb..47818e6 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -109,6 +109,8 @@ sub new { lat => ( split( ',', $argsRef->{location} ) )[0], long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, + forecast => $argsRef->{forecast}, + alerts => $argsRef->{alerts}, }; $self->{cachemaxage} = ( @@ -181,6 +183,22 @@ sub setLocation { return 0; } +sub setAlerts { + my $self = shift; + my $alerts = shift // 0; + + $self->{alerts} = $alerts; + return; +} + +sub setForecast { + my $self = shift; + my $forecast = shift // ''; + + $self->{forecast} = $forecast; + return; +} + sub getFetchTime { my $self = shift; -- 2.47.2 From fdd6f0964aae0b8e13961e88887735c53f78fb33 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 25 Nov 2022 14:09:45 +0100 Subject: [PATCH 05/24] full api support weather and onecall --- 59_Weather.pm | 12 ------------ OpenWeatherMapAPI.pm | 37 ++++++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index cc80c32..2137dc3 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -361,18 +361,6 @@ sub Weather_WriteReadings { 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} ) { diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index d9a39db..4c8d897 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -488,11 +488,11 @@ sub _ProcessingRetrieveData { "%a, %e %b %Y %H:%M", localtime( $data->{dt} ) ), - }; + 'visibility' => int( sprintf( + "%.1f", $data->{visibility} ) + 0.5 + ), - $self->{cached}->{current}->{'visibility'} = - int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ) - if ( exists $data->{visibility} ); + }; } when ('onecall') { @@ -539,6 +539,21 @@ sub _ProcessingRetrieveData { ) ) + 0.5 ), + 'tempFeelsLike' => int( + sprintf( + "%.1f", + ( + $data->{hourly}->[$i] + ->{feels_like} + ) + ) + 0.5 + ), + 'dew_point' => int( + sprintf( + "%.1f", + $data->{hourly}->[$i]->{dew_point} + ) + 0.5 + ), 'humidity' => $data->{hourly}->[$i] ->{humidity}, @@ -592,6 +607,12 @@ sub _ProcessingRetrieveData { $data->{hourly}->[$i]->{rain}->{'1h'}, 'snow1h' => $data->{hourly}->[$i]->{snow}->{'1h'}, + 'uvi' => + $data->{hourly}->[$i]->{uvi}, + 'visibility' => int( sprintf( + "%.1f", $data->{hourly}->[$i]->{visibility} ) + + 0.5 + ), }, ); @@ -648,6 +669,9 @@ sub _ProcessingRetrieveData { ) - 3600 ) ), + 'moon_phase' => $data->{daily}->[$i] + ->{moon_phase} + ), 'moonset' => strftime( "%a, %H:%M", localtime( @@ -747,6 +771,8 @@ sub _ProcessingRetrieveData { $data->{daily}->[$i]->{weather} ->[0]->{description} ), + 'code' => $codes{ $data->{daily}->[$i]->{weather}->[0]->{id} }, + 'iconAPI' => $data->{daily}->[$i]->{weather}->[0]->{icon}, 'pressure' => int( sprintf( "%.1f", $data->{daily}->[$i]->{pressure} @@ -843,10 +869,11 @@ sub _ProcessingRetrieveData { ), }, ); - $i++; } } + + $self->{cached}->{current}->{dew_point} = $data->{current}->{dew_point} } } } -- 2.47.2 From 43af5222c533ef9d4cdee8493bd0b2704c272236 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 25 Nov 2022 20:02:39 +0100 Subject: [PATCH 06/24] remove double dewpoint reading --- OpenWeatherMapAPI.pm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 4c8d897..f6be947 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -670,8 +670,7 @@ sub _ProcessingRetrieveData { ) ), 'moon_phase' => $data->{daily}->[$i] - ->{moon_phase} - ), + ->{moon_phase}, 'moonset' => strftime( "%a, %H:%M", localtime( @@ -869,11 +868,10 @@ sub _ProcessingRetrieveData { ), }, ); + $i++; } } - - $self->{cached}->{current}->{dew_point} = $data->{current}->{dew_point} } } } -- 2.47.2 From 8ec9dae9028908d1eaad968817ad57ebe6ae963b Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 26 Nov 2022 06:43:20 +0100 Subject: [PATCH 07/24] better formart --- 59_Weather.pm | 162 +++++++++++++------------- OpenWeatherMapAPI.pm | 263 ++++++++++++++++++++++--------------------- 2 files changed, 210 insertions(+), 215 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 2137dc3..f28569e 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -31,14 +31,13 @@ package main; use strict; use warnings; -use Time::HiRes qw(gettimeofday); +use Time::HiRes qw(gettimeofday); use experimental qw /switch/; use Readonly; use FHEM::Meta; use vars qw($FW_ss); - # use Data::Dumper; # for Debug only my %pressure_trend_txt_en = ( 0 => "steady", 1 => "rising", 2 => "falling" ); @@ -277,19 +276,19 @@ sub Weather_DebugCodes { sub Weather_Initialize { my $hash = shift; - $hash->{DefFn} = \&Weather_Define; - $hash->{UndefFn} = \&Weather_Undef; - $hash->{GetFn} = \&Weather_Get; - $hash->{SetFn} = \&Weather_Set; - $hash->{AttrFn} = \&Weather_Attr; - $hash->{AttrList} = + $hash->{DefFn} = \&Weather_Define; + $hash->{UndefFn} = \&Weather_Undef; + $hash->{GetFn} = \&Weather_Get; + $hash->{SetFn} = \&Weather_Set; + $hash->{AttrFn} = \&Weather_Attr; + $hash->{AttrList} = 'disable:0,1 ' . 'forecast:multiple-strict,hourly,daily ' . 'forecastLimit ' . 'alerts:0,1 ' . $readingFnAttributes; - $hash->{NotifyFn} = \&Weather_Notify; - $hash->{parseParams} = 1; + $hash->{NotifyFn} = \&Weather_Notify; + $hash->{parseParams} = 1; return FHEM::Meta::InitMod( __FILE__, $hash ); } @@ -332,7 +331,7 @@ sub Weather_ReturnWithError { sub Weather_RetrieveCallbackFn { my $name = shift; - + return unless ( IsDevice($name) ); @@ -350,14 +349,13 @@ sub Weather_RetrieveCallbackFn { } sub Weather_WriteReadings { - my $hash = shift; + my $hash = shift; my $dataRef = shift; - my $name = $hash->{NAME}; - my $hourly = ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1: 0 ); - my $daily = ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); - my $alerts = ( AttrVal( $name, 'forecast', '' ) =~ m{alerts}xms ? 1 : 0 ); - + my $name = $hash->{NAME}; + my $hourly = ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1 : 0 ); + my $daily = ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); + my $alerts = ( AttrVal( $name, 'forecast', '' ) =~ m{alerts}xms ? 1 : 0 ); readingsBeginUpdate($hash); @@ -400,17 +398,15 @@ sub Weather_WriteReadings { ### forecast if ( ref( $dataRef->{forecast} ) eq 'HASH' - && ($hourly || $daily) ) + && ( $hourly || $daily ) ) { ## hourly - if ( - defined( $dataRef->{forecast}->{hourly} ) + if ( defined( $dataRef->{forecast}->{hourly} ) && ref( $dataRef->{forecast}->{hourly} ) eq 'ARRAY' && scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0 - && $hourly - ) + && $hourly ) { - my $i = 0; + my $i = 0; my $limit = AttrVal( $name, 'forecastLimit', -1 ); foreach my $fc ( @{ $dataRef->{forecast}->{hourly} } ) { $i++; @@ -429,7 +425,7 @@ sub Weather_WriteReadings { if ( defined( - $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} + $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} ) && $dataRef->{forecast}->{hourly}[ $i - 1 ]{wind_direction} && defined( @@ -458,14 +454,12 @@ sub Weather_WriteReadings { } ## daily - if ( - defined( $dataRef->{forecast}->{daily} ) + if ( defined( $dataRef->{forecast}->{daily} ) && ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY' && scalar( @{ $dataRef->{forecast}->{daily} } ) > 0 - && $daily - ) + && $daily ) { - my $i = 0; + my $i = 0; my $limit = AttrVal( $name, 'forecastLimit', -1 ); foreach my $fc ( @{ $dataRef->{forecast}->{daily} } ) { $i++; @@ -484,7 +478,7 @@ sub Weather_WriteReadings { if ( defined( - $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} + $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} ) && $dataRef->{forecast}->{daily}[ $i - 1 ]{wind_direction} && defined( @@ -513,9 +507,9 @@ sub Weather_WriteReadings { } if ( ref( $dataRef->{alerts} ) eq 'HASH' - && $alerts ) + && $alerts ) { - while ( my ( $r, $v ) = each %{ $dataRef->{alerts} } ) { + while ( my ( $r, $v ) = each %{ $dataRef->{alerts} } ) { readingsBulkUpdate( $hash, $r, $v ) if ( ref( $dataRef->{$r} ) ne 'HASH' && ref( $dataRef->{$r} ) ne 'ARRAY' ); @@ -568,7 +562,7 @@ sub Weather_Get { my $hash = shift // return; my $aRef = shift // return; - my $name = shift @$aRef // return; + my $name = shift @$aRef // return; my $reading = shift @$aRef // return; my $value; @@ -597,37 +591,36 @@ sub Weather_Set { // return qq{"set $name" needs at least one argument}; # usage check - if ( scalar(@{$aRef}) == 0 - && $cmd eq 'update' ) + if ( scalar( @{$aRef} ) == 0 + && $cmd eq 'update' ) { Weather_DisarmTimer($hash); Weather_GetUpdate($hash); return; } - elsif ( scalar(@{$aRef}) == 1 - && $cmd eq "newLocation" ) + elsif ( scalar( @{$aRef} ) == 1 + && $cmd eq "newLocation" ) { if ( $hash->{API} eq 'DarkSkyAPI' || $hash->{API} eq 'OpenWeatherMapAPI' - || $hash->{API} eq 'wundergroundAPI' - ) + || $hash->{API} eq 'wundergroundAPI' ) { - my ($lat,$long); - ($lat,$long) = split(',',$aRef->[0]) - if ( defined($aRef->[0]) && $aRef->[0] ); - ($lat,$long) = split(',',$hash->{fhem}->{LOCATION}) + my ( $lat, $long ); + ( $lat, $long ) = split( ',', $aRef->[0] ) + if ( defined( $aRef->[0] ) && $aRef->[0] ); + ( $lat, $long ) = split( ',', $hash->{fhem}->{LOCATION} ) unless ( defined($lat) && defined($long) - && $lat =~ m{(-?\d+(\.\d+)?)}xms + && $lat =~ m{(-?\d+(\.\d+)?)}xms && $long =~ m{(-?\d+(\.\d+)?)}xms ); - $hash->{fhem}->{api}->setLocation($lat,$long); + $hash->{fhem}->{api}->setLocation( $lat, $long ); Weather_DisarmTimer($hash); Weather_GetUpdate($hash); return; } - else { return 'this API is not ' . $aRef->[0] .' supported' } + else { return 'this API is not ' . $aRef->[0] . ' supported' } } else { return "Unknown argument $cmd, choose one of update:noArg newLocation"; @@ -636,12 +629,12 @@ sub Weather_Set { ################################### sub Weather_RearmTimer { - my $hash = shift; - my $t = shift; + my $hash = shift; + my $t = shift; Log3( $hash, 4, "Weather $hash->{NAME}: Rearm new Timer" ); InternalTimer( $t, "Weather_GetUpdate", $hash, 0 ); - + return; } @@ -654,11 +647,11 @@ sub Weather_DisarmTimer { } sub Weather_Notify { - my $hash = shift; - my $dev = shift; - - my $name = $hash->{NAME}; - my $type = $hash->{TYPE}; + my $hash = shift; + my $dev = shift; + + my $name = $hash->{NAME}; + my $type = $hash->{TYPE}; return if ( $dev->{NAME} ne "global" ); return if ( !grep { /^INITIALIZED|REREADCFG$/ } @{ $dev->{CHANGED} } ); @@ -685,10 +678,9 @@ sub Weather_Notify { ##################################### sub Weather_Define { - my $hash = shift // return; - my $aRef = shift // return; - my $hRef = shift // undef; - + my $hash = shift // return; + my $aRef = shift // return; + my $hRef = shift // undef; return $@ unless ( FHEM::Meta::SetInternals($hash) ); use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); @@ -700,11 +692,11 @@ sub Weather_Define { return $usage unless ( scalar @{$aRef} == 2 ); my $name = $aRef->[0]; - my $location = $hRef->{location} // undef; - my $apikey = $hRef->{apikey} // undef; - my $lang = $hRef->{lang} // undef; - my $interval = $hRef->{interval} // 3600; - my $API = $hRef->{API} // "DarkSkyAPI,cachemaxage:600"; + my $location = $hRef->{location} // undef; + my $apikey = $hRef->{apikey} // undef; + my $lang = $hRef->{lang} // undef; + my $interval = $hRef->{interval} // 3600; + my $API = $hRef->{API} // "DarkSkyAPI,cachemaxage:600"; # evaluate API options my ( $api, $apioptions ) = split( ',', $API, 2 ); @@ -726,15 +718,15 @@ sub Weather_Define { ? $lang : lc( AttrVal( 'global', 'language', 'de' ) ) ); - $hash->{API} = $api; - $hash->{MODEL} = $api; - $hash->{APIKEY} = $apikey; - $hash->{APIOPTIONS} = $apioptions; - $hash->{VERSION} = version->parse($VERSION)->normal; - $hash->{fhem}->{allowCache} = 1; + $hash->{API} = $api; + $hash->{MODEL} = $api; + $hash->{APIKEY} = $apikey; + $hash->{APIOPTIONS} = $apioptions; + $hash->{VERSION} = version->parse($VERSION)->normal; + $hash->{fhem}->{allowCache} = 1; - readingsSingleUpdate($hash,'current_date_time',TimeNow(),0); - readingsSingleUpdate($hash,'current_date_time','none',0); + readingsSingleUpdate( $hash, 'current_date_time', TimeNow(), 0 ); + readingsSingleUpdate( $hash, 'current_date_time', 'none', 0 ); readingsSingleUpdate( $hash, 'state', 'Initialized', 1 ); Weather_LanguageInitialize( $hash->{LANG} ); @@ -747,8 +739,8 @@ sub Weather_Define { location => $hash->{fhem}->{LOCATION}, apioptions => $hash->{APIOPTIONS}, language => $hash->{LANG}, - forecast => AttrVal($name,'forecast',''), - alerts => AttrVal($name,'alerts',0) + forecast => AttrVal( $name, 'forecast', '' ), + alerts => AttrVal( $name, 'alerts', 0 ) } ); @@ -760,8 +752,8 @@ sub Weather_Define { ##################################### sub Weather_Undef { - my $hash = shift; - my $arg = shift; + my $hash = shift; + my $arg = shift; RemoveInternalTimer($hash); return; @@ -774,7 +766,7 @@ sub Weather_Attr { given ($attrName) { when ('forecast') { if ( $cmd eq 'set' ) { - $hash->{fhem}->{api}->setForecast($attrVal) + $hash->{fhem}->{api}->setForecast($attrVal); } $hash->{fhem}->{api}->setForecast(); @@ -802,11 +794,11 @@ Readonly my $ICONSCALE => 0.5; ##################################### sub WeatherIconIMGTag { - my $icon = shift; + my $icon = shift; - my $width = int( $ICONSCALE * $ICONWIDTH ); - my $url = FW_IconURL("weather/$icon"); - my $style = " width=$width"; + my $width = int( $ICONSCALE * $ICONWIDTH ); + my $url = FW_IconURL("weather/$icon"); + my $style = " width=$width"; return "\"$icon\""; } @@ -827,8 +819,8 @@ sub WeatherAsHtmlV { my $fc; if ( defined($f) - && ( $f eq 'h' - || $f eq 'd' ) + && ( $f eq 'h' + || $f eq 'd' ) ) { $fc = ( $f eq 'd' ? 'fc' : 'hfc' ); @@ -911,8 +903,8 @@ sub WeatherAsHtmlH { my $fc; if ( defined($f) - && ( $f eq 'h' - || $f eq 'd' ) + && ( $f eq 'h' + || $f eq 'd' ) ) { $fc = ( $f eq 'd' ? 'fc' : 'hfc' ); @@ -1014,7 +1006,7 @@ sub WeatherCheckOptions { if ( defined($op1) && $op1 && $op1 =~ m{[0-9]}xms ) { $items = $op1; } if ( defined($op2) && $op2 && $op2 =~ m{[dh]}xms ) { $f = $op2; } - $f =~ tr/dh/./cd if ( defined $f && $f ); + $f =~ tr/dh/./cd if ( defined $f && $f ); $items =~ tr/0-9/./cd if ( defined($items) && $items ); $items = 6 if ( !$items ); diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index f6be947..c65fac8 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -205,10 +205,11 @@ sub new { : 900 ); - $self->{apiversion} = ($apioptions->{version} ? $apioptions->{version} : '2.5'); + $self->{apiversion} = + ( $apioptions->{version} ? $apioptions->{version} : '2.5' ); + + $self->{cached} = _CreateForecastRef($self); - $self->{cached} = _CreateForecastRef($self); - bless $self, $class; return $self; } @@ -231,18 +232,18 @@ sub _parseApiOptions { } sub setAlerts { - my $self = shift; - my $alerts = shift // 0; + my $self = shift; + my $alerts = shift // 0; $self->{alerts} = $alerts; return; } sub setForecast { - my $self = shift; - my $forecast = shift // ''; + my $self = shift; + my $forecast = shift // ''; - $self->{forecast} = $forecast; + $self->{forecast} = $forecast; return; } @@ -261,9 +262,9 @@ sub setRetrieveData { } sub setLocation { - my $self = shift; - my $lat = shift; - my $long = shift; + my $self = shift; + my $lat = shift; + my $long = shift; $self->{lat} = $lat; $self->{long} = $long; @@ -339,10 +340,12 @@ sub _RetrieveDataFromOpenWeatherMap { . $self->{lat} . '&' . 'lon=' . $self->{long} . '&' . 'APPID=' - . $self->{key} . '&' . 'units=' + . $self->{key} . '&' + . 'units=' . 'metric' . '&' . 'lang=' - . $self->{lang} . '&' . 'exclude=' - . _createExcludeString($self->{forecast},$self->{alerts}); + . $self->{lang} . '&' + . 'exclude=' + . _createExcludeString( $self->{forecast}, $self->{alerts} ); ::HttpUtils_NonblockingGet($paramRef); } @@ -351,17 +354,17 @@ sub _RetrieveDataFromOpenWeatherMap { } sub _createExcludeString { - my $forecast = shift; - my $alerts = shift; - - my @exclude = qw/alerts minutely hourly daily/; - my @forecast = split(',',$forecast); - my @alerts = ( $alerts ? ',alerts' : '' ); + my $forecast = shift; + my $alerts = shift; - my %in_forecast = map {$_ => 1} @forecast,@alerts; - my @diff = grep {not $in_forecast{$_}} @exclude; + my @exclude = qw/alerts minutely hourly daily/; + my @forecast = split( ',', $forecast ); + my @alerts = ( $alerts ? ',alerts' : '' ); - return join(',',@alerts); + my %in_forecast = map { $_ => 1 } @forecast, @alerts; + my @diff = grep { not $in_forecast{$_} } @exclude; + + return join( ',', @alerts ); } sub _RetrieveDataFinished { @@ -410,8 +413,8 @@ sub _ProcessingRetrieveData { } else { ### Debug - # print '!!! DEBUG !!! - Endpoint: ' . $self->{endpoint} . "\n"; - # print '!!! DEBUG !!! - Response: ' . Dumper $data; + # print '!!! DEBUG !!! - Endpoint: ' . $self->{endpoint} . "\n"; + # print '!!! DEBUG !!! - Response: ' . Dumper $data; ###### Ab hier wird die ResponseHash Referenze für die Rückgabe zusammen gestellt $self->{cached}->{current_date_time} = strftimeWrapper( "%a, %e %b %Y %H:%M", @@ -424,32 +427,30 @@ sub _ProcessingRetrieveData { $self->{cached}->{license}{text} = 'none'; $self->{cached}->{current} = { 'temperature' => int( - sprintf( "%.1f", - $data->{main}->{temp}) + 0.5 + sprintf( "%.1f", $data->{main}->{temp} ) + 0.5 ), 'temp_c' => int( - sprintf( "%.1f", - $data->{main}->{temp}) + 0.5 + sprintf( "%.1f", $data->{main}->{temp} ) + 0.5 ), 'low_c' => int( - sprintf( "%.1f", - $data->{main}->{temp_min}) + 0.5 + sprintf( "%.1f", $data->{main}->{temp_min} ) + + 0.5 ), 'high_c' => int( - sprintf( "%.1f", - $data->{main}->{temp_max}) + 0.5 + sprintf( "%.1f", $data->{main}->{temp_max} ) + + 0.5 ), 'tempLow' => int( - sprintf( "%.1f", - $data->{main}->{temp_min}) + 0.5 + sprintf( "%.1f", $data->{main}->{temp_min} ) + + 0.5 ), 'tempHigh' => int( - sprintf( "%.1f", - $data->{main}->{temp_max}) + 0.5 + sprintf( "%.1f", $data->{main}->{temp_max} ) + + 0.5 ), 'tempFeelsLike_c' => int( - sprintf( "%.1f", - $data->{main}->{feels_like}) + 0.5 + sprintf( "%.1f", $data->{main}->{feels_like} ) + + 0.5 ), 'humidity' => $data->{main}->{humidity}, 'condition' => encode_utf8( @@ -488,8 +489,8 @@ sub _ProcessingRetrieveData { "%a, %e %b %Y %H:%M", localtime( $data->{dt} ) ), - 'visibility' => int( sprintf( - "%.1f", $data->{visibility} ) + 0.5 + 'visibility' => int( + sprintf( "%.1f", $data->{visibility} ) + 0.5 ), }; @@ -549,14 +550,12 @@ sub _ProcessingRetrieveData { ) + 0.5 ), 'dew_point' => int( - sprintf( - "%.1f", - $data->{hourly}->[$i]->{dew_point} - ) + 0.5 + sprintf( "%.1f", + $data->{hourly}->[$i] + ->{dew_point} ) + 0.5 ), 'humidity' => - $data->{hourly}->[$i] - ->{humidity}, + $data->{hourly}->[$i]->{humidity}, 'condition' => encode_utf8( $data->{hourly}->[$i]->{weather} ->[0]->{description} @@ -594,8 +593,7 @@ sub _ProcessingRetrieveData { ) + 0.5 ), 'cloudCover' => - $data->{hourly}->[$i] - ->{clouds}, + $data->{hourly}->[$i]->{clouds}, 'code' => $codes{ $data->{hourly}->[$i]->{weather} ->[0]->{id} @@ -604,14 +602,14 @@ sub _ProcessingRetrieveData { $data->{hourly}->[$i]->{weather}->[0] ->{icon}, 'rain1h' => - $data->{hourly}->[$i]->{rain}->{'1h'}, + $data->{hourly}->[$i]->{rain}->{'1h'}, 'snow1h' => - $data->{hourly}->[$i]->{snow}->{'1h'}, - 'uvi' => - $data->{hourly}->[$i]->{uvi}, - 'visibility' => int( sprintf( - "%.1f", $data->{hourly}->[$i]->{visibility} ) - + 0.5 + $data->{hourly}->[$i]->{snow}->{'1h'}, + 'uvi' => $data->{hourly}->[$i]->{uvi}, + 'visibility' => int( + sprintf( "%.1f", + $data->{hourly}->[$i] + ->{visibility} ) + 0.5 ), }, ); @@ -669,8 +667,8 @@ sub _ProcessingRetrieveData { ) - 3600 ) ), - 'moon_phase' => $data->{daily}->[$i] - ->{moon_phase}, + 'moon_phase' => + $data->{daily}->[$i]->{moon_phase}, 'moonset' => strftime( "%a, %H:%M", localtime( @@ -681,88 +679,75 @@ sub _ProcessingRetrieveData { ) ), 'temperature' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{day} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{day} ) + 0.5 ), 'temperature_morn' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{morn} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{morn} ) + 0.5 ), 'temperature_eve' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{eve} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{eve} ) + 0.5 ), 'temperature_night' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{night} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{night} ) + 0.5 ), 'tempFeelsLike_morn' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{feels_like}->{morn} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i] + ->{feels_like}->{morn} ) + 0.5 ), 'tempFeelsLike_eve' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{feels_like}->{eve} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i] + ->{feels_like}->{eve} ) + 0.5 ), 'tempFeelsLike_night' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{feels_like}->{night} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i] + ->{feels_like}->{night} ) + + 0.5 ), 'tempFeelsLike_day' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{feels_like}->{day} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i] + ->{feels_like}->{day} ) + 0.5 ), 'temp_c' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{day} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{day} ) + 0.5 ), 'low_c' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{min} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{min} ) + 0.5 ), 'high_c' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{max} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{max} ) + 0.5 ), 'tempLow' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{min} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{min} ) + 0.5 ), 'tempHigh' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{temp}->{max} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i]->{temp} + ->{max} ) + 0.5 ), 'dew_point' => int( - sprintf( - "%.1f", - $data->{daily}->[$i]->{dew_point} - ) + 0.5 + sprintf( "%.1f", + $data->{daily}->[$i] + ->{dew_point} ) + 0.5 ), 'humidity' => $data->{daily}->[$i]->{humidity}, @@ -770,8 +755,13 @@ sub _ProcessingRetrieveData { $data->{daily}->[$i]->{weather} ->[0]->{description} ), - 'code' => $codes{ $data->{daily}->[$i]->{weather}->[0]->{id} }, - 'iconAPI' => $data->{daily}->[$i]->{weather}->[0]->{icon}, + 'code' => $codes{ + $data->{daily}->[$i]->{weather} + ->[0]->{id} + }, + 'iconAPI' => + $data->{daily}->[$i]->{weather}->[0] + ->{icon}, 'pressure' => int( sprintf( "%.1f", $data->{daily}->[$i]->{pressure} @@ -843,29 +833,42 @@ sub _ProcessingRetrieveData { push( @{ $self->{cached}->{alerts} }, { - 'warn_'.$i.'_End' => strftimeWrapper( + 'warn_' + . $i + . '_End' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( - ( $data->{alerts}->[$i]->{end} ) - - 3600 + ( + $data->{alerts}->[$i]->{end} + ) - 3600 ) - ), - 'warn_'.$i.'_Start' => strftimeWrapper( + ), + 'warn_' + . $i + . '_Start' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( - ( $data->{alerts}->[$i]->{start} ) - - 3600 + ( + $data->{alerts}->[$i] + ->{start} + ) - 3600 ) - ), - 'warn_'.$i.'_Description' => encode_utf8( + ), + 'warn_' + . $i + . '_Description' => encode_utf8( $data->{alerts}->[$i]->{description} - ), - 'warn_'.$i.'_SenderName' => encode_utf8( + ), + 'warn_' + . $i + . '_SenderName' => encode_utf8( $data->{alerts}->[$i]->{sender_name} - ), - 'warn_'.$i.'_Event' => encode_utf8( + ), + 'warn_' + . $i + . '_Event' => encode_utf8( $data->{alerts}->[$i]->{event} - ), + ), }, ); -- 2.47.2 From 7ac7e75d825226ea7837cb94829b60d36f2b4a2a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 13 Dec 2022 14:00:46 +0100 Subject: [PATCH 08/24] fix litte bugs in API modul add first code for daily and hourly forcast delete count --- 59_Weather.pm | 12 ++++++ OpenWeatherMapAPI.pm | 92 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index f28569e..0969f91 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -329,6 +329,18 @@ sub Weather_ReturnWithError { return; } +sub Weather_DeleteReadings { + my $hash = shift; + my $name = $hash->{NAME}; + + my $delReadingRegEx; + + CommandDeleteReading( undef, $name . ' .?(ASC)_.*' ); + CommandDeleteReading( undef, $name . ' ' . $delReadingRegEx ); + + return; +} + sub Weather_RetrieveCallbackFn { my $name = shift; diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index c65fac8..6a77a38 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -492,11 +492,94 @@ sub _ProcessingRetrieveData { 'visibility' => int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ), - + 'dew_point' => int( + sprintf( + "%.1f", $data->{current}->{dew_point} + ) + 0.5 + ), }; } when ('onecall') { + if ( !exists( $self->{cached}->{current} ) ) { + $self->{cached}->{license}{text} = 'none'; + $self->{cached}->{current} = { + 'temperature' => int( + sprintf( "%.1f", $data->{current}->{temp} ) + + 0.5 + ), + 'temp_c' => int( + sprintf( "%.1f", $data->{current}->{temp} ) + + 0.5 + ), + 'tempFeelsLike_c' => int( + sprintf( "%.1f", + $data->{current}->{feels_like} ) + 0.5 + ), + + 'humidity' => $data->{current}->{humidity}, + 'condition' => encode_utf8( + $data->{current}->{weather}->[0] + ->{description} + ), + 'pressure' => int( + sprintf( "%.1f", + $data->{current}->{pressure} ) + 0.5 + ), + 'wind' => int( + sprintf( + "%.1f", + ( + $data->{current}->{wind_speed} * 3.6 + ) + ) + 0.5 + ), + 'wind_speed' => int( + sprintf( + "%.1f", + ( + $data->{current}->{wind_speed} * 3.6 + ) + ) + 0.5 + ), + 'wind_gust' => int( + sprintf( "%.1f", + ( $data->{current}->{wind_gust} * 3.6 ) + ) + 0.5 + ), + 'wind_direction' => + $data->{current}->{wind_deg}, + 'rain_1h' => $data->{rain}->{'1h'}, + 'cloudCover' => $data->{current}->{clouds}, + 'code' => $codes{ + $data->{current}->{weather}->[0]->{id} + }, + 'iconAPI' => + $data->{current}->{weather}->[0]->{icon}, + 'condition' => encode_utf8( + $data->{current}->{weather}->[0] + ->{description} + ), + 'sunsetTime' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{current}->{sunset} ) + ), + 'sunriseTime' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{current}->{sunrise} ) + ), + 'pubDate' => strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{current}->{dt} ) + ), + 'visibility' => int( + sprintf( "%.1f", + $data->{current}->{visibility} ) + 0.5 + ), + 'uvi' => $data->{current}->{uvi}, + }; + } + if ( ref( $data->{hourly} ) eq "ARRAY" && scalar( @{ $data->{hourly} } ) > 0 ) { @@ -592,6 +675,8 @@ sub _ProcessingRetrieveData { ) ) + 0.5 ), + 'wind_direction' => + $data->{hourly}->[$i]->{wind_deg}, 'cloudCover' => $data->{hourly}->[$i]->{clouds}, 'code' => $codes{ @@ -809,9 +894,6 @@ sub _ProcessingRetrieveData { $data->{daily}->[$i]->{weather} ->[0]->{id} }, - 'iconAPI' => - $data->{daily}->[$i]->{weather}->[0] - ->{icon}, 'rain' => $data->{daily}->[$i]->{rain}, 'snow' => $data->{daily}->[$i]->{snow}, 'uvi' => $data->{daily}->[$i]->{uvi}, @@ -967,7 +1049,7 @@ sub strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v1.2.0", + "version": "v3.0.1", "author": [ "Marko Oldenburg " ], -- 2.47.2 From 670c06bd2fc57783e12bf63c1e3ae9644523ca32 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 13 Dec 2022 19:04:01 +0100 Subject: [PATCH 09/24] fix empty deklaration --- OpenWeatherMapAPI.pm | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 6a77a38..1873aba 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -492,17 +492,11 @@ sub _ProcessingRetrieveData { 'visibility' => int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ), - 'dew_point' => int( - sprintf( - "%.1f", $data->{current}->{dew_point} - ) + 0.5 - ), }; } when ('onecall') { if ( !exists( $self->{cached}->{current} ) ) { - $self->{cached}->{license}{text} = 'none'; $self->{cached}->{current} = { 'temperature' => int( sprintf( "%.1f", $data->{current}->{temp} ) @@ -516,7 +510,10 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", $data->{current}->{feels_like} ) + 0.5 ), - + 'dew_point' => int( + sprintf( "%.1f", + $data->{current}->{dew_point} ) + 0.5 + ), 'humidity' => $data->{current}->{humidity}, 'condition' => encode_utf8( $data->{current}->{weather}->[0] -- 2.47.2 From 46cc10e80e568112a315ffca635a150d6a876cc8 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 10:38:04 +0100 Subject: [PATCH 10/24] fix forecast exclude and change commandref --- 59_Weather.pm | 84 ++++++++++++++++++++++++++++++++------------ OpenWeatherMapAPI.pm | 10 +++--- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 0969f91..ff30d3a 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -331,12 +331,21 @@ sub Weather_ReturnWithError { sub Weather_DeleteReadings { my $hash = shift; - my $name = $hash->{NAME}; - my $delReadingRegEx; + my $name = $hash->{NAME}; + my $forecastConfig = Weather_ForcastConfig($hash); + my $forecastLimit = AttrVal( $name, 'forecastLimit', 5 ); + my $forecastLimitNoForecast = 1; - CommandDeleteReading( undef, $name . ' .?(ASC)_.*' ); - CommandDeleteReading( undef, $name . ' ' . $delReadingRegEx ); + $forecastLimit = $forecastLimitNoForecast + if ( !$forecastConfig->{daily} ); + CommandDeleteReading( undef, + $name . ' ' . 'fc[' . $forecastLimit . '-99]_.*' ); + + $forecastLimit = $forecastLimitNoForecast + if ( !$forecastConfig->{hourly} ); + CommandDeleteReading( undef, + $name . ' ' . 'hfc[' . $forecastLimit . '-99]_.*' ); return; } @@ -360,14 +369,28 @@ sub Weather_RetrieveCallbackFn { return; } +sub Weather_ForcastConfig { + my $hash = shift; + + my $name = $hash->{NAME}; + my %forecastConfig; + + $forecastConfig{hourly} = + ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1 : 0 ); + $forecastConfig{daily} = + ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); + $forecastConfig{alerts} = + ( AttrVal( $name, 'forecast', '' ) =~ m{alerts}xms ? 1 : 0 ); + + return \%forecastConfig; +} + sub Weather_WriteReadings { my $hash = shift; my $dataRef = shift; - my $name = $hash->{NAME}; - my $hourly = ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1 : 0 ); - my $daily = ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); - my $alerts = ( AttrVal( $name, 'forecast', '' ) =~ m{alerts}xms ? 1 : 0 ); + my $forecastConfig = Weather_ForcastConfig($hash); + my $name = $hash->{NAME}; readingsBeginUpdate($hash); @@ -410,16 +433,16 @@ sub Weather_WriteReadings { ### forecast if ( ref( $dataRef->{forecast} ) eq 'HASH' - && ( $hourly || $daily ) ) + && ( $forecastConfig->{hourly} || $forecastConfig->{daily} ) ) { ## hourly if ( defined( $dataRef->{forecast}->{hourly} ) && ref( $dataRef->{forecast}->{hourly} ) eq 'ARRAY' && scalar( @{ $dataRef->{forecast}->{hourly} } ) > 0 - && $hourly ) + && $forecastConfig->{hourly} ) { my $i = 0; - my $limit = AttrVal( $name, 'forecastLimit', -1 ); + my $limit = AttrVal( $name, 'forecastLimit', 5 ); foreach my $fc ( @{ $dataRef->{forecast}->{hourly} } ) { $i++; my $f = "hfc" . $i . "_"; @@ -469,10 +492,10 @@ sub Weather_WriteReadings { if ( defined( $dataRef->{forecast}->{daily} ) && ref( $dataRef->{forecast}->{daily} ) eq 'ARRAY' && scalar( @{ $dataRef->{forecast}->{daily} } ) > 0 - && $daily ) + && $forecastConfig->{daily} ) { my $i = 0; - my $limit = AttrVal( $name, 'forecastLimit', -1 ); + my $limit = AttrVal( $name, 'forecastLimit', 5 ); foreach my $fc ( @{ $dataRef->{forecast}->{daily} } ) { $i++; my $f = "fc" . $i . "_"; @@ -519,7 +542,7 @@ sub Weather_WriteReadings { } if ( ref( $dataRef->{alerts} ) eq 'HASH' - && $alerts ) + && $forecastConfig->{alerts} ) { while ( my ( $r, $v ) = each %{ $dataRef->{alerts} } ) { readingsBulkUpdate( $hash, $r, $v ) @@ -751,8 +774,6 @@ sub Weather_Define { location => $hash->{fhem}->{LOCATION}, apioptions => $hash->{APIOPTIONS}, language => $hash->{LANG}, - forecast => AttrVal( $name, 'forecast', '' ), - alerts => AttrVal( $name, 'alerts', 0 ) } ); @@ -780,16 +801,26 @@ sub Weather_Attr { if ( $cmd eq 'set' ) { $hash->{fhem}->{api}->setForecast($attrVal); } + elsif ( $cmd eq 'del' ) { + $hash->{fhem}->{api}->setForecast(); + } - $hash->{fhem}->{api}->setForecast(); + InternalTimer( gettimeofday() + 1, \&Weather_DeleteReadings, + $hash ); + } + + when ('forecastLimit') { + InternalTimer( gettimeofday() + 1, \&Weather_DeleteReadings, + $hash ); } when ('alerts') { if ( $cmd eq 'set' ) { $hash->{fhem}->{api}->setAlerts($attrVal); } - - $hash->{fhem}->{api}->setAlerts(); + elsif ( $cmd eq 'del' ) { + $hash->{fhem}->{api}->setAlerts(); + } } } @@ -1119,7 +1150,9 @@ sub WeatherCheckOptions {
APIwundergroundAPI
apioptionscachemaxage=<cachemaxage> Zeitdauer in +
apioptionscachemaxage:<cachemaxage> Zeitdauer in Sekunden, innerhalb derer die Wettervorhersage nicht neu abgerufen sondern aus dem Cache zurück geliefert wird.
stationId:ID-Num
die ID der Station von welcher die Daten gelesen werden sollen.
+ in seconds to retrieve the forecast from the cache instead from the API + + sondern aus dem Cache zurück geliefert wird. +
APIOpenWeatherMapAPI
apioptionscachemaxage:<cachemaxage>
duration - in seconds to retrieve the forecast from the cache instead from the API
version:<version> API version which should be used. + 2.5 by default, 3.0 is still possible but only with an additional subscription
location<latitude,longitude>
geographic coordinates in degrees of the location for which the weather is forecast; if missing, the values of the attributes @@ -1229,8 +1262,9 @@ sub WeatherCheckOptions {
  • disable: disables the retrieval of weather data - the timer runs according to schedule, though no data is requested from the API.
  • readingFnAttributes
  • -
  • forecast - every/hourly/daily/off, show of forecast data. All, only hour forecast, only day forecast, none.
  • +
  • forecast - hourly/daily, display of forecast data.
  • forecastLimit - Number of forecast data records which should be written as a reading.
  • +
  • alerts - 0/1 should alert messages be written similar to Unwetterwarnung

  • @@ -1314,7 +1348,9 @@ sub WeatherCheckOptions {
    APIOpenWeatherMapAPI
    apioptionscachemaxage:<cachemaxage> Zeitdauer in Sekunden, innerhalb derer die Wettervorhersage nicht neu abgerufen - sondern aus dem Cache zurück geliefert wird.
    version:<version> API Version welche verwendet werden soll. + Per Default 2.5, möglich ist noch 3.0 aber nur mit Zusatzsubscription
    location<latitude,longitude> Geographische Breite und Länge des Ortes in Grad, für den das Wetter vorhergesagt wird. Bei fehlender Angabe werden die Werte aus den gleichnamigen Attributen @@ -1426,8 +1462,10 @@ sub WeatherCheckOptions { gemäß Plan doch es werden keine Daten vom API angefordert.
  • readingFnAttributes
  • -
  • forecast - every/hourly/daily/off, Anzeige von forecast Daten. Alle, nur Stundenforecast, nur Tageforecast, keine.
  • +
  • forecast - hourly/daily, Anzeige von forecast Daten.
  • forecastLimit - Anzahl der Forecast-Datensätze welche als Reading geschrieben werden sollen.
  • +
  • alerts - 0/1 Sollen Alert Meldungen änlich Unwetterwarnung geschrieben werden.
  • +
    diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 1873aba..a54576d 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -195,8 +195,8 @@ sub new { long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, endpoint => 'none', - forecast => $argsRef->{forecast}, - alerts => $argsRef->{alerts}, + forecast => '', + alerts => 0, }; $self->{cachemaxage} = ( @@ -345,7 +345,7 @@ sub _RetrieveDataFromOpenWeatherMap { . 'metric' . '&' . 'lang=' . $self->{lang} . '&' . 'exclude=' - . _createExcludeString( $self->{forecast}, $self->{alerts} ); + . _CreateExcludeString( $self->{forecast}, $self->{alerts} ); ::HttpUtils_NonblockingGet($paramRef); } @@ -353,7 +353,7 @@ sub _RetrieveDataFromOpenWeatherMap { return; } -sub _createExcludeString { +sub _CreateExcludeString { my $forecast = shift; my $alerts = shift; @@ -364,7 +364,7 @@ sub _createExcludeString { my %in_forecast = map { $_ => 1 } @forecast, @alerts; my @diff = grep { not $in_forecast{$_} } @exclude; - return join( ',', @alerts ); + return join( ',', @diff ); } sub _RetrieveDataFinished { -- 2.47.2 From b6c0dc2ef1bc43c8dbbaa82af0b55f7f52e88fd4 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 11:08:36 +0100 Subject: [PATCH 11/24] fix delete Reading Counter --- 59_Weather.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index ff30d3a..d7c242e 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -334,18 +334,18 @@ sub Weather_DeleteReadings { my $name = $hash->{NAME}; my $forecastConfig = Weather_ForcastConfig($hash); - my $forecastLimit = AttrVal( $name, 'forecastLimit', 5 ); + my $forecastLimit = AttrVal( $name, 'forecastLimit', 5 ) + 1; my $forecastLimitNoForecast = 1; $forecastLimit = $forecastLimitNoForecast if ( !$forecastConfig->{daily} ); CommandDeleteReading( undef, - $name . ' ' . 'fc[' . $forecastLimit . '-99]_.*' ); + $name . ' ' . 'fc[' . $forecastLimit . '-9][0-9]?_.*' ); $forecastLimit = $forecastLimitNoForecast if ( !$forecastConfig->{hourly} ); CommandDeleteReading( undef, - $name . ' ' . 'hfc[' . $forecastLimit . '-99]_.*' ); + $name . ' ' . 'hfc[' . $forecastLimit . '-9][0-9]?_.*' ); return; } -- 2.47.2 From a9d9ad722c5777d611466c91a20f10fff89c7a1a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 11:26:32 +0100 Subject: [PATCH 12/24] fix delete Readings --- 59_Weather.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index d7c242e..de278a8 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -340,12 +340,12 @@ sub Weather_DeleteReadings { $forecastLimit = $forecastLimitNoForecast if ( !$forecastConfig->{daily} ); CommandDeleteReading( undef, - $name . ' ' . 'fc[' . $forecastLimit . '-9][0-9]?_.*' ); + $name . ' ' . 'fc([' . $forecastLimit . '-9]|[0-9]{2})_.*' ); $forecastLimit = $forecastLimitNoForecast if ( !$forecastConfig->{hourly} ); CommandDeleteReading( undef, - $name . ' ' . 'hfc[' . $forecastLimit . '-9][0-9]?_.*' ); + $name . ' ' . 'hfc([' . $forecastLimit . '-9]|[0-9]{2})_.*' ); return; } -- 2.47.2 From c600937332425ab4c4624a436c4c0f06cd22a6cb Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 16:21:14 +0100 Subject: [PATCH 13/24] add numericPrecision=decimal option --- wundergroundAPI.pm | 81 ++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 726b4eb..43004a3 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -23,14 +23,11 @@ eval { require JSON::MaybeXS; import JSON::MaybeXS qw( decode_json encode_json ); 1; -}; -if ($@) { - $@ = undef; +} or do { # try to use JSON wrapper # for chance of better performance eval { - # JSON preference order local $ENV{PERL_JSON_BACKEND} = 'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP' @@ -39,10 +36,7 @@ if ($@) { require JSON; import JSON qw( decode_json encode_json ); 1; - }; - - if ($@) { - $@ = undef; + } or do { # In rare cases, Cpanel::JSON::XS may # be installed but JSON|JSON::MaybeXS not ... @@ -50,10 +44,7 @@ if ($@) { require Cpanel::JSON::XS; import Cpanel::JSON::XS qw(decode_json encode_json); 1; - }; - - if ($@) { - $@ = undef; + } or do { # In rare cases, JSON::XS may # be installed but JSON not ... @@ -61,10 +52,7 @@ if ($@) { require JSON::XS; import JSON::XS qw(decode_json encode_json); 1; - }; - - if ($@) { - $@ = undef; + } or do { # Fallback to built-in JSON which SHOULD # be available since 5.014 ... @@ -72,20 +60,17 @@ if ($@) { require JSON::PP; import JSON::PP qw(decode_json encode_json); 1; - }; - - if ($@) { - $@ = undef; + } or do { # Fallback to JSON::backportPP in really rare cases require JSON::backportPP; import JSON::backportPP qw(decode_json encode_json); 1; - } - } - } - } -} + }; + }; + }; + }; +}; use Data::Dumper; # for Debug only ## API URL @@ -109,6 +94,8 @@ sub new { lat => ( split( ',', $argsRef->{location} ) )[0], long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, + forecast => $argsRef->{forecast}, + alerts => $argsRef->{alerts}, }; $self->{cachemaxage} = ( @@ -173,14 +160,30 @@ sub setRetrieveData { } sub setLocation { - my ($self,$lat,$long) = @_; + my ( $self, $lat, $long ) = @_; - $self->{lat} = $lat; - $self->{long} = $long; + $self->{lat} = $lat; + $self->{long} = $long; return 0; } +sub setAlerts { + my $self = shift; + my $alerts = shift // 0; + + $self->{alerts} = $alerts; + return; +} + +sub setForecast { + my $self = shift; + my $forecast = shift // ''; + + $self->{forecast} = $forecast; + return; +} + sub getFetchTime { my $self = shift; @@ -197,15 +200,14 @@ sub _RetrieveDataFromWU($) { my $self = shift; # retrieve data from cache - if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} + if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} and $self->{cached}->{lat} == $self->{lat} - and $self->{cached}->{long} == $self->{long} - ) + and $self->{cached}->{long} == $self->{long} ) { return _CallWeatherCallbackFn($self); } - $self->{cached}->{lat} = $self->{lat} + $self->{cached}->{lat} = $self->{lat} unless ( $self->{cached}->{lat} == $self->{lat} ); $self->{cached}->{long} = $self->{long} unless ( $self->{cached}->{long} == $self->{long} ); @@ -239,6 +241,7 @@ sub _RetrieveDataFromWU($) { my $options = 'geocode=' . $self->{lat} . ',' . $self->{long}; $options .= '&format=json'; $options .= '&units=' . $self->{units}; + $options .= '&numericPrecision=decimal'; $options .= '&language=' . ( $self->{lang} eq 'en' @@ -293,7 +296,7 @@ sub _RetrieveDataFinished($$$) { # we got PWS and forecast data if ( defined( $paramRef->{forecast} ) ) { if ( !$data || $data eq '' ) { - $err = 'No Data Found for specific PWS' unless ($err); + $err = 'No Data Found for specific PWS' unless ($err); $response = $paramRef->{forecast}; } elsif ( $paramRef->{forecast} =~ m/^\{(.*)\}$/ ) { @@ -303,12 +306,12 @@ sub _RetrieveDataFinished($$$) { $response = '{' . $fc . ',' . $1 . '}'; } else { - $err = 'PWS data is not in JSON format' unless ($err); + $err = 'PWS data is not in JSON format' unless ($err); $response = $data; } } else { - $err = 'Forecast data is not in JSON format' unless ($err); + $err = 'Forecast data is not in JSON format' unless ($err); $response = $data; } } @@ -324,7 +327,7 @@ sub _RetrieveDataFinished($$$) { } if ( !$err ) { - $self->{cached}{status} = 'ok'; + $self->{cached}{status} = 'ok'; $self->{cached}{validity} = 'up-to-date', $self->{fetchTime} = time(); _ProcessingRetrieveData( $self, $response ); } @@ -647,7 +650,7 @@ sub _ProcessingRetrieveData($$) { 'narrative' => $data->{narrative}[$i], 'precipChance' => $data->{precipChance}[$i], 'precipType' => $data->{precipType}[$i], - 'precipProbability' => $data->{qpf}[$i], + 'precipProbability' => $data->{qpf}[$i], 'precipProbabilitySnow' => $data->{qpfSnow}[$i], 'qualifierPhrase' => @@ -656,7 +659,7 @@ sub _ProcessingRetrieveData($$) { 'snowRange' => $data->{snowRange}[$i], 'temp_c' => $data->{temperature}[$i], 'temperature' => $data->{temperature}[$i], - 'heatIndex' => + 'heatIndex' => $data->{temperatureHeatIndex}[$i], 'wind_chill' => $data->{temperatureWindChill}[$i], @@ -665,7 +668,7 @@ sub _ProcessingRetrieveData($$) { 'thunderIndex' => $data->{thunderIndex}[$i], 'uvDescription' => $data->{uvDescription}[$i], - 'uvIndex' => $data->{uvIndex}[$i], + 'uvIndex' => $data->{uvIndex}[$i], 'wind_direction' => $data->{windDirection}[$i], 'wind_directionCardinal' => -- 2.47.2 From 15e7de73eb3604af15be96bb422d459801c81a52 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 19:24:14 +0100 Subject: [PATCH 14/24] fix older entrys --- wundergroundAPI.pm | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 43004a3..9419ea5 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -72,7 +72,7 @@ eval { }; }; -use Data::Dumper; # for Debug only +# use Data::Dumper; # for Debug only ## API URL use constant DEMODATA => '{"daily":{"dayOfWeek":["Freitag","Samstag","Sonntag","Montag","Dienstag","Mittwoch"],"expirationTimeUtc":[1555688120,1555688120,1555688120,1555688120,1555688120,1555688120],"moonPhase":["Vollmond","abnehmender Halbmond","abnehmender Halbmond","abnehmender Halbmond","abnehmender Halbmond","abnehmender Halbmond"],"moonPhaseCode":["F","WNG","WNG","WNG","WNG","WNG"],"moonPhaseDay":[15,16,17,18,19,20],"moonriseTimeLocal":["2019-04-19T20:09:54+0200","2019-04-20T21:30:54+0200","2019-04-21T22:48:07+0200","","2019-04-23T00:00:38+0200","2019-04-24T01:05:27+0200"],"moonriseTimeUtc":[1555697394,1555788654,1555879687,null,1555970438,1556060727],"moonsetTimeLocal":["2019-04-19T06:31:01+0200","2019-04-20T06:54:19+0200","2019-04-21T07:20:19+0200","2019-04-22T07:50:19+0200","2019-04-23T08:25:54+0200","2019-04-24T09:09:28+0200"],"moonsetTimeUtc":[1555648261,1555736059,1555824019,1555912219,1556000754,1556089768],"narrative":["Meistens klar. Tiefsttemperatur 5C.","Meistens klar. Höchsttemperaturen 19 bis 21C und Tiefsttemperaturen 4 bis 6C.","Meistens klar. Höchsttemperaturen 20 bis 22C und Tiefsttemperaturen 6 bis 8C.","Meistens klar. Höchsttemperaturen 20 bis 22C und Tiefsttemperaturen 9 bis 11C.","Teilweise bedeckt und windig. Höchsttemperaturen 21 bis 23C und Tiefsttemperaturen 11 bis 13C.","Teilweise bedeckt. Höchsttemperaturen 22 bis 24C und Tiefsttemperaturen 12 bis 14C."],"qpf":[0.0,0.0,0.0,0.0,0.0,0.0],"qpfSnow":[0.0,0.0,0.0,0.0,0.0,0.0],"sunriseTimeLocal":["2019-04-19T06:00:46+0200","2019-04-20T05:58:38+0200","2019-04-21T05:56:31+0200","2019-04-22T05:54:25+0200","2019-04-23T05:52:20+0200","2019-04-24T05:50:15+0200"],"sunriseTimeUtc":[1555646446,1555732718,1555818991,1555905265,1555991540,1556077815],"sunsetTimeLocal":["2019-04-19T20:11:02+0200","2019-04-20T20:12:46+0200","2019-04-21T20:14:29+0200","2019-04-22T20:16:13+0200","2019-04-23T20:17:56+0200","2019-04-24T20:19:40+0200"],"sunsetTimeUtc":[1555697462,1555783966,1555870469,1555956973,1556043476,1556129980],"temperatureMax":[null,20,21,21,22,23],"temperatureMin":[5,5,7,10,12,13],"validTimeLocal":["2019-04-19T07:00:00+0200","2019-04-20T07:00:00+0200","2019-04-21T07:00:00+0200","2019-04-22T07:00:00+0200","2019-04-23T07:00:00+0200","2019-04-24T07:00:00+0200"],"validTimeUtc":[1555650000,1555736400,1555822800,1555909200,1555995600,1556082000],"daypart":[{"cloudCover":[null,0,25,8,0,0,7,26,55,46,62,44],"dayOrNight":[null,"N","D","N","D","N","D","N","D","N","D","N"],"daypartName":[null,"Heute Abend","Morgen","Morgen Abend","Sonntag","Sonntagnacht","Montag","Montagnacht","Dienstag","Dienstagnacht","Mittwoch","Mittwochnacht"],"iconCode":[null,31,34,33,32,31,34,33,24,29,30,29],"iconCodeExtend":[null,3100,3400,3300,3200,3100,3400,3300,3010,2900,3000,2900],"narrative":[null,"Meistens klar. Tiefsttemperatur 5C. Wind aus NO mit 2 bis 4 m/s.","Meistens klar. Höchsttemperatur 20C. Wind aus NNO mit 2 bis 4 m/s.","Meistens klar. Tiefsttemperatur 5C. Wind aus NO mit 2 bis 4 m/s.","Meistens klar. Höchsttemperatur 21C. Wind aus O und wechselhaft.","Meistens klar. Tiefsttemperatur 7C. Wind aus ONO und wechselhaft.","Meistens klar. Höchsttemperatur 21C. Wind aus O mit 4 bis 9 m/s.","Meistens klar. Tiefsttemperatur 10C. Wind aus O mit 4 bis 9 m/s.","Teilweise bedeckt und windig. Höchsttemperatur 22C. Wind aus OSO mit 9 bis 13 m/s.","Teilweise bedeckt. Tiefsttemperatur 12C. Wind aus SO mit 4 bis 9 m/s.","Teilweise bedeckt. Höchsttemperatur 23C. Wind aus SO mit 4 bis 9 m/s.","Teilweise bedeckt. Tiefsttemperatur 13C. Wind aus SO mit 2 bis 4 m/s."],"precipChance":[null,0,0,0,0,0,20,20,0,0,0,10],"precipType":[null,"rain","rain","rain","rain","rain","rain","rain","rain","rain","rain","rain"],"qpf":[null,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"qpfSnow":[null,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],"qualifierCode":[null,null,null,null,null,null,null,null,null,null,null,null],"qualifierPhrase":[null,null,null,null,null,null,null,null,null,null,null,null],"relativeHumidity":[null,50,44,55,41,55,42,48,45,55,53,64],"snowRange":[null,"","","","","","","","","","",""],"temperature":[null,5,20,5,21,7,21,10,22,12,23,13],"temperatureHeatIndex":[null,21,20,18,20,18,20,18,22,20,23,22],"temperatureWindChill":[null,5,5,5,6,6,7,8,8,10,10,13],"thunderCategory":[null,null,null,null,null,null,null,null,null,null,null,null],"thunderIndex":[null,0,0,0,0,0,0,0,0,0,0,0],"uvDescription":[null,"Niedrig","Mittel","Niedrig","Mittel","Niedrig","Mittel","Niedrig","Mittel","Niedrig","Mittel","Niedrig"],"uvIndex":[null,0,4,0,4,0,4,0,4,0,4,0],"windDirection":[null,45,18,41,85,74,95,98,114,124,139,131],"windDirectionCardinal":[null,"NO","NNO","NO","O","ONO","O","O","OSO","SO","SO","SO"],"windPhrase":[null,"Wind aus NO mit 2 bis 4 m/s.","Wind aus NNO mit 2 bis 4 m/s.","Wind aus NO mit 2 bis 4 m/s.","Wind aus O und wechselhaft.","Wind aus ONO und wechselhaft.","Wind aus O mit 4 bis 9 m/s.","Wind aus O mit 4 bis 9 m/s.","Wind aus OSO mit 9 bis 13 m/s.","Wind aus SO mit 4 bis 9 m/s.","Wind aus SO mit 4 bis 9 m/s.","Wind aus SO mit 2 bis 4 m/s."],"windSpeed":[null,4,3,3,2,2,6,6,9,7,6,4],"wxPhraseLong":[null,"Klar","Meist sonnig","Meist klar","Sonnig","Klar","Meist sonnig","Meist klar","Teilweise bedeckt/Wind","Wolkig","Wolkig","Wolkig"],"wxPhraseShort":[null,"","","","","","","","","","",""]}]},"observations":[{"stationID":"IMUNICH344","obsTimeUtc":"2019-04-19T15:24:22Z","obsTimeLocal":"2019-04-19 17:24:22","neighborhood":"Am Hartmannshofer Baechl 34","softwareType":"weewx-3.8.2","country":"DE","solarRadiation":null,"lon":11.49312592,"realtimeFrequency":null,"epoch":1555687462,"lat":48.18364716,"uv":null,"winddir":null,"humidity":27,"qcStatus":1,"metric_si":{"temp":23,"heatIndex":22,"dewpt":3,"windChill":23,"windSpeed":0,"windGust":1,"pressure":1025.84,"precipRate":0.0,"precipTotal":0.0,"elev":502}}]}'; @@ -94,8 +94,6 @@ sub new { lat => ( split( ',', $argsRef->{location} ) )[0], long => ( split( ',', $argsRef->{location} ) )[1], fetchTime => 0, - forecast => $argsRef->{forecast}, - alerts => $argsRef->{alerts}, }; $self->{cachemaxage} = ( @@ -169,18 +167,14 @@ sub setLocation { } sub setAlerts { - my $self = shift; - my $alerts = shift // 0; + my $self = shift; - $self->{alerts} = $alerts; return; } sub setForecast { - my $self = shift; - my $forecast = shift // ''; + my $self = shift; - $self->{forecast} = $forecast; return; } @@ -241,7 +235,6 @@ sub _RetrieveDataFromWU($) { my $options = 'geocode=' . $self->{lat} . ',' . $self->{long}; $options .= '&format=json'; $options .= '&units=' . $self->{units}; - $options .= '&numericPrecision=decimal'; $options .= '&language=' . ( $self->{lang} eq 'en' @@ -281,6 +274,7 @@ sub _RetrieveDataFromPWS($$$) { my $options = 'stationId=' . $self->{stationId}; $options .= '&format=json'; $options .= '&units=' . $self->{units}; + $options .= '&numericPrecision=decimal'; $options .= '&apiKey=' . $self->{key}; $paramRefPWS->{url} = URL . 'v2/pws/observations/current?' . $options; -- 2.47.2 From d4c2f84566c98bcda43dedf9c2fa3636260ca807 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 19:51:18 +0100 Subject: [PATCH 15/24] Subject line (try to keep under 50 characters) Multi-line description of commit, feel free to be detailed. [Ticket: X] --- wundergroundAPI.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 9419ea5..2a0cf55 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -1,5 +1,4 @@ # $Id$ - package wundergroundAPI; use strict; use warnings; -- 2.47.2 From 84915674a84dccce4c69cb1d9da4593a310bce70 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 20:10:58 +0100 Subject: [PATCH 16/24] fix integer declaration for temperature values --- wundergroundAPI.pm | 52 ++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 2a0cf55..fc3d2c3 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -388,18 +388,16 @@ sub _ProcessingRetrieveData($$) { ); $self->{cached}{current} = { - 'dewPoint' => - int( sprintf( "%.1f", $data->{$unit}{dewpt} ) + 0.5 ), - 'heatIndex' => $data->{$unit}{heatIndex}, + 'dewPoint' => sprintf( "%.1f", $data->{$unit}{dewpt} ), + 'heatIndex' => $data->{$unit}{heatIndex}, 'precipRate' => $data->{$unit}{precipRate}, 'precipTotal' => $data->{$unit}{precipTotal}, 'pressure' => int( sprintf( "%.1f", $data->{$unit}{pressure} ) + 0.5 ), 'temperature' => - int( sprintf( "%.1f", $data->{$unit}{temp} ) + 0.5 ), - 'temp_c' => - int( sprintf( "%.1f", $data->{$unit}{temp} ) + 0.5 ), + sprintf( "%.1f", $data->{$unit}{temp} ), + 'temp_c' => sprintf( "%.1f", $data->{$unit}{temp} ), 'wind_chill' => int( sprintf( "%.1f", ( $data->{$unit}{windChill} ) ) + 0.5 @@ -507,33 +505,27 @@ sub _ProcessingRetrieveData($$) { ) ) ), - 'low_c' => int( - sprintf( "%.1f", - $data->{temperatureMin}[$i] ) + 0.5 + 'low_c' => sprintf( + "%.1f", $data->{temperatureMin}[$i] ), - 'high_c' => int( - sprintf( - "%.1f", - ( - $data->{temperatureMax}[$i] - ? $data->{temperatureMax}[$i] - : 0 - ) - ) + 0.5 + 'high_c' => sprintf( + "%.1f", + ( + $data->{temperatureMax}[$i] + ? $data->{temperatureMax}[$i] + : 0 + ) ), - 'tempLow' => int( - sprintf( "%.1f", - $data->{temperatureMin}[$i] ) + 0.5 + 'tempLow' => sprintf( + "%.1f", $data->{temperatureMin}[$i] ), - 'tempHigh' => int( - sprintf( - "%.1f", - ( - $data->{temperatureMax}[$i] - ? $data->{temperatureMax}[$i] - : 0 - ) - ) + 0.5 + 'tempHigh' => sprintf( + "%.1f", + ( + $data->{temperatureMax}[$i] + ? $data->{temperatureMax}[$i] + : 0 + ) ), } ); -- 2.47.2 From 7875522849eca74181f715484a9ceef276065bbd Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 20:42:07 +0100 Subject: [PATCH 17/24] change pressure value to decimal --- wundergroundAPI.pm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index fc3d2c3..5779e1f 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -392,9 +392,8 @@ sub _ProcessingRetrieveData($$) { 'heatIndex' => $data->{$unit}{heatIndex}, 'precipRate' => $data->{$unit}{precipRate}, 'precipTotal' => $data->{$unit}{precipTotal}, - 'pressure' => int( - sprintf( "%.1f", $data->{$unit}{pressure} ) + 0.5 - ), + 'pressure' => + sprintf( "%.1f", $data->{$unit}{pressure} ), 'temperature' => sprintf( "%.1f", $data->{$unit}{temp} ), 'temp_c' => sprintf( "%.1f", $data->{$unit}{temp} ), -- 2.47.2 From 6611390059da425c6ec6b1f2f6c3b4dbab5f9602 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 21:35:21 +0100 Subject: [PATCH 18/24] change wind_chill to decimal --- wundergroundAPI.pm | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 5779e1f..15d65de 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -397,10 +397,8 @@ sub _ProcessingRetrieveData($$) { 'temperature' => sprintf( "%.1f", $data->{$unit}{temp} ), 'temp_c' => sprintf( "%.1f", $data->{$unit}{temp} ), - 'wind_chill' => int( - sprintf( "%.1f", ( $data->{$unit}{windChill} ) ) + - 0.5 - ), + 'wind_chill' => + sprintf( "%.1f", ( $data->{$unit}{windChill} ) ), 'windGust' => int( sprintf( "%.1f", ( $data->{$unit}{windGust} ) ) + 0.5 -- 2.47.2 From 96ce4119480cd765293f517410e51ed7540fa61c Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sun, 18 Dec 2022 10:58:24 +0100 Subject: [PATCH 19/24] fix onecall update, remove weather endpoint --- OpenWeatherMapAPI.pm | 159 ++++++++++--------------------------------- 1 file changed, 37 insertions(+), 122 deletions(-) diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index a54576d..6a7b220 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -421,161 +421,76 @@ sub _ProcessingRetrieveData { localtime( $self->{fetchTime} ) ); given ( $self->{endpoint} ) { - when ('weather') { - $self->{cached}->{country} = $data->{sys}->{country}; - $self->{cached}->{city} = encode_utf8( $data->{name} ); - $self->{cached}->{license}{text} = 'none'; - $self->{cached}->{current} = { + when ('onecall') { + $self->{cached}->{current} = { 'temperature' => int( - sprintf( "%.1f", $data->{main}->{temp} ) + 0.5 + sprintf( "%.1f", $data->{current}->{temp} ) + + 0.5 ), 'temp_c' => int( - sprintf( "%.1f", $data->{main}->{temp} ) + 0.5 - ), - 'low_c' => int( - sprintf( "%.1f", $data->{main}->{temp_min} ) + - 0.5 - ), - 'high_c' => int( - sprintf( "%.1f", $data->{main}->{temp_max} ) + - 0.5 - ), - 'tempLow' => int( - sprintf( "%.1f", $data->{main}->{temp_min} ) + - 0.5 - ), - 'tempHigh' => int( - sprintf( "%.1f", $data->{main}->{temp_max} ) + + sprintf( "%.1f", $data->{current}->{temp} ) + 0.5 ), 'tempFeelsLike_c' => int( - sprintf( "%.1f", $data->{main}->{feels_like} ) - + 0.5 + sprintf( "%.1f", + $data->{current}->{feels_like} ) + 0.5 ), - 'humidity' => $data->{main}->{humidity}, + 'dew_point' => int( + sprintf( + "%.1f", $data->{current}->{dew_point} + ) + 0.5 + ), + 'humidity' => $data->{current}->{humidity}, 'condition' => encode_utf8( - $data->{weather}->[0]->{description} + $data->{current}->{weather}->[0]->{description} ), 'pressure' => int( - sprintf( "%.1f", $data->{main}->{pressure} ) + - 0.5 + sprintf( "%.1f", $data->{current}->{pressure} ) + + 0.5 ), 'wind' => int( sprintf( "%.1f", - ( $data->{wind}->{speed} * 3.6 ) ) + 0.5 + ( $data->{current}->{wind_speed} * 3.6 ) ) + + 0.5 ), 'wind_speed' => int( sprintf( "%.1f", - ( $data->{wind}->{speed} * 3.6 ) ) + 0.5 + ( $data->{current}->{wind_speed} * 3.6 ) ) + + 0.5 ), 'wind_gust' => int( sprintf( "%.1f", - ( $data->{wind}->{gust} * 3.6 ) ) + 0.5 + ( $data->{current}->{wind_gust} * 3.6 ) ) + + 0.5 ), - 'wind_direction' => $data->{wind}->{deg}, + 'wind_direction' => $data->{current}->{wind_deg}, 'rain_1h' => $data->{rain}->{'1h'}, - 'cloudCover' => $data->{clouds}->{all}, - 'code' => $codes{ $data->{weather}->[0]->{id} }, - 'iconAPI' => $data->{weather}->[0]->{icon}, + 'cloudCover' => $data->{current}->{clouds}, + 'code' => + $codes{ $data->{current}->{weather}->[0]->{id} }, + 'iconAPI' => + $data->{current}->{weather}->[0]->{icon}, + 'condition' => encode_utf8( + $data->{current}->{weather}->[0]->{description} + ), 'sunsetTime' => strftimeWrapper( "%a, %e %b %Y %H:%M", - localtime( $data->{sys}->{sunset} ) + localtime( $data->{current}->{sunset} ) ), 'sunriseTime' => strftimeWrapper( "%a, %e %b %Y %H:%M", - localtime( $data->{sys}->{sunrise} ) + localtime( $data->{current}->{sunrise} ) ), 'pubDate' => strftimeWrapper( "%a, %e %b %Y %H:%M", - localtime( $data->{dt} ) + localtime( $data->{current}->{dt} ) ), 'visibility' => int( - sprintf( "%.1f", $data->{visibility} ) + 0.5 + sprintf( "%.1f", + $data->{current}->{visibility} ) + 0.5 ), + 'uvi' => $data->{current}->{uvi}, }; - } - - when ('onecall') { - if ( !exists( $self->{cached}->{current} ) ) { - $self->{cached}->{current} = { - 'temperature' => int( - sprintf( "%.1f", $data->{current}->{temp} ) - + 0.5 - ), - 'temp_c' => int( - sprintf( "%.1f", $data->{current}->{temp} ) - + 0.5 - ), - 'tempFeelsLike_c' => int( - sprintf( "%.1f", - $data->{current}->{feels_like} ) + 0.5 - ), - 'dew_point' => int( - sprintf( "%.1f", - $data->{current}->{dew_point} ) + 0.5 - ), - 'humidity' => $data->{current}->{humidity}, - 'condition' => encode_utf8( - $data->{current}->{weather}->[0] - ->{description} - ), - 'pressure' => int( - sprintf( "%.1f", - $data->{current}->{pressure} ) + 0.5 - ), - 'wind' => int( - sprintf( - "%.1f", - ( - $data->{current}->{wind_speed} * 3.6 - ) - ) + 0.5 - ), - 'wind_speed' => int( - sprintf( - "%.1f", - ( - $data->{current}->{wind_speed} * 3.6 - ) - ) + 0.5 - ), - 'wind_gust' => int( - sprintf( "%.1f", - ( $data->{current}->{wind_gust} * 3.6 ) - ) + 0.5 - ), - 'wind_direction' => - $data->{current}->{wind_deg}, - 'rain_1h' => $data->{rain}->{'1h'}, - 'cloudCover' => $data->{current}->{clouds}, - 'code' => $codes{ - $data->{current}->{weather}->[0]->{id} - }, - 'iconAPI' => - $data->{current}->{weather}->[0]->{icon}, - 'condition' => encode_utf8( - $data->{current}->{weather}->[0] - ->{description} - ), - 'sunsetTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{current}->{sunset} ) - ), - 'sunriseTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{current}->{sunrise} ) - ), - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{current}->{dt} ) - ), - 'visibility' => int( - sprintf( "%.1f", - $data->{current}->{visibility} ) + 0.5 - ), - 'uvi' => $data->{current}->{uvi}, - }; - } if ( ref( $data->{hourly} ) eq "ARRAY" && scalar( @{ $data->{hourly} } ) > 0 ) -- 2.47.2 From d82d4fa01dea4169d66508ea1aa0ba1e61ac2dae Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Mon, 19 Dec 2022 11:24:27 +0100 Subject: [PATCH 20/24] fix negativ integer value round fix alerts integration --- 59_Weather.pm | 26 +++++--- OpenWeatherMapAPI.pm | 142 +++++++++++++++++-------------------------- 2 files changed, 76 insertions(+), 92 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index de278a8..789bb65 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -377,10 +377,11 @@ sub Weather_ForcastConfig { $forecastConfig{hourly} = ( AttrVal( $name, 'forecast', '' ) =~ m{hourly}xms ? 1 : 0 ); + $forecastConfig{daily} = ( AttrVal( $name, 'forecast', '' ) =~ m{daily}xms ? 1 : 0 ); - $forecastConfig{alerts} = - ( AttrVal( $name, 'forecast', '' ) =~ m{alerts}xms ? 1 : 0 ); + + $forecastConfig{alerts} = AttrVal( $name, 'alerts', 0 ); return \%forecastConfig; } @@ -541,16 +542,27 @@ sub Weather_WriteReadings { } } - if ( ref( $dataRef->{alerts} ) eq 'HASH' + ### alerts + if ( defined( $dataRef->{alerts} ) + && ref( $dataRef->{alerts} ) eq 'ARRAY' + && scalar( @{ $dataRef->{alerts} } ) > 0 && $forecastConfig->{alerts} ) { - while ( my ( $r, $v ) = each %{ $dataRef->{alerts} } ) { - readingsBulkUpdate( $hash, $r, $v ) - if ( ref( $dataRef->{$r} ) ne 'HASH' - && ref( $dataRef->{$r} ) ne 'ARRAY' ); + my $i = 0; + foreach my $warn ( @{ $dataRef->{alerts} } ) { + $i++; + my $w = "warn_" . $i . "_"; + + while ( my ( $r, $v ) = each %{$warn} ) { + readingsBulkUpdate( $hash, $w . $r, $v ) + if ( ref( $dataRef->{$r} ) ne 'HASH' + && ref( $dataRef->{$r} ) ne 'ARRAY' ); + } + } } + ### state my $val = 'T: ' . $dataRef->{current}->{temperature} . ' °C' . ' ' . substr( $status_items_txt_i18n{1}, 0, 1 ) . ': ' diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 6a7b220..b2ef12c 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -7,6 +7,7 @@ # All rights reserved # # Special thanks goes to: +# - Harry (harryman) for many tests and patch that implements onecall API # # # This script is free software; you can redistribute it and/or modify @@ -304,9 +305,7 @@ sub _RetrieveDataFromOpenWeatherMap { my $paramRef = { timeout => 15, self => $self, - endpoint => $self->{endpoint} eq 'none' ? 'weather' - : $self->{endpoint} eq 'weather' ? 'onecall' - : 'weather', + endpoint => $self->{endpoint} eq 'none' ? 'onecall' : 'none', callback => \&_RetrieveDataFinished, }; @@ -424,21 +423,19 @@ sub _ProcessingRetrieveData { when ('onecall') { $self->{cached}->{current} = { 'temperature' => int( - sprintf( "%.1f", $data->{current}->{temp} ) + - 0.5 + sprintf( "%.0f", $data->{current}->{temp} ) ), 'temp_c' => int( - sprintf( "%.1f", $data->{current}->{temp} ) + - 0.5 + sprintf( "%.0f", $data->{current}->{temp} ) ), 'tempFeelsLike_c' => int( - sprintf( "%.1f", - $data->{current}->{feels_like} ) + 0.5 + sprintf( "%.0f", + $data->{current}->{feels_like} ) ), 'dew_point' => int( sprintf( - "%.1f", $data->{current}->{dew_point} - ) + 0.5 + "%.0f", $data->{current}->{dew_point} + ) ), 'humidity' => $data->{current}->{humidity}, 'condition' => encode_utf8( @@ -518,36 +515,22 @@ sub _ProcessingRetrieveData { ) ), 'temperature' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{temp} - ) - ) + 0.5 + sprintf( "%.0f", + $data->{hourly}->[$i]->{temp} ) ), 'temp_c' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{temp} - ) - ) + 0.5 + sprintf( "%.0f", + $data->{hourly}->[$i]->{temp} ) ), 'tempFeelsLike' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{feels_like} - ) - ) + 0.5 + sprintf( "%.0f", + $data->{hourly}->[$i] + ->{feels_like} ) ), 'dew_point' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{hourly}->[$i] - ->{dew_point} ) + 0.5 + ->{dew_point} ) ), 'humidity' => $data->{hourly}->[$i]->{humidity}, @@ -676,75 +659,74 @@ sub _ProcessingRetrieveData { ) ), 'temperature' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{day} ) + 0.5 + ->{day} ) ), 'temperature_morn' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{morn} ) + 0.5 + ->{morn} ) ), 'temperature_eve' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{eve} ) + 0.5 + ->{eve} ) ), 'temperature_night' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{night} ) + 0.5 + ->{night} ) ), 'tempFeelsLike_morn' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i] - ->{feels_like}->{morn} ) + 0.5 + ->{feels_like}->{morn} ) ), 'tempFeelsLike_eve' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i] - ->{feels_like}->{eve} ) + 0.5 + ->{feels_like}->{eve} ) ), 'tempFeelsLike_night' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i] - ->{feels_like}->{night} ) + - 0.5 + ->{feels_like}->{night} ) ), 'tempFeelsLike_day' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i] - ->{feels_like}->{day} ) + 0.5 + ->{feels_like}->{day} ) ), 'temp_c' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{day} ) + 0.5 + ->{day} ) ), 'low_c' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{min} ) + 0.5 + ->{min} ) ), 'high_c' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{max} ) + 0.5 + ->{max} ) ), 'tempLow' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{min} ) + 0.5 + ->{min} ) ), 'tempHigh' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i]->{temp} - ->{max} ) + 0.5 + ->{max} ) ), 'dew_point' => int( - sprintf( "%.1f", + sprintf( "%.0f", $data->{daily}->[$i] - ->{dew_point} ) + 0.5 + ->{dew_point} ) ), 'humidity' => $data->{daily}->[$i]->{humidity}, @@ -827,42 +809,32 @@ sub _ProcessingRetrieveData { push( @{ $self->{cached}->{alerts} }, { - 'warn_' - . $i - . '_End' => strftimeWrapper( + 'End' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( ( $data->{alerts}->[$i]->{end} - ) - 3600 + ) ) - ), - 'warn_' - . $i - . '_Start' => strftimeWrapper( + ), + 'Start' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( ( $data->{alerts}->[$i] ->{start} - ) - 3600 + ) ) - ), - 'warn_' - . $i - . '_Description' => encode_utf8( + ), + 'Description' => encode_utf8( $data->{alerts}->[$i]->{description} - ), - 'warn_' - . $i - . '_SenderName' => encode_utf8( + ), + 'SenderName' => encode_utf8( $data->{alerts}->[$i]->{sender_name} - ), - 'warn_' - . $i - . '_Event' => encode_utf8( + ), + 'Event' => encode_utf8( $data->{alerts}->[$i]->{event} - ), + ), }, ); -- 2.47.2 From 9f005d050e5c5430fbc67e49e5f20f1402366c30 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Mon, 19 Dec 2022 15:29:06 +0100 Subject: [PATCH 21/24] add warnCount reading --- 59_Weather.pm | 42 ++++++++++++++++++++++++++++++++++++------ OpenWeatherMapAPI.pm | 39 +++++++++++++-------------------------- 2 files changed, 49 insertions(+), 32 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 789bb65..04ac65c 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -329,7 +329,7 @@ sub Weather_ReturnWithError { return; } -sub Weather_DeleteReadings { +sub Weather_DeleteForecastReadings { my $hash = shift; my $name = $hash->{NAME}; @@ -350,6 +350,23 @@ sub Weather_DeleteReadings { return; } +sub Weather_DeleteAlertsReadings { + my $hash = shift; + my $alertsLimit = shift // 0; + + my $name = $hash->{NAME}; + my $alertsConfig = Weather_ForcastConfig($hash); + my $alertsLimitNoAlerts = 0; + + $alertsLimit = $alertsLimitNoAlerts + if ( !$alertsConfig->{alerts} ); + + CommandDeleteReading( undef, + $name . ' ' . 'warn_([' . $alertsLimit . '-9]|[0-9]{2})_.*' ); + + return; +} + sub Weather_RetrieveCallbackFn { my $name = shift; @@ -550,7 +567,6 @@ sub Weather_WriteReadings { { my $i = 0; foreach my $warn ( @{ $dataRef->{alerts} } ) { - $i++; my $w = "warn_" . $i . "_"; while ( my ( $r, $v ) = each %{$warn} ) { @@ -559,7 +575,18 @@ sub Weather_WriteReadings { && ref( $dataRef->{$r} ) ne 'ARRAY' ); } + $i++; } + + Weather_DeleteAlertsReadings( $hash, + scalar( @{ $dataRef->{alerts} } ) ); + readingsBulkUpdate( $hash, 'warnCount', + scalar( @{ $dataRef->{alerts} } ) ); + } + else { + Weather_DeleteAlertsReadings($hash); + readingsBulkUpdate( $hash, 'warnCount', + scalar( @{ $dataRef->{alerts} } ) ); } ### state @@ -817,13 +844,13 @@ sub Weather_Attr { $hash->{fhem}->{api}->setForecast(); } - InternalTimer( gettimeofday() + 1, \&Weather_DeleteReadings, - $hash ); + InternalTimer( gettimeofday() + 0.5, + \&Weather_DeleteForecastReadings, $hash ); } when ('forecastLimit') { - InternalTimer( gettimeofday() + 1, \&Weather_DeleteReadings, - $hash ); + InternalTimer( gettimeofday() + 0.5, + \&Weather_DeleteForecastReadings, $hash ); } when ('alerts') { @@ -833,6 +860,9 @@ sub Weather_Attr { elsif ( $cmd eq 'del' ) { $hash->{fhem}->{api}->setAlerts(); } + + InternalTimer( gettimeofday() + 0.5, + \&Weather_DeleteAlertsReadings, $hash ); } } diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index b2ef12c..b30cff4 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -157,6 +157,7 @@ my %codes = ( 602 => 13, 611 => 46, 612 => 46, + 613 => 46, 615 => 5, 616 => 5, 620 => 14, @@ -358,7 +359,7 @@ sub _CreateExcludeString { my @exclude = qw/alerts minutely hourly daily/; my @forecast = split( ',', $forecast ); - my @alerts = ( $alerts ? ',alerts' : '' ); + my @alerts = ( $alerts ? 'alerts' : '' ); my %in_forecast = map { $_ => 1 } @forecast, @alerts; my @diff = grep { not $in_forecast{$_} } @exclude; @@ -486,7 +487,9 @@ sub _ProcessingRetrieveData { sprintf( "%.1f", $data->{current}->{visibility} ) + 0.5 ), - 'uvi' => $data->{current}->{uvi}, + 'uvi' => $data->{current}->{uvi}, + 'timezone' => $data->{timezone}, + 'timezone_offset' => $data->{timezone_offset}, }; if ( ref( $data->{hourly} ) eq "ARRAY" @@ -503,15 +506,13 @@ sub _ProcessingRetrieveData { 'pubDate' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( - ( $data->{hourly}->[$i]->{dt} ) - - 3600 + $data->{hourly}->[$i]->{dt} ) ), 'day_of_week' => strftime( "%a, %H:%M", localtime( - ( $data->{hourly}->[$i]->{dt} ) - - 3600 + $data->{hourly}->[$i]->{dt} ) ), 'temperature' => int( @@ -609,42 +610,31 @@ sub _ProcessingRetrieveData { 'pubDate' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( - ( $data->{daily}->[$i]->{dt} ) - - 3600 + $data->{daily}->[$i]->{dt} ) ), 'day_of_week' => strftime( "%a, %H:%M", localtime( - ( $data->{daily}->[$i]->{dt} ) - - 3600 + $data->{daily}->[$i]->{dt} ) ), 'sunrise' => strftime( "%H:%M", localtime( - ( - $data->{daily}->[$i] - ->{sunrise} - ) - 3600 + $data->{daily}->[$i]->{sunrise} ) ), 'sunset' => strftime( "%a, %H:%M", localtime( - ( - $data->{daily}->[$i] - ->{sunset} - ) - 3600 + $data->{daily}->[$i]->{sunset} ) ), 'moonrise' => strftime( "%a, %H:%M", localtime( - ( - $data->{daily}->[$i] - ->{moonrise} - ) - 3600 + $data->{daily}->[$i]->{moonrise} ) ), 'moon_phase' => @@ -652,10 +642,7 @@ sub _ProcessingRetrieveData { 'moonset' => strftime( "%a, %H:%M", localtime( - ( - $data->{daily}->[$i] - ->{moonset} - ) - 3600 + $data->{daily}->[$i]->{moonset} ) ), 'temperature' => int( -- 2.47.2 From ca343e104b10c6c27c753ed83a886cd029c1f60c Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 12:53:44 +0100 Subject: [PATCH 22/24] fix Weather_CheckOptions func --- 59_Weather.pm | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 04ac65c..564e848 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -895,7 +895,7 @@ sub WeatherAsHtmlV { my $op1 = shift; my $op2 = shift; - my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); + my ( $f, $items ) = Weather_CheckOptions( $d, $op1, $op2 ); my $h = $defs{$d}; my $width = int( $ICONSCALE * $ICONWIDTH ); @@ -966,7 +966,7 @@ sub WeatherAsHtml { my $op1 = shift; my $op2 = shift; - my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); + my ( $f, $items ) = Weather_CheckOptions( $d, $op1, $op2 ); return WeatherAsHtmlV( $d, $f, $items ); } @@ -976,7 +976,7 @@ sub WeatherAsHtmlH { my $op1 = shift; my $op2 = shift; - my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); + my ( $f, $items ) = Weather_CheckOptions( $d, $op1, $op2 ); my $h = $defs{$d}; my $width = int( $ICONSCALE * $ICONWIDTH ); @@ -1068,7 +1068,7 @@ sub WeatherAsHtmlD { my $op1 = shift; my $op2 = shift; - my ( $f, $items ) = WeatherCheckOptions( $d, $op1, $op2 ); + my ( $f, $items ) = Weather_CheckOptions( $d, $op1, $op2 ); if ($FW_ss) { WeatherAsHtmlV( $d, $f, $items ); @@ -1080,11 +1080,15 @@ sub WeatherAsHtmlD { return; } -sub WeatherCheckOptions { +sub Weather_CheckOptions { my $d = shift; my $op1 = shift; my $op2 = shift; + return "$d is not a Weather instance
    " + if ( !$defs{$d} || $defs{$d}->{TYPE} ne "Weather" ); + + my $hash = $defs{$d}; my $items = $op2; my $f = $op1; @@ -1096,16 +1100,12 @@ sub WeatherCheckOptions { $items = 6 if ( !$items ); - return "$d is not a Weather instance
    " - if ( !$defs{$d} || $defs{$d}->{TYPE} ne "Weather" ); - - if ( AttrVal( $d, 'forecast', 'none' ) ne 'none' ) { - $f = ( - AttrVal( $d, 'forecast', 'none' ) eq 'daily' - ? 'd' - : ( AttrVal( $d, 'forecast', 'none' ) eq 'every' ? $f : 'h' ) - ); - } + my $forecastConfig = Weather_ForcastConfig($hash); + $f = ( + $forecastConfig->{daily} + ? 'd' + : ( $forecastConfig->{daily} && $forecastConfig->{hourly} ? $f : 'h' ) + ) if !( defined($f) and $f ); $f = 'h' if ( !$f || length($f) > 1 ); -- 2.47.2 From b2d84725f7cfa522f879bc26a1fe584498f26ab1 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 13:24:15 +0100 Subject: [PATCH 23/24] fix remove old data --- OpenWeatherMapAPI.pm | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index b30cff4..62e23fb 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -422,6 +422,15 @@ sub _ProcessingRetrieveData { given ( $self->{endpoint} ) { when ('onecall') { + ## löschen des alten current Datensatzes + delete $self->{cached}->{current}; + + ## löschen des alten forecast Datensatzes + delete $self->{cached}->{forecast}; + + ## löschen des alten Alerts Datensatzes + delete $self->{cached}->{alerts}; + $self->{cached}->{current} = { 'temperature' => int( sprintf( "%.0f", $data->{current}->{temp} ) @@ -495,9 +504,6 @@ sub _ProcessingRetrieveData { if ( ref( $data->{hourly} ) eq "ARRAY" && scalar( @{ $data->{hourly} } ) > 0 ) { - ## löschen des alten Datensatzes - delete $self->{cached}->{forecast}; - my $i = 0; for ( @{ $data->{hourly} } ) { push( @@ -788,9 +794,6 @@ sub _ProcessingRetrieveData { if ( ref( $data->{alerts} ) eq "ARRAY" && scalar( @{ $data->{alerts} } ) > 0 ) { - ## löschen des alten Datensatzes - delete $self->{cached}->{alerts}; - my $i = 0; for ( @{ $data->{alerts} } ) { push( -- 2.47.2 From 52490f38bb4e4ae27d177af7eb211b7cd331d51a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 14:43:12 +0100 Subject: [PATCH 24/24] change versions of API modules add Copyright year range --- 59_Weather.pm | 11 +++++---- DarkSkyAPI.pm | 59 ++++++++++++++++++++++---------------------- OpenWeatherMapAPI.pm | 12 +++++---- wundergroundAPI.pm | 2 +- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 564e848..41dfef9 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -2,7 +2,7 @@ ############################################################################## # # 59_Weather.pm -# Copyright by Dr. Boris Neubert +# (c) 2009-2023 Copyright by Dr. Boris Neubert # e-mail: omega at online dot de # # Contributors: @@ -1098,7 +1098,8 @@ sub Weather_CheckOptions { $f =~ tr/dh/./cd if ( defined $f && $f ); $items =~ tr/0-9/./cd if ( defined($items) && $items ); - $items = 6 if ( !$items ); + $items = AttrVal( $d, 'forecastLimit', 6 ) + if ( !$items ); my $forecastConfig = Weather_ForcastConfig($hash); $f = ( @@ -1532,15 +1533,15 @@ sub Weather_CheckOptions { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.2.5", + "version": "v2.2.6", "author": [ - "Marko Oldenburg " + "Marko Oldenburg " ], "x_fhem_maintainer": [ "CoolTux" ], "x_fhem_maintainer_github": [ - "LeonGaultier" + "CoolTuxNet" ], "prereqs": { "runtime": { diff --git a/DarkSkyAPI.pm b/DarkSkyAPI.pm index 52afcce..4902524 100644 --- a/DarkSkyAPI.pm +++ b/DarkSkyAPI.pm @@ -1,9 +1,9 @@ # $Id: $ ############################################################################### # -# Developed with Kate +# Developed with VSCodium and richterger perl plugin # -# (c) 2019 Copyright: Marko Oldenburg (leongaultier at gmail dot com) +# (c) 2019-2023 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) # All rights reserved # # Special thanks goes to: @@ -123,7 +123,7 @@ eval "use Encode qw(encode_utf8);1" or $missingModul .= "Encode "; use constant DEMODATA => '{"latitude":50.112,"longitude":8.686,"timezone":"Europe/Berlin","currently":{"time":1551214558,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":9.65,"apparentTemperature":9.65,"dewPoint":1.39,"humidity":0.56,"pressure":1032.69,"windSpeed":0.41,"windGust":1.35,"windBearing":84,"cloudCover":0,"uvIndex":0,"visibility":10.01,"ozone":276.41},"hourly":{"summary":"Leicht bewölkt am heute Nacht.","icon":"partly-cloudy-night","data":[{"time":1551211200,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":10.59,"apparentTemperature":10.59,"dewPoint":1.84,"humidity":0.55,"pressure":1032.7,"windSpeed":0.28,"windGust":1.15,"windBearing":89,"cloudCover":0,"uvIndex":0,"visibility":10.01,"ozone":277},{"time":1551214800,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":9.58,"apparentTemperature":9.58,"dewPoint":1.36,"humidity":0.56,"pressure":1032.69,"windSpeed":0.42,"windGust":1.37,"windBearing":83,"cloudCover":0,"uvIndex":0,"visibility":10.01,"ozone":276.37},{"time":1551218400,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":8.61,"apparentTemperature":8.61,"dewPoint":0.73,"humidity":0.58,"pressure":1032.63,"windSpeed":0.5,"windGust":1.47,"windBearing":72,"cloudCover":0,"uvIndex":0,"visibility":11.47,"ozone":275.56},{"time":1551222000,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0,"precipProbability":0,"temperature":8.06,"apparentTemperature":8.06,"dewPoint":-0.45,"humidity":0.55,"pressure":1032.55,"windSpeed":0.86,"windGust":1.5,"windBearing":40,"cloudCover":0.26,"uvIndex":0,"visibility":16.09,"ozone":274.76},{"time":1551225600,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0,"precipProbability":0,"temperature":7.48,"apparentTemperature":7.48,"dewPoint":-1.38,"humidity":0.53,"pressure":1032.4,"windSpeed":1.14,"windGust":1.49,"windBearing":33,"cloudCover":0.42,"uvIndex":0,"visibility":16.09,"ozone":274.13},{"time":1551229200,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0,"precipProbability":0,"temperature":6.62,"apparentTemperature":6.62,"dewPoint":-1.89,"humidity":0.54,"pressure":1032.12,"windSpeed":1.11,"windGust":1.43,"windBearing":38,"cloudCover":0.36,"uvIndex":0,"visibility":16.09,"ozone":273.77},{"time":1551232800,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0,"precipProbability":0,"temperature":5.73,"apparentTemperature":5.73,"dewPoint":-2.39,"humidity":0.56,"pressure":1031.83,"windSpeed":1.07,"windGust":1.34,"windBearing":46,"cloudCover":0.29,"uvIndex":0,"visibility":16.09,"ozone":273.55},{"time":1551236400,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":4.91,"apparentTemperature":4.91,"dewPoint":-2.81,"humidity":0.57,"pressure":1031.49,"windSpeed":1.03,"windGust":1.23,"windBearing":54,"cloudCover":0.23,"uvIndex":0,"visibility":16.09,"ozone":273.44},{"time":1551240000,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":4.02,"apparentTemperature":4.02,"dewPoint":-3.26,"humidity":0.59,"pressure":1031.18,"windSpeed":0.99,"windGust":1.15,"windBearing":63,"cloudCover":0.21,"uvIndex":0,"visibility":16.09,"ozone":273.43},{"time":1551243600,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":3.26,"apparentTemperature":3.26,"dewPoint":-3.61,"humidity":0.61,"pressure":1030.85,"windSpeed":0.96,"windGust":1.08,"windBearing":73,"cloudCover":0.22,"uvIndex":0,"visibility":16.09,"ozone":273.5},{"time":1551247200,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":3.19,"apparentTemperature":3.19,"dewPoint":-3.51,"humidity":0.61,"pressure":1030.54,"windSpeed":0.92,"windGust":1.01,"windBearing":83,"cloudCover":0.22,"uvIndex":0,"visibility":16.09,"ozone":273.65},{"time":1551250800,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":4.28,"apparentTemperature":4.28,"dewPoint":-2.62,"humidity":0.61,"pressure":1030.25,"windSpeed":0.83,"windGust":0.88,"windBearing":93,"cloudCover":0.2,"uvIndex":0,"visibility":16.09,"ozone":273.79},{"time":1551254400,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":6.29,"apparentTemperature":6.29,"dewPoint":-1.28,"humidity":0.58,"pressure":1029.92,"windSpeed":0.72,"windGust":0.78,"windBearing":105,"cloudCover":0.19,"uvIndex":1,"visibility":16.09,"ozone":273.91},{"time":1551258000,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":8.45,"apparentTemperature":8.45,"dewPoint":-0.11,"humidity":0.55,"pressure":1029.54,"windSpeed":0.68,"windGust":0.77,"windBearing":116,"cloudCover":0.16,"uvIndex":1,"visibility":16.09,"ozone":274.33},{"time":1551261600,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":10.79,"apparentTemperature":10.79,"dewPoint":0.73,"humidity":0.5,"pressure":1028.98,"windSpeed":0.81,"windGust":0.9,"windBearing":125,"cloudCover":0.14,"uvIndex":2,"visibility":16.09,"ozone":275.21},{"time":1551265200,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":13.36,"apparentTemperature":13.36,"dewPoint":1.44,"humidity":0.44,"pressure":1028.33,"windSpeed":1.06,"windGust":1.12,"windBearing":131,"cloudCover":0.11,"uvIndex":3,"visibility":16.09,"ozone":276.39},{"time":1551268800,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":15.58,"apparentTemperature":15.58,"dewPoint":1.98,"humidity":0.4,"pressure":1027.59,"windSpeed":1.26,"windGust":1.28,"windBearing":140,"cloudCover":0.08,"uvIndex":3,"visibility":16.09,"ozone":277.31},{"time":1551272400,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":17.3,"apparentTemperature":17.3,"dewPoint":2.23,"humidity":0.36,"pressure":1026.71,"windSpeed":1.3,"windGust":1.31,"windBearing":154,"cloudCover":0.05,"uvIndex":2,"visibility":16.09,"ozone":277.73},{"time":1551276000,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":18.44,"apparentTemperature":18.44,"dewPoint":2.27,"humidity":0.34,"pressure":1025.7,"windSpeed":1.28,"windGust":1.28,"windBearing":172,"cloudCover":0.02,"uvIndex":2,"visibility":16.09,"ozone":277.96},{"time":1551279600,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":18.49,"apparentTemperature":18.49,"dewPoint":2.21,"humidity":0.34,"pressure":1024.91,"windSpeed":1.24,"windGust":1.27,"windBearing":184,"cloudCover":0,"uvIndex":1,"visibility":16.09,"ozone":278.36},{"time":1551283200,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":17.44,"apparentTemperature":17.44,"dewPoint":2.05,"humidity":0.36,"pressure":1024.53,"windSpeed":1.18,"windGust":1.25,"windBearing":191,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":278.97},{"time":1551286800,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":14.98,"apparentTemperature":14.98,"dewPoint":1.79,"humidity":0.41,"pressure":1024.34,"windSpeed":1.12,"windGust":1.27,"windBearing":194,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":279.76},{"time":1551290400,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":12.61,"apparentTemperature":12.61,"dewPoint":1.52,"humidity":0.47,"pressure":1024.09,"windSpeed":1.11,"windGust":1.39,"windBearing":195,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":280.33},{"time":1551294000,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":10.99,"apparentTemperature":10.99,"dewPoint":1.18,"humidity":0.51,"pressure":1023.68,"windSpeed":1.2,"windGust":1.51,"windBearing":194,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":280.69},{"time":1551297600,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":9.98,"apparentTemperature":9.98,"dewPoint":0.83,"humidity":0.53,"pressure":1023.18,"windSpeed":1.34,"windGust":1.64,"windBearing":191,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":280.94},{"time":1551301200,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":9.18,"apparentTemperature":8.72,"dewPoint":0.53,"humidity":0.55,"pressure":1022.72,"windSpeed":1.49,"windGust":1.77,"windBearing":190,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":281.11},{"time":1551304800,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":8.5,"apparentTemperature":7.72,"dewPoint":0.36,"humidity":0.57,"pressure":1022.32,"windSpeed":1.71,"windGust":1.9,"windBearing":194,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":281.02},{"time":1551308400,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":7.97,"apparentTemperature":6.87,"dewPoint":0.24,"humidity":0.58,"pressure":1021.93,"windSpeed":1.94,"windGust":2.05,"windBearing":200,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":280.74},{"time":1551312000,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":7.47,"apparentTemperature":6.15,"dewPoint":0.17,"humidity":0.6,"pressure":1021.49,"windSpeed":2.11,"windGust":2.19,"windBearing":206,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":280.75},{"time":1551315600,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":6.82,"apparentTemperature":5.37,"dewPoint":0.09,"humidity":0.62,"pressure":1020.9,"windSpeed":2.12,"windGust":2.28,"windBearing":208,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":281.23},{"time":1551319200,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":6.09,"apparentTemperature":4.58,"dewPoint":0.06,"humidity":0.65,"pressure":1020.22,"windSpeed":2.06,"windGust":2.36,"windBearing":210,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":281.97},{"time":1551322800,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":5.44,"apparentTemperature":3.82,"dewPoint":0.01,"humidity":0.68,"pressure":1019.67,"windSpeed":2.06,"windGust":2.58,"windBearing":211,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":282.78},{"time":1551326400,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":4.78,"apparentTemperature":2.93,"dewPoint":-0.17,"humidity":0.7,"pressure":1019.37,"windSpeed":2.18,"windGust":3.01,"windBearing":214,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":283.53},{"time":1551330000,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":4.24,"apparentTemperature":2.13,"dewPoint":-0.34,"humidity":0.72,"pressure":1019.2,"windSpeed":2.36,"windGust":3.59,"windBearing":216,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":284.44},{"time":1551333600,"summary":"Heiter","icon":"clear-night","precipIntensity":0,"precipProbability":0,"temperature":4.24,"apparentTemperature":1.93,"dewPoint":-0.18,"humidity":0.73,"pressure":1019.01,"windSpeed":2.58,"windGust":4.25,"windBearing":218,"cloudCover":0,"uvIndex":0,"visibility":16.09,"ozone":285.68},{"time":1551337200,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":5.15,"apparentTemperature":2.83,"dewPoint":0.57,"humidity":0.72,"pressure":1018.8,"windSpeed":2.82,"windGust":5.04,"windBearing":219,"cloudCover":0.2,"uvIndex":0,"visibility":16.09,"ozone":287.28},{"time":1551340800,"summary":"Leicht bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":6.58,"apparentTemperature":4.36,"dewPoint":1.66,"humidity":0.71,"pressure":1018.55,"windSpeed":3.07,"windGust":5.91,"windBearing":219,"cloudCover":0.47,"uvIndex":1,"visibility":16.09,"ozone":289.23},{"time":1551344400,"summary":"Überwiegend bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":8.36,"apparentTemperature":6.28,"dewPoint":2.82,"humidity":0.68,"pressure":1018.24,"windSpeed":3.44,"windGust":6.82,"windBearing":222,"cloudCover":0.66,"uvIndex":1,"visibility":16.09,"ozone":291.68},{"time":1551348000,"summary":"Überwiegend bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":10.57,"apparentTemperature":10.57,"dewPoint":4.07,"humidity":0.64,"pressure":1017.81,"windSpeed":4.01,"windGust":7.74,"windBearing":226,"cloudCover":0.7,"uvIndex":2,"visibility":16.09,"ozone":294.4},{"time":1551351600,"summary":"Überwiegend bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":13.39,"apparentTemperature":13.39,"dewPoint":5.36,"humidity":0.58,"pressure":1017.26,"windSpeed":4.7,"windGust":8.69,"windBearing":231,"cloudCover":0.66,"uvIndex":2,"visibility":16.09,"ozone":297.66},{"time":1551355200,"summary":"Leicht bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":15.38,"apparentTemperature":15.38,"dewPoint":6.21,"humidity":0.54,"pressure":1016.79,"windSpeed":5.32,"windGust":9.65,"windBearing":237,"cloudCover":0.58,"uvIndex":2,"visibility":16.09,"ozone":302.31},{"time":1551358800,"summary":"Leicht bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":16.34,"apparentTemperature":16.34,"dewPoint":6.33,"humidity":0.52,"pressure":1016.42,"windSpeed":5.9,"windGust":10.76,"windBearing":244,"cloudCover":0.47,"uvIndex":2,"visibility":16.09,"ozone":309.63},{"time":1551362400,"summary":"Leicht bewölkt","icon":"partly-cloudy-day","precipIntensity":0,"precipProbability":0,"temperature":16.41,"apparentTemperature":16.41,"dewPoint":6.01,"humidity":0.5,"pressure":1016.08,"windSpeed":6.41,"windGust":11.88,"windBearing":253,"cloudCover":0.32,"uvIndex":1,"visibility":16.09,"ozone":318.45},{"time":1551366000,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":16.25,"apparentTemperature":16.25,"dewPoint":5.68,"humidity":0.5,"pressure":1015.79,"windSpeed":6.54,"windGust":12.43,"windBearing":257,"cloudCover":0.23,"uvIndex":1,"visibility":16.09,"ozone":325.57},{"time":1551369600,"summary":"Heiter","icon":"clear-day","precipIntensity":0,"precipProbability":0,"temperature":15.56,"apparentTemperature":15.56,"dewPoint":5.41,"humidity":0.51,"pressure":1015.57,"windSpeed":5.99,"windGust":11.93,"windBearing":252,"cloudCover":0.24,"uvIndex":0,"visibility":16.09,"ozone":329.15},{"time":1551373200,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0,"precipProbability":0,"temperature":14.88,"apparentTemperature":14.88,"dewPoint":5.13,"humidity":0.52,"pressure":1015.39,"windSpeed":5.11,"windGust":10.86,"windBearing":244,"cloudCover":0.32,"uvIndex":0,"visibility":16.09,"ozone":331.01},{"time":1551376800,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0.0102,"precipProbability":0.02,"precipType":"rain","temperature":14.14,"apparentTemperature":14.14,"dewPoint":5.03,"humidity":0.54,"pressure":1015.15,"windSpeed":4.55,"windGust":10.16,"windBearing":239,"cloudCover":0.42,"uvIndex":0,"visibility":16.09,"ozone":334.57},{"time":1551380400,"summary":"Leicht bewölkt","icon":"partly-cloudy-night","precipIntensity":0.0406,"precipProbability":0.06,"precipType":"rain","temperature":13.44,"apparentTemperature":13.44,"dewPoint":5.32,"humidity":0.58,"pressure":1014.77,"windSpeed":4.64,"windGust":10.41,"windBearing":240,"cloudCover":0.53,"uvIndex":0,"visibility":16.09,"ozone":342.62},{"time":1551384000,"summary":"Überwiegend bewölkt","icon":"partly-cloudy-night","precipIntensity":0.1346,"precipProbability":0.14,"precipType":"rain","temperature":12.71,"apparentTemperature":12.71,"dewPoint":5.82,"humidity":0.63,"pressure":1014.32,"windSpeed":4.96,"windGust":11.04,"windBearing":244,"cloudCover":0.67,"uvIndex":0,"visibility":13.53,"ozone":352.36}]},"daily":{"summary":"Leichter Regen von Freitag bis Montag mit fallender Temperatur von 11°C am nächsten Dienstag.","icon":"rain","data":[{"time":1551135600,"summary":"Leicht bewölkt in der Nacht.","icon":"partly-cloudy-night","sunriseTime":1551161800,"sunsetTime":1551200555,"moonPhase":0.75,"precipIntensity":0,"precipIntensityMax":0.0051,"precipIntensityMaxTime":1551186000,"precipProbability":0,"temperatureHigh":21.42,"temperatureHighTime":1551193200,"temperatureLow":3.19,"temperatureLowTime":1551247200,"apparentTemperatureHigh":21.42,"apparentTemperatureHighTime":1551193200,"apparentTemperatureLow":3.19,"apparentTemperatureLowTime":1551247200,"dewPoint":0.24,"humidity":0.55,"pressure":1034.3,"windSpeed":0.51,"windGust":2.01,"windGustTime":1551189600,"windBearing":16,"cloudCover":0.02,"uvIndex":3,"uvIndexTime":1551182400,"visibility":10.2,"ozone":285.07,"temperatureMin":1.38,"temperatureMinTime":1551153600,"temperatureMax":21.42,"temperatureMaxTime":1551193200,"apparentTemperatureMin":1.38,"apparentTemperatureMinTime":1551153600,"apparentTemperatureMax":21.42,"apparentTemperatureMaxTime":1551193200},{"time":1551222000,"summary":"Den ganzen Tag lang heiter.","icon":"clear-day","sunriseTime":1551248079,"sunsetTime":1551287056,"moonPhase":0.78,"precipIntensity":0.0025,"precipIntensityMax":0.0051,"precipIntensityMaxTime":1551229200,"precipProbability":0.02,"precipType":"rain","temperatureHigh":18.49,"temperatureHighTime":1551279600,"temperatureLow":4.24,"temperatureLowTime":1551330000,"apparentTemperatureHigh":18.49,"apparentTemperatureHighTime":1551279600,"apparentTemperatureLow":1.93,"apparentTemperatureLowTime":1551333600,"dewPoint":-0.17,"humidity":0.5,"pressure":1027.91,"windSpeed":0.59,"windGust":1.9,"windGustTime":1551304800,"windBearing":139,"cloudCover":0.13,"uvIndex":3,"uvIndexTime":1551265200,"visibility":16.09,"ozone":276.58,"temperatureMin":3.19,"temperatureMinTime":1551247200,"temperatureMax":18.49,"temperatureMaxTime":1551279600,"apparentTemperatureMin":3.19,"apparentTemperatureMinTime":1551247200,"apparentTemperatureMax":18.49,"apparentTemperatureMaxTime":1551279600},{"time":1551308400,"summary":"Den ganzen Tag lang überwiegend bewölkt.","icon":"partly-cloudy-night","sunriseTime":1551334356,"sunsetTime":1551373557,"moonPhase":0.81,"precipIntensity":0.033,"precipIntensityMax":0.3175,"precipIntensityMaxTime":1551391200,"precipProbability":0.38,"precipType":"rain","temperatureHigh":16.41,"temperatureHighTime":1551362400,"temperatureLow":8.43,"temperatureLowTime":1551423600,"apparentTemperatureHigh":16.41,"apparentTemperatureHighTime":1551362400,"apparentTemperatureLow":6.74,"apparentTemperatureLowTime":1551423600,"dewPoint":3.24,"humidity":0.62,"pressure":1017.5,"windSpeed":3.8,"windGust":12.43,"windGustTime":1551366000,"windBearing":235,"cloudCover":0.34,"uvIndex":2,"uvIndexTime":1551348000,"visibility":15.27,"ozone":307.52,"temperatureMin":4.24,"temperatureMinTime":1551330000,"temperatureMax":16.41,"temperatureMaxTime":1551362400,"apparentTemperatureMin":1.93,"apparentTemperatureMinTime":1551333600,"apparentTemperatureMax":16.41,"apparentTemperatureMaxTime":1551362400},{"time":1551394800,"summary":"Überwiegend bewölkt bis abends.","icon":"partly-cloudy-day","sunriseTime":1551420633,"sunsetTime":1551460057,"moonPhase":0.84,"precipIntensity":0.188,"precipIntensityMax":0.8179,"precipIntensityMaxTime":1551409200,"precipProbability":0.88,"precipType":"rain","temperatureHigh":14.23,"temperatureHighTime":1551452400,"temperatureLow":5.98,"temperatureLowTime":1551506400,"apparentTemperatureHigh":14.23,"apparentTemperatureHighTime":1551452400,"apparentTemperatureLow":4.09,"apparentTemperatureLowTime":1551510000,"dewPoint":5.37,"humidity":0.71,"pressure":1014.79,"windSpeed":2.63,"windGust":8.53,"windGustTime":1551394800,"windBearing":284,"cloudCover":0.62,"uvIndex":2,"uvIndexTime":1551438000,"visibility":13.52,"ozone":343.54,"temperatureMin":8.43,"temperatureMinTime":1551423600,"temperatureMax":14.23,"temperatureMaxTime":1551452400,"apparentTemperatureMin":6.74,"apparentTemperatureMinTime":1551423600,"apparentTemperatureMax":14.23,"apparentTemperatureMaxTime":1551452400},{"time":1551481200,"summary":"Den ganzen Tag lang überwiegend bewölkt.","icon":"partly-cloudy-day","sunriseTime":1551506909,"sunsetTime":1551546556,"moonPhase":0.87,"precipIntensity":0.0381,"precipIntensityMax":0.2667,"precipIntensityMaxTime":1551549600,"precipProbability":0.29,"precipType":"rain","temperatureHigh":13.86,"temperatureHighTime":1551542400,"temperatureLow":9.92,"temperatureLowTime":1551596400,"apparentTemperatureHigh":13.86,"apparentTemperatureHighTime":1551542400,"apparentTemperatureLow":6.67,"apparentTemperatureLowTime":1551596400,"dewPoint":4.98,"humidity":0.73,"pressure":1017.33,"windSpeed":3.4,"windGust":11.14,"windGustTime":1551564000,"windBearing":223,"cloudCover":0.7,"uvIndex":2,"uvIndexTime":1551520800,"visibility":16.09,"ozone":338.68,"temperatureMin":5.98,"temperatureMinTime":1551506400,"temperatureMax":13.86,"temperatureMaxTime":1551542400,"apparentTemperatureMin":4.09,"apparentTemperatureMinTime":1551510000,"apparentTemperatureMax":13.86,"apparentTemperatureMaxTime":1551542400},{"time":1551567600,"summary":"Den ganzen Tag lang leichter Wind und Nacht leichter Regen.","icon":"rain","sunriseTime":1551593184,"sunsetTime":1551633056,"moonPhase":0.9,"precipIntensity":0.3886,"precipIntensityMax":0.8484,"precipIntensityMaxTime":1551650400,"precipProbability":1,"precipType":"rain","temperatureHigh":12.89,"temperatureHighTime":1551618000,"temperatureLow":9.58,"temperatureLowTime":1551664800,"apparentTemperatureHigh":12.89,"apparentTemperatureHighTime":1551618000,"apparentTemperatureLow":7.11,"apparentTemperatureLowTime":1551661200,"dewPoint":6.11,"humidity":0.71,"pressure":1010.92,"windSpeed":6.64,"windGust":18.8,"windGustTime":1551650400,"windBearing":229,"cloudCover":0.94,"uvIndex":2,"uvIndexTime":1551607200,"visibility":10.01,"ozone":332.44,"temperatureMin":9.78,"temperatureMinTime":1551600000,"temperatureMax":12.89,"temperatureMaxTime":1551618000,"apparentTemperatureMin":6.37,"apparentTemperatureMinTime":1551600000,"apparentTemperatureMax":12.89,"apparentTemperatureMaxTime":1551618000},{"time":1551654000,"summary":"Den ganzen Tag lang überwiegend bewölkt sowie leichter Wind bis Nachmittag.","icon":"wind","sunriseTime":1551679459,"sunsetTime":1551719555,"moonPhase":0.93,"precipIntensity":0.3023,"precipIntensityMax":0.8128,"precipIntensityMaxTime":1551654000,"precipProbability":0.92,"precipType":"rain","temperatureHigh":14.28,"temperatureHighTime":1551711600,"temperatureLow":7.89,"temperatureLowTime":1551769200,"apparentTemperatureHigh":14.28,"apparentTemperatureHighTime":1551711600,"apparentTemperatureLow":4.87,"apparentTemperatureLowTime":1551769200,"dewPoint":5.51,"humidity":0.67,"pressure":1003.91,"windSpeed":6.15,"windGust":20.06,"windGustTime":1551657600,"windBearing":230,"cloudCover":0.91,"uvIndex":2,"uvIndexTime":1551693600,"visibility":12.46,"ozone":369.63,"temperatureMin":9.58,"temperatureMinTime":1551664800,"temperatureMax":14.28,"temperatureMaxTime":1551711600,"apparentTemperatureMin":7.11,"apparentTemperatureMinTime":1551661200,"apparentTemperatureMax":14.28,"apparentTemperatureMaxTime":1551711600},{"time":1551740400,"summary":"Nachmittags Nebel.","icon":"fog","sunriseTime":1551765733,"sunsetTime":1551806054,"moonPhase":0.96,"precipIntensity":0.2083,"precipIntensityMax":0.4597,"precipIntensityMaxTime":1551780000,"precipProbability":0.72,"precipType":"rain","temperatureHigh":11.26,"temperatureHighTime":1551794400,"temperatureLow":5.99,"temperatureLowTime":1551855600,"apparentTemperatureHigh":11.26,"apparentTemperatureHighTime":1551794400,"apparentTemperatureLow":2.28,"apparentTemperatureLowTime":1551855600,"dewPoint":3.41,"humidity":0.65,"pressure":1001.87,"windSpeed":5.37,"windGust":16.22,"windGustTime":1551754800,"windBearing":230,"cloudCover":0.79,"uvIndex":1,"uvIndexTime":1551776400,"visibility":8.96,"ozone":442.27,"temperatureMin":7.89,"temperatureMinTime":1551769200,"temperatureMax":11.26,"temperatureMaxTime":1551794400,"apparentTemperatureMin":4.82,"apparentTemperatureMinTime":1551772800,"apparentTemperatureMax":11.26,"apparentTemperatureMaxTime":1551794400}]},"flags":{"sources":["meteoalarm","cmc","gfs","icon","isd","madis"],"meteoalarm-license":"Based on data from EUMETNET - MeteoAlarm [https://www.meteoalarm.eu/]. Time delays between this website and the MeteoAlarm website are possible; for the most up to date information about alert levels as published by the participating National Meteorological Services please use the MeteoAlarm website.","nearest-station":8.711,"units":"si"},"offset":1}'; -use constant URL => 'https://api.darksky.net/forecast/'; +use constant URL => 'https://api.darksky.net/forecast/'; my %codes = ( 'clear-day' => 32, @@ -164,7 +164,8 @@ sub new { $self->{cachemaxage} = ( defined( $apioptions->{cachemaxage} ) ? $apioptions->{cachemaxage} - : 900 ); + : 900 + ); $self->{extend} = ( defined( $apioptions->{extend} ) ? $apioptions->{extend} : 'none' ); $self->{cached} = _CreateForecastRef($self); @@ -192,18 +193,18 @@ sub parseApiOptions($) { } sub setAlerts { - my $self = shift; - my $alerts = shift // 0; + my $self = shift; + my $alerts = shift // 0; $self->{alerts} = $alerts; return; } sub setForecast { - my $self = shift; - my $forecast = shift // ''; + my $self = shift; + my $forecast = shift // ''; - $self->{forecast} = $forecast; + $self->{forecast} = $forecast; return; } @@ -222,10 +223,10 @@ sub setRetrieveData { } sub setLocation { - my ($self,$lat,$long) = @_; + my ( $self, $lat, $long ) = @_; - $self->{lat} = $lat; - $self->{long} = $long; + $self->{lat} = $lat; + $self->{long} = $long; return 0; } @@ -246,15 +247,14 @@ sub _RetrieveDataFromDarkSky($) { my $self = shift; # retrieve data from cache - if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} + if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} and $self->{cached}->{lat} == $self->{lat} - and $self->{cached}->{long} == $self->{long} - ) + and $self->{cached}->{long} == $self->{long} ) { return _CallWeatherCallbackFn($self); } - $self->{cached}->{lat} = $self->{lat} + $self->{cached}->{lat} = $self->{lat} unless ( $self->{cached}->{lat} == $self->{lat} ); $self->{cached}->{long} = $self->{long} unless ( $self->{cached}->{long} == $self->{long} ); @@ -311,7 +311,7 @@ sub _RetrieveDataFinished($$$) { my $self = $paramRef->{self}; if ( !$err ) { - $self->{cached}->{status} = 'ok'; + $self->{cached}->{status} = 'ok'; $self->{cached}->{validity} = 'up-to-date', $self->{fetchTime} = time(); _ProcessingRetrieveData( $self, $response ); } @@ -486,9 +486,9 @@ sub _ProcessingRetrieveData($$) { ->{apparentTemperatureHighTime} ) ), - 'code' => - $codes{ $data->{daily}->{data}->[$i]->{icon} - }, + 'code' => $codes{ + $data->{daily}->{data}->[$i]->{icon} + }, 'iconAPI' => $data->{daily}->{data}->[$i]->{icon}, 'condition' => encode_utf8( @@ -662,9 +662,9 @@ sub _ProcessingRetrieveData($$) { 'temperature' => sprintf( "%.1f", $data->{hourly}->{data}->[$i] ->{temperature} ), - 'code' => - $codes{ $data->{hourly}->{data}->[$i] - ->{icon} }, + 'code' => $codes{ + $data->{hourly}->{data}->[$i]->{icon} + }, 'iconAPI' => $data->{hourly}->{data}->[$i]->{icon}, 'condition' => encode_utf8( @@ -786,11 +786,11 @@ sub _CreateForecastRef($) { my $forecastRef = ( { - lat => $self->{lat}, - long => $self->{long}, + lat => $self->{lat}, + long => $self->{long}, apiMaintainer => 'Marko Oldenburg (CoolTux)', - apiVersion => version->parse(DarkSkyAPI->VERSION())->normal, + apiVersion => version->parse( DarkSkyAPI->VERSION() )->normal, } ); @@ -820,7 +820,6 @@ sub strftimeWrapper(@) { 1; - =pod =encoding utf8 @@ -833,15 +832,15 @@ sub strftimeWrapper(@) { "abstract": "Wetter API für Weather DarkSky" } }, - "version": "v1.0.0", + "version": "v1.2.0", "author": [ - "Marko Oldenburg " + "Marko Oldenburg " ], "x_fhem_maintainer": [ "CoolTux" ], "x_fhem_maintainer_github": [ - "LeonGaultier" + "CoolTuxNet" ], "prereqs": { "runtime": { diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 62e23fb..472c795 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -1,9 +1,9 @@ # $Id: $ ############################################################################### # -# Developed with VSCodium and richterger perl plugin. +# Developed with VSCodium and richterger perl plugin # -# (c) 2019-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) +# (c) 2019-2023 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) # All rights reserved # # Special thanks goes to: @@ -182,7 +182,9 @@ my %codes = ( sub new { ### geliefert wird ein Hash - my ( $class, $argsRef ) = @_; + my $class = shift; + my $argsRef = shift; + my $apioptions = _parseApiOptions( $argsRef->{apioptions} ); my $self = { @@ -923,7 +925,7 @@ sub strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v3.0.1", + "version": "v3.0.2", "author": [ "Marko Oldenburg " ], @@ -931,7 +933,7 @@ sub strftimeWrapper { "CoolTux" ], "x_fhem_maintainer_github": [ - "LeonGaultier" + "CoolTuxNet" ], "prereqs": { "runtime": { diff --git a/wundergroundAPI.pm b/wundergroundAPI.pm index 15d65de..2cf4704 100644 --- a/wundergroundAPI.pm +++ b/wundergroundAPI.pm @@ -747,7 +747,7 @@ sub strftimeWrapper(@) { "abstract": "Wetter API für Weather Underground" } }, - "version": "v1.0.1", + "version": "v1.0.2", "author": [ "Julian Pawlowski " ], -- 2.47.2