From be8ece6238136927eef47ff0f7e85cd6ddb57a83 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 12 Feb 2022 12:56:04 +0100 Subject: [PATCH 01/48] 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__ From 714c95d6d04c7c46d27d42693dc2c5100aa5e6a5 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 17 Nov 2022 19:58:56 +0100 Subject: [PATCH 02/48] 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 From ddefbac55a5c4e1c3f30fb3c9c453a785724c8ff Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sun, 20 Nov 2022 21:10:22 +0100 Subject: [PATCH 03/48] 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 From ee989a84bfc7a4e0d1db901a2f6c13a7b25caa89 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 24 Nov 2022 19:22:40 +0100 Subject: [PATCH 04/48] 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; From fdd6f0964aae0b8e13961e88887735c53f78fb33 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 25 Nov 2022 14:09:45 +0100 Subject: [PATCH 05/48] 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} } } } From 43af5222c533ef9d4cdee8493bd0b2704c272236 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 25 Nov 2022 20:02:39 +0100 Subject: [PATCH 06/48] 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} } } } From 8ec9dae9028908d1eaad968817ad57ebe6ae963b Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 26 Nov 2022 06:43:20 +0100 Subject: [PATCH 07/48] 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} - ), + ), }, ); From 7ac7e75d825226ea7837cb94829b60d36f2b4a2a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 13 Dec 2022 14:00:46 +0100 Subject: [PATCH 08/48] 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 " ], From 670c06bd2fc57783e12bf63c1e3ae9644523ca32 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 13 Dec 2022 19:04:01 +0100 Subject: [PATCH 09/48] 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] From 46cc10e80e568112a315ffca635a150d6a876cc8 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 10:38:04 +0100 Subject: [PATCH 10/48] 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. + + 2.5 by default, 3.0 is still possible but only with an additional subscription +
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 { From b6c0dc2ef1bc43c8dbbaa82af0b55f7f52e88fd4 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 11:08:36 +0100 Subject: [PATCH 11/48] 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; } From a9d9ad722c5777d611466c91a20f10fff89c7a1a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 11:26:32 +0100 Subject: [PATCH 12/48] 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; } From c600937332425ab4c4624a436c4c0f06cd22a6cb Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 16:21:14 +0100 Subject: [PATCH 13/48] 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' => From 15e7de73eb3604af15be96bb422d459801c81a52 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 19:24:14 +0100 Subject: [PATCH 14/48] 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; From d4c2f84566c98bcda43dedf9c2fa3636260ca807 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 19:51:18 +0100 Subject: [PATCH 15/48] 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; From 84915674a84dccce4c69cb1d9da4593a310bce70 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 20:10:58 +0100 Subject: [PATCH 16/48] 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 + ) ), } ); From 7875522849eca74181f715484a9ceef276065bbd Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 20:42:07 +0100 Subject: [PATCH 17/48] 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} ), From 6611390059da425c6ec6b1f2f6c3b4dbab5f9602 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 14 Dec 2022 21:35:21 +0100 Subject: [PATCH 18/48] 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 From 96ce4119480cd765293f517410e51ed7540fa61c Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sun, 18 Dec 2022 10:58:24 +0100 Subject: [PATCH 19/48] 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 ) From d82d4fa01dea4169d66508ea1aa0ba1e61ac2dae Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Mon, 19 Dec 2022 11:24:27 +0100 Subject: [PATCH 20/48] 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} - ), + ), }, ); From 9f005d050e5c5430fbc67e49e5f20f1402366c30 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Mon, 19 Dec 2022 15:29:06 +0100 Subject: [PATCH 21/48] 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( From ca343e104b10c6c27c753ed83a886cd029c1f60c Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 12:53:44 +0100 Subject: [PATCH 22/48] 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 ); From b2d84725f7cfa522f879bc26a1fe584498f26ab1 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 13:24:15 +0100 Subject: [PATCH 23/48] 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( From 52490f38bb4e4ae27d177af7eb211b7cd331d51a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 14:43:12 +0100 Subject: [PATCH 24/48] 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 " ], From 889851ff053431d4bd64babd0d4c7e7c8844b58f Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 15:29:49 +0100 Subject: [PATCH 25/48] fix IsDisabled change condition for is disabled in GetUpdate fn --- 59_Weather.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/59_Weather.pm b/59_Weather.pm index 41dfef9..2f0fc4f 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -615,7 +615,7 @@ sub Weather_GetUpdate { my $name = $hash->{NAME}; - if ( $attr{$name} && $attr{$name}->{disable} ) { + if ( IsDisabled($name) ) { Log3 $hash, 5, "Weather $name: retrieval of weather data is disabled by attribute."; readingsBeginUpdate($hash); From 776b78c0c66aa07567240d34de243ca661cc3993 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 18:04:45 +0100 Subject: [PATCH 26/48] change code for better def modify --- 59_Weather.pm | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 2f0fc4f..5f52059 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -728,17 +728,22 @@ sub Weather_Notify { my $type = $hash->{TYPE}; return if ( $dev->{NAME} ne "global" ); - return if ( !grep { /^INITIALIZED|REREADCFG$/ } @{ $dev->{CHANGED} } ); - # return if($attr{$name} && $attr{$name}->{disable}); + # set forcast and alerts values to api object + if ( grep { /^MODIFIED.$name$/x } @{ $dev->{CHANGED} } ) { + $hash->{fhem}->{api}->setForecast( AttrVal( $name, 'forecast', '' ) ); + $hash->{fhem}->{api}->setAlerts( AttrVal( $name, 'alerts', 0 ) ); + + Weather_GetUpdate($hash); + } + + return if ( !grep { /^INITIALIZED|REREADCFG$/x } @{ $dev->{CHANGED} } ); # update weather after initialization or change of configuration # wait 10 to 29 seconds to avoid congestion due to concurrent activities Weather_DisarmTimer($hash); my $delay = 10 + int( rand(20) ); - #$delay= 3; # delay removed until further notice - Log3 $hash, 5, "Weather $name: FHEM initialization or rereadcfg triggered update, delay $delay seconds."; Weather_RearmTimer( $hash, gettimeofday() + $delay ); @@ -816,9 +821,6 @@ sub Weather_Define { } ); - Weather_GetUpdate($hash) - if ($init_done); - return; } From 82a7b06021fcf5017f1868c317cd405281e1a838 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Tue, 20 Dec 2022 18:42:36 +0100 Subject: [PATCH 27/48] expand notify fn grep syntax --- 59_Weather.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/59_Weather.pm b/59_Weather.pm index 5f52059..5ae171a 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -737,7 +737,12 @@ sub Weather_Notify { Weather_GetUpdate($hash); } - return if ( !grep { /^INITIALIZED|REREADCFG$/x } @{ $dev->{CHANGED} } ); + return + if ( + !grep { +/^INITIALIZED|REREADCFG|DELETEATTR.$name.disable|ATTR.$name.disable.[0-1]$/x + } @{ $dev->{CHANGED} } + ); # update weather after initialization or change of configuration # wait 10 to 29 seconds to avoid congestion due to concurrent activities From 0fbe1867bd33b17f8bc971dba5f234e84877a56e Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 21 Dec 2022 09:06:54 +0100 Subject: [PATCH 28/48] fix forecast number for weblink --- 59_Weather.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index 5ae171a..e64fcf0 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -1105,7 +1105,7 @@ sub Weather_CheckOptions { $f =~ tr/dh/./cd if ( defined $f && $f ); $items =~ tr/0-9/./cd if ( defined($items) && $items ); - $items = AttrVal( $d, 'forecastLimit', 6 ) + $items = AttrVal( $d, 'forecastLimit', 5 ) if ( !$items ); my $forecastConfig = Weather_ForcastConfig($hash); @@ -1117,7 +1117,7 @@ sub Weather_CheckOptions { $f = 'h' if ( !$f || length($f) > 1 ); - return ( $f, $items ); + return ( $f, $items + 1 ); } ##################################### From 18fb5aaada6255515f06aa8c4029cd3ce956ce5c Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 22 Dec 2022 18:45:07 +0100 Subject: [PATCH 29/48] move API files to lib/FHEM/APIs/Weather --- 59_Weather.pm | 4 ++-- OpenWeatherMapAPI.pm | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/59_Weather.pm b/59_Weather.pm index e64fcf0..f5b8983 100755 --- a/59_Weather.pm +++ b/59_Weather.pm @@ -785,7 +785,7 @@ sub Weather_Define { # evaluate API options my ( $api, $apioptions ) = split( ',', $API, 2 ); $apioptions = "" unless ( defined($apioptions) ); - eval { require $api . '.pm'; }; + eval { require 'FHEM/APIs/Weather/' . $api . '.pm'; }; return "$name: cannot load API $api: $@" if ($@); $hash->{NOTIFYDEV} = "global"; @@ -815,7 +815,7 @@ sub Weather_Define { readingsSingleUpdate( $hash, 'state', 'Initialized', 1 ); Weather_LanguageInitialize( $hash->{LANG} ); - my $apistring = $api . '::Weather'; + my $apistring = 'FHEM::APIs::Weather::' . $api; $hash->{fhem}->{api} = $apistring->new( { devName => $hash->{NAME}, diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm index 472c795..9c6cc3d 100644 --- a/OpenWeatherMapAPI.pm +++ b/OpenWeatherMapAPI.pm @@ -31,25 +31,25 @@ ### 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://api.openweathermap.org/data/3.0/onecall?lat=[lat]&lon=[long]&APPID=[API] Current,Forecast # https://openweathermap.org/weather-conditions Icons und Conditions ID's -package OpenWeatherMapAPI; +package FHEM::APIs::Weather::OpenWeatherMapAPI; use strict; use warnings; use FHEM::Meta; -FHEM::Meta::Load(__PACKAGE__); -use version 0.50; our $VERSION = $::packages{OpenWeatherMapAPI}{META}{version}; - -package OpenWeatherMapAPI::Weather; -use strict; -use warnings; - use POSIX; use HttpUtils; use experimental qw /switch/; +my %META; +my $ret = FHEM::Meta::getMetadata( __FILE__, \%META ); +return "$@" if ($@); +return $ret if ($ret); +$::packages{OpenWeatherMapAPI}{META} = \%META; +use version 0.77; our $VERSION = $::packages{OpenWeatherMapAPI}{META}{version}; + # use Data::Dumper; # try to use JSON::MaybeXS wrapper @@ -120,7 +120,7 @@ eval { use Readonly; 1 } # 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 +## URL . 'onecall?' for current,forecast data my %codes = ( 200 => 45, @@ -881,8 +881,7 @@ sub _CreateForecastRef { long => $self->{long}, apiMaintainer => 'Marko Oldenburg (CoolTux)', - apiVersion => - version->parse( OpenWeatherMapAPI->VERSION() )->normal, + apiVersion => version->parse( __PACKAGE__->VERSION() )->normal, } ); From 4b8aad2047d2847d7861d8228ada052803b2ce77 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 24 Dec 2022 04:39:45 +0100 Subject: [PATCH 30/48] add directory structure move API files remove YahooWeatherAPI --- 59_Weather.pm => FHEM/59_Weather.pm | 0 OpenWeatherMapAPI.pm | 962 --------------- YahooWeatherAPI.pm | 374 ------ .../FHEM/APIs/Weather/DarkSkyAPI.pm | 947 +++++++-------- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 1029 +++++++++++++++++ .../FHEM/APIs/Weather/wundergroundAPI.pm | 210 ++-- 6 files changed, 1601 insertions(+), 1921 deletions(-) rename 59_Weather.pm => FHEM/59_Weather.pm (100%) delete mode 100644 OpenWeatherMapAPI.pm delete mode 100644 YahooWeatherAPI.pm rename DarkSkyAPI.pm => lib/FHEM/APIs/Weather/DarkSkyAPI.pm (59%) create mode 100644 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm rename wundergroundAPI.pm => lib/FHEM/APIs/Weather/wundergroundAPI.pm (90%) diff --git a/59_Weather.pm b/FHEM/59_Weather.pm similarity index 100% rename from 59_Weather.pm rename to FHEM/59_Weather.pm diff --git a/OpenWeatherMapAPI.pm b/OpenWeatherMapAPI.pm deleted file mode 100644 index 9c6cc3d..0000000 --- a/OpenWeatherMapAPI.pm +++ /dev/null @@ -1,962 +0,0 @@ -# $Id: $ -############################################################################### -# -# Developed with VSCodium and richterger perl plugin -# -# (c) 2019-2023 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) -# 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 -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License,or -# any later version. -# -# The GNU General Public License can be found at -# http://www.gnu.org/copyleft/gpl.html. -# A copy is found in the textfile GPL.txt and important notices to the license -# from the author is found in LICENSE.txt distributed with these scripts. -# -# This script is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# -############################################################################### - -### 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/3.0/onecall?lat=[lat]&lon=[long]&APPID=[API] Current,Forecast -# https://openweathermap.org/weather-conditions Icons und Conditions ID's - -package FHEM::APIs::Weather::OpenWeatherMapAPI; -use strict; -use warnings; -use FHEM::Meta; - -use POSIX; -use HttpUtils; -use experimental qw /switch/; - -my %META; -my $ret = FHEM::Meta::getMetadata( __FILE__, \%META ); -return "$@" if ($@); -return $ret if ($ret); -$::packages{OpenWeatherMapAPI}{META} = \%META; -use version 0.77; our $VERSION = $::packages{OpenWeatherMapAPI}{META}{version}; - -# use Data::Dumper; - -# try to use JSON::MaybeXS wrapper -# for chance of better performance + open code -eval { - require JSON::MaybeXS; - import JSON::MaybeXS qw( decode_json encode_json ); - 1; -} 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' - unless ( defined( $ENV{PERL_JSON_BACKEND} ) ); - - require JSON; - import JSON qw( decode_json encode_json ); - 1; - } or do { - - # In rare cases, Cpanel::JSON::XS may - # be installed but JSON|JSON::MaybeXS not ... - eval { - require Cpanel::JSON::XS; - import Cpanel::JSON::XS qw(decode_json encode_json); - 1; - } or do { - - # In rare cases, JSON::XS may - # be installed but JSON not ... - eval { - require JSON::XS; - import JSON::XS qw(decode_json encode_json); - 1; - } or do { - - # Fallback to built-in JSON which SHOULD - # be available since 5.014 ... - eval { - require JSON::PP; - import JSON::PP qw(decode_json encode_json); - 1; - } or do { - - # Fallback to JSON::backportPP in really rare cases - require JSON::backportPP; - import JSON::backportPP qw(decode_json encode_json); - 1; - }; - }; - }; - }; -}; - -my $missingModul = ''; -## 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 -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/'; -## URL . 'weather?' for current data -## URL . 'onecall?' for current,forecast data - -my %codes = ( - 200 => 45, - 201 => 45, - 202 => 45, - 210 => 4, - 211 => 4, - 212 => 3, - 221 => 4, - 230 => 45, - 231 => 45, - 232 => 45, - 300 => 9, - 301 => 9, - 302 => 9, - 310 => 9, - 311 => 9, - 312 => 9, - 313 => 9, - 314 => 9, - 321 => 9, - 500 => 35, - 501 => 35, - 502 => 35, - 503 => 35, - 504 => 35, - 511 => 35, - 520 => 35, - 521 => 35, - 522 => 35, - 531 => 35, - 600 => 14, - 601 => 16, - 602 => 13, - 611 => 46, - 612 => 46, - 613 => 46, - 615 => 5, - 616 => 5, - 620 => 14, - 621 => 46, - 622 => 42, - 701 => 19, - 711 => 22, - 721 => 19, - 731 => 23, - 741 => 20, - 751 => 23, - 761 => 19, - 762 => 3200, - 771 => 1, - 781 => 0, - 800 => 32, - 801 => 30, - 802 => 26, - 803 => 26, - 804 => 28, -); - -sub new { - ### geliefert wird ein Hash - my $class = shift; - my $argsRef = shift; - - my $apioptions = _parseApiOptions( $argsRef->{apioptions} ); - - my $self = { - devName => $argsRef->{devName}, - key => ( - ( defined( $argsRef->{apikey} ) && $argsRef->{apikey} ) - ? $argsRef->{apikey} - : 'none' - ), - lang => $argsRef->{language}, - lat => ( split( ',', $argsRef->{location} ) )[0], - long => ( split( ',', $argsRef->{location} ) )[1], - fetchTime => 0, - endpoint => 'none', - forecast => '', - alerts => 0, - }; - - $self->{cachemaxage} = ( - defined( $apioptions->{cachemaxage} ) - ? $apioptions->{cachemaxage} - : 900 - ); - - $self->{apiversion} = - ( $apioptions->{version} ? $apioptions->{version} : '2.5' ); - - $self->{cached} = _CreateForecastRef($self); - - bless $self, $class; - return $self; -} - -sub _parseApiOptions { - my $apioptions = shift; - - my @params; - my %h; - - @params = split( ',', $apioptions ); - while (@params) { - my $param = shift(@params); - next if ( $param eq '' ); - my ( $key, $value ) = split( ':', $param, 2 ); - $h{$key} = $value; - } - - 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; -} - -sub setRetrieveData { - my $self = shift; - - _RetrieveDataFromOpenWeatherMap($self); - return; -} - -sub setLocation { - my $self = shift; - my $lat = shift; - my $long = shift; - - $self->{lat} = $lat; - $self->{long} = $long; - - return; -} - -sub getFetchTime { - my $self = shift; - - return $self->{fetchTime}; -} - -sub getWeather { - my $self = shift; - - return $self->{cached}; -} - -sub _RetrieveDataFromOpenWeatherMap { - my $self = shift; - - # retrieve data from cache - if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} - && $self->{cached}->{lat} == $self->{lat} - && $self->{cached}->{long} == $self->{long} - && $self->{endpoint} eq 'none' ) - { - return _CallWeatherCallbackFn($self); - } - - $self->{cached}->{lat} = $self->{lat} - unless ( $self->{cached}->{lat} == $self->{lat} ); - $self->{cached}->{long} = $self->{long} - unless ( $self->{cached}->{long} == $self->{long} ); - - my $paramRef = { - timeout => 15, - self => $self, - endpoint => $self->{endpoint} eq 'none' ? 'onecall' : 'none', - callback => \&_RetrieveDataFinished, - }; - - $self->{endpoint} = $paramRef->{endpoint}; - - if ( $self->{lat} eq 'error' - || $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' || $self->{long} eq 'error' ); - - _RetrieveDataFinished( $paramRef, - 'No given api key. (define myWeather Weather apikey=[KEY])', - undef ) - if ( $self->{key} eq 'none' ); - - _RetrieveDataFinished( $paramRef, - 'Perl modul ' . $missingModul . ' is missing.', undef ) - if ($missingModul); - } - else { - $paramRef->{url} = - $URL - . $self->{apiversion} . '/' - . $paramRef->{endpoint} . '?' . 'lat=' - . $self->{lat} . '&' . 'lon=' - . $self->{long} . '&' - . 'APPID=' - . $self->{key} . '&' - . 'units=' - . 'metric' . '&' . 'lang=' - . $self->{lang} . '&' - . 'exclude=' - . _CreateExcludeString( $self->{forecast}, $self->{alerts} ); - - ::HttpUtils_NonblockingGet($paramRef); - } - - 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( ',', @diff ); -} - -sub _RetrieveDataFinished { - my $paramRef = shift; - my $err = shift; - my $response = shift; - my $self = $paramRef->{self}; - - if ( !$err ) { - $self->{cached}->{status} = 'ok'; - $self->{cached}->{validity} = 'up-to-date'; - $self->{fetchTime} = time(); - _ProcessingRetrieveData( $self, $response ); - } - else { - $self->{fetchTime} = time() if ( not defined( $self->{fetchTime} ) ); - _ErrorHandling( $self, $err ); - _ProcessingRetrieveData( $self, $response ); - } - - return; -} - -sub _ProcessingRetrieveData { - my $self = shift; - my $response = shift; - - if ( $self->{cached}->{status} eq 'ok' - && defined($response) - && $response ) - { - if ( $response =~ m/^{.*}$/x ) { - my $data = eval { decode_json($response) }; - - if ($@) { - _ErrorHandling( $self, - 'OpenWeatherMap Weather decode JSON err ' . $@ ); - } - elsif (defined( $data->{cod} ) - && $data->{cod} - && $data->{cod} != 200 - && defined( $data->{message} ) - && $data->{message} ) - { - _ErrorHandling( $self, $data->{cod} . ': ' . $data->{message} ); - } - else { - ### Debug - # 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", - localtime( $self->{fetchTime} ) ); - - 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} ) - ), - 'temp_c' => int( - sprintf( "%.0f", $data->{current}->{temp} ) - ), - 'tempFeelsLike_c' => int( - sprintf( "%.0f", - $data->{current}->{feels_like} ) - ), - 'dew_point' => int( - sprintf( - "%.0f", $data->{current}->{dew_point} - ) - ), - '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}, - 'timezone' => $data->{timezone}, - 'timezone_offset' => $data->{timezone_offset}, - }; - - if ( ref( $data->{hourly} ) eq "ARRAY" - && scalar( @{ $data->{hourly} } ) > 0 ) - { - my $i = 0; - for ( @{ $data->{hourly} } ) { - push( - @{ $self->{cached}->{forecast}->{hourly} }, - { - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{hourly}->[$i]->{dt} - ) - ), - 'day_of_week' => strftime( - "%a, %H:%M", - localtime( - $data->{hourly}->[$i]->{dt} - ) - ), - 'temperature' => int( - sprintf( "%.0f", - $data->{hourly}->[$i]->{temp} ) - ), - 'temp_c' => int( - sprintf( "%.0f", - $data->{hourly}->[$i]->{temp} ) - ), - 'tempFeelsLike' => int( - sprintf( "%.0f", - $data->{hourly}->[$i] - ->{feels_like} ) - ), - 'dew_point' => int( - sprintf( "%.0f", - $data->{hourly}->[$i] - ->{dew_point} ) - ), - 'humidity' => - $data->{hourly}->[$i]->{humidity}, - 'condition' => encode_utf8( - $data->{hourly}->[$i]->{weather} - ->[0]->{description} - ), - 'pressure' => int( - sprintf( "%.1f", - $data->{hourly}->[$i] - ->{pressure} ) + 0.5 - ), - 'wind' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{wind_speed} * 3.6 - ) - ) + 0.5 - ), - 'wind_speed' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{wind_speed} * 3.6 - ) - ) + 0.5 - ), - 'wind_gust' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->[$i] - ->{wind_gust} * 3.6 - ) - ) + 0.5 - ), - 'wind_direction' => - $data->{hourly}->[$i]->{wind_deg}, - 'cloudCover' => - $data->{hourly}->[$i]->{clouds}, - 'code' => $codes{ - $data->{hourly}->[$i]->{weather} - ->[0]->{id} - }, - 'iconAPI' => - $data->{hourly}->[$i]->{weather}->[0] - ->{icon}, - 'rain1h' => - $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 - ), - }, - ); - - $i++; - } - } - - 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} - ) - ), - 'day_of_week' => strftime( - "%a, %H:%M", - localtime( - $data->{daily}->[$i]->{dt} - ) - ), - 'sunrise' => strftime( - "%H:%M", - localtime( - $data->{daily}->[$i]->{sunrise} - ) - ), - 'sunset' => strftime( - "%a, %H:%M", - localtime( - $data->{daily}->[$i]->{sunset} - ) - ), - 'moonrise' => strftime( - "%a, %H:%M", - localtime( - $data->{daily}->[$i]->{moonrise} - ) - ), - 'moon_phase' => - $data->{daily}->[$i]->{moon_phase}, - 'moonset' => strftime( - "%a, %H:%M", - localtime( - $data->{daily}->[$i]->{moonset} - ) - ), - 'temperature' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{day} ) - ), - 'temperature_morn' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{morn} ) - ), - 'temperature_eve' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{eve} ) - ), - 'temperature_night' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{night} ) - ), - 'tempFeelsLike_morn' => int( - sprintf( "%.0f", - $data->{daily}->[$i] - ->{feels_like}->{morn} ) - ), - 'tempFeelsLike_eve' => int( - sprintf( "%.0f", - $data->{daily}->[$i] - ->{feels_like}->{eve} ) - ), - 'tempFeelsLike_night' => int( - sprintf( "%.0f", - $data->{daily}->[$i] - ->{feels_like}->{night} ) - ), - 'tempFeelsLike_day' => int( - sprintf( "%.0f", - $data->{daily}->[$i] - ->{feels_like}->{day} ) - ), - 'temp_c' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{day} ) - ), - 'low_c' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{min} ) - ), - 'high_c' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{max} ) - ), - 'tempLow' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{min} ) - ), - 'tempHigh' => int( - sprintf( "%.0f", - $data->{daily}->[$i]->{temp} - ->{max} ) - ), - 'dew_point' => int( - sprintf( "%.0f", - $data->{daily}->[$i] - ->{dew_point} ) - ), - 'humidity' => - $data->{daily}->[$i]->{humidity}, - 'condition' => encode_utf8( - $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} - ) + 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} - }, - 'rain' => $data->{daily}->[$i]->{rain}, - 'snow' => $data->{daily}->[$i]->{snow}, - 'uvi' => $data->{daily}->[$i]->{uvi}, - }, - ); - - $i++; - } - } - - if ( ref( $data->{alerts} ) eq "ARRAY" - && scalar( @{ $data->{alerts} } ) > 0 ) - { - my $i = 0; - for ( @{ $data->{alerts} } ) { - push( - @{ $self->{cached}->{alerts} }, - { - 'End' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - ( - $data->{alerts}->[$i]->{end} - ) - ) - ), - 'Start' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - ( - $data->{alerts}->[$i] - ->{start} - ) - ) - ), - 'Description' => encode_utf8( - $data->{alerts}->[$i]->{description} - ), - 'SenderName' => encode_utf8( - $data->{alerts}->[$i]->{sender_name} - ), - 'Event' => encode_utf8( - $data->{alerts}->[$i]->{event} - ), - }, - ); - - $i++; - } - } - } - } - } - } - else { _ErrorHandling( $self, 'OpenWeatherMap ' . $response ); } - } - - $self->{endpoint} = 'none' if ( $self->{endpoint} eq 'onecall' ); - - _RetrieveDataFromOpenWeatherMap($self) - if ( $self->{endpoint} eq 'weather' ); - - _CallWeatherCallbackFn($self) if ( $self->{endpoint} eq 'none' ); - - return; -} - -sub _CallWeatherCallbackFn { - my $self = shift; - - # print 'Dumperausgabe: ' . Dumper $self; - ### Aufruf der callbackFn - ::Weather_RetrieveCallbackFn( $self->{devName} ); - - return; -} - -sub _ErrorHandling { - my $self = shift; - my $err = shift; - - $self->{cached}->{current_date_time} = - strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); - $self->{cached}->{status} = $err; - $self->{cached}->{validity} = 'stale'; - - return; -} - -sub _CreateForecastRef { - my $self = shift; - - my $forecastRef = ( - { - lat => $self->{lat}, - long => $self->{long}, - apiMaintainer => -'Marko Oldenburg (CoolTux)', - apiVersion => version->parse( __PACKAGE__->VERSION() )->normal, - } - ); - - return $forecastRef; -} - -sub strftimeWrapper { - my @data = @_; - my $string = POSIX::strftime(@data); - - $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; -} - -############################################################################## - -1; - -=pod - -=encoding utf8 - -=for :application/json;q=META.json OpenWeatherMapAPI.pm -{ - "abstract": "Weather API for Weather OpenWeatherMap", - "x_lang": { - "de": { - "abstract": "Wetter API für OpenWeatherMap" - } - }, - "version": "v3.0.2", - "author": [ - "Marko Oldenburg " - ], - "x_fhem_maintainer": [ - "CoolTux" - ], - "x_fhem_maintainer_github": [ - "CoolTuxNet" - ], - "prereqs": { - "runtime": { - "requires": { - "FHEM::Meta": 0, - "HttpUtils": 0, - "strict": 0, - "warnings": 0, - "constant": 0, - "POSIX": 0, - "JSON::PP": 0 - }, - "recommends": { - "JSON": 0 - }, - "suggests": { - "JSON::XS": 0, - "Cpanel::JSON::XS": 0 - } - } - } -} -=end :application/json;q=META.json - -=cut - -__END__ diff --git a/YahooWeatherAPI.pm b/YahooWeatherAPI.pm deleted file mode 100644 index a7be94c..0000000 --- a/YahooWeatherAPI.pm +++ /dev/null @@ -1,374 +0,0 @@ -# $Id: YahooWeatherAPI.pm 16641 2018-04-21 12:28:38Z neubert $ - -############################################################################## -# -# YahooWeatherAPI.pm -# Copyright by Dr. Boris Neubert -# e-mail: omega at online dot de -# -# This file is part of fhem. -# -# Fhem is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# -# Fhem is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with fhem. If not, see . -# -############################################################################## - -package main; - -use strict; -use warnings; -use HttpUtils; -use JSON; # apt-get install libperl-JSON on Debian and derivatives -#use Data::Dumper; # for Debug only - -# Yahoo! Weather API: http://developer.yahoo.com/weather/ - -use constant URL => "https://query.yahooapis.com/v1/public/yql?q=select%%20*%%20from%%20weather.forecast%%20where%%20woeid=%s%%20and%%20u=%%27c%%27&format=%s&env=store%%3A%%2F%%2Fdatatables.org%%2Falltableswithkeys"; - - -# Mapping / translation of current weather codes 0-47 -my @YahooCodes_en = ( - 'tornado', 'tropical storm', 'hurricane', 'severe thunderstorms', 'thunderstorms', 'mixed rain and snow', - 'mixed rain and sleet', 'mixed snow and sleet', 'freezing drizzle', 'drizzle', 'freezing rain' ,'showers', - 'showers', 'snow flurries', 'light snow showers', 'blowing snow', 'snow', 'hail', - 'sleet', 'dust', 'foggy', 'haze', 'smoky', 'blustery', - 'windy', 'cold', 'cloudy', - 'mostly cloudy', # night - 'mostly cloudy', # day - 'partly cloudy', # night - 'partly cloudy', # day - 'clear', - 'sunny', - 'fair', #night - 'fair', #day - 'mixed rain and hail', - 'hot', 'isolated thunderstorms', 'scattered thunderstorms', 'scattered thunderstorms', 'scattered showers', 'heavy snow', - 'scattered snow showers', 'heavy snow', 'partly cloudy', 'thundershowers', 'snow showers', 'isolated thundershowers'); - -my @YahooCodes_de = ( - 'Tornado', 'schwerer Sturm', 'Orkan', 'schwere Gewitter', 'Gewitter', 'Regen und Schnee', - 'Regen und Graupel', 'Schnee und Graupel', 'Eisregen', 'Nieselregen', 'gefrierender Regen' ,'Schauer', - 'Schauer', 'Schneetreiben', 'leichte Schneeschauer', 'Schneeverwehungen', 'Schnee', 'Hagel', - 'Graupel', 'Staub', 'Nebel', 'Dunst', 'Smog', 'Sturm', - 'windig', 'kalt', 'wolkig', - 'überwiegend wolkig', # night - 'überwiegend wolkig', # day - 'teilweise wolkig', # night - 'teilweise wolkig', # day - 'klar', # night - 'sonnig', - 'heiter', # night - 'heiter', # day - 'Regen und Hagel', - 'heiß', 'einzelne Gewitter', 'vereinzelt Gewitter', 'vereinzelt Gewitter', 'vereinzelt Schauer', 'starker Schneefall', - 'vereinzelt Schneeschauer', 'starker Schneefall', 'teilweise wolkig', 'Gewitterregen', 'Schneeschauer', 'vereinzelt Gewitter'); - -my @YahooCodes_nl = ( - 'tornado', 'zware storm', 'orkaan', 'hevig onweer', 'onweer', - 'regen en sneeuw', - 'regen en ijzel', 'sneeuw en ijzel', 'aanvriezende motregen', - 'motregen', 'aanvriezende regen' ,'buien', - 'buien', 'sneeuw windstoten', 'lichte sneeuwbuien', - 'stuifsneeuw', 'sneeuw', 'hagel', - 'ijzel', 'stof', 'mist', 'waas', 'smog', 'onstuimig', - 'winderig', 'koud', 'bewolkt', - 'overwegend bewolkt', # night - 'overwegend bewolkt', # day - 'gedeeltelijk bewolkt', # night - 'gedeeltelijk bewolkt', # day - 'helder', #night - 'zonnig', - 'mooi', #night - 'mooi', #day - 'regen en hagel', - 'heet', 'plaatselijk onweer', 'af en toe onweer', 'af en toe onweer', 'af en toe regenbuien', 'hevige sneeuwval', - 'af en toe sneeuwbuien', 'hevige sneeuwval', 'deels bewolkt', - 'onweersbuien', 'sneeuwbuien', 'af en toe onweersbuien'); - -my @YahooCodes_fr = ( - 'tornade', 'tempête tropicale', 'ouragan', 'tempête sévère', 'orage', 'pluie et neige', - 'pluie et grésil', 'neige et grésil', 'bruine verglassante', 'bruine', 'pluie verglassante' ,'averse', - 'averses', 'tourbillon de neige', 'légères averses de neige', 'rafale de neige', 'neige', 'grêle', - 'giboulées', 'poussières', 'brouillard', 'brume', 'enfumé', 'orageux', - 'venteux', 'froid', 'nuageux', - 'couverte', # night - 'couvert', # day - 'partiellement couverte', # night - 'partiellement couvert', # day - 'clair', - 'ensoleillé', - 'douce', #night - 'agréable', #day - 'pluie et grêle', - 'chaud', 'orages isolés', 'tempêtes éparses', 'orages épars', 'averses éparses', 'tempête de neige', - 'chûtes de neiges éparses', 'tempêtes de neige', 'partielement nuageux', 'averses orageuses', 'chûte de neige', 'chûtes de neige isolées'); - -my @YahooCodes_pl = ( - 'tornado', 'burza tropikalna', 'huragan', 'porywiste burze', 'burze', 'deszcz ze śniegiem', - 'deszcz i deszcz ze śniegiem', 'śnieg i deszcz ze śniegiem', 'marznąca mżawka', 'mżawka', 'marznący deszcz' ,'deszcz', - 'deszcz', 'przelotne opady śniegu', 'lekkie opady śniegu', 'zamieć śnieżna', 'śnieg', 'grad', - 'deszcz ze śniegiem', 'pył', 'mgła', 'mgła', 'smog', 'przenikliwie', - 'wietrznie', 'zimno', 'pochmurno', - 'pochmurno', # night - 'pochmurno', # day - 'częściowe zachmurzenie', # night - 'częściowe zachmurzenie', # day - 'czyste niebo', - 'słonecznie', - 'ładna noc', #night - 'ładny dzień', #day - 'deszcz z gradem', - 'gorąco', 'gdzieniegdzie burze', 'burze', 'burze', 'przelotne opady śniegu', 'duże opady śniegu', - 'ciężkie opady śniegu', 'dużo śniegu', 'częściowe zachmurzenie', 'burze z deszczem', 'opady śniegu', 'przejściowo burze'); - -my @YahooCodes_it = ( - 'tromba d\'aria', 'tempesta tropicale', 'uragano', 'temporali di grande intensità', 'temporali', 'pioggia mista e neve', - 'pioggia mista e nevischio', 'neve mista e nevischio', 'pioggia gelata', 'pioggia leggera', 'grandine' ,'rovesci', - 'piogge', 'raffiche di neve', 'deboli nevicate', 'bufera di neve', 'neve', 'grandine', - 'nevischio', 'pulviscolo', 'nebbia', 'foschia', 'smog', 'ventoso', - 'ventoso', 'freddo', 'nuvoloso', - 'parzialmente nuvoloso', # night - 'parzialmente nuvoloso', # day - 'parzialmente nuvoloso', # night - 'parzialmente nuvoloso', # day - 'sereno', - 'soleggiato', - 'bel tempo', #night - 'bel tempo', #day - 'pioggia mista a grandine', - 'caldo', 'temporali isolati', 'temporali sparsi', 'temporali sparsi', 'piogge sparse', 'forti nevicate', - 'nevicate sparse', 'forti nevicate', 'parzialmente nuvoloso', 'rovesci temporaleschi', 'rovesci di neve', 'temporali isolati'); - - -################################### - -# Cache -my %YahooWeatherAPI_CachedData= (); -my %YahooWeatherAPI_CachedDataTs= (); - -################################### - -# -# there is a bug in the Yahoo Weather API that gets all units wrong -# these routines fix that - - -sub value_to_C($) { - my ($F)= @_; - return(int(($F-32)*5/9+0.5)); -} - -sub value_to_hPa($) { - my ($inHg)= @_; - return int($inHg/33.86390+0.5); -} - -sub value_to_km($) { - my ($value)= @_; - return int($value/1.609347219+0.5); -} - -################################### - -# call: YahooWeatherAPI_RetrieveData(%%args) -# -# the args hash reference must contain at least -# woeid => WOEID [WHERE-ON-EARTH-ID], go to http://weather.yahoo.com to find out -# format => xml or json -# blocking => 0 or 1 -# callbackFnRef => reference to callback function with arguments ($argsRef, $err, $result) -# the args hash reference is returned as first argument of the callbackFn -# - -sub YahooWeatherAPI_RetrieveData($) { - my ($argsRef)= @_; - YahooWeatherAPI_RetrieveDataWithCache(0, $argsRef); -} - -sub YahooWeatherAPI_RetrieveDataWithCache($$) { - - my ($maxage, $argsRef)= @_; - my $woeid= $argsRef->{woeid}; - - Log3 undef, 5, "YahooWeatherAPI: retrieve weather for $woeid."; - - # retrieve data from cache - my $ts= $YahooWeatherAPI_CachedDataTs{$woeid}; - if(defined($ts)) { - my $now= time(); - my $age= $now- $ts; - if($age< $maxage) { - Log3 undef, 5, "YahooWeatherAPI: data is cached, age $age seconds < $maxage seconds."; - $argsRef->{callbackFnRef}($argsRef, "", $YahooWeatherAPI_CachedData{$woeid}); - return; - } else { - Log3 undef, 5, "YahooWeatherAPI: cache is expired, age $age seconds > $maxage seconds."; - } - } else { - Log3 undef, 5, "YahooWeatherAPI: no data in cache."; - } - - my $format= $argsRef->{format}; - my $blocking= $argsRef->{blocking}; - my $callbackFnRef= $argsRef->{callbackFnRef}; - - my $url = sprintf(URL, $woeid, $format); - - #Debug "Retrieve Yahoo Weather data for " . $argsRef->{hash}->{NAME}; - - if ($blocking) { - # do not use noshutdown => 0 in parameters - my $response = HttpUtils_BlockingGet({ url => $url, timeout => 15 }); - my %param= (argsRef => $argsRef); - YahooWeatherAPI_RetrieveDataFinished(\%param, undef, $response); - } else { - # do not use noshutdown => 0 in parameters - HttpUtils_NonblockingGet({ - url => $url, - timeout => 15, - argsRef => $argsRef, - callback => \&YahooWeatherAPI_RetrieveDataFinished, - }); - } -} - -sub YahooWeatherAPI_RetrieveDataFinished($$$) { - my ($paramRef, $err, $response) = @_; - my $argsRef= $paramRef->{argsRef}; - #Debug "Finished retrieving Yahoo Weather data for " . $argsRef->{hash}->{NAME}; - if(!$err) { - my $woeid= $argsRef->{woeid}; - $YahooWeatherAPI_CachedDataTs{$woeid}= time(); - $YahooWeatherAPI_CachedData{$woeid}= $response; - Log3 undef, 5, "YahooWeatherAPI: caching data."; - } - $argsRef->{callbackFnRef}($argsRef, $err, $response); -} - - -# this decodes a JSON result and returns the Weather Channel hash reference -sub YahooWeatherAPI_JSONReturnChannelData($) { - my ($response)= @_; - return("empty response", undef) unless($response); - #Debug "Decoding response: $response"; - #Debug "response: " . Dumper($response); - my $data; - eval { $data= decode_json($response) }; - return($@, undef) if($@); - my $query= $data->{query}; - #Debug Dumper($query); - my $count= $query->{count}; - #Debug "$count result(s)."; - return("$count result(s) retrieved", undef) unless($count == 1); - my $channel= $query->{results}{channel}; - return(undef, $channel); -} - -sub YahooWeatherAPI_ConvertChannelData($) { - my ($data)= @_; # hash reference - - $data->{wind}{chill}= value_to_C($data->{wind}{chill}); # # API delivers wrong value - $data->{atmosphere}{pressure}= value_to_hPa($data->{atmosphere}{pressure}); # API delivers wrong value - - - my $units= YahooWeatherAPI_units($data); # units hash reference - - $data->{wind}{speed}= value_to_km($data->{wind}{speed}); # API delivers km - $data->{atmosphere}{visibility}= value_to_km($data->{atmosphere}{visibility}); # API delivers km - - return 0 if($units->{temperature} eq "C"); - - my $item= $data->{item}; - $item->{condition}{temp}= value_to_C($item->{condition}{temp}); - - my $forecast= $item->{forecast}; - foreach my $fc (@{$forecast}) { - $fc->{low}= value_to_C($fc->{low}); - $fc->{high}= value_to_C($fc->{high}); - } - return 1; - -} - - -sub YahooWeatherAPI_ParseDateTime($) { - - my ($value)= @_; ### "Fri, 13 Nov 2015 8:00 am CET" - - my @months= qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; - my %monthindex; - @monthindex{@months} = (0..$#months); - - if($value =~ '^(\w{3}), (\d{1,2}) (\w{3}) (\d{4}) (\d{1,2}):(\d{2}) (\w{2}) (\w{3,4})$') { - my ($wd, $d, $mon, $y, $h, $n, $p, $tz)= ($1,$2,$3,$4,$5,$6,$7,$8); - # 12 AM= 0, 12 PM= 12 - $h+=12 if($h==12); if($p eq "PM") { $h= ($h+12) % 24 } else { $h%= 12 }; - my $m= $monthindex{$mon}; - return undef unless defined($m); - #main::Debug "###### $value -> $wd $d $m $y $h:$n $tz"; - # $mday= 1.. - # $month= 0..11 - # $year is year-1900 - # we ignore the time zone as it probably never changes for a weather device an assume - # local time zone - return fhemTimeLocal(0, $n, $h, $d, $m, $y-1900); - } else { - return undef; - } -} - -sub YahooWeatherAPI_pubDate($) { - - my ($channel)= @_; - - ### pubDate Fri, 13 Nov 2015 8:00 am CET - if(!defined($channel->{item}{pubDate})) { - return("no pubDate received", "", undef); - }; - my $pubDate= $channel->{item}{pubDate}; - my $ts= YahooWeatherAPI_ParseDateTime($pubDate); - if(defined($ts)) { - return("okay", $pubDate, $ts); - } else { - return("could not parse pubDate $pubDate", $pubDate, undef); - } -} - -sub YahooWeatherAPI_units($) { - - my ($channel)= @_; - return $channel->{units}; -} - -sub YahooWeatherAPI_getYahooCodes($) { - - my ($lang)= @_; - - if($lang eq "de") { - return @YahooCodes_de; - } elsif($lang eq "nl") { - return @YahooCodes_nl; - } elsif($lang eq "fr") { - return @YahooCodes_fr; - } elsif($lang eq "pl") { - return @YahooCodes_pl; - } elsif($lang eq "it") { - return @YahooCodes_it; - } else { - return @YahooCodes_en; - } -} - -############################################################################## - -1; diff --git a/DarkSkyAPI.pm b/lib/FHEM/APIs/Weather/DarkSkyAPI.pm similarity index 59% rename from DarkSkyAPI.pm rename to lib/FHEM/APIs/Weather/DarkSkyAPI.pm index 4902524..077b05c 100644 --- a/DarkSkyAPI.pm +++ b/lib/FHEM/APIs/Weather/DarkSkyAPI.pm @@ -27,22 +27,24 @@ # # ############################################################################### - -package DarkSkyAPI; +package FHEM::APIs::Weather::DarkSkyAPI; use strict; use warnings; use FHEM::Meta; -use Data::Dumper; - -FHEM::Meta::Load(__PACKAGE__); -use version 0.50; our $VERSION = $main::packages{DarkSkyAPI}{META}{version}; - -package DarkSkyAPI::Weather; -use strict; -use warnings; use POSIX; use HttpUtils; +use experimental qw /switch/; + +my $META = {}; +my $ret = FHEM::Meta::getMetadata( __FILE__, $META ); +return "$@" if ($@); +return $ret if ($ret); +$::packages{DarkSkyAPI}{META} = $META; + +use version 0.77; our $VERSION = $META->{version}; + +# use Data::Dumper; # try to use JSON::MaybeXS wrapper # for chance of better performance + open code @@ -50,15 +52,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' @@ -67,10 +65,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 ... @@ -78,10 +73,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 ... @@ -89,10 +81,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 ... @@ -100,30 +89,32 @@ 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 '; + +eval { use Readonly; 1 } + or $missingModul .= 'Readonly '; # apt install libreadonly-perl +## use critic # use Data::Dumper; # for Debug only ## API URL -use constant DEMODATA => +Readonly my $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/'; +Readonly my $URL => 'https://api.darksky.net/forecast/'; my %codes = ( 'clear-day' => 32, @@ -141,10 +132,11 @@ my %codes = ( 'tornado' => 0, ); +### begin public function sub new { ### geliefert wird ein Hash my ( $class, $argsRef ) = @_; - my $apioptions = parseApiOptions( $argsRef->{apioptions} ); + my $apioptions = _parseApiOptions( $argsRef->{apioptions} ); my $self = { devName => $argsRef->{devName}, @@ -175,23 +167,6 @@ sub new { return $self; } -sub parseApiOptions($) { - my $apioptions = shift; - - my @params; - my %h; - - @params = split( ',', $apioptions ); - while (@params) { - my $param = shift(@params); - next if ( $param eq '' ); - my ( $key, $value ) = split( ':', $param, 2 ); - $h{$key} = $value; - } - - return \%h; -} - sub setAlerts { my $self = shift; my $alerts = shift // 0; @@ -243,7 +218,29 @@ sub getWeather { return $self->{cached}; } -sub _RetrieveDataFromDarkSky($) { +### begin privat function +sub _parseApiOptions { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $apioptions = shift; + + my @params; + my %h; + + @params = split( ',', $apioptions ); + while (@params) { + my $param = shift(@params); + next if ( $param eq '' ); + my ( $key, $value ) = split( ':', $param, 2 ); + $h{$key} = $value; + } + + return \%h; +} + +sub _RetrieveDataFromDarkSky { + return 0 unless ( __PACKAGE__ eq caller(0) ); + my $self = shift; # retrieve data from cache @@ -291,7 +288,7 @@ sub _RetrieveDataFromDarkSky($) { if ( $self->{extend} ne 'none' ); $paramRef->{url} = - URL + $URL . $self->{key} . '/' . $self->{lat} . ',' . $self->{long} @@ -300,19 +297,26 @@ sub _RetrieveDataFromDarkSky($) { . $options; if ( lc( $self->{key} ) eq 'demo' ) { - _RetrieveDataFinished( $paramRef, undef, DEMODATA ); + _RetrieveDataFinished( $paramRef, undef, $DEMODATA ); } - else { main::HttpUtils_NonblockingGet($paramRef); } + else { ::HttpUtils_NonblockingGet($paramRef); } } + + return; } -sub _RetrieveDataFinished($$$) { - my ( $paramRef, $err, $response ) = @_; - my $self = $paramRef->{self}; +sub _RetrieveDataFinished { + return 0 unless ( 'main' eq caller(0) ); + + my $paramRef = shift; + my $err = shift; + my $response = shift; + my $self = $paramRef->{self}; if ( !$err ) { $self->{cached}->{status} = 'ok'; - $self->{cached}->{validity} = 'up-to-date', $self->{fetchTime} = time(); + $self->{cached}->{validity} = 'up-to-date'; + $self->{fetchTime} = time(); _ProcessingRetrieveData( $self, $response ); } else { @@ -320,16 +324,21 @@ sub _RetrieveDataFinished($$$) { _ErrorHandling( $self, $err ); _ProcessingRetrieveData( $self, $response ); } + + return; } -sub _ProcessingRetrieveData($$) { - my ( $self, $response ) = @_; +sub _ProcessingRetrieveData { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $response = shift; if ( $self->{cached}->{status} eq 'ok' and defined($response) and $response ) { - if ( $response =~ m/^{.*}$/ ) { + if ( $response =~ m/^{.*}$/x ) { my $data = eval { decode_json($response) }; if ($@) { @@ -345,417 +354,7 @@ sub _ProcessingRetrieveData($$) { 'Code: ' . $data->{code} . ' Error: ' . $data->{error} ); } else { - # print Dumper $data; ## für Debugging - - $self->{cached}->{current_date_time} = - strftimeWrapper( "%a, %e %b %Y %H:%M", - localtime( $self->{fetchTime} ) ); - $self->{cached}->{timezone} = $data->{timezone}; - $self->{cached}->{license}{text} = - $data->{flags}->{'meteoalarm-license'}; - $self->{cached}->{current} = { - 'temperature' => int( - sprintf( "%.1f", $data->{currently}->{temperature} ) + - 0.5 - ), - 'temp_c' => int( - sprintf( "%.1f", $data->{currently}->{temperature} ) + - 0.5 - ), - 'dewPoint' => int( - sprintf( "%.1f", $data->{currently}->{dewPoint} ) + 0.5 - ), - 'humidity' => $data->{currently}->{humidity} * 100, - 'condition' => encode_utf8( $data->{currently}->{summary} ), - 'pressure' => int( - sprintf( "%.1f", $data->{currently}->{pressure} ) + 0.5 - ), - 'wind' => int( - sprintf( "%.1f", - ( $data->{currently}->{windSpeed} * 3.6 ) ) + 0.5 - ), - 'wind_speed' => int( - sprintf( "%.1f", - ( $data->{currently}->{windSpeed} * 3.6 ) ) + 0.5 - ), - 'wind_direction' => $data->{currently}->{windBearing}, - 'windGust' => int( - sprintf( "%.1f", - ( $data->{currently}->{windGust} * 3.6 ) ) + 0.5 - ), - 'cloudCover' => $data->{currently}->{cloudCover} * 100, - 'uvIndex' => $data->{currently}->{uvIndex}, - 'visibility' => int( - sprintf( "%.1f", $data->{currently}->{visibility} ) + - 0.5 - ), - 'ozone' => $data->{currently}->{ozone}, - 'code' => $codes{ $data->{currently}->{icon} }, - 'iconAPI' => $data->{currently}->{icon}, - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( $data->{currently}->{'time'} ) - ), - 'precipProbability' => - $data->{currently}->{precipProbability} * 100, - 'apparentTemperature' => int( - sprintf( "%.1f", - $data->{currently}->{apparentTemperature} ) + 0.5 - ), - 'precipIntensity' => $data->{currently}->{precipIntensity}, - }; - - if ( ref( $data->{daily}->{data} ) eq "ARRAY" - and scalar( @{ $data->{daily}->{data} } ) > 0 ) - { - ### löschen des alten Datensatzes - delete $self->{cached}->{forecast}; - - my $i = 0; - foreach ( @{ $data->{daily}->{data} } ) { - push( - @{ $self->{cached}->{forecast}->{daily} }, - { - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i]->{'time'} - ) - ), - 'day_of_week' => strftime( - "%a", - localtime( - $data->{daily}->{data}->[$i]->{'time'} - ) - ), - 'low_c' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{temperatureLow} ) + 0.5 - ), - 'high_c' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{temperatureHigh} ) + 0.5 - ), - 'tempLow' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{temperatureLow} ) + 0.5 - ), - 'tempLowTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{temperatureLowTime} - ) - ), - 'tempHigh' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{temperatureHigh} ) + 0.5 - ), - 'tempHighTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{temperatureHighTime} - ) - ), - 'apparentTempLow' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{apparentTemperatureLow} ) + 0.5 - ), - 'apparentTempLowTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{apparentTemperatureLowTime} - ) - ), - 'apparentTempHigh' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{apparentTemperatureHigh} ) + 0.5 - ), - 'apparentTempHighTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{apparentTemperatureHighTime} - ) - ), - 'code' => $codes{ - $data->{daily}->{data}->[$i]->{icon} - }, - 'iconAPI' => - $data->{daily}->{data}->[$i]->{icon}, - 'condition' => encode_utf8( - $data->{daily}->{data}->[$i]->{summary} - ), - 'ozone' => - $data->{daily}->{data}->[$i]->{ozone}, - 'uvIndex' => - $data->{daily}->{data}->[$i]->{uvIndex}, - 'uvIndexTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{uvIndexTime} - ) - ), - 'dewPoint' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i]->{dewPoint} - ) + 0.5 - ), - 'humidity' => - $data->{daily}->{data}->[$i]->{humidity} * - 100, - 'cloudCover' => - $data->{daily}->{data}->[$i]->{cloudCover} * - 100, - 'wind_direction' => - $data->{daily}->{data}->[$i]->{windBearing}, - 'wind' => int( - sprintf( - "%.1f", - ( - $data->{daily}->{data}->[$i] - ->{windSpeed} * 3.6 - ) - ) + 0.5 - ), - 'wind_speed' => int( - sprintf( - "%.1f", - ( - $data->{daily}->{data}->[$i] - ->{windSpeed} * 3.6 - ) - ) + 0.5 - ), - 'windGust' => int( - sprintf( - "%.1f", - ( - $data->{daily}->{data}->[$i] - ->{windGust} * 3.6 - ) - ) + 0.5 - ), - 'windGustTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{windGustTime} - ) - ), - 'moonPhase' => - $data->{daily}->{data}->[$i]->{moonPhase}, - 'sunsetTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{sunsetTime} - ) - ), - 'sunriseTime' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{sunriseTime} - ) - ), - 'pressure' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i]->{pressure} - ) + 0.5 - ), - 'visibility' => int( - sprintf( "%.1f", - $data->{daily}->{data}->[$i] - ->{visibility} ) + 0.5 - ), - } - ); - - $self->{cached}->{forecast} - ->{daily}[$i]{precipIntensityMax} = ( - defined( - $data->{daily}->{data}->[$i] - ->{precipIntensityMax} - ) - ? $data->{daily}->{data}->[$i]->{precipIntensityMax} - : '-' - ); - $self->{cached}->{forecast} - ->{daily}[$i]{precipIntensity} = ( - defined( - $data->{daily}->{data}->[$i]->{precipIntensity} - ) - ? $data->{daily}->{data}->[$i]->{precipIntensity} - : '-' - ); - $self->{cached}->{forecast} - ->{daily}[$i]{precipProbability} = ( - defined( - $data->{daily}->{data}->[$i] - ->{precipProbability} - ) - ? $data->{daily}->{data}->[$i]->{precipProbability} - * 100 - : '-' - ); - $self->{cached}->{forecast}->{daily}[$i]{precipType} = ( - defined( - $data->{daily}->{data}->[$i]->{precipType} - ) - ? $data->{daily}->{data}->[$i]->{precipType} - : '-' - ); - $self->{cached}->{forecast} - ->{daily}[$i]{precipIntensityMaxTime} = ( - defined( - $data->{daily}->{data}->[$i] - ->{precipIntensityMaxTime} - ) - ? strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{daily}->{data}->[$i] - ->{precipIntensityMaxTime} - ) - ) - : '-' - ); - - $i++; - } - - if ( ref( $data->{hourly}->{data} ) eq "ARRAY" - and scalar( @{ $data->{hourly}->{data} } ) > 0 ) - { - ### löschen des alten Datensatzes - delete $self->{cached}->{forecast}->{hourly}; - - my $i = 0; - foreach ( @{ $data->{hourly}->{data} } ) { - push( - @{ $self->{cached}->{forecast}->{hourly} }, - { - 'pubDate' => strftimeWrapper( - "%a, %e %b %Y %H:%M", - localtime( - $data->{hourly}->{data}->[$i] - ->{'time'} - ) - ), - 'day_of_week' => strftime( - "%a, %H:%M", - localtime( - $data->{hourly}->{data}->[$i] - ->{'time'} - ) - ), - 'temperature' => sprintf( "%.1f", - $data->{hourly}->{data}->[$i] - ->{temperature} ), - 'code' => $codes{ - $data->{hourly}->{data}->[$i]->{icon} - }, - 'iconAPI' => - $data->{hourly}->{data}->[$i]->{icon}, - 'condition' => encode_utf8( - $data->{hourly}->{data}->[$i]->{summary} - ), - 'ozone' => - $data->{hourly}->{data}->[$i]->{ozone}, - 'uvIndex' => - $data->{hourly}->{data}->[$i]->{uvIndex}, - 'dewPoint' => sprintf( "%.1f", - $data->{hourly}->{data}->[$i] - ->{dewPoint} ), - 'humidity' => - $data->{hourly}->{data}->[$i]->{humidity} - * 100, - 'cloudCover' => - $data->{hourly}->{data}->[$i] - ->{cloudCover} * 100, - 'wind_direction' => - $data->{hourly}->{data}->[$i] - ->{windBearing}, - 'wind' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->{data}->[$i] - ->{windSpeed} * 3.6 - ) - ) + 0.5 - ), - 'wind_speed' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->{data}->[$i] - ->{windSpeed} * 3.6 - ) - ) + 0.5 - ), - 'windGust' => int( - sprintf( - "%.1f", - ( - $data->{hourly}->{data}->[$i] - ->{windGust} * 3.6 - ) - ) + 0.5 - ), - - 'pressure' => sprintf( "%.1f", - $data->{hourly}->{data}->[$i] - ->{pressure} ), - 'visibility' => sprintf( "%.1f", - $data->{hourly}->{data}->[$i] - ->{visibility} ), - } - ); - - $self->{cached}->{forecast} - ->{hourly}[$i]{precipIntensity} = ( - defined( - $data->{hourly}->{data}->[$i] - ->{precipIntensity} - ) - ? $data->{hourly}->{data}->[$i] - ->{precipIntensity} - : '-' - ); - $self->{cached}->{forecast} - ->{hourly}[$i]{precipProbability} = ( - defined( - $data->{hourly}->{data}->[$i] - ->{precipProbability} - ) - ? $data->{hourly}->{data}->[$i] - ->{precipProbability} * 100 - : '-' - ); - $self->{cached}->{forecast} - ->{hourly}[$i]{precipType} = ( - defined( - $data->{hourly}->{data}->[$i]->{precipType} - ) - ? $data->{hourly}->{data}->[$i]->{precipType} - : '-' - ); - - $i++; - } - } - } + $self = _FillSelfHashWithWeatherResponse( $self, $data ); } } else { _ErrorHandling( $self, 'DarkSky Weather ' . $response ); } @@ -763,55 +362,397 @@ sub _ProcessingRetrieveData($$) { ## Aufruf der callbackFn _CallWeatherCallbackFn($self); + + return; } -sub _CallWeatherCallbackFn($) { +sub _FillSelfHashWithWeatherResponse { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + # print Dumper $data; ## für Debugging + $self->{cached}->{current_date_time} = + _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); + $self->{cached}->{timezone} = $data->{timezone}; + $self->{cached}->{license}{text} = + $data->{flags}->{'meteoalarm-license'}; + + ## löschen des alten current Datensatzes + delete $self->{cached}->{current}; + + ## löschen des alten forecast Datensatzes + delete $self->{cached}->{forecast}; + + $self = _FillSelfHashWithWeatherResponseForCurrent( $self, $data ); + + if ( ref( $data->{daily}->{data} ) eq "ARRAY" + and scalar( @{ $data->{daily}->{data} } ) > 0 ) + { + $self = + _FillSelfHashWithWeatherResponseForForecastDaily( $self, $data ); + + } + + if ( ref( $data->{hourly}->{data} ) eq "ARRAY" + and scalar( @{ $data->{hourly}->{data} } ) > 0 ) + { + $self = + _FillSelfHashWithWeatherResponseForForecastHourly( $self, $data ); + } + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForCurrent { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + $self->{cached}->{current} = { + 'temperature' => + int( sprintf( "%.0f", $data->{currently}->{temperature} ) ), + 'temp_c' => int( sprintf( "%.0f", $data->{currently}->{temperature} ) ), + 'dewPoint' => int( sprintf( "%.0f", $data->{currently}->{dewPoint} ) ), + 'humidity' => $data->{currently}->{humidity} * 100, + 'condition' => encode_utf8( $data->{currently}->{summary} ), + 'pressure' => + int( sprintf( "%.1f", $data->{currently}->{pressure} ) + 0.5 ), + 'wind' => int( + sprintf( "%.1f", ( $data->{currently}->{windSpeed} * 3.6 ) ) + 0.5 + ), + 'wind_speed' => int( + sprintf( "%.1f", ( $data->{currently}->{windSpeed} * 3.6 ) ) + 0.5 + ), + 'wind_direction' => $data->{currently}->{windBearing}, + 'windGust' => int( + sprintf( "%.1f", ( $data->{currently}->{windGust} * 3.6 ) ) + 0.5 + ), + 'cloudCover' => $data->{currently}->{cloudCover} * 100, + 'uvIndex' => $data->{currently}->{uvIndex}, + 'visibility' => + int( sprintf( "%.1f", $data->{currently}->{visibility} ) + 0.5 ), + 'ozone' => $data->{currently}->{ozone}, + 'code' => $codes{ $data->{currently}->{icon} }, + 'iconAPI' => $data->{currently}->{icon}, + 'pubDate' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{currently}->{'time'} ) + ), + 'precipProbability' => $data->{currently}->{precipProbability} * 100, + 'apparentTemperature' => + int( sprintf( "%.0f", $data->{currently}->{apparentTemperature} ) ), + 'precipIntensity' => $data->{currently}->{precipIntensity}, + }; + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForForecastDaily { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + my $i = 0; + foreach ( @{ $data->{daily}->{data} } ) { + push( + @{ $self->{cached}->{forecast}->{daily} }, + { + 'pubDate' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{daily}->{data}->[$i]->{'time'} ) + ), + 'day_of_week' => strftime( + "%a", localtime( $data->{daily}->{data}->[$i]->{'time'} ) + ), + 'low_c' => int( + sprintf( "%.0f", + $data->{daily}->{data}->[$i]->{temperatureLow} ) + ), + 'high_c' => int( + sprintf( "%.0f", + $data->{daily}->{data}->[$i]->{temperatureHigh} ) + ), + 'tempLow' => int( + sprintf( "%.0f", + $data->{daily}->{data}->[$i]->{temperatureLow} ) + ), + 'tempLowTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + $data->{daily}->{data}->[$i]->{temperatureLowTime} + ) + ), + 'tempHigh' => int( + sprintf( "%.0f", + $data->{daily}->{data}->[$i]->{temperatureHigh} ) + ), + 'tempHighTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + $data->{daily}->{data}->[$i]->{temperatureHighTime} + ) + ), + 'apparentTempLow' => int( + sprintf( "%.0f", + $data->{daily}->{data}->[$i]->{apparentTemperatureLow} ) + ), + 'apparentTempLowTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + $data->{daily}->{data}->[$i] + ->{apparentTemperatureLowTime} + ) + ), + 'apparentTempHigh' => int( + sprintf( "%.0f", + $data->{daily}->{data}->[$i]->{apparentTemperatureHigh} + ) + ), + 'apparentTempHighTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + $data->{daily}->{data}->[$i] + ->{apparentTemperatureHighTime} + ) + ), + 'code' => $codes{ $data->{daily}->{data}->[$i]->{icon} }, + 'iconAPI' => $data->{daily}->{data}->[$i]->{icon}, + 'condition' => + encode_utf8( $data->{daily}->{data}->[$i]->{summary} ), + 'ozone' => $data->{daily}->{data}->[$i]->{ozone}, + 'uvIndex' => $data->{daily}->{data}->[$i]->{uvIndex}, + 'uvIndexTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{daily}->{data}->[$i]->{uvIndexTime} ) + ), + 'dewPoint' => int( + sprintf( "%.0f", $data->{daily}->{data}->[$i]->{dewPoint} ) + ), + 'humidity' => $data->{daily}->{data}->[$i]->{humidity} * 100, + 'cloudCover' => $data->{daily}->{data}->[$i]->{cloudCover} * + 100, + 'wind_direction' => $data->{daily}->{data}->[$i]->{windBearing}, + 'wind' => int( + sprintf( "%.1f", + ( $data->{daily}->{data}->[$i]->{windSpeed} * 3.6 ) ) + + 0.5 + ), + 'wind_speed' => int( + sprintf( "%.1f", + ( $data->{daily}->{data}->[$i]->{windSpeed} * 3.6 ) ) + + 0.5 + ), + 'windGust' => int( + sprintf( "%.1f", + ( $data->{daily}->{data}->[$i]->{windGust} * 3.6 ) ) + + 0.5 + ), + 'windGustTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{daily}->{data}->[$i]->{windGustTime} ) + ), + 'moonPhase' => $data->{daily}->{data}->[$i]->{moonPhase}, + 'sunsetTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{daily}->{data}->[$i]->{sunsetTime} ) + ), + 'sunriseTime' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{daily}->{data}->[$i]->{sunriseTime} ) + ), + 'pressure' => int( + sprintf( "%.1f", $data->{daily}->{data}->[$i]->{pressure} ) + + 0.5 + ), + 'visibility' => int( + sprintf( "%.1f", + $data->{daily}->{data}->[$i]->{visibility} ) + 0.5 + ), + } + ); + + $self->{cached}->{forecast}->{daily}[$i]{precipIntensityMax} = ( + defined( $data->{daily}->{data}->[$i]->{precipIntensityMax} ) + ? $data->{daily}->{data}->[$i]->{precipIntensityMax} + : '-' + ); + $self->{cached}->{forecast}->{daily}[$i]{precipIntensity} = ( + defined( $data->{daily}->{data}->[$i]->{precipIntensity} ) + ? $data->{daily}->{data}->[$i]->{precipIntensity} + : '-' + ); + $self->{cached}->{forecast}->{daily}[$i]{precipProbability} = ( + defined( $data->{daily}->{data}->[$i]->{precipProbability} ) + ? $data->{daily}->{data}->[$i]->{precipProbability} * 100 + : '-' + ); + $self->{cached}->{forecast}->{daily}[$i]{precipType} = ( + defined( $data->{daily}->{data}->[$i]->{precipType} ) + ? $data->{daily}->{data}->[$i]->{precipType} + : '-' + ); + $self->{cached}->{forecast}->{daily}[$i]{precipIntensityMaxTime} = ( + defined( $data->{daily}->{data}->[$i]->{precipIntensityMaxTime} ) + ? _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( + $data->{daily}->{data}->[$i]->{precipIntensityMaxTime} + ) + ) + : '-' + ); + + $i++; + } + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForForecastHourly { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + my $i = 0; + + foreach ( @{ $data->{hourly}->{data} } ) { + push( + @{ $self->{cached}->{forecast}->{hourly} }, + { + 'pubDate' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{hourly}->{data}->[$i]->{'time'} ) + ), + 'day_of_week' => strftime( + "%a, %H:%M", + localtime( $data->{hourly}->{data}->[$i]->{'time'} ) + ), + 'temperature' => sprintf( "%.0f", + $data->{hourly}->{data}->[$i]->{temperature} ), + 'code' => $codes{ $data->{hourly}->{data}->[$i]->{icon} }, + 'iconAPI' => $data->{hourly}->{data}->[$i]->{icon}, + 'condition' => + encode_utf8( $data->{hourly}->{data}->[$i]->{summary} ), + 'ozone' => $data->{hourly}->{data}->[$i]->{ozone}, + 'uvIndex' => $data->{hourly}->{data}->[$i]->{uvIndex}, + 'dewPoint' => + sprintf( "%.0f", $data->{hourly}->{data}->[$i]->{dewPoint} ), + 'humidity' => $data->{hourly}->{data}->[$i]->{humidity} * 100, + 'cloudCover' => $data->{hourly}->{data}->[$i]->{cloudCover} * + 100, + 'wind_direction' => + $data->{hourly}->{data}->[$i]->{windBearing}, + 'wind' => int( + sprintf( "%.1f", + ( $data->{hourly}->{data}->[$i]->{windSpeed} * 3.6 ) ) + + 0.5 + ), + 'wind_speed' => int( + sprintf( "%.1f", + ( $data->{hourly}->{data}->[$i]->{windSpeed} * 3.6 ) ) + + 0.5 + ), + 'windGust' => int( + sprintf( "%.1f", + ( $data->{hourly}->{data}->[$i]->{windGust} * 3.6 ) ) + + 0.5 + ), + + 'pressure' => + sprintf( "%.1f", $data->{hourly}->{data}->[$i]->{pressure} ), + 'visibility' => sprintf( + "%.1f", $data->{hourly}->{data}->[$i]->{visibility} + ), + } + ); + + $self->{cached}->{forecast}->{hourly}[$i]{precipIntensity} = ( + defined( $data->{hourly}->{data}->[$i]->{precipIntensity} ) + ? $data->{hourly}->{data}->[$i]->{precipIntensity} + : '-' + ); + $self->{cached}->{forecast}->{hourly}[$i]{precipProbability} = ( + defined( $data->{hourly}->{data}->[$i]->{precipProbability} ) + ? $data->{hourly}->{data}->[$i]->{precipProbability} * 100 + : '-' + ); + $self->{cached}->{forecast}->{hourly}[$i]{precipType} = ( + defined( $data->{hourly}->{data}->[$i]->{precipType} ) + ? $data->{hourly}->{data}->[$i]->{precipType} + : '-' + ); + + $i++; + } + + return $self; +} + +sub _CallWeatherCallbackFn { + return 0 unless ( __PACKAGE__ eq caller(0) ); + my $self = shift; # ## Aufruf der callbackFn - main::Weather_RetrieveCallbackFn( $self->{devName} ); + return ::Weather_RetrieveCallbackFn( $self->{devName} ); } -sub _ErrorHandling($$) { - my ( $self, $err ) = @_; +sub _ErrorHandling { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + 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($) { +sub _CreateForecastRef { + return 0 unless ( __PACKAGE__ eq caller(0) ); + my $self = shift; my $forecastRef = ( { lat => $self->{lat}, long => $self->{long}, - apiMaintainer => -'Marko Oldenburg (CoolTux)', - apiVersion => version->parse( DarkSkyAPI->VERSION() )->normal, + apiMaintainer => 'Marko Oldenburg (' + . $META->{x_fhem_maintainer}[0] . ')', + apiVersion => version->parse( __PACKAGE__->VERSION() )->normal, } ); return $forecastRef; } -sub strftimeWrapper(@) { - my $string = POSIX::strftime(@_); +sub _strftimeWrapper { + my @data = @_; + return 0 unless ( __PACKAGE__ eq caller(0) ); - $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; + my $string = POSIX::strftime(@data); + + $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; } diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm new file mode 100644 index 0000000..e4f5adc --- /dev/null +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -0,0 +1,1029 @@ +# $Id: $ +############################################################################### +# +# Developed with VSCodium and richterger perl plugin +# +# (c) 2019-2023 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net) +# 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 +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License,or +# any later version. +# +# The GNU General Public License can be found at +# http://www.gnu.org/copyleft/gpl.html. +# A copy is found in the textfile GPL.txt and important notices to the license +# from the author is found in LICENSE.txt distributed with these scripts. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# +############################################################################### + +### 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/3.0/onecall?lat=[lat]&lon=[long]&APPID=[API] Current,Forecast +# https://openweathermap.org/weather-conditions Icons und Conditions ID's + +package FHEM::APIs::Weather::OpenWeatherMapAPI; +use strict; +use warnings; +use FHEM::Meta; + +use POSIX; +use HttpUtils; +use experimental qw /switch/; + +my $META = {}; +my $ret = FHEM::Meta::getMetadata( __FILE__, $META ); +return "$@" if ($@); +return $ret if ($ret); +$::packages{OpenWeatherMapAPI}{META} = $META; + +use version 0.77; our $VERSION = $META->{version}; + +# use Data::Dumper; + +# try to use JSON::MaybeXS wrapper +# for chance of better performance + open code +eval { + require JSON::MaybeXS; + import JSON::MaybeXS qw( decode_json encode_json ); + 1; +} 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' + unless ( defined( $ENV{PERL_JSON_BACKEND} ) ); + + require JSON; + import JSON qw( decode_json encode_json ); + 1; + } or do { + + # In rare cases, Cpanel::JSON::XS may + # be installed but JSON|JSON::MaybeXS not ... + eval { + require Cpanel::JSON::XS; + import Cpanel::JSON::XS qw(decode_json encode_json); + 1; + } or do { + + # In rare cases, JSON::XS may + # be installed but JSON not ... + eval { + require JSON::XS; + import JSON::XS qw(decode_json encode_json); + 1; + } or do { + + # Fallback to built-in JSON which SHOULD + # be available since 5.014 ... + eval { + require JSON::PP; + import JSON::PP qw(decode_json encode_json); + 1; + } or do { + + # Fallback to JSON::backportPP in really rare cases + require JSON::backportPP; + import JSON::backportPP qw(decode_json encode_json); + 1; + }; + }; + }; + }; +}; + +my $missingModul = ''; +## 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 +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/'; +## URL . 'weather?' for current data +## URL . 'onecall?' for current,forecast data + +my %codes = ( + 200 => 45, + 201 => 45, + 202 => 45, + 210 => 4, + 211 => 4, + 212 => 3, + 221 => 4, + 230 => 45, + 231 => 45, + 232 => 45, + 300 => 9, + 301 => 9, + 302 => 9, + 310 => 9, + 311 => 9, + 312 => 9, + 313 => 9, + 314 => 9, + 321 => 9, + 500 => 35, + 501 => 35, + 502 => 35, + 503 => 35, + 504 => 35, + 511 => 35, + 520 => 35, + 521 => 35, + 522 => 35, + 531 => 35, + 600 => 14, + 601 => 16, + 602 => 13, + 611 => 46, + 612 => 46, + 613 => 46, + 615 => 5, + 616 => 5, + 620 => 14, + 621 => 46, + 622 => 42, + 701 => 19, + 711 => 22, + 721 => 19, + 731 => 23, + 741 => 20, + 751 => 23, + 761 => 19, + 762 => 3200, + 771 => 1, + 781 => 0, + 800 => 32, + 801 => 30, + 802 => 26, + 803 => 26, + 804 => 28, +); + +### begin public function +sub new { + ### geliefert wird ein Hash + my $class = shift; + my $argsRef = shift; + + my $apioptions = _parseApiOptions( $argsRef->{apioptions} ); + + my $self = { + devName => $argsRef->{devName}, + key => ( + ( defined( $argsRef->{apikey} ) && $argsRef->{apikey} ) + ? $argsRef->{apikey} + : 'none' + ), + lang => $argsRef->{language}, + lat => ( split( ',', $argsRef->{location} ) )[0], + long => ( split( ',', $argsRef->{location} ) )[1], + fetchTime => 0, + endpoint => 'none', + forecast => '', + alerts => 0, + }; + + $self->{cachemaxage} = ( + defined( $apioptions->{cachemaxage} ) + ? $apioptions->{cachemaxage} + : 900 + ); + + $self->{apiversion} = + ( $apioptions->{version} ? $apioptions->{version} : '2.5' ); + + $self->{cached} = _CreateForecastRef($self); + + bless $self, $class; + return $self; +} + +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; +} + +sub setRetrieveData { + my $self = shift; + + _RetrieveDataFromOpenWeatherMap($self); + return; +} + +sub setLocation { + my $self = shift; + my $lat = shift; + my $long = shift; + + $self->{lat} = $lat; + $self->{long} = $long; + + return; +} + +sub getFetchTime { + my $self = shift; + + return $self->{fetchTime}; +} + +sub getWeather { + my $self = shift; + + return $self->{cached}; +} + +### begin privat function +sub _parseApiOptions { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $apioptions = shift; + + my @params; + my %h; + + @params = split( ',', $apioptions ); + while (@params) { + my $param = shift(@params); + next if ( $param eq '' ); + my ( $key, $value ) = split( ':', $param, 2 ); + $h{$key} = $value; + } + + return \%h; +} + +sub _RetrieveDataFromOpenWeatherMap { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + + # retrieve data from cache + if ( ( time() - $self->{fetchTime} ) < $self->{cachemaxage} + && $self->{cached}->{lat} == $self->{lat} + && $self->{cached}->{long} == $self->{long} + && $self->{endpoint} eq 'none' ) + { + return _CallWeatherCallbackFn($self); + } + + $self->{cached}->{lat} = $self->{lat} + unless ( $self->{cached}->{lat} == $self->{lat} ); + $self->{cached}->{long} = $self->{long} + unless ( $self->{cached}->{long} == $self->{long} ); + + my $paramRef = { + timeout => 15, + self => $self, + endpoint => $self->{endpoint} eq 'none' + ? ( $self->{apiversion} == 3.0 ? 'onecall' : 'weather' ) + : 'forecast', + callback => \&_RetrieveDataFinished, + }; + + $self->{endpoint} = $paramRef->{endpoint}; + + if ( $self->{lat} eq 'error' + || $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' || $self->{long} eq 'error' ); + + _RetrieveDataFinished( $paramRef, + 'No given api key. (define myWeather Weather apikey=[KEY])', + undef ) + if ( $self->{key} eq 'none' ); + + _RetrieveDataFinished( $paramRef, + 'Perl modul ' . $missingModul . ' is missing.', undef ) + if ($missingModul); + } + else { + $paramRef->{url} = + $URL + . $self->{apiversion} . '/' + . $paramRef->{endpoint} . '?' . 'lat=' + . $self->{lat} . '&' . 'lon=' + . $self->{long} . '&' + . 'APPID=' + . $self->{key} . '&' + . 'units=' + . 'metric' . '&' . 'lang=' + . $self->{lang} . '&' + . 'exclude=' + . _CreateExcludeString( $self->{forecast}, $self->{alerts} ); + + ::HttpUtils_NonblockingGet($paramRef); + } + + return; +} + +sub _CreateExcludeString { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + 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( ',', @diff ); +} + +sub _RetrieveDataFinished { + return 0 unless ( 'main' eq caller(0) ); + + my $paramRef = shift; + my $err = shift; + my $response = shift; + my $self = $paramRef->{self}; + + if ( !$err ) { + $self->{cached}->{status} = 'ok'; + $self->{cached}->{validity} = 'up-to-date'; + $self->{fetchTime} = time(); + _ProcessingRetrieveData( $self, $response ); + } + else { + $self->{fetchTime} = time() if ( not defined( $self->{fetchTime} ) ); + _ErrorHandling( $self, $err ); + _ProcessingRetrieveData( $self, $response ); + } + + return; +} + +sub _ProcessingRetrieveData { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $response = shift; + + if ( $self->{cached}->{status} eq 'ok' + && defined($response) + && $response ) + { + if ( $response =~ m/^{.*}$/x ) { + my $data = eval { decode_json($response) }; + + if ($@) { + _ErrorHandling( $self, + 'OpenWeatherMap Weather decode JSON err ' . $@ ); + } + elsif (defined( $data->{cod} ) + && $data->{cod} + && $data->{cod} != 200 + && defined( $data->{message} ) + && $data->{message} ) + { + _ErrorHandling( $self, $data->{cod} . ': ' . $data->{message} ); + } + else { + $self = _FillSelfHashWithWeatherResponse( $self, $data ); + } + } + else { _ErrorHandling( $self, 'OpenWeatherMap ' . $response ); } + } + + $self->{endpoint} = 'none' + if ( $self->{endpoint} eq 'onecall' + or $self->{endpoint} eq 'forecast' ); + + _RetrieveDataFromOpenWeatherMap($self) + if ( $self->{endpoint} eq 'weather' ); + + _CallWeatherCallbackFn($self) if ( $self->{endpoint} eq 'none' ); + + return; +} + +sub _FillSelfHashWithWeatherResponse { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + ### Debug + # 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", localtime( $self->{fetchTime} ) ); + $self->{cached}->{country} = $data->{sys}->{country}; + $self->{cached}->{city} = encode_utf8( $data->{name} ); + $self->{cached}->{license}{text} = 'none'; + + 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 = + _FillSelfHashWithWeatherResponseForOnecallCurrent( $self, $data ); + + if ( ref( $data->{hourly} ) eq "ARRAY" + && scalar( @{ $data->{hourly} } ) > 0 ) + { + $self = + _FillSelfHashWithWeatherResponseForOnecallHourly( $self, + $data ); + } + + if ( ref( $data->{daily} ) eq "ARRAY" + && scalar( @{ $data->{daily} } ) > 0 ) + { + $self = + _FillSelfHashWithWeatherResponseForOnecallDaily( $self, + $data ); + } + + if ( ref( $data->{alerts} ) eq "ARRAY" + && scalar( @{ $data->{alerts} } ) > 0 ) + { + $self = + _FillSelfHashWithWeatherResponseForOnecallAlerts( $self, + $data ); + } + } + + when ('weather') { + ## löschen des alten current Datensatzes + delete $self->{cached}->{current}; + + ## löschen des alten Alerts Datensatzes + delete $self->{cached}->{alerts}; + + $self = + _FillSelfHashWithWeatherResponseForWeatherCurrent( $self, $data ); + } + + when ('forecast') { + ## löschen des alten forecast Datensatzes + delete $self->{cached}->{forecast}; + + if ( ref( $data->{list} ) eq "ARRAY" + and scalar( @{ $data->{list} } ) > 0 ) + { + $self = + _FillSelfHashWithWeatherResponseForForecastHourly( $self, + $data ); + } + } + } + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForOnecallAlerts { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + my $i = 0; + for ( @{ $data->{alerts} } ) { + push( + @{ $self->{cached}->{alerts} }, + { + 'End' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( ( $data->{alerts}->[$i]->{end} ) ) + ), + 'Start' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( ( $data->{alerts}->[$i]->{start} ) ) + ), + 'Description' => + encode_utf8( $data->{alerts}->[$i]->{description} ), + 'SenderName' => + encode_utf8( $data->{alerts}->[$i]->{sender_name} ), + 'Event' => encode_utf8( $data->{alerts}->[$i]->{event} ), + }, + ); + + $i++; + } + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForWeatherCurrent { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + $self->{cached}->{current} = { + 'temperature' => int( sprintf( "%.0f", $data->{main}->{temp} ) ), + 'temp_c' => int( sprintf( "%.0f", $data->{main}->{temp} ) ), + 'low_c' => int( sprintf( "%.0f", $data->{main}->{temp_min} ) ), + 'high_c' => int( sprintf( "%.0f", $data->{main}->{temp_max} ) ), + 'tempLow' => int( sprintf( "%.0f", $data->{main}->{temp_min} ) ), + 'tempHigh' => int( sprintf( "%.0f", $data->{main}->{temp_max} ) ), + 'tempFeelsLike_c' => + int( sprintf( "%.0f", $data->{main}->{feels_like} ) ), + '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} ); + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForForecastHourly { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + my $i = 0; + for ( @{ $data->{list} } ) { + push( + @{ $self->{cached}->{forecast}->{hourly} }, + { + 'pubDate' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{list}->[$i]->{dt} ) + ), + 'day_of_week' => strftime( + "%a, %H:%M", localtime( $data->{list}->[$i]->{dt} ) + ), + 'temperature' => + int( sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp} ) ), + 'temp_c' => + int( sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp} ) ), + 'low_c' => int( + sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp_min} ) + ), + 'high_c' => int( + sprintf( "%.0f", + $data->{list}->[$i]->{main}->{temp_max} - 273.15 ) + ), + 'tempLow' => int( + sprintf( "%.0f", + $data->{list}->[$i]->{main}->{temp_min} - 273.15 ) + ), + 'tempHigh' => int( + sprintf( "%.0f", + $data->{list}->[$i]->{main}->{temp_max} - 273.15 ) + ), + '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} }, + '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++; + } + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForOnecallCurrent { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + $self->{cached}->{current} = { + 'temperature' => int( sprintf( "%.0f", $data->{current}->{temp} ) ), + 'temp_c' => int( sprintf( "%.0f", $data->{current}->{temp} ) ), + 'tempFeelsLike_c' => + int( sprintf( "%.0f", $data->{current}->{feels_like} ) ), + 'dew_point' => int( sprintf( "%.0f", $data->{current}->{dew_point} ) ), + '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}, + 'timezone' => $data->{timezone}, + 'timezone_offset' => $data->{timezone_offset}, + }; + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForOnecallDaily { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + my $i = 0; + for ( @{ $data->{daily} } ) { + push( + @{ $self->{cached}->{forecast}->{daily} }, + { + 'pubDate' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{daily}->[$i]->{dt} ) + ), + 'day_of_week' => strftime( + "%a, %H:%M", localtime( $data->{daily}->[$i]->{dt} ) + ), + 'sunrise' => strftime( + "%H:%M", localtime( $data->{daily}->[$i]->{sunrise} ) + ), + 'sunset' => strftime( + "%a, %H:%M", localtime( $data->{daily}->[$i]->{sunset} ) + ), + 'moonrise' => strftime( + "%a, %H:%M", localtime( $data->{daily}->[$i]->{moonrise} ) + ), + 'moon_phase' => $data->{daily}->[$i]->{moon_phase}, + 'moonset' => strftime( + "%a, %H:%M", localtime( $data->{daily}->[$i]->{moonset} ) + ), + 'temperature' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{day} ) ), + 'temperature_morn' => int( + sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{morn} ) + ), + 'temperature_eve' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{eve} ) ), + 'temperature_night' => int( + sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{night} ) + ), + 'tempFeelsLike_morn' => int( + sprintf( "%.0f", + $data->{daily}->[$i]->{feels_like}->{morn} ) + ), + 'tempFeelsLike_eve' => int( + sprintf( + "%.0f", $data->{daily}->[$i]->{feels_like}->{eve} + ) + ), + 'tempFeelsLike_night' => int( + sprintf( "%.0f", + $data->{daily}->[$i]->{feels_like}->{night} ) + ), + 'tempFeelsLike_day' => int( + sprintf( + "%.0f", $data->{daily}->[$i]->{feels_like}->{day} + ) + ), + 'temp_c' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{day} ) ), + 'low_c' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{min} ) ), + 'high_c' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{max} ) ), + 'tempLow' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{min} ) ), + 'tempHigh' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{temp}->{max} ) ), + 'dew_point' => + int( sprintf( "%.0f", $data->{daily}->[$i]->{dew_point} ) ), + 'humidity' => $data->{daily}->[$i]->{humidity}, + 'condition' => encode_utf8( + $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} ) + 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} }, + 'rain' => $data->{daily}->[$i]->{rain}, + 'snow' => $data->{daily}->[$i]->{snow}, + 'uvi' => $data->{daily}->[$i]->{uvi}, + }, + ); + + $i++; + } + + return $self; +} + +sub _FillSelfHashWithWeatherResponseForOnecallHourly { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $data = shift; + + my $i = 0; + for ( @{ $data->{hourly} } ) { + push( + @{ $self->{cached}->{forecast}->{hourly} }, + { + 'pubDate' => _strftimeWrapper( + "%a, %e %b %Y %H:%M", + localtime( $data->{hourly}->[$i]->{dt} ) + ), + 'day_of_week' => strftime( + "%a, %H:%M", localtime( $data->{hourly}->[$i]->{dt} ) + ), + 'temperature' => + int( sprintf( "%.0f", $data->{hourly}->[$i]->{temp} ) ), + 'temp_c' => + int( sprintf( "%.0f", $data->{hourly}->[$i]->{temp} ) ), + 'tempFeelsLike' => + int( sprintf( "%.0f", $data->{hourly}->[$i]->{feels_like} ) ), + 'dew_point' => + int( sprintf( "%.0f", $data->{hourly}->[$i]->{dew_point} ) ), + 'humidity' => $data->{hourly}->[$i]->{humidity}, + 'condition' => encode_utf8( + $data->{hourly}->[$i]->{weather}->[0]->{description} + ), + 'pressure' => int( + sprintf( "%.1f", $data->{hourly}->[$i]->{pressure} ) + 0.5 + ), + 'wind' => int( + sprintf( "%.1f", + ( $data->{hourly}->[$i]->{wind_speed} * 3.6 ) ) + 0.5 + ), + 'wind_speed' => int( + sprintf( "%.1f", + ( $data->{hourly}->[$i]->{wind_speed} * 3.6 ) ) + 0.5 + ), + 'wind_gust' => int( + sprintf( "%.1f", + ( $data->{hourly}->[$i]->{wind_gust} * 3.6 ) ) + 0.5 + ), + 'wind_direction' => $data->{hourly}->[$i]->{wind_deg}, + 'cloudCover' => $data->{hourly}->[$i]->{clouds}, + 'code' => $codes{ $data->{hourly}->[$i]->{weather}->[0]->{id} }, + 'iconAPI' => $data->{hourly}->[$i]->{weather}->[0]->{icon}, + 'rain1h' => $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 + ), + }, + ); + + $i++; + } + + return $self; +} + +sub _CallWeatherCallbackFn { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + + # print 'Dumperausgabe: ' . Dumper $self; + ### Aufruf der callbackFn + return ::Weather_RetrieveCallbackFn( $self->{devName} ); +} + +sub _ErrorHandling { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $err = shift; + + $self->{cached}->{current_date_time} = + _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); + $self->{cached}->{status} = $err; + $self->{cached}->{validity} = 'stale'; + + return; +} + +sub _CreateForecastRef { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + + my $forecastRef = ( + { + lat => $self->{lat}, + long => $self->{long}, + apiMaintainer => 'Marko Oldenburg (' + . $META->{x_fhem_maintainer}[0] . ')', + apiVersion => version->parse( __PACKAGE__->VERSION() )->normal, + } + ); + + return $forecastRef; +} + +sub _strftimeWrapper { + my @data = @_; + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $string = POSIX::strftime(@data); + + $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; +} + +############################################################################## + +1; + +=pod + +=encoding utf8 + +=for :application/json;q=META.json OpenWeatherMapAPI.pm +{ + "abstract": "Weather API for Weather OpenWeatherMap", + "x_lang": { + "de": { + "abstract": "Wetter API für OpenWeatherMap" + } + }, + "version": "v3.0.2", + "author": [ + "Marko Oldenburg " + ], + "x_fhem_maintainer": [ + "CoolTux" + ], + "x_fhem_maintainer_github": [ + "CoolTuxNet" + ], + "prereqs": { + "runtime": { + "requires": { + "FHEM::Meta": 0, + "HttpUtils": 0, + "strict": 0, + "warnings": 0, + "constant": 0, + "POSIX": 0, + "JSON::PP": 0 + }, + "recommends": { + "JSON": 0 + }, + "suggests": { + "JSON::XS": 0, + "Cpanel::JSON::XS": 0 + } + } + } +} +=end :application/json;q=META.json + +=cut + +__END__ diff --git a/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm similarity index 90% rename from wundergroundAPI.pm rename to lib/FHEM/APIs/Weather/wundergroundAPI.pm index 2cf4704..3e7de9e 100644 --- a/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -1,20 +1,22 @@ # $Id$ -package wundergroundAPI; +package FHEM::APIs::Weather::wundergroundAPI; use strict; use warnings; use FHEM::Meta; -use Data::Dumper; - -FHEM::Meta::Load(__PACKAGE__); -use version 0.77; our $VERSION = $main::packages{wundergroundAPI}{META}{version}; - -package wundergroundAPI::Weather; -use strict; -use warnings; use POSIX; -use Encode; use HttpUtils; +use experimental qw /switch/; + +my $META = {}; +my $ret = FHEM::Meta::getMetadata( __FILE__, $META ); +return "$@" if ($@); +return $ret if ($ret); +$::packages{wundergroundAPI}{META} = $META; + +use version 0.77; our $VERSION = $META->{version}; + +# use Data::Dumper; # try to use JSON::MaybeXS wrapper # for chance of better performance + open code @@ -71,16 +73,25 @@ eval { }; }; +my $missingModul = ''; + # use Data::Dumper; # for Debug only ## API URL -use constant DEMODATA => +eval { use Readonly; 1 } + or $missingModul .= 'Readonly '; # apt install libreadonly-perl +## use critic + +# use Data::Dumper; # for Debug only +## API URL +Readonly my $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}}]}'; -use constant URL => 'https://api.weather.com/'; +Readonly my $URL => 'https://api.weather.com/'; +### begin public function sub new { my ( $class, $argsRef ) = @_; - my $apioptions = parseApiOptions( $argsRef->{apioptions} ); + my $apioptions = _parseApiOptions( $argsRef->{apioptions} ); my $self = { devName => $argsRef->{devName}, @@ -125,23 +136,6 @@ sub new { return $self; } -sub parseApiOptions($) { - my $apioptions = shift; - - my @params; - my %h; - - @params = split( ',', $apioptions ); - while (@params) { - my $param = shift(@params); - next if ( $param eq '' ); - my ( $key, $value ) = split( ':', $param, 2 ); - $h{$key} = $value; - } - - return \%h; -} - sub setFetchTime { my $self = shift; @@ -189,7 +183,29 @@ sub getWeather { return $self->{cached}; } -sub _RetrieveDataFromWU($) { +### begin privat function +sub _parseApiOptions { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $apioptions = shift; + + my @params; + my %h; + + @params = split( ',', $apioptions ); + while (@params) { + my $param = shift(@params); + next if ( $param eq '' ); + my ( $key, $value ) = split( ':', $param, 2 ); + $h{$key} = $value; + } + + return \%h; +} + +sub _RetrieveDataFromWU { + return 0 unless ( __PACKAGE__ eq caller(0) ); + my $self = shift; # retrieve data from cache @@ -243,21 +259,27 @@ sub _RetrieveDataFromWU($) { $options .= '&apiKey=' . $self->{key}; $paramRef->{url} = - URL + $URL . 'v3/wx/forecast/daily/' . $self->{days} . 'day' . '?' . $options; if ( lc( $self->{key} ) eq 'demo' ) { - _RetrieveDataFinished( $paramRef, undef, 'DEMODATA' . DEMODATA ); + _RetrieveDataFinished( $paramRef, undef, 'DEMODATA' . $DEMODATA ); } - else { main::HttpUtils_NonblockingGet($paramRef); } + else { ::HttpUtils_NonblockingGet($paramRef); } } + + return; } -sub _RetrieveDataFromPWS($$$) { - my ( $paramRef, $err, $response ) = @_; - my $self = $paramRef->{self}; +sub _RetrieveDataFromPWS { + return 0 unless ( 'main' eq caller(0) ); + + my $paramRef = shift; + my $err = shift; + my $response = shift; + my $self = $paramRef->{self}; my $paramRefPWS = { timeout => 15, @@ -276,14 +298,18 @@ sub _RetrieveDataFromPWS($$$) { $options .= '&numericPrecision=decimal'; $options .= '&apiKey=' . $self->{key}; - $paramRefPWS->{url} = URL . 'v2/pws/observations/current?' . $options; + $paramRefPWS->{url} = $URL . 'v2/pws/observations/current?' . $options; - main::HttpUtils_NonblockingGet($paramRefPWS); + return ::HttpUtils_NonblockingGet($paramRefPWS); } -sub _RetrieveDataFinished($$$) { - my ( $paramRef, $err, $data ) = @_; - my $self = $paramRef->{self}; +sub _RetrieveDataFinished { + return 0 unless ( 'main' eq caller(0) ); + + my $paramRef = shift; + my $err = shift; + my $data = shift; + my $self = $paramRef->{self}; my $response; # we got PWS and forecast data @@ -292,10 +318,10 @@ sub _RetrieveDataFinished($$$) { $err = 'No Data Found for specific PWS' unless ($err); $response = $paramRef->{forecast}; } - elsif ( $paramRef->{forecast} =~ m/^\{(.*)\}$/ ) { + elsif ( $paramRef->{forecast} =~ m/^\{(.*)\}$/x ) { my $fc = $1; - if ( $data =~ m/^\{(.*)\}$/ ) { + if ( $data =~ m/^\{(.*)\}$/x ) { $response = '{' . $fc . ',' . $1 . '}'; } else { @@ -310,7 +336,7 @@ sub _RetrieveDataFinished($$$) { } # just demo data - elsif ( $data =~ m/^DEMODATA(\{.*\})$/ ) { + elsif ( $data =~ m/^DEMODATA(\{.*\})$/x ) { $response = $1; } @@ -321,7 +347,8 @@ sub _RetrieveDataFinished($$$) { if ( !$err ) { $self->{cached}{status} = 'ok'; - $self->{cached}{validity} = 'up-to-date', $self->{fetchTime} = time(); + $self->{cached}{validity} = 'up-to-date'; + $self->{fetchTime} = time(); _ProcessingRetrieveData( $self, $response ); } else { @@ -329,10 +356,15 @@ sub _RetrieveDataFinished($$$) { _ErrorHandling( $self, $err ); _ProcessingRetrieveData( $self, $response ); } + + return; } -sub _ProcessingRetrieveData($$) { - my ( $self, $response ) = @_; +sub _ProcessingRetrieveData { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + my $response = shift; if ( $self->{cached}{status} eq 'ok' and defined($response) @@ -358,7 +390,7 @@ sub _ProcessingRetrieveData($$) { # print Dumper $data; ## für Debugging $self->{cached}{current_date_time} = - strftimeWrapper( "%a, %e %b %Y %H:%M", + _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); # $self->{cached}{timezone} = $data->{timezone}; @@ -415,7 +447,7 @@ sub _ProcessingRetrieveData($$) { 'solarRadiation' => $data->{solarRadiation}, 'uvIndex' => $data->{uv}, 'humidity' => $data->{humidity}, - 'pubDate' => strftimeWrapper( + 'pubDate' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( $data->{obsTimeLocal} ) @@ -444,12 +476,14 @@ sub _ProcessingRetrieveData($$) { { ### löschen des alten Datensatzes delete $self->{cached}{forecast}; - - my $data = + my $data; + $data = exists( $data->{daily} ) ? $data->{daily} : $data; my $days = scalar @{ $data->{temperatureMin} }; - my $i = 0; + my $i; + $i = 0; + while ( $i < $days ) { $data->{moonriseTimeLocal}[$i] =~ s/^(....-..-..T..:..).*/$1/; @@ -467,7 +501,7 @@ sub _ProcessingRetrieveData($$) { 'moonPhase' => $data->{moonPhase}[$i], 'moonPhaseCode' => $data->{moonPhaseCode}[$i], 'moonPhaseDay' => $data->{moonPhaseDay}[$i], - 'moonriseTime' => strftimeWrapper( + 'moonriseTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -475,7 +509,7 @@ sub _ProcessingRetrieveData($$) { ) ) ), - 'moonsetTime' => strftimeWrapper( + 'moonsetTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -486,7 +520,7 @@ sub _ProcessingRetrieveData($$) { 'narrative' => $data->{narrative}[$i], 'precipProbability' => $data->{qpf}[$i], 'precipProbabilitySnow' => $data->{qpfSnow}[$i], - 'sunriseTime' => strftimeWrapper( + 'sunriseTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -494,7 +528,7 @@ sub _ProcessingRetrieveData($$) { ) ) ), - 'sunsetTime' => strftimeWrapper( + 'sunsetTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -537,10 +571,10 @@ sub _ProcessingRetrieveData($$) { and ref( $data->{daypart}[0]{daypartName} ) eq "ARRAY" and scalar @{ $data->{daypart}[0]{daypartName} } > 0 ) { - my $data = $data->{daypart}[0]; + $data = $data->{daypart}[0]; my $dayparts = scalar @{ $data->{daypartName} }; - my $i = 0; + $i = 0; my $day = 0; while ( $i < $dayparts ) { @@ -678,26 +712,35 @@ sub _ProcessingRetrieveData($$) { } ## Aufruf der callbackFn - _CallWeatherCallbackFn($self); + return _CallWeatherCallbackFn($self); } -sub _CallWeatherCallbackFn($) { +sub _CallWeatherCallbackFn { + return 0 unless ( __PACKAGE__ eq caller(0) ); + my $self = shift; # ## Aufruf der callbackFn - main::Weather_RetrieveCallbackFn( $self->{devName} ); + return ::Weather_RetrieveCallbackFn( $self->{devName} ); } -sub _ErrorHandling($$) { - my ( $self, $err ) = @_; +sub _ErrorHandling { + return 0 unless ( __PACKAGE__ eq caller(0) ); + + my $self = shift; + 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($) { +sub _CreateForecastRef { + return 0 unless ( __PACKAGE__ eq caller(0) ); + my $self = shift; my $forecastRef = ( @@ -705,28 +748,31 @@ sub _CreateForecastRef($) { lat => $self->{lat}, long => $self->{long}, apiMaintainer => 'Julian Pawlowski (loredo)', - apiVersion => wundergroundAPI->VERSION(), + apiVersion => version->parse( __PACKAGE__->VERSION() )->normal, } ); return $forecastRef; } -sub strftimeWrapper(@) { - my $string = POSIX::strftime(@_); +sub _strftimeWrapper { + my @data = @_; + return 0 unless ( __PACKAGE__ eq caller(0) ); - $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; + my $string = POSIX::strftime(@data); + + $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; } From e6c757d1f25d69c1d577f0c4800808e287db39f8 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 24 Dec 2022 04:48:42 +0100 Subject: [PATCH 31/48] move and rename API.de API description change README.m API.de description --- docs/{API.de.md => API.de/README.md} | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) rename docs/{API.de.md => API.de/README.md} (92%) diff --git a/docs/API.de.md b/docs/API.de/README.md similarity index 92% rename from docs/API.de.md rename to docs/API.de/README.md index d0e4d68..488ba0b 100644 --- a/docs/API.de.md +++ b/docs/API.de/README.md @@ -8,9 +8,11 @@ Da sich die API selbst noch in Entwicklung befindet, sollte vor jedem neuen API- Das Modul muss zwingend Objektorientiert geschrieben werden. Man sollte sich also schon einmal damit befasst haben um zu verstehen, wie so ein objektorientiertes Modul funktioniert. -Der Packagename muß im Format `API::Weather` angegeben werden, also z.B. +Alle API Moduldateien für 59_Weather.pm werden unter `lib/FHEM/APIs/Weather/` abgelegt - package FoobarAPI::Weather +Der Packagename muß im Format `FHEM::APIs::WeatherAPI` angegeben werden, also z.B. + + package FHEM::APIs::Weather::FoobarAPI für den Wetter-Service Foobar. @@ -70,9 +72,9 @@ Wird `LOCATION` und `LANGUAGE` beim Define des Devices nicht angeben, wird von ` Es müssen zwingend zwei Objektmethoden vorhanden sein. `Weather` wird dann über den Konstruktor eine neue Instanz als Instanzvariable anlegen. Über diese Instanzvariablen werden dann die zwei Methodenaufrufe durchgeführt. -`$obj→setRetrieveData` wird in `Weather` in der GetUpdate-Funktion aufgerufen. +`$obj->setRetrieveData` wird in `Weather` in der GetUpdate-Funktion aufgerufen. -`$obj→getWeather` wird in der `CallbackFn-Funktion aufgerufen um die Daten aus dem API-Modul zu erhalten. Vorher muss aus dem API-Modul heraus die `CallbackFn` aufgerufen und der Name der `Weather`-Instanz übergeben werden: `Weather_CallbackFn(devName);` +`$obj->getWeather` wird in der `CallbackFn-Funktion aufgerufen um die Daten aus dem API-Modul zu erhalten. Vorher muss aus dem API-Modul heraus die `CallbackFn` aufgerufen und der Name der `Weather`-Instanz übergeben werden: `Weather_CallbackFn(devName);` Für beide Methodenaufrufe **muss** eine entsprechende Methode existieren. Das ist Pflicht. From 7d0a835c71bb4c17786e5ab3402a8b3d13cebc5a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 24 Dec 2022 10:20:09 +0100 Subject: [PATCH 32/48] change versions --- FHEM/59_Weather.pm | 2 +- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index f5b8983..ebcada5 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -1540,7 +1540,7 @@ sub Weather_CheckOptions { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.2.6", + "version": "v2.2.10", "author": [ "Marko Oldenburg " ], diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index e4f5adc..0a71f45 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -991,7 +991,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v3.0.2", + "version": "v3.0.10", "author": [ "Marko Oldenburg " ], From 745fea37e2590d3923f374103b79dd295f3b4102 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 24 Dec 2022 12:57:41 +0100 Subject: [PATCH 33/48] fix undefined value Can't use an undefined value as an ARRAY reference at ./FHEM/59_Weather.pm line 589 --- FHEM/59_Weather.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index ebcada5..9af9e3f 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -586,7 +586,9 @@ sub Weather_WriteReadings { else { Weather_DeleteAlertsReadings($hash); readingsBulkUpdate( $hash, 'warnCount', - scalar( @{ $dataRef->{alerts} } ) ); + scalar( @{ $dataRef->{alerts} } ) ) + if ( defined( $dataRef->{alerts} ) + && ref( $dataRef->{alerts} ) eq 'ARRAY' ); } ### state From df08ffb7c4d608ad13bf884f5747a59e3ae2aad3 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sun, 25 Dec 2022 09:20:58 +0100 Subject: [PATCH 34/48] fix "PERL WARNING: Use of uninitialized value" no {wind}->{gust} field in $data --- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index 0a71f45..5187b7f 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -585,8 +585,6 @@ sub _FillSelfHashWithWeatherResponseForWeatherCurrent { 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} }, @@ -606,6 +604,9 @@ sub _FillSelfHashWithWeatherResponseForWeatherCurrent { $self->{cached}->{current}->{'visibility'} = int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ) if ( exists $data->{visibility} ); + $self->{cached}->{current}->{'wind_gust'} = + int( sprintf( "%.1f", ( $data->{wind}->{gust} * 3.6 ) ) + 0.5 ) + if ( exists $data->{wind}->{gust} ); return $self; } From 232af18f02aab7bc49a0bd02412a1d5ee810afb2 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 28 Dec 2022 09:10:14 +0100 Subject: [PATCH 35/48] remove celvin substration --- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index 5187b7f..0c6fb71 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -637,16 +637,13 @@ sub _FillSelfHashWithWeatherResponseForForecastHourly { sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp_min} ) ), 'high_c' => int( - sprintf( "%.0f", - $data->{list}->[$i]->{main}->{temp_max} - 273.15 ) + sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp_max} ) ), 'tempLow' => int( - sprintf( "%.0f", - $data->{list}->[$i]->{main}->{temp_min} - 273.15 ) + sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp_min} ) ), 'tempHigh' => int( - sprintf( "%.0f", - $data->{list}->[$i]->{main}->{temp_max} - 273.15 ) + sprintf( "%.0f", $data->{list}->[$i]->{main}->{temp_max} ) ), 'humidity' => $data->{list}->[$i]->{main}->{humidity}, 'condition' => encode_utf8( @@ -758,7 +755,7 @@ sub _FillSelfHashWithWeatherResponseForOnecallDaily { "%a, %H:%M", localtime( $data->{daily}->[$i]->{dt} ) ), 'sunrise' => strftime( - "%H:%M", localtime( $data->{daily}->[$i]->{sunrise} ) + "%a, %H:%M", localtime( $data->{daily}->[$i]->{sunrise} ) ), 'sunset' => strftime( "%a, %H:%M", localtime( $data->{daily}->[$i]->{sunset} ) @@ -992,7 +989,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v3.0.10", + "version": "v3.0.11", "author": [ "Marko Oldenburg " ], From 3d4996777f7a1a410229789462ab26a91f26aa2a Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Wed, 28 Dec 2022 09:24:12 +0100 Subject: [PATCH 36/48] change versions and add control file --- FHEM/59_Weather.pm | 2 +- controls_Weather.txt | 4 ++++ lib/FHEM/APIs/Weather/DarkSkyAPI.pm | 2 +- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 2 +- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 2 +- 5 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 controls_Weather.txt diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index 9af9e3f..1f956e7 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -1542,7 +1542,7 @@ sub Weather_CheckOptions { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.2.10", + "version": "v2.2.11", "author": [ "Marko Oldenburg " ], diff --git a/controls_Weather.txt b/controls_Weather.txt new file mode 100644 index 0000000..2691ecf --- /dev/null +++ b/controls_Weather.txt @@ -0,0 +1,4 @@ +UPD 2022-12-28_09:21:01 54815 FHEM/59_Weather.pm +UPD 2022-12-28_09:20:44 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm +UPD 2022-12-28_09:20:52 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2022-12-28_09:20:32 35424 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/DarkSkyAPI.pm b/lib/FHEM/APIs/Weather/DarkSkyAPI.pm index 077b05c..9f9fa22 100644 --- a/lib/FHEM/APIs/Weather/DarkSkyAPI.pm +++ b/lib/FHEM/APIs/Weather/DarkSkyAPI.pm @@ -773,7 +773,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für Weather DarkSky" } }, - "version": "v1.2.0", + "version": "v1.2.1", "author": [ "Marko Oldenburg " ], diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index 0c6fb71..1514290 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -989,7 +989,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v3.0.11", + "version": "v3.0.12", "author": [ "Marko Oldenburg " ], diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index 3e7de9e..9a9396f 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -793,7 +793,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für Weather Underground" } }, - "version": "v1.0.2", + "version": "v1.0.3", "author": [ "Julian Pawlowski " ], From 7ef05e0c00fb97d16b9876f67ac2bfbffd00938d Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 29 Dec 2022 04:41:10 +0100 Subject: [PATCH 37/48] fix some regex formating --- controls_Weather.txt | 2 +- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index 2691ecf..f343b82 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ UPD 2022-12-28_09:21:01 54815 FHEM/59_Weather.pm UPD 2022-12-28_09:20:44 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm UPD 2022-12-28_09:20:52 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2022-12-28_09:20:32 35424 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2022-12-29_04:40:05 35600 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index 9a9396f..ce68fe2 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -7,6 +7,7 @@ use FHEM::Meta; use POSIX; use HttpUtils; use experimental qw /switch/; +use Encode; my $META = {}; my $ret = FHEM::Meta::getMetadata( __FILE__, $META ); @@ -75,8 +76,10 @@ eval { my $missingModul = ''; -# use Data::Dumper; # for Debug only -## API URL +## no critic (Conditional "use" statement. Use "require" to conditionally include a module (Modules::ProhibitConditionalUseStatements)) +eval { use Encode qw /encode_utf8/; 1 } + or $missingModul .= 'Encode '; + eval { use Readonly; 1 } or $missingModul .= 'Readonly '; # apt install libreadonly-perl ## use critic @@ -370,7 +373,7 @@ sub _ProcessingRetrieveData { and defined($response) and $response ) { - if ( $response =~ m/^\{.*\}$/ ) { + if ( $response =~ m/^\{.*\}$/x ) { my $data = eval { decode_json( encode_utf8($response) ) }; if ($@) { _ErrorHandling( $self, @@ -486,13 +489,13 @@ sub _ProcessingRetrieveData { while ( $i < $days ) { $data->{moonriseTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..).*/$1/x; $data->{moonsetTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..).*/$1/x; $data->{sunriseTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..).*/$1/x; $data->{sunsetTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..).*/$1/x; push( @{ $self->{cached}{forecast}{daily} }, From c6fe48168f92325ae59570db83424836da94bbfb Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 30 Dec 2022 12:11:19 +0100 Subject: [PATCH 38/48] fix undefined value as an ARRAY reference --- controls_Weather.txt | 2 +- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 102 ++++++++++++----------- 2 files changed, 56 insertions(+), 48 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index f343b82..1f63c98 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ UPD 2022-12-28_09:21:01 54815 FHEM/59_Weather.pm UPD 2022-12-28_09:20:44 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm UPD 2022-12-28_09:20:52 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2022-12-29_04:40:05 35600 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2022-12-30_12:10:48 36045 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index ce68fe2..cd17355 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -366,14 +366,13 @@ sub _RetrieveDataFinished { sub _ProcessingRetrieveData { return 0 unless ( __PACKAGE__ eq caller(0) ); - my $self = shift; - my $response = shift; + my ( $self, $response ) = @_; if ( $self->{cached}{status} eq 'ok' and defined($response) and $response ) { - if ( $response =~ m/^\{.*\}$/x ) { + if ( $response =~ m/^\{.*\}$/ ) { my $data = eval { decode_json( encode_utf8($response) ) }; if ($@) { _ErrorHandling( $self, @@ -393,7 +392,7 @@ sub _ProcessingRetrieveData { # print Dumper $data; ## für Debugging $self->{cached}{current_date_time} = - _strftimeWrapper( "%a, %e %b %Y %H:%M", + strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); # $self->{cached}{timezone} = $data->{timezone}; @@ -423,17 +422,22 @@ sub _ProcessingRetrieveData { ); $self->{cached}{current} = { - 'dewPoint' => sprintf( "%.1f", $data->{$unit}{dewpt} ), - 'heatIndex' => $data->{$unit}{heatIndex}, + 'dewPoint' => + int( sprintf( "%.1f", $data->{$unit}{dewpt} ) + 0.5 ), + 'heatIndex' => $data->{$unit}{heatIndex}, 'precipRate' => $data->{$unit}{precipRate}, 'precipTotal' => $data->{$unit}{precipTotal}, - 'pressure' => - sprintf( "%.1f", $data->{$unit}{pressure} ), + 'pressure' => int( + sprintf( "%.1f", $data->{$unit}{pressure} ) + 0.5 + ), 'temperature' => - sprintf( "%.1f", $data->{$unit}{temp} ), - 'temp_c' => sprintf( "%.1f", $data->{$unit}{temp} ), - 'wind_chill' => - sprintf( "%.1f", ( $data->{$unit}{windChill} ) ), + int( sprintf( "%.1f", $data->{$unit}{temp} ) + 0.5 ), + 'temp_c' => + int( sprintf( "%.1f", $data->{$unit}{temp} ) + 0.5 ), + 'wind_chill' => int( + sprintf( "%.1f", ( $data->{$unit}{windChill} ) ) + + 0.5 + ), 'windGust' => int( sprintf( "%.1f", ( $data->{$unit}{windGust} ) ) + 0.5 @@ -450,7 +454,7 @@ sub _ProcessingRetrieveData { 'solarRadiation' => $data->{solarRadiation}, 'uvIndex' => $data->{uv}, 'humidity' => $data->{humidity}, - 'pubDate' => _strftimeWrapper( + 'pubDate' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( $data->{obsTimeLocal} ) @@ -479,23 +483,21 @@ sub _ProcessingRetrieveData { { ### löschen des alten Datensatzes delete $self->{cached}{forecast}; - my $data; - $data = + + my $data = exists( $data->{daily} ) ? $data->{daily} : $data; my $days = scalar @{ $data->{temperatureMin} }; - my $i; - $i = 0; - + my $i = 0; while ( $i < $days ) { $data->{moonriseTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/x; + s/^(....-..-..T..:..).*/$1/; $data->{moonsetTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/x; + s/^(....-..-..T..:..).*/$1/; $data->{sunriseTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/x; + s/^(....-..-..T..:..).*/$1/; $data->{sunsetTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/x; + s/^(....-..-..T..:..).*/$1/; push( @{ $self->{cached}{forecast}{daily} }, @@ -504,7 +506,7 @@ sub _ProcessingRetrieveData { 'moonPhase' => $data->{moonPhase}[$i], 'moonPhaseCode' => $data->{moonPhaseCode}[$i], 'moonPhaseDay' => $data->{moonPhaseDay}[$i], - 'moonriseTime' => _strftimeWrapper( + 'moonriseTime' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -512,7 +514,7 @@ sub _ProcessingRetrieveData { ) ) ), - 'moonsetTime' => _strftimeWrapper( + 'moonsetTime' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -523,7 +525,7 @@ sub _ProcessingRetrieveData { 'narrative' => $data->{narrative}[$i], 'precipProbability' => $data->{qpf}[$i], 'precipProbabilitySnow' => $data->{qpfSnow}[$i], - 'sunriseTime' => _strftimeWrapper( + 'sunriseTime' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -531,7 +533,7 @@ sub _ProcessingRetrieveData { ) ) ), - 'sunsetTime' => _strftimeWrapper( + 'sunsetTime' => strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -539,27 +541,33 @@ sub _ProcessingRetrieveData { ) ) ), - 'low_c' => sprintf( - "%.1f", $data->{temperatureMin}[$i] + 'low_c' => int( + sprintf( "%.1f", + $data->{temperatureMin}[$i] ) + 0.5 ), - 'high_c' => sprintf( - "%.1f", - ( - $data->{temperatureMax}[$i] - ? $data->{temperatureMax}[$i] - : 0 - ) + 'high_c' => int( + sprintf( + "%.1f", + ( + $data->{temperatureMax}[$i] + ? $data->{temperatureMax}[$i] + : 0 + ) + ) + 0.5 ), - 'tempLow' => sprintf( - "%.1f", $data->{temperatureMin}[$i] + 'tempLow' => int( + sprintf( "%.1f", + $data->{temperatureMin}[$i] ) + 0.5 ), - 'tempHigh' => sprintf( - "%.1f", - ( - $data->{temperatureMax}[$i] - ? $data->{temperatureMax}[$i] - : 0 - ) + 'tempHigh' => int( + sprintf( + "%.1f", + ( + $data->{temperatureMax}[$i] + ? $data->{temperatureMax}[$i] + : 0 + ) + ) + 0.5 ), } ); @@ -574,10 +582,10 @@ sub _ProcessingRetrieveData { and ref( $data->{daypart}[0]{daypartName} ) eq "ARRAY" and scalar @{ $data->{daypart}[0]{daypartName} } > 0 ) { - $data = $data->{daypart}[0]; + my $data = $data->{daypart}[0]; my $dayparts = scalar @{ $data->{daypartName} }; - $i = 0; + my $i = 0; my $day = 0; while ( $i < $dayparts ) { @@ -715,7 +723,7 @@ sub _ProcessingRetrieveData { } ## Aufruf der callbackFn - return _CallWeatherCallbackFn($self); + _CallWeatherCallbackFn($self); } sub _CallWeatherCallbackFn { From b256d1e64b1e4b0841f54b8b1c4ea502a28cd155 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 30 Dec 2022 12:14:33 +0100 Subject: [PATCH 39/48] fix sub --- controls_Weather.txt | 2 +- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index 1f63c98..00c076c 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ UPD 2022-12-28_09:21:01 54815 FHEM/59_Weather.pm UPD 2022-12-28_09:20:44 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm UPD 2022-12-28_09:20:52 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2022-12-30_12:10:48 36045 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2022-12-30_12:14:13 36063 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index cd17355..b73f968 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -366,7 +366,8 @@ sub _RetrieveDataFinished { sub _ProcessingRetrieveData { return 0 unless ( __PACKAGE__ eq caller(0) ); - my ( $self, $response ) = @_; + my $self = shift; + my $response = shift; if ( $self->{cached}{status} eq 'ok' and defined($response) From 420fbff2330081d4c3e8975a3c3218ac8bd01247 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 30 Dec 2022 12:17:37 +0100 Subject: [PATCH 40/48] fix --- controls_Weather.txt | 8 ++++---- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index 00c076c..e603430 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2022-12-28_09:21:01 54815 FHEM/59_Weather.pm -UPD 2022-12-28_09:20:44 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2022-12-28_09:20:52 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2022-12-30_12:14:13 36063 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2022-12-30_12:15:47 54815 FHEM/59_Weather.pm +UPD 2022-12-30_12:15:47 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm +UPD 2022-12-30_12:15:47 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2022-12-30_12:17:04 36070 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index b73f968..45d379d 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -724,7 +724,7 @@ sub _ProcessingRetrieveData { } ## Aufruf der callbackFn - _CallWeatherCallbackFn($self); + return _CallWeatherCallbackFn($self); } sub _CallWeatherCallbackFn { From bf9e07c57448359e0bdbcfe4ed3ee67de8de63b3 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Fri, 30 Dec 2022 13:17:40 +0100 Subject: [PATCH 41/48] fix Undefined subroutine Undefined subroutine &FHEM::APIs::Weather::wundergroundAPI::strftimeWrapper --- controls_Weather.txt | 8 ++++---- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index e603430..4b868d0 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2022-12-30_12:15:47 54815 FHEM/59_Weather.pm -UPD 2022-12-30_12:15:47 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2022-12-30_12:15:47 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2022-12-30_12:17:04 36070 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2022-12-30_12:22:17 54815 FHEM/59_Weather.pm +UPD 2022-12-30_12:22:17 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm +UPD 2022-12-30_12:22:17 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2022-12-30_13:17:00 36076 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index 45d379d..b729217 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -393,7 +393,7 @@ sub _ProcessingRetrieveData { # print Dumper $data; ## für Debugging $self->{cached}{current_date_time} = - strftimeWrapper( "%a, %e %b %Y %H:%M", + _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( $self->{fetchTime} ) ); # $self->{cached}{timezone} = $data->{timezone}; @@ -455,7 +455,7 @@ sub _ProcessingRetrieveData { 'solarRadiation' => $data->{solarRadiation}, 'uvIndex' => $data->{uv}, 'humidity' => $data->{humidity}, - 'pubDate' => strftimeWrapper( + 'pubDate' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( $data->{obsTimeLocal} ) @@ -507,7 +507,7 @@ sub _ProcessingRetrieveData { 'moonPhase' => $data->{moonPhase}[$i], 'moonPhaseCode' => $data->{moonPhaseCode}[$i], 'moonPhaseDay' => $data->{moonPhaseDay}[$i], - 'moonriseTime' => strftimeWrapper( + 'moonriseTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -515,7 +515,7 @@ sub _ProcessingRetrieveData { ) ) ), - 'moonsetTime' => strftimeWrapper( + 'moonsetTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -526,7 +526,7 @@ sub _ProcessingRetrieveData { 'narrative' => $data->{narrative}[$i], 'precipProbability' => $data->{qpf}[$i], 'precipProbabilitySnow' => $data->{qpfSnow}[$i], - 'sunriseTime' => strftimeWrapper( + 'sunriseTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -534,7 +534,7 @@ sub _ProcessingRetrieveData { ) ) ), - 'sunsetTime' => strftimeWrapper( + 'sunsetTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( From b459f6264a5906c0bfc3e7ea64acfb50540d6119 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Mon, 2 Jan 2023 09:27:41 +0100 Subject: [PATCH 42/48] fix regex for sun and moon set and rise --- controls_Weather.txt | 2 +- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index 4b868d0..f4de39e 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ UPD 2022-12-30_12:22:17 54815 FHEM/59_Weather.pm UPD 2022-12-30_12:22:17 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm UPD 2022-12-30_12:22:17 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2022-12-30_13:17:00 36076 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2023-01-02_09:25:12 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index b729217..9b95fac 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -492,13 +492,13 @@ sub _ProcessingRetrieveData { my $i = 0; while ( $i < $days ) { $data->{moonriseTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..:..).*/$1/x; $data->{moonsetTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..:..).*/$1/x; $data->{sunriseTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..:..).*/$1/x; $data->{sunsetTimeLocal}[$i] =~ - s/^(....-..-..T..:..).*/$1/; + s/^(....-..-..T..:..:..).*/$1/x; push( @{ $self->{cached}{forecast}{daily} }, @@ -507,7 +507,10 @@ sub _ProcessingRetrieveData { 'moonPhase' => $data->{moonPhase}[$i], 'moonPhaseCode' => $data->{moonPhaseCode}[$i], 'moonPhaseDay' => $data->{moonPhaseDay}[$i], - 'moonriseTime' => _strftimeWrapper( + 'narrative' => $data->{narrative}[$i], + 'precipProbability' => $data->{qpf}[$i], + 'precipProbabilitySnow' => $data->{qpfSnow}[$i], + 'moonriseTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( @@ -523,10 +526,7 @@ sub _ProcessingRetrieveData { ) ) ), - 'narrative' => $data->{narrative}[$i], - 'precipProbability' => $data->{qpf}[$i], - 'precipProbabilitySnow' => $data->{qpfSnow}[$i], - 'sunriseTime' => _strftimeWrapper( + 'sunriseTime' => _strftimeWrapper( "%a, %e %b %Y %H:%M", localtime( main::time_str2num( From 785f9d07b497312fccdac071cd04de0256ad44a0 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 5 Jan 2023 08:18:05 +0100 Subject: [PATCH 43/48] add hook example add support for onecall endpoint with api version 2.5 --- FHEM/59_Weather.pm | 16 ++++++--- controls_Weather.txt | 8 ++--- hooks/pre-commit | 41 ++++++++++++++++++++++ lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 7 ++-- 4 files changed, 62 insertions(+), 10 deletions(-) create mode 100755 hooks/pre-commit diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index 1f956e7..d87bb5f 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -1204,7 +1204,10 @@ sub Weather_CheckOptions {
    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
    endpoint:onecall only to test whether the API key which not + officially for onecall is not supported yet onecall via API version 2.5. IMPORTANT!!! + apioption version must not be set to 3.0
    location<latitude,longitude>
    geographic coordinates in degrees of the location for which the weather is forecast; if missing, the values of the attributes @@ -1398,11 +1401,16 @@ sub Weather_CheckOptions { - + + Per Default 2.5, möglich ist noch 3.0 aber nur mit Zusatzsubscription + +
    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. version:<version> API Version welche verwendet werden soll. - Per Default 2.5, möglich ist noch 3.0 aber nur mit Zusatzsubscription
    endpoint:onecall nur zum testen ob der API Key welcher nicht + offiziell für onecall ist nicht doch onecall über die API Version 2.5 unterstützt. WICHTIG!!! + apioption version darf nicht auf 3.0 gesetzt werden
    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 @@ -1542,7 +1550,7 @@ sub Weather_CheckOptions { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.2.11", + "version": "v2.2.15", "author": [ "Marko Oldenburg " ], diff --git a/controls_Weather.txt b/controls_Weather.txt index f4de39e..7e6e317 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2022-12-30_12:22:17 54815 FHEM/59_Weather.pm -UPD 2022-12-30_12:22:17 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2022-12-30_12:22:17 32101 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2023-01-02_09:25:12 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2023-01-05_08:17:10 55320 FHEM/59_Weather.pm +UPD 2023-01-04_16:37:24 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm +UPD 2023-01-05_08:05:28 32255 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2023-01-04_16:37:24 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/hooks/pre-commit b/hooks/pre-commit new file mode 100755 index 0000000..4fc1c9a --- /dev/null +++ b/hooks/pre-commit @@ -0,0 +1,41 @@ +#!/usr/bin/perl -w + +use File::Basename; +use POSIX qw(strftime); +use strict; + +my @filenames = ( 'FHEM/59_Weather.pm', + 'lib/FHEM/APIs/Weather/DarkSkyAPI.pm', + 'lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm', + 'lib/FHEM/APIs/Weather/wundergroundAPI.pm', + ); + +my $controlsfile = 'controls_Weather.txt'; + +open(FH, ">$controlsfile") || return("Can't open $controlsfile: $!"); + +for my $filename (@filenames) { + my @statOutput = stat($filename); + + if (scalar @statOutput != 13) { + printf 'error: stat has unexpected return value for ' . $filename . "\n"; + next; + } + + my $mtime = $statOutput[9]; + my $date = POSIX::strftime("%Y-%m-%d", localtime($mtime)); + my $time = POSIX::strftime("%H:%M:%S", localtime($mtime)); + my $filetime = $date."_".$time; + + my $filesize = $statOutput[7]; + + printf FH 'UPD ' . $filetime . ' ' . $filesize . ' ' .$filename . "\n"; +} + +close(FH); + +system("git add $controlsfile"); + +print 'Create controls File succesfully' . "\n"; + +exit 0; diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index 1514290..d5dc975 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -213,6 +213,8 @@ sub new { $self->{apiversion} = ( $apioptions->{version} ? $apioptions->{version} : '2.5' ); + $self->{endpointType} = + ( $apioptions->{endpoint} ? $apioptions->{endpoint} : 'forecast' ); $self->{cached} = _CreateForecastRef($self); @@ -316,7 +318,8 @@ sub _RetrieveDataFromOpenWeatherMap { timeout => 15, self => $self, endpoint => $self->{endpoint} eq 'none' - ? ( $self->{apiversion} == 3.0 ? 'onecall' : 'weather' ) + ? ( $self->{apiversion} == 3.0 + || $self->{endpointType} eq 'onecall' ? 'onecall' : 'weather' ) : 'forecast', callback => \&_RetrieveDataFinished, }; @@ -989,7 +992,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v3.0.12", + "version": "v3.0.15", "author": [ "Marko Oldenburg " ], From 0b7f3a6a1b3a33ddca8aa7bcdbba820d54c1bab9 Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 5 Jan 2023 17:02:46 +0100 Subject: [PATCH 44/48] change old snow and rain value to zero --- controls_Weather.txt | 4 +- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 60 ++++++++++++++++------ 2 files changed, 47 insertions(+), 17 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index 7e6e317..334cdca 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2023-01-05_08:17:10 55320 FHEM/59_Weather.pm +UPD 2023-01-05_08:20:31 55320 FHEM/59_Weather.pm UPD 2023-01-04_16:37:24 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2023-01-05_08:05:28 32255 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2023-01-05_17:01:28 33191 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm UPD 2023-01-04_16:37:24 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index d5dc975..234df26 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -671,10 +671,26 @@ sub _FillSelfHashWithWeatherResponseForForecastHourly { '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'}, + 'rain1h' => ( + $data->{list}->[$i]->{rain}->{'1h'} + ? $data->{list}->[$i]->{rain}->{'1h'} + : 0 + ), + 'rain3h' => ( + $data->{list}->[$i]->{rain}->{'3h'} + ? $data->{list}->[$i]->{rain}->{'3h'} + : 0 + ), + 'snow1h' => ( + $data->{list}->[$i]->{snow}->{'1h'} + ? $data->{list}->[$i]->{snow}->{'1h'} + : 0 + ), + 'snow3h' => ( + $data->{list}->[$i]->{snow}->{'3h'} + ? $data->{list}->[$i]->{snow}->{'1h'} + : 0 + ), } ); @@ -711,11 +727,11 @@ sub _FillSelfHashWithWeatherResponseForOnecallCurrent { 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' => + 'rain_1h' => ( $data->{rain}->{'1h'} ? $data->{rain}->{'1h'} : 0 ), + '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", @@ -836,9 +852,15 @@ sub _FillSelfHashWithWeatherResponseForOnecallDaily { ), 'cloudCover' => $data->{daily}->[$i]->{clouds}, 'code' => $codes{ $data->{daily}->[$i]->{weather}->[0]->{id} }, - 'rain' => $data->{daily}->[$i]->{rain}, - 'snow' => $data->{daily}->[$i]->{snow}, - 'uvi' => $data->{daily}->[$i]->{uvi}, + 'rain' => ( + $data->{daily}->[$i]->{rain} ? $data->{daily}->[$i]->{rain} + : 0 + ), + 'snow' => ( + $data->{daily}->[$i]->{snow} ? $data->{daily}->[$i]->{snow} + : 0 + ), + 'uvi' => $data->{daily}->[$i]->{uvi}, }, ); @@ -896,9 +918,17 @@ sub _FillSelfHashWithWeatherResponseForOnecallHourly { 'wind_direction' => $data->{hourly}->[$i]->{wind_deg}, 'cloudCover' => $data->{hourly}->[$i]->{clouds}, 'code' => $codes{ $data->{hourly}->[$i]->{weather}->[0]->{id} }, - 'iconAPI' => $data->{hourly}->[$i]->{weather}->[0]->{icon}, - 'rain1h' => $data->{hourly}->[$i]->{rain}->{'1h'}, - 'snow1h' => $data->{hourly}->[$i]->{snow}->{'1h'}, + 'iconAPI' => $data->{hourly}->[$i]->{weather}->[0]->{icon}, + 'rain1h' => ( + $data->{hourly}->[$i]->{rain}->{'1h'} + ? $data->{hourly}->[$i]->{rain}->{'1h'} + : 0 + ), + 'snow1h' => ( + $data->{hourly}->[$i]->{snow}->{'1h'} + ? $data->{hourly}->[$i]->{snow}->{'1h'} + : 0 + ), 'uvi' => $data->{hourly}->[$i]->{uvi}, 'visibility' => int( sprintf( "%.1f", $data->{hourly}->[$i]->{visibility} ) + 0.5 From dc5a675442974a4787e65ec698f7bd5238f9a6df Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Thu, 5 Jan 2023 19:40:21 +0100 Subject: [PATCH 45/48] fix uninitialized value in multiplication when wind_guest not given --- controls_Weather.txt | 4 ++-- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/controls_Weather.txt b/controls_Weather.txt index 334cdca..f8b3466 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2023-01-05_08:20:31 55320 FHEM/59_Weather.pm +UPD 2023-01-05_18:36:31 55320 FHEM/59_Weather.pm UPD 2023-01-04_16:37:24 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2023-01-05_17:01:28 33191 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2023-01-05_19:39:00 33259 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm UPD 2023-01-04_16:37:24 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index 234df26..ac9070e 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -723,8 +723,10 @@ sub _FillSelfHashWithWeatherResponseForOnecallCurrent { '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_gust' => ( + $data->{current}->{wind_gust} ? int( + sprintf( "%.1f", ( $data->{current}->{wind_gust} * 3.6 ) ) + 0.5 + ) : 0 ), 'wind_direction' => $data->{current}->{wind_deg}, 'rain_1h' => ( $data->{rain}->{'1h'} ? $data->{rain}->{'1h'} : 0 ), From f79f0c6fa825fd07ce294f709fce2ae8257efc3d Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 7 Jan 2023 13:21:27 +0100 Subject: [PATCH 46/48] docs: change commandref change commandref with id tags --- FHEM/59_Weather.pm | 144 ++++++++++++++++++++++++++++++------------- controls_Weather.txt | 4 +- hooks/commit-msg | 36 +++++++++++ hooks/post-commit | 18 ++++++ 4 files changed, 156 insertions(+), 46 deletions(-) create mode 100755 hooks/commit-msg create mode 100755 hooks/post-commit diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index d87bb5f..22b8925 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -1132,7 +1132,7 @@ sub Weather_CheckOptions { =item summary_DE stellt Wetterbericht und -vorhersage bereit =begin html - +

    Weather

      Note: you need the JSON perl module. Use apt-get install @@ -1148,8 +1148,8 @@ sub Weather_CheckOptions { Such a virtual Weather device periodically gathers current and forecast weather conditions from the chosen weather API.

      - - Define

      + +

      Define



        define <name> Weather [API=<API>[,<apioptions>]] [apikey=<apikey>] [location=<location>] [interval=<interval>] [lang=<lang>]

        @@ -1184,7 +1184,7 @@ sub Weather_CheckOptions { API-specific documentation follows.

        - Dark Sky

        +

        Dark Sky

        @@ -1197,7 +1197,7 @@ sub Weather_CheckOptions {
        APIDarkSkyAPI

        - OpenWeatherMap

        +

        OpenWeatherMap

        @@ -1215,7 +1215,7 @@ sub Weather_CheckOptions {
        APIOpenWeatherMapAPI

        - Wunderground

        +

        Wunderground

        @@ -1247,17 +1247,19 @@ sub Weather_CheckOptions {
        - - Set + +

        Set

          +
        • - set <name> update

          + set <name> update

          Forces the retrieval of the weather data. The next automatic retrieval is scheduled to occur interval seconds later.
        • +
        • - set <name> newLocation latitude,longitude

          + set <name> newLocation latitude,longitude

          set a new temporary location. the value pair Latitude Longitude is separated by a comma. @@ -1266,39 +1268,65 @@ sub Weather_CheckOptions {

        - - Get + +

        Get

          get <name> <reading>

          Valid readings and their meaning (? can be one of 1, 2, 3, 4, 5 and stands for today, tomorrow, etc.):
        APIwundergroundAPI
        + + + + + + + + + + + + + + + + + + + + + + + + + +
        .licenselicense of the API provider, if available
        cityname of town returned for location
        codecurrent condition code
        conditioncurrent condition
        current_date_timelast update of forecast on server
        fc?_codeforecast condition code
        fc?_conditionforecast condition
        fc?_day_of_weekday of week for day +?
        fc?_high_cforecasted daily high in degrees centigrade
        fc?_iconforecast icon
        fc?_low_cforecasted daily low in degrees centigrade
        humiditycurrent humidity in %
        iconrelative path for current icon
        pressureair pressure in hPa
        pressure_trendair pressure trend (0= steady, 1= rising, 2= falling)
        pressure_trend_txttextual representation of air pressure trend
        pressure_trend_symsymbolic representation of air pressure trend
        temperaturecurrent temperature in degrees centigrade
        temp_ccurrent temperature in degrees centigrade
        temp_fcurrent temperature in degrees Fahrenheit
        visibilityvisibility in km
        windwind speed in km/h
        wind_chillwind chill in degrees centigrade
        wind_conditionwind direction and speed
        wind_directiondirection wind comes from in degrees (0 = north wind)
        wind_speedsame as wind

        @@ -1311,15 +1339,19 @@ sub Weather_CheckOptions {


      - - Attributes + +

      Attributes

        -
      • disable: disables the retrieval of weather data - the timer runs according to schedule, + +
      • disable - disables the retrieval of weather data - the timer runs according to schedule, though no data is requested from the API.
      • readingFnAttributes
      • -
      • 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
      • + +
      • 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

    @@ -1328,7 +1360,7 @@ sub Weather_CheckOptions { =end html =begin html_DE - +

    Weather

      Hinweis: es wird das Perl-Modul JSON benötigt. Mit apt-get install @@ -1345,9 +1377,8 @@ sub Weather_CheckOptions { Eine solche virtuelle Wetterstation sammelt periodisch aktuelle Wetterdaten und Wettervorhersagen aus dem verwendeten API.

      - - - Define

      + +

      Define



        define <name> Weather [API=<API>[,<apioptions>]] [apikey=<apikey>] [location=<location>] [interval=<interval>] [lang=<lang>]

        @@ -1383,7 +1414,7 @@ sub Weather_CheckOptions { Es folgt die API-spezifische Dokumentation.

        - Dark Sky

        +

        Dark Sky

        @@ -1397,7 +1428,7 @@ sub Weather_CheckOptions {
        APIDarkSkyAPI

        - OpenWeatherMap

        +

        OpenWeatherMap

        @@ -1418,7 +1449,7 @@ sub Weather_CheckOptions {
        APIOpenWeatherMapAPI

        - Wunderground

        +

        Wunderground

        @@ -1455,55 +1486,76 @@ sub Weather_CheckOptions {
        - - Set + +

        Set

          -
        • - set <name> update

          - - Erzwingt eine Abfrage der Wetterdaten. Die darauffolgende Abfrage - wird gemäß dem eingestellten - Intervall interval Sekunden später durchgeführt. + +
        • set <name> update

          + Erzwingt eine Abfrage der Wetterdaten. Die darauffolgende Abfrage + wird gemäß dem eingestellten + Intervall interval Sekunden später durchgeführt.
        • +
        • - set <name> newLocation latitude,longitude

          - + set <name> newLocation latitude,longitude

          Gibt die Möglichkeit eine neue temporäre Location zu setzen. Das Wertepaar Latitude Longitude wird durch ein Komma getrennt übergeben. Wird kein Wert mitgegebn (leere Übergabe) wird automatisch die per Definition erkannte Location genommen


        - - - Get + +

        Get

          get <name> <reading>

          Gültige ausgelesene Daten (readings) und ihre Bedeutung (das ? kann einen der Werte 1, 2, 3 , 4 oder 5 annehmen und steht für heute, morgen, übermorgen etc.):

        APIwundergroundAPI
        + + + + + + + + + + + + + + + + + + + + + + +
        .licenseLizenz des jeweiligen API-Anbieters, sofern vorhanden
        cityName der Stadt, der für die location übermittelt wird
        codeCode für die aktuellen Wetterverhältnisse
        conditionaktuelle Wetterverhältnisse
        current_date_timeZeitstempel der letzten Aktualisierung der Wetterdaten vom Server
        fc?_codeCode für die vorhergesagten Wetterverhältnisse
        fc?_conditionvorhergesagte Wetterverhältnisse
        fc?_day_of_weekWochentag des Tages, der durch ? dargestellt wird
        fc?_high_cvorhergesagte maximale Tagestemperatur in Grad Celsius
        fc?_iconIcon für Vorhersage
        fc?_low_cvorhergesagte niedrigste Tagestemperatur in Grad Celsius
        humiditygegenwärtige Luftfeuchtgkeit in %
        iconrelativer Pfad für das aktuelle Icon
        pressureLuftdruck in hPa
        temperaturegegenwärtige Temperatur in Grad Celsius
        temp_cgegenwärtige Temperatur in Grad Celsius
        temp_fgegenwärtige Temperatur in Grad Celsius
        visibilitySichtweite in km
        windWindgeschwindigkeit in km/h
        wind_conditionWindrichtung und -geschwindigkeit
        wind_directionGradangabe der Windrichtung (0 = Nordwind)
        wind_speedWindgeschwindigkeit in km/h (mit wind identisch)
        validitystale, wenn der Veröffentlichungszeitpunkt auf dem entfernten Server vor dem Zeitpunkt der aktuellen Daten (readings) liegt

        @@ -1515,16 +1567,20 @@ sub Weather_CheckOptions {


      - - Attribute + +

      Attribute

        -
      • disable: stellt die Abfrage der Wetterdaten ab - der Timer läft + +
      • disable - stellt die Abfrage der Wetterdaten ab - der Timer läft gemäß Plan doch es werden keine Daten vom API angefordert.
      • readingFnAttributes
      • -
      • 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.
      • + +
      • 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/controls_Weather.txt b/controls_Weather.txt index f8b3466..31a21f0 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2023-01-05_18:36:31 55320 FHEM/59_Weather.pm +UPD 2023-01-07_13:19:38 57768 FHEM/59_Weather.pm UPD 2023-01-04_16:37:24 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2023-01-05_19:39:00 33259 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2023-01-05_19:42:34 33259 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm UPD 2023-01-04_16:37:24 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/hooks/commit-msg b/hooks/commit-msg new file mode 100755 index 0000000..ae9eb2c --- /dev/null +++ b/hooks/commit-msg @@ -0,0 +1,36 @@ +#!/bin/sh +# +# An example hook script to check the commit log message. +# Called by "git commit" with one argument, the name of the file +# that has the commit message. The hook should exit with non-zero +# status after issuing an appropriate message if it wants to stop the +# commit. The hook is allowed to edit the commit message file. +# +# To enable this hook, rename this file to "commit-msg". + +# Uncomment the below to add a Signed-off-by line to the message. +# Doing this in a hook is a bad idea in general, but the prepare-commit-msg +# hook is more suited to it. +# +# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') +# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" + +# This example catches duplicate Signed-off-by lines. + +commit_msg=$(cat "${1:?Missing commit message file}") + +test "" = "$(grep '^Signed-off-by: ' "$1" | + sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { + echo >&2 Duplicate Signed-off-by lines. + exit 1 +} + +if ! echo "$commit_msg" | grep -Eq "^(build|chore|ci|docs|feat|feat!|fix|perf|refactor|revert|style|test)(\(.+\))?: .*$" ; then + + echo "Invalid commit message" + + exit 1 + +fi + +echo "Commit message is valid!" diff --git a/hooks/post-commit b/hooks/post-commit new file mode 100755 index 0000000..bf571f5 --- /dev/null +++ b/hooks/post-commit @@ -0,0 +1,18 @@ +#!/bin/bash +set -eu + +# destination of the final changelog file +OUTPUT_FILE=CHANGELOG.md + +# generate the changelog +git --no-pager log --no-merges --format="### %s%d%n>%aD%n%n>Author: %aN (%aE)%n%n>Commiter: %cN (%cE)%n%n%b%n%N%n" > $OUTPUT_FILE + +# prevent recursion! +# since a 'commit --amend' will trigger the post-commit script again +# we have to check if the changelog file has changed or not +res=$(git status --porcelain | grep -c ".\$OUTPUT_FILE$") +if [ "$res" -gt 0 ]; then + git add $OUTPUT_FILE + git commit --amend + echo "Populated Changelog in $OUTPUT_FILE" +fi From f3d621a88f3669481a2d00feb4cecc6f7a3c415d Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 7 Jan 2023 13:33:17 +0100 Subject: [PATCH 47/48] style: change version change version numbers --- CHANGELOG.md | 1463 ++++++++++++++++++++ FHEM/59_Weather.pm | 2 +- controls_Weather.txt | 8 +- lib/FHEM/APIs/Weather/DarkSkyAPI.pm | 2 +- lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | 2 +- lib/FHEM/APIs/Weather/wundergroundAPI.pm | 2 +- 6 files changed, 1471 insertions(+), 8 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d2c92db --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,1463 @@ +### docs: change commandref (HEAD -> patch-rewriteCommandref) +>Sat, 7 Jan 2023 13:21:27 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +change commandref with id tags + + + +### fix uninitialized value in multiplication +>Thu, 5 Jan 2023 19:40:21 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +when wind_guest not given + + + +### change old snow and rain value to zero +>Thu, 5 Jan 2023 17:02:46 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### add hook example add support for onecall endpoint with api version 2.5 +>Thu, 5 Jan 2023 08:18:05 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix regex for sun and moon set and rise +>Mon, 2 Jan 2023 09:27:41 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix Undefined subroutine +>Fri, 30 Dec 2022 13:17:40 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +Undefined subroutine &FHEM::APIs::Weather::wundergroundAPI::strftimeWrapper + + + +### fix +>Fri, 30 Dec 2022 12:17:37 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix sub +>Fri, 30 Dec 2022 12:14:33 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix undefined value as an ARRAY reference +>Fri, 30 Dec 2022 12:11:19 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix some regex formating +>Thu, 29 Dec 2022 04:41:10 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### change versions and add control file +>Wed, 28 Dec 2022 09:24:12 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### remove celvin substration +>Wed, 28 Dec 2022 09:10:14 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix "PERL WARNING: Use of uninitialized value" +>Sun, 25 Dec 2022 09:20:58 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +no {wind}->{gust} field in $data + + + +### fix undefined value +>Sat, 24 Dec 2022 12:57:41 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +Can't use an undefined value as an ARRAY reference at ./FHEM/59_Weather.pm line 589 + + + +### change versions +>Sat, 24 Dec 2022 10:20:09 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### move and rename API.de API description +>Sat, 24 Dec 2022 04:48:42 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +change README.m API.de description + + + +### add directory structure move API files remove YahooWeatherAPI +>Sat, 24 Dec 2022 04:39:45 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### move API files to lib/FHEM/APIs/Weather +>Thu, 22 Dec 2022 18:45:07 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix forecast number for weblink +>Wed, 21 Dec 2022 09:06:54 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### expand notify fn grep syntax +>Tue, 20 Dec 2022 18:42:36 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### change code for better def modify +>Tue, 20 Dec 2022 18:04:45 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix IsDisabled +>Tue, 20 Dec 2022 15:29:49 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +change condition for is disabled in GetUpdate fn + + + +### change versions of API modules add Copyright year range +>Tue, 20 Dec 2022 14:43:12 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix remove old data +>Tue, 20 Dec 2022 13:24:15 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix Weather_CheckOptions func +>Tue, 20 Dec 2022 12:53:44 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### add warnCount reading +>Mon, 19 Dec 2022 15:29:06 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix negativ integer value round fix alerts integration +>Mon, 19 Dec 2022 11:24:27 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix onecall update, remove weather endpoint +>Sun, 18 Dec 2022 10:58:24 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### change wind_chill to decimal +>Wed, 14 Dec 2022 21:35:21 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### change pressure value to decimal +>Wed, 14 Dec 2022 20:42:07 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix integer declaration for temperature values +>Wed, 14 Dec 2022 20:10:58 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### Subject line (try to keep under 50 characters) +>Wed, 14 Dec 2022 19:51:18 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +Multi-line description of commit, +feel free to be detailed. + +[Ticket: X] + + + +### fix older entrys +>Wed, 14 Dec 2022 19:24:14 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### add numericPrecision=decimal option +>Wed, 14 Dec 2022 16:21:14 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix delete Readings +>Wed, 14 Dec 2022 11:26:32 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix delete Reading Counter +>Wed, 14 Dec 2022 11:08:36 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix forecast exclude and change commandref +>Wed, 14 Dec 2022 10:38:04 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix empty deklaration +>Tue, 13 Dec 2022 19:04:01 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### fix litte bugs in API modul +>Tue, 13 Dec 2022 14:00:46 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +add first code for daily and hourly forcast delete count + + + +### better formart +>Sat, 26 Nov 2022 06:43:20 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### remove double dewpoint reading +>Fri, 25 Nov 2022 20:02:39 +0100 + +>Author: Marko Oldenburg (oldenburg@b1-systems.de) + +>Commiter: Marko Oldenburg (oldenburg@b1-systems.de) + + + + +### full api support weather and onecall +>Fri, 25 Nov 2022 14:09:45 +0100 + +>Author: Marko Oldenburg (oldenburg@b1-systems.de) + +>Commiter: Marko Oldenburg (oldenburg@b1-systems.de) + + + + +### change OpenWeatherMapAPI Code and extend 59_Weather.pm Modul +>Thu, 24 Nov 2022 19:22:40 +0100 + +>Author: Marko Oldenburg (oldenburg@b1-systems.de) + +>Commiter: Marko Oldenburg (oldenburg@b1-systems.de) + + + + +### new OpenWeatherMapAPI onecall v3 Support +>Sun, 20 Nov 2022 21:10:22 +0100 + +>Author: Marko Oldenburg (oldenburg@b1-systems.de) + +>Commiter: Marko Oldenburg (oldenburg@b1-systems.de) + + + + +### new API Call and Data +>Thu, 17 Nov 2022 19:58:56 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + + + + +### add Onecall to Endpoint +>Sat, 12 Feb 2022 12:56:04 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +for daily output + +[Ticket: no] + + + +### new version +>Wed, 9 Jun 2021 20:37:02 +0200 + +>Author: Marko Oldenburg (marko.oldenburg@cooltux.net) + +>Commiter: Marko Oldenburg (marko.oldenburg@cooltux.net) + + + + +### add wind_gust +>Wed, 9 Jun 2021 20:30:35 +0200 + +>Author: Marko Oldenburg (marko.oldenburg@cooltux.net) + +>Commiter: Marko Oldenburg (marko.oldenburg@cooltux.net) + + + + +### change Maintainer name from pseudo name to real name +>Sat, 30 Jan 2021 18:41:41 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### insert forcast attributs to english commandref +>Sat, 17 Oct 2020 14:40:20 +0200 + +>Author: Marko Oldenburg (marko.oldenburg@cooltux.net) + +>Commiter: Marko Oldenburg (marko.oldenburg@cooltux.net) + + + + +### code style +>Sat, 25 Apr 2020 08:51:34 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix no visibility is available +>Sat, 25 Apr 2020 08:49:56 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix Use of uninitialized value in sprintf +>Wed, 22 Apr 2020 09:18:12 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add wunderground documentation in commandref +>Tue, 4 Feb 2020 14:09:47 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### delete table options in commandref +>Fri, 6 Dec 2019 13:12:35 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### check newLocation insert +>Mon, 16 Sep 2019 08:16:55 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add wundergroundAPI for setter newLocation +>Fri, 13 Sep 2019 11:03:44 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add commandref entry for set command newLocation +>Wed, 11 Sep 2019 09:00:13 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add support fpr Meta.pm and little Code changes +>Wed, 11 Sep 2019 08:42:42 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add Meta.pm Support +>Wed, 11 Sep 2019 08:20:28 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add support for set weather newLocation, temorary location change +>Wed, 11 Sep 2019 08:19:31 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change version numbers +>Wed, 11 Sep 2019 08:02:08 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change code in SetFn, change delimiter for set cmd newLocation +>Tue, 10 Sep 2019 10:50:30 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add setter locationTemp to change location temporary +>Mon, 9 Sep 2019 17:35:59 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add setter locationTemp to change location temporary +>Mon, 9 Sep 2019 17:35:35 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### Bugfix forum #971394 +>Mon, 2 Sep 2019 13:43:53 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### Remove SVN data +>Sun, 14 Jul 2019 13:48:03 +0200 + +>Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + + + + +### fix default value for units +>Sun, 14 Jul 2019 13:47:28 +0200 + +>Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + + + + +### change loglevel +>Sat, 15 Jun 2019 09:45:03 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### try to use JSON::MaybeXS wrapper for chance of better performance + open code +>Sat, 15 Jun 2019 09:42:07 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add more logout, code stype, add LICENSE +>Sat, 15 Jun 2019 09:36:11 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix README.md +>Fri, 7 Jun 2019 21:27:41 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### Update to version v1.0.0 +>Fri, 7 Jun 2019 21:04:21 +0200 + +>Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + +night/day forecasts for 5 days now implemented using hourly readings hfcX_ (misleading but no other option ...) + + +### little gugfix +>Tue, 4 Jun 2019 22:12:19 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add more Information to README.md +>Tue, 4 Jun 2019 21:34:33 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add more Information to README.md +>Tue, 4 Jun 2019 21:32:38 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add README.md file +>Tue, 4 Jun 2019 21:29:03 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### Create wundergroundAPI.pm +>Tue, 4 Jun 2019 17:23:16 +0200 + +>Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + + + + +### fix little bug in weblink creator +>Tue, 14 May 2019 13:49:34 +0200 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add sub for apioptions, add darksky api extend option +>Tue, 19 Mar 2019 21:35:59 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change version +>Mon, 18 Mar 2019 13:40:08 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change translation for nl +>Mon, 18 Mar 2019 13:26:00 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### rewrite options sub for weblink +>Mon, 18 Mar 2019 13:17:55 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix bugs in weblink and expand hourly forcast +>Mon, 18 Mar 2019 10:31:41 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change package deklaration +>Fri, 15 Mar 2019 21:41:32 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix umlaute problem +>Thu, 14 Mar 2019 18:40:42 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### suchen und ersetzen der Sonderzeichen +>Thu, 14 Mar 2019 14:34:59 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add contributors +>Thu, 14 Mar 2019 08:43:14 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### Update 59_Weather.pm +>Wed, 13 Mar 2019 07:00:45 +0100 + +>Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + +Bugfix zum letzten merge meines patches: +in sub WeatherAsHtmlH($;$$) fehlte: + $f =~ tr/dh/./cd; + $f = "h" if ( !$f || length($f) > 1); + $items =~ tr/0-9/./cd; + $items = 6 if ( !$items ); + + +### Update 59_Weather.pm +>Tue, 12 Mar 2019 21:07:58 +0100 + +>Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + +Paramater 2 und 3 werden automatisch dem zugehörigen internen Parameter Anzahl oder daily/hourly zugeordnet. +Damit ist die Reihenfolge beim Aufruf frei wählbar und beide Parameter können beim Aufruf beliebig weggelassen werden. + + +### Update 59_Weather.pm +>Tue, 12 Mar 2019 06:46:11 +0100 + +>Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + +Bugfix: +761: ReadingsVal( $d, "${fc}${i}_day_of_week", "" ),
      %s =>Das
      %s gehört da nicht hin. +In Zeile 763 und 773 fehlt ein Komma als Zeilenabschluss. + + +### Update 59_Weather.pm +>Thu, 7 Mar 2019 22:34:49 +0100 + +>Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + +Aktualisierung des Änderungsvorschlags wie besprochen: +- WeatherAsHtml() haben jetzt alle die gleiche Schnittstelle ($d, $f , $items)und sind damit abwärtskompatibel entsprechend Doku (ebenfalls angepasst) +Zur Absicherung der optionalen Parameter in WeatherAsHtml(): Filter auf die erlaubten Zeichen und setzen eines defaultwertes, falls der Parameter leer ist. Eine Abfrage auf defined() ist nicht notwendig, habe alle möglichen Eingabekombinationen abgeprüft. +In WeatherAsHtmlH($;$$) wird, wie vereinbart, _low_c und _high_c nur angezeigt, wenn die Readings vorhanden sind, ansonsten wird _temperature verwendet. +Gleiches habe ich in WeatherAsHtmlV($;$$) angepasst. +Die Änderungen laufen bei mir mit DarkSkyAPI und OpenWeatherMapAPI einwandfrei. Aussehen habe ich ebenfalls gecheckt. + +Beste Grüße +Lippie + + +### little change +>Tue, 5 Mar 2019 21:01:23 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### Update WeatherAsHtml +>Tue, 5 Mar 2019 20:24:24 +0100 + +>Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) + +>Commiter: GitHub (noreply@github.com) + +- alle WeatherAsHtml-functionen auf $d, $items, $i gleichgestellt +- Abfrage in WeatherAsHtmlH, ob *fc(i)_low_c vorhanden, wenn nicht Verwendung von *fc(i)_temperature +- Erweiterung der Beispiels um Anzahl und daily/hourly-Angabe + + +### Fixed an minor typo +>Tue, 5 Mar 2019 18:02:06 +0100 + +>Author: Christoph Morrison (post@christoph-jeschke.de) + +>Commiter: Christoph Morrison (post@christoph-jeschke.de) + + + + +### Added markdown version of the german API description +>Tue, 5 Mar 2019 18:01:13 +0100 + +>Author: Christoph Morrison (post@christoph-jeschke.de) + +>Commiter: Christoph Morrison (post@christoph-jeschke.de) + + + + +### add code to use demo data up to start +>Mon, 4 Mar 2019 21:34:17 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### little code changing +>Wed, 27 Feb 2019 08:23:28 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change version +>Wed, 27 Feb 2019 07:55:46 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add DEMO Support +>Wed, 27 Feb 2019 07:53:29 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix bug then month are umlauts +>Tue, 26 Feb 2019 12:54:09 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### typo fix in commandref +>Wed, 23 Jan 2019 07:37:52 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change commandref +>Mon, 21 Jan 2019 11:53:50 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add configuration forecast data +>Mon, 21 Jan 2019 11:41:33 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add forecastLimit and forecast Attribut +>Mon, 21 Jan 2019 08:49:38 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add internal MODEL for statistic +>Fri, 18 Jan 2019 21:07:48 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### remove attribut mode +>Fri, 18 Jan 2019 20:21:51 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix change attribut model +>Fri, 18 Jan 2019 19:08:07 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add note for day_of_week tranlation +>Fri, 18 Jan 2019 14:07:50 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add apiVersion to API Documentation +>Fri, 18 Jan 2019 10:29:28 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix readingupdate +>Thu, 17 Jan 2019 22:22:44 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### code style, add attribut model +>Thu, 17 Jan 2019 11:49:06 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix uninitialized value in localtime at FHEM/DarkSkyAPI.pm line 430 +>Wed, 16 Jan 2019 23:05:41 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add snow and rain for the last 1 and/or 3 hour +>Tue, 15 Jan 2019 14:51:50 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### neue Datei +>Tue, 15 Jan 2019 13:49:27 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add 59_Weather API-Modul Documentation, fix setreading line +>Tue, 15 Jan 2019 13:45:31 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix bugs, change day_of_week formated +>Mon, 14 Jan 2019 18:16:39 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add factor 3.6 to windGust +>Mon, 14 Jan 2019 10:50:35 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change wind speed to factor 3.6 for hourly +>Mon, 14 Jan 2019 05:53:43 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change formated output, add hourly support for DarkSky - thanks to Lippie, multiple factor to wind speed and many bugfix +>Sun, 13 Jan 2019 21:13:54 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix table commandref +>Sun, 13 Jan 2019 10:30:37 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix table bug +>Sun, 13 Jan 2019 10:22:39 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix more typo in commandref +>Sun, 13 Jan 2019 09:37:01 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### expand error handling, fix typo in commandref +>Sun, 13 Jan 2019 09:22:28 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix negative tagcount for table +>Sat, 12 Jan 2019 16:35:03 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### style code +>Sat, 12 Jan 2019 16:20:31 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### added $Id tag for subversion +>Sat, 12 Jan 2019 15:02:53 +0100 + +>Author: Dr. Boris Neubert (omega@online.de) + +>Commiter: Dr. Boris Neubert (omega@online.de) + + + + +### removed dead or deprecated code, updated commandref +>Sat, 12 Jan 2019 15:00:52 +0100 + +>Author: Dr. Boris Neubert (omega@online.de) + +>Commiter: Dr. Boris Neubert (omega@online.de) + + + + +### change and expand the commandref for 59_Weather +>Sat, 12 Jan 2019 11:54:39 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix weblink bug by hourly forcast +>Sat, 12 Jan 2019 08:18:52 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change day_of_week processing +>Sat, 12 Jan 2019 07:34:54 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change day_of_week language handling +>Fri, 11 Jan 2019 08:47:13 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix error in empty Hash check +>Thu, 10 Jan 2019 22:35:57 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change license reading +>Thu, 10 Jan 2019 14:58:51 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### forcast Datastructure change to HashRef daily and hourly +>Thu, 10 Jan 2019 14:08:31 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change hashRef for parameter from cachemaxage to apioptions +>Thu, 10 Jan 2019 10:55:03 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### publish forecast data in OpenWeatherMap, code change in 59_Weather, change response data structure for Weather Modul +>Thu, 10 Jan 2019 09:30:26 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add daily and hourly support code for WOM, fix bug Readingvalue HASH +>Thu, 10 Jan 2019 00:11:19 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### add yahoo icon mapping +>Wed, 9 Jan 2019 13:42:27 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change dumper output +>Wed, 9 Jan 2019 13:14:06 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### change data handle for callBackFn - wichtig alle Module Updaten +>Wed, 9 Jan 2019 12:55:21 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### fix write Hash then ref current not present +>Wed, 9 Jan 2019 12:17:10 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + +### first commit +>Wed, 9 Jan 2019 10:37:55 +0100 + +>Author: Marko Oldenburg (leongaultier@gmail.com) + +>Commiter: Marko Oldenburg (leongaultier@gmail.com) + + + + diff --git a/FHEM/59_Weather.pm b/FHEM/59_Weather.pm index 22b8925..97a276c 100755 --- a/FHEM/59_Weather.pm +++ b/FHEM/59_Weather.pm @@ -1606,7 +1606,7 @@ sub Weather_CheckOptions { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.2.15", + "version": "v2.2.20", "author": [ "Marko Oldenburg " ], diff --git a/controls_Weather.txt b/controls_Weather.txt index 31a21f0..a4ed2a8 100644 --- a/controls_Weather.txt +++ b/controls_Weather.txt @@ -1,4 +1,4 @@ -UPD 2023-01-07_13:19:38 57768 FHEM/59_Weather.pm -UPD 2023-01-04_16:37:24 49883 lib/FHEM/APIs/Weather/DarkSkyAPI.pm -UPD 2023-01-05_19:42:34 33259 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm -UPD 2023-01-04_16:37:24 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm +UPD 2023-01-07_13:28:16 57768 FHEM/59_Weather.pm +UPD 2023-01-07_13:28:44 49884 lib/FHEM/APIs/Weather/DarkSkyAPI.pm +UPD 2023-01-07_13:28:59 33258 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +UPD 2023-01-07_13:29:12 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm diff --git a/lib/FHEM/APIs/Weather/DarkSkyAPI.pm b/lib/FHEM/APIs/Weather/DarkSkyAPI.pm index 9f9fa22..72c6447 100644 --- a/lib/FHEM/APIs/Weather/DarkSkyAPI.pm +++ b/lib/FHEM/APIs/Weather/DarkSkyAPI.pm @@ -773,7 +773,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für Weather DarkSky" } }, - "version": "v1.2.1", + "version": "v1.2.10", "author": [ "Marko Oldenburg " ], diff --git a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm index ac9070e..838780c 100644 --- a/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm +++ b/lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm @@ -1024,7 +1024,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für OpenWeatherMap" } }, - "version": "v3.0.15", + "version": "v3.2.5", "author": [ "Marko Oldenburg " ], diff --git a/lib/FHEM/APIs/Weather/wundergroundAPI.pm b/lib/FHEM/APIs/Weather/wundergroundAPI.pm index 9b95fac..8ffe716 100644 --- a/lib/FHEM/APIs/Weather/wundergroundAPI.pm +++ b/lib/FHEM/APIs/Weather/wundergroundAPI.pm @@ -805,7 +805,7 @@ sub _strftimeWrapper { "abstract": "Wetter API für Weather Underground" } }, - "version": "v1.0.3", + "version": "v1.2.0", "author": [ "Julian Pawlowski " ], From 73ec2f52c7e15b134138b671d1a47b02b4188f5e Mon Sep 17 00:00:00 2001 From: Marko Oldenburg Date: Sat, 7 Jan 2023 13:39:03 +0100 Subject: [PATCH 48/48] feat: v2.2.20 out new version is out, Release 2.2.20 --- CHANGELOG.md | 1006 +------------------------------------------------- 1 file changed, 13 insertions(+), 993 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2c92db..c5e7151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ -### docs: change commandref (HEAD -> patch-rewriteCommandref) +### style: change version (HEAD -> dev) +>Sat, 7 Jan 2023 13:33:17 +0100 + +>Author: Marko Oldenburg (fhemdevelopment@cooltux.net) + +>Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) + +change version numbers + + + +### docs: change commandref >Sat, 7 Jan 2023 13:21:27 +0100 >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) @@ -469,995 +480,4 @@ add first code for daily and hourly forcast delete count for daily output -[Ticket: no] - - - -### new version ->Wed, 9 Jun 2021 20:37:02 +0200 - ->Author: Marko Oldenburg (marko.oldenburg@cooltux.net) - ->Commiter: Marko Oldenburg (marko.oldenburg@cooltux.net) - - - - -### add wind_gust ->Wed, 9 Jun 2021 20:30:35 +0200 - ->Author: Marko Oldenburg (marko.oldenburg@cooltux.net) - ->Commiter: Marko Oldenburg (marko.oldenburg@cooltux.net) - - - - -### change Maintainer name from pseudo name to real name ->Sat, 30 Jan 2021 18:41:41 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### insert forcast attributs to english commandref ->Sat, 17 Oct 2020 14:40:20 +0200 - ->Author: Marko Oldenburg (marko.oldenburg@cooltux.net) - ->Commiter: Marko Oldenburg (marko.oldenburg@cooltux.net) - - - - -### code style ->Sat, 25 Apr 2020 08:51:34 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix no visibility is available ->Sat, 25 Apr 2020 08:49:56 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix Use of uninitialized value in sprintf ->Wed, 22 Apr 2020 09:18:12 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add wunderground documentation in commandref ->Tue, 4 Feb 2020 14:09:47 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### delete table options in commandref ->Fri, 6 Dec 2019 13:12:35 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### check newLocation insert ->Mon, 16 Sep 2019 08:16:55 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add wundergroundAPI for setter newLocation ->Fri, 13 Sep 2019 11:03:44 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add commandref entry for set command newLocation ->Wed, 11 Sep 2019 09:00:13 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add support fpr Meta.pm and little Code changes ->Wed, 11 Sep 2019 08:42:42 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add Meta.pm Support ->Wed, 11 Sep 2019 08:20:28 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add support for set weather newLocation, temorary location change ->Wed, 11 Sep 2019 08:19:31 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change version numbers ->Wed, 11 Sep 2019 08:02:08 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change code in SetFn, change delimiter for set cmd newLocation ->Tue, 10 Sep 2019 10:50:30 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add setter locationTemp to change location temporary ->Mon, 9 Sep 2019 17:35:59 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add setter locationTemp to change location temporary ->Mon, 9 Sep 2019 17:35:35 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### Bugfix forum #971394 ->Mon, 2 Sep 2019 13:43:53 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### Remove SVN data ->Sun, 14 Jul 2019 13:48:03 +0200 - ->Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - - - - -### fix default value for units ->Sun, 14 Jul 2019 13:47:28 +0200 - ->Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - - - - -### change loglevel ->Sat, 15 Jun 2019 09:45:03 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### try to use JSON::MaybeXS wrapper for chance of better performance + open code ->Sat, 15 Jun 2019 09:42:07 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add more logout, code stype, add LICENSE ->Sat, 15 Jun 2019 09:36:11 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix README.md ->Fri, 7 Jun 2019 21:27:41 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### Update to version v1.0.0 ->Fri, 7 Jun 2019 21:04:21 +0200 - ->Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - -night/day forecasts for 5 days now implemented using hourly readings hfcX_ (misleading but no other option ...) - - -### little gugfix ->Tue, 4 Jun 2019 22:12:19 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add more Information to README.md ->Tue, 4 Jun 2019 21:34:33 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add more Information to README.md ->Tue, 4 Jun 2019 21:32:38 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add README.md file ->Tue, 4 Jun 2019 21:29:03 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### Create wundergroundAPI.pm ->Tue, 4 Jun 2019 17:23:16 +0200 - ->Author: Julian Pawlowski (jpawlowski@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - - - - -### fix little bug in weblink creator ->Tue, 14 May 2019 13:49:34 +0200 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add sub for apioptions, add darksky api extend option ->Tue, 19 Mar 2019 21:35:59 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change version ->Mon, 18 Mar 2019 13:40:08 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change translation for nl ->Mon, 18 Mar 2019 13:26:00 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### rewrite options sub for weblink ->Mon, 18 Mar 2019 13:17:55 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix bugs in weblink and expand hourly forcast ->Mon, 18 Mar 2019 10:31:41 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change package deklaration ->Fri, 15 Mar 2019 21:41:32 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix umlaute problem ->Thu, 14 Mar 2019 18:40:42 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### suchen und ersetzen der Sonderzeichen ->Thu, 14 Mar 2019 14:34:59 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add contributors ->Thu, 14 Mar 2019 08:43:14 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### Update 59_Weather.pm ->Wed, 13 Mar 2019 07:00:45 +0100 - ->Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - -Bugfix zum letzten merge meines patches: -in sub WeatherAsHtmlH($;$$) fehlte: - $f =~ tr/dh/./cd; - $f = "h" if ( !$f || length($f) > 1); - $items =~ tr/0-9/./cd; - $items = 6 if ( !$items ); - - -### Update 59_Weather.pm ->Tue, 12 Mar 2019 21:07:58 +0100 - ->Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - -Paramater 2 und 3 werden automatisch dem zugehörigen internen Parameter Anzahl oder daily/hourly zugeordnet. -Damit ist die Reihenfolge beim Aufruf frei wählbar und beide Parameter können beim Aufruf beliebig weggelassen werden. - - -### Update 59_Weather.pm ->Tue, 12 Mar 2019 06:46:11 +0100 - ->Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - -Bugfix: -761: ReadingsVal( $d, "${fc}${i}_day_of_week", "" ),
      %s =>Das
      %s gehört da nicht hin. -In Zeile 763 und 773 fehlt ein Komma als Zeilenabschluss. - - -### Update 59_Weather.pm ->Thu, 7 Mar 2019 22:34:49 +0100 - ->Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - -Aktualisierung des Änderungsvorschlags wie besprochen: -- WeatherAsHtml() haben jetzt alle die gleiche Schnittstelle ($d, $f , $items)und sind damit abwärtskompatibel entsprechend Doku (ebenfalls angepasst) -Zur Absicherung der optionalen Parameter in WeatherAsHtml(): Filter auf die erlaubten Zeichen und setzen eines defaultwertes, falls der Parameter leer ist. Eine Abfrage auf defined() ist nicht notwendig, habe alle möglichen Eingabekombinationen abgeprüft. -In WeatherAsHtmlH($;$$) wird, wie vereinbart, _low_c und _high_c nur angezeigt, wenn die Readings vorhanden sind, ansonsten wird _temperature verwendet. -Gleiches habe ich in WeatherAsHtmlV($;$$) angepasst. -Die Änderungen laufen bei mir mit DarkSkyAPI und OpenWeatherMapAPI einwandfrei. Aussehen habe ich ebenfalls gecheckt. - -Beste Grüße -Lippie - - -### little change ->Tue, 5 Mar 2019 21:01:23 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### Update WeatherAsHtml ->Tue, 5 Mar 2019 20:24:24 +0100 - ->Author: Lippie81 (46738685+Lippie81@users.noreply.github.com) - ->Commiter: GitHub (noreply@github.com) - -- alle WeatherAsHtml-functionen auf $d, $items, $i gleichgestellt -- Abfrage in WeatherAsHtmlH, ob *fc(i)_low_c vorhanden, wenn nicht Verwendung von *fc(i)_temperature -- Erweiterung der Beispiels um Anzahl und daily/hourly-Angabe - - -### Fixed an minor typo ->Tue, 5 Mar 2019 18:02:06 +0100 - ->Author: Christoph Morrison (post@christoph-jeschke.de) - ->Commiter: Christoph Morrison (post@christoph-jeschke.de) - - - - -### Added markdown version of the german API description ->Tue, 5 Mar 2019 18:01:13 +0100 - ->Author: Christoph Morrison (post@christoph-jeschke.de) - ->Commiter: Christoph Morrison (post@christoph-jeschke.de) - - - - -### add code to use demo data up to start ->Mon, 4 Mar 2019 21:34:17 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### little code changing ->Wed, 27 Feb 2019 08:23:28 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change version ->Wed, 27 Feb 2019 07:55:46 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add DEMO Support ->Wed, 27 Feb 2019 07:53:29 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix bug then month are umlauts ->Tue, 26 Feb 2019 12:54:09 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### typo fix in commandref ->Wed, 23 Jan 2019 07:37:52 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change commandref ->Mon, 21 Jan 2019 11:53:50 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add configuration forecast data ->Mon, 21 Jan 2019 11:41:33 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add forecastLimit and forecast Attribut ->Mon, 21 Jan 2019 08:49:38 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add internal MODEL for statistic ->Fri, 18 Jan 2019 21:07:48 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### remove attribut mode ->Fri, 18 Jan 2019 20:21:51 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix change attribut model ->Fri, 18 Jan 2019 19:08:07 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add note for day_of_week tranlation ->Fri, 18 Jan 2019 14:07:50 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add apiVersion to API Documentation ->Fri, 18 Jan 2019 10:29:28 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix readingupdate ->Thu, 17 Jan 2019 22:22:44 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### code style, add attribut model ->Thu, 17 Jan 2019 11:49:06 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix uninitialized value in localtime at FHEM/DarkSkyAPI.pm line 430 ->Wed, 16 Jan 2019 23:05:41 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add snow and rain for the last 1 and/or 3 hour ->Tue, 15 Jan 2019 14:51:50 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### neue Datei ->Tue, 15 Jan 2019 13:49:27 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add 59_Weather API-Modul Documentation, fix setreading line ->Tue, 15 Jan 2019 13:45:31 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix bugs, change day_of_week formated ->Mon, 14 Jan 2019 18:16:39 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add factor 3.6 to windGust ->Mon, 14 Jan 2019 10:50:35 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change wind speed to factor 3.6 for hourly ->Mon, 14 Jan 2019 05:53:43 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change formated output, add hourly support for DarkSky - thanks to Lippie, multiple factor to wind speed and many bugfix ->Sun, 13 Jan 2019 21:13:54 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix table commandref ->Sun, 13 Jan 2019 10:30:37 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix table bug ->Sun, 13 Jan 2019 10:22:39 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix more typo in commandref ->Sun, 13 Jan 2019 09:37:01 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### expand error handling, fix typo in commandref ->Sun, 13 Jan 2019 09:22:28 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix negative tagcount for table ->Sat, 12 Jan 2019 16:35:03 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### style code ->Sat, 12 Jan 2019 16:20:31 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### added $Id tag for subversion ->Sat, 12 Jan 2019 15:02:53 +0100 - ->Author: Dr. Boris Neubert (omega@online.de) - ->Commiter: Dr. Boris Neubert (omega@online.de) - - - - -### removed dead or deprecated code, updated commandref ->Sat, 12 Jan 2019 15:00:52 +0100 - ->Author: Dr. Boris Neubert (omega@online.de) - ->Commiter: Dr. Boris Neubert (omega@online.de) - - - - -### change and expand the commandref for 59_Weather ->Sat, 12 Jan 2019 11:54:39 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix weblink bug by hourly forcast ->Sat, 12 Jan 2019 08:18:52 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change day_of_week processing ->Sat, 12 Jan 2019 07:34:54 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change day_of_week language handling ->Fri, 11 Jan 2019 08:47:13 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix error in empty Hash check ->Thu, 10 Jan 2019 22:35:57 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change license reading ->Thu, 10 Jan 2019 14:58:51 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### forcast Datastructure change to HashRef daily and hourly ->Thu, 10 Jan 2019 14:08:31 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change hashRef for parameter from cachemaxage to apioptions ->Thu, 10 Jan 2019 10:55:03 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### publish forecast data in OpenWeatherMap, code change in 59_Weather, change response data structure for Weather Modul ->Thu, 10 Jan 2019 09:30:26 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add daily and hourly support code for WOM, fix bug Readingvalue HASH ->Thu, 10 Jan 2019 00:11:19 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### add yahoo icon mapping ->Wed, 9 Jan 2019 13:42:27 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change dumper output ->Wed, 9 Jan 2019 13:14:06 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### change data handle for callBackFn - wichtig alle Module Updaten ->Wed, 9 Jan 2019 12:55:21 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### fix write Hash then ref current not present ->Wed, 9 Jan 2019 12:17:10 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - -### first commit ->Wed, 9 Jan 2019 10:37:55 +0100 - ->Author: Marko Oldenburg (leongaultier@gmail.com) - ->Commiter: Marko Oldenburg (leongaultier@gmail.com) - - - - +[Ticket: no] \ No newline at end of file