From 18fb5aaada6255515f06aa8c4029cd3ce956ce5c Mon Sep 17 00:00:00 2001
From: Marko Oldenburg <fhemdevelopment@cooltux.net>
Date: Thu, 22 Dec 2022 18:45:07 +0100
Subject: [PATCH 01/10] 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 (<a href=https://forum.fhem.de/index.php?action=profile;u=13684>CoolTux</a>)',
-            apiVersion =>
-              version->parse( OpenWeatherMapAPI->VERSION() )->normal,
+            apiVersion => version->parse( __PACKAGE__->VERSION() )->normal,
         }
     );
 

From 4b8aad2047d2847d7861d8228ada052803b2ce77 Mon Sep 17 00:00:00 2001
From: Marko Oldenburg <fhemdevelopment@cooltux.net>
Date: Sat, 24 Dec 2022 04:39:45 +0100
Subject: [PATCH 02/10] 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 (<a href=https://forum.fhem.de/index.php?action=profile;u=13684>CoolTux</a>)',
-            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 <fhemdevelopment@cooltux.net>"
-  ],
-  "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 <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-package main;
-
-use strict;
-use warnings;
-use HttpUtils;
-use JSON;               # apt-get install libperl-JSON on Debian and derivatives
-#use Data::Dumper;       # for Debug only
-
-# Yahoo! Weather API: http://developer.yahoo.com/weather/
-
-use constant URL => "https://query.yahooapis.com/v1/public/yql?q=select%%20*%%20from%%20weather.forecast%%20where%%20woeid=%s%%20and%%20u=%%27c%%27&format=%s&env=store%%3A%%2F%%2Fdatatables.org%%2Falltableswithkeys";
-
-
-# Mapping / translation of current weather codes 0-47
-my @YahooCodes_en = (
-       'tornado', 'tropical storm', 'hurricane', 'severe thunderstorms', 'thunderstorms', 'mixed rain and snow',
-       'mixed rain and sleet', 'mixed snow and sleet', 'freezing drizzle', 'drizzle', 'freezing rain' ,'showers',
-       'showers', 'snow flurries', 'light snow showers', 'blowing snow', 'snow', 'hail',
-       'sleet', 'dust', 'foggy', 'haze', 'smoky', 'blustery',
-       'windy', 'cold', 'cloudy',
-       'mostly cloudy', # night
-       'mostly cloudy', # day
-       'partly cloudy', # night
-       'partly cloudy', # day
-       'clear',
-       'sunny',
-       'fair', #night
-       'fair', #day
-       'mixed rain and hail',
-       'hot', 'isolated thunderstorms', 'scattered thunderstorms', 'scattered thunderstorms', 'scattered showers', 'heavy snow',
-       'scattered snow showers', 'heavy snow', 'partly cloudy', 'thundershowers', 'snow showers', 'isolated thundershowers');
-
-my @YahooCodes_de = (
-       'Tornado', 'schwerer Sturm', 'Orkan', 'schwere Gewitter', 'Gewitter', 'Regen und Schnee',
-       'Regen und Graupel', 'Schnee und Graupel', 'Eisregen', 'Nieselregen', 'gefrierender Regen' ,'Schauer',
-       'Schauer', 'Schneetreiben', 'leichte Schneeschauer', 'Schneeverwehungen', 'Schnee', 'Hagel',
-       'Graupel', 'Staub', 'Nebel', 'Dunst', 'Smog', 'Sturm',
-       'windig', 'kalt', 'wolkig',
-       'überwiegend wolkig', # night
-       'überwiegend wolkig', # day
-       'teilweise wolkig', # night
-       'teilweise wolkig', # day
-       'klar', # night
-       'sonnig',
-       'heiter', # night
-       'heiter', # day
-       'Regen und Hagel',
-       'heiß', 'einzelne Gewitter', 'vereinzelt Gewitter', 'vereinzelt Gewitter', 'vereinzelt Schauer', 'starker Schneefall',
-       'vereinzelt Schneeschauer', 'starker Schneefall', 'teilweise wolkig', 'Gewitterregen', 'Schneeschauer', 'vereinzelt Gewitter');
-
-my @YahooCodes_nl = (
-       'tornado', 'zware storm', 'orkaan', 'hevig onweer', 'onweer',
-       'regen en sneeuw',
-       'regen en ijzel', 'sneeuw en ijzel', 'aanvriezende motregen',
-       'motregen', 'aanvriezende regen' ,'buien',
-       'buien', 'sneeuw windstoten', 'lichte sneeuwbuien',
-       'stuifsneeuw', 'sneeuw', 'hagel',
-       'ijzel', 'stof', 'mist', 'waas', 'smog', 'onstuimig',
-       'winderig', 'koud', 'bewolkt',
-       'overwegend bewolkt', # night
-       'overwegend bewolkt', # day
-       'gedeeltelijk bewolkt', # night
-       'gedeeltelijk bewolkt', # day
-       'helder', #night
-       'zonnig',
-       'mooi', #night
-       'mooi', #day
-       'regen en hagel',
-       'heet', 'plaatselijk onweer', 'af en toe onweer', 'af en toe onweer', 'af en toe regenbuien', 'hevige sneeuwval',
-       'af en toe sneeuwbuien', 'hevige sneeuwval', 'deels bewolkt',
-       'onweersbuien', 'sneeuwbuien', 'af en toe onweersbuien');
-
-my @YahooCodes_fr = (
-       'tornade', 'tempête tropicale', 'ouragan', 'tempête sévère', 'orage', 'pluie et neige',
-       'pluie et grésil', 'neige et grésil', 'bruine verglassante', 'bruine', 'pluie verglassante' ,'averse',
-       'averses', 'tourbillon de neige', 'légères averses de neige', 'rafale de neige', 'neige', 'grêle',
-       'giboulées', 'poussières', 'brouillard', 'brume', 'enfumé', 'orageux',
-       'venteux', 'froid', 'nuageux',
-       'couverte', # night
-       'couvert', # day
-       'partiellement couverte', # night
-       'partiellement couvert', # day
-       'clair',
-       'ensoleillé',
-       'douce', #night
-       'agréable', #day
-       'pluie et grêle',
-       'chaud', 'orages isolés', 'tempêtes éparses', 'orages épars', 'averses éparses', 'tempête de neige',
-       'chûtes de neiges éparses', 'tempêtes de neige', 'partielement nuageux', 'averses orageuses', 'chûte de neige', 'chûtes de neige isolées');
-
-my @YahooCodes_pl = (
-       'tornado', 'burza tropikalna', 'huragan', 'porywiste burze', 'burze', 'deszcz ze śniegiem',
-       'deszcz i deszcz ze śniegiem', 'śnieg i deszcz ze śniegiem', 'marznąca mżawka', 'mżawka', 'marznący deszcz' ,'deszcz',
-       'deszcz', 'przelotne opady śniegu', 'lekkie opady śniegu', 'zamieć śnieżna', 'śnieg', 'grad',
-       'deszcz ze śniegiem', 'pył', 'mgła', 'mgła', 'smog', 'przenikliwie',
-       'wietrznie', 'zimno', 'pochmurno',
-       'pochmurno', # night
-       'pochmurno', # day
-       'częściowe zachmurzenie', # night
-       'częściowe zachmurzenie', # day
-       'czyste niebo',
-       'słonecznie',
-       'ładna noc', #night
-       'ładny dzień', #day
-       'deszcz z gradem',
-       'gorąco', 'gdzieniegdzie burze', 'burze', 'burze', 'przelotne opady śniegu', 'duże opady śniegu',
-       'ciężkie opady śniegu', 'dużo śniegu', 'częściowe zachmurzenie', 'burze z deszczem', 'opady śniegu', 'przejściowo burze');
-
-my @YahooCodes_it = (
-       'tromba d\'aria', 'tempesta tropicale', 'uragano', 'temporali di grande intensità', 'temporali', 'pioggia mista e neve',
-       'pioggia mista e nevischio', 'neve mista e nevischio', 'pioggia gelata', 'pioggia leggera', 'grandine' ,'rovesci',
-       'piogge', 'raffiche di neve', 'deboli nevicate', 'bufera di neve', 'neve', 'grandine',
-       'nevischio', 'pulviscolo', 'nebbia', 'foschia', 'smog', 'ventoso',
-       'ventoso', 'freddo', 'nuvoloso',
-       'parzialmente nuvoloso', # night
-       'parzialmente nuvoloso', # day
-       'parzialmente nuvoloso', # night
-       'parzialmente nuvoloso', # day
-       'sereno',
-       'soleggiato',
-       'bel tempo', #night
-       'bel tempo', #day
-       'pioggia mista a grandine',
-       'caldo', 'temporali isolati', 'temporali sparsi', 'temporali sparsi', 'piogge sparse', 'forti nevicate',
-       'nevicate sparse', 'forti nevicate', 'parzialmente nuvoloso', 'rovesci temporaleschi', 'rovesci di neve', 'temporali isolati');
-
-
-###################################
-
-# Cache
-my %YahooWeatherAPI_CachedData= ();
-my %YahooWeatherAPI_CachedDataTs= ();
-
-###################################
-
-#
-# there is a bug in the Yahoo Weather API that gets all units wrong
-# these routines fix that
-
-
-sub value_to_C($) {
-    my ($F)= @_;
-    return(int(($F-32)*5/9+0.5));
-}
-
-sub value_to_hPa($) {
-    my ($inHg)= @_;
-    return int($inHg/33.86390+0.5);
-}
-
-sub value_to_km($) {
-    my ($value)= @_;
-    return int($value/1.609347219+0.5);
-}
-
-###################################
-
-# call: YahooWeatherAPI_RetrieveData(%%args)
-#
-# the args hash reference must contain at least
-#   woeid          => WOEID [WHERE-ON-EARTH-ID], go to http://weather.yahoo.com to find out
-#   format         => xml or json
-#   blocking       => 0 or 1
-#   callbackFnRef  => reference to callback function with arguments ($argsRef, $err, $result)
-# the args hash reference is returned as first argument of the callbackFn
-#
-
-sub YahooWeatherAPI_RetrieveData($) {
-    my ($argsRef)= @_;
-    YahooWeatherAPI_RetrieveDataWithCache(0, $argsRef);
-}
-
-sub YahooWeatherAPI_RetrieveDataWithCache($$) {
-
-    my ($maxage, $argsRef)= @_;
-    my $woeid= $argsRef->{woeid};
-
-    Log3 undef, 5, "YahooWeatherAPI: retrieve weather for $woeid.";
-
-    # retrieve data from cache
-    my $ts= $YahooWeatherAPI_CachedDataTs{$woeid};
-    if(defined($ts)) {
-        my $now= time();
-        my $age= $now- $ts;
-        if($age< $maxage) {
-            Log3 undef, 5, "YahooWeatherAPI: data is cached, age $age seconds < $maxage seconds.";
-            $argsRef->{callbackFnRef}($argsRef, "", $YahooWeatherAPI_CachedData{$woeid});
-            return;
-        } else {
-            Log3 undef, 5, "YahooWeatherAPI: cache is expired, age $age seconds > $maxage seconds.";
-        }
-    } else {
-            Log3 undef, 5, "YahooWeatherAPI: no data in cache.";
-    }
-
-    my $format= $argsRef->{format};
-    my $blocking= $argsRef->{blocking};
-    my $callbackFnRef= $argsRef->{callbackFnRef};
-
-    my $url = sprintf(URL, $woeid, $format);
-
-    #Debug "Retrieve Yahoo Weather data for " . $argsRef->{hash}->{NAME};
-
-    if ($blocking) {
-        # do not use noshutdown => 0 in parameters
-        my $response = HttpUtils_BlockingGet({ url => $url, timeout => 15 });
-        my %param= (argsRef => $argsRef);
-        YahooWeatherAPI_RetrieveDataFinished(\%param, undef, $response);
-    } else {
-        # do not use noshutdown => 0 in parameters
-        HttpUtils_NonblockingGet({
-            url        => $url,
-            timeout    => 15,
-            argsRef    => $argsRef,
-            callback   => \&YahooWeatherAPI_RetrieveDataFinished,
-            });
-    }
-}
-
-sub YahooWeatherAPI_RetrieveDataFinished($$$) {
-    my ($paramRef, $err, $response) = @_;
-    my $argsRef= $paramRef->{argsRef};
-    #Debug "Finished retrieving Yahoo Weather data for " . $argsRef->{hash}->{NAME};
-    if(!$err) {
-        my $woeid= $argsRef->{woeid};
-        $YahooWeatherAPI_CachedDataTs{$woeid}= time();
-        $YahooWeatherAPI_CachedData{$woeid}= $response;
-        Log3 undef, 5, "YahooWeatherAPI: caching data.";
-    }
-    $argsRef->{callbackFnRef}($argsRef, $err, $response);
-}
-
-
-# this decodes a JSON result and returns the Weather Channel hash reference
-sub YahooWeatherAPI_JSONReturnChannelData($) {
-    my ($response)= @_;
-    return("empty response", undef) unless($response);
-    #Debug "Decoding response: $response";
-    #Debug "response: " . Dumper($response);
-    my $data;
-    eval { $data= decode_json($response) };
-    return($@, undef) if($@);
-    my $query= $data->{query};
-    #Debug Dumper($query);
-    my $count= $query->{count};
-    #Debug "$count result(s).";
-    return("$count result(s) retrieved", undef) unless($count == 1);
-    my $channel= $query->{results}{channel};
-    return(undef, $channel);
-}
-
-sub YahooWeatherAPI_ConvertChannelData($) {
-    my ($data)= @_; # hash reference
-
-    $data->{wind}{chill}= value_to_C($data->{wind}{chill}); # # API delivers wrong value
-    $data->{atmosphere}{pressure}= value_to_hPa($data->{atmosphere}{pressure}); # API delivers wrong value
-
-
-    my $units= YahooWeatherAPI_units($data); # units hash reference
-
-    $data->{wind}{speed}= value_to_km($data->{wind}{speed}); # API delivers km
-    $data->{atmosphere}{visibility}= value_to_km($data->{atmosphere}{visibility}); # API delivers km
-
-    return 0 if($units->{temperature} eq "C");
-
-    my $item= $data->{item};
-    $item->{condition}{temp}= value_to_C($item->{condition}{temp});
-
-    my $forecast= $item->{forecast};
-    foreach my $fc (@{$forecast}) {
-        $fc->{low}= value_to_C($fc->{low});
-        $fc->{high}= value_to_C($fc->{high});
-    }
-    return 1;
-
-}
-
-
-sub YahooWeatherAPI_ParseDateTime($) {
-
-    my ($value)= @_; ### "Fri, 13 Nov 2015 8:00 am CET"
-
-    my @months= qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
-    my %monthindex;
-    @monthindex{@months} = (0..$#months);
-
-    if($value =~ '^(\w{3}), (\d{1,2}) (\w{3}) (\d{4}) (\d{1,2}):(\d{2}) (\w{2}) (\w{3,4})$') {
-        my ($wd, $d, $mon, $y, $h, $n, $p, $tz)= ($1,$2,$3,$4,$5,$6,$7,$8);
-        # 12 AM= 0, 12 PM= 12
-        $h+=12 if($h==12); if($p eq "PM") { $h= ($h+12) % 24 } else { $h%= 12 };
-        my $m= $monthindex{$mon};
-        return undef unless defined($m);
-        #main::Debug "######  $value -> $wd $d $m $y $h:$n $tz";
-        # $mday= 1..
-        # $month= 0..11
-        # $year is year-1900
-        # we ignore the time zone as it probably never changes for a weather device an assume
-        # local time zone
-        return fhemTimeLocal(0, $n, $h, $d, $m, $y-1900);
-    } else {
-        return undef;
-    }
-}
-
-sub YahooWeatherAPI_pubDate($) {
-
-    my ($channel)= @_;
-
-    ### pubDate  Fri, 13 Nov 2015 8:00 am CET
-    if(!defined($channel->{item}{pubDate})) {
-        return("no pubDate received", "", undef);
-    };
-    my $pubDate= $channel->{item}{pubDate};
-    my $ts= YahooWeatherAPI_ParseDateTime($pubDate);
-    if(defined($ts)) {
-        return("okay", $pubDate, $ts);
-    } else {
-        return("could not parse pubDate $pubDate", $pubDate, undef);
-    }
-}
-
-sub YahooWeatherAPI_units($) {
-
-    my ($channel)= @_;
-    return $channel->{units};
-}
-
-sub YahooWeatherAPI_getYahooCodes($) {
-
-    my ($lang)= @_;
-
-    if($lang eq "de") {
-        return @YahooCodes_de;
-    } elsif($lang eq "nl") {
-        return @YahooCodes_nl;
-    } elsif($lang eq "fr") {
-        return @YahooCodes_fr;
-    } elsif($lang eq "pl") {
-        return @YahooCodes_pl;
-    } elsif($lang eq "it") {
-        return @YahooCodes_it;
-    } else {
-        return @YahooCodes_en;
-    }
-}
-
-##############################################################################
-
-1;
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 (<a href=https://forum.fhem.de/index.php?action=profile;u=13684>CoolTux</a>)',
-            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 <fhemdevelopment@cooltux.net>"
+  ],
+  "x_fhem_maintainer": [
+    "<a href=https://forum.fhem.de/index.php?action=profile;u=13684>CoolTux</a>"
+  ],
+  "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 <fhemdevelopment@cooltux.net>
Date: Sat, 24 Dec 2022 04:48:42 +0100
Subject: [PATCH 03/10] 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 `<SERIVCE_NAME>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::Weather<SERIVCE_NAME>API` 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 <fhemdevelopment@cooltux.net>
Date: Sat, 24 Dec 2022 10:20:09 +0100
Subject: [PATCH 04/10] 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 <fhemdevelopment@cooltux.net>"
   ],
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 <fhemdevelopment@cooltux.net>"
   ],

From 745fea37e2590d3923f374103b79dd295f3b4102 Mon Sep 17 00:00:00 2001
From: Marko Oldenburg <fhemdevelopment@cooltux.net>
Date: Sat, 24 Dec 2022 12:57:41 +0100
Subject: [PATCH 05/10] 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 <fhemdevelopment@cooltux.net>
Date: Sun, 25 Dec 2022 09:20:58 +0100
Subject: [PATCH 06/10] 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 <fhemdevelopment@cooltux.net>
Date: Wed, 28 Dec 2022 09:10:14 +0100
Subject: [PATCH 07/10] 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 <fhemdevelopment@cooltux.net>"
   ],

From 3d4996777f7a1a410229789462ab26a91f26aa2a Mon Sep 17 00:00:00 2001
From: Marko Oldenburg <fhemdevelopment@cooltux.net>
Date: Wed, 28 Dec 2022 09:24:12 +0100
Subject: [PATCH 08/10] 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 <fhemdevelopment@cooltux.net>"
   ],
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 <fhemdevelopment@cooltux.net>"
   ],
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 <fhemdevelopment@cooltux.net>"
   ],
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 <julian.pawlowski@gmail.com>"
   ],

From 7ef05e0c00fb97d16b9876f67ac2bfbffd00938d Mon Sep 17 00:00:00 2001
From: Marko Oldenburg <fhemdevelopment@cooltux.net>
Date: Thu, 29 Dec 2022 04:41:10 +0100
Subject: [PATCH 09/10] 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 <fhemdevelopment@cooltux.net>
Date: Fri, 30 Dec 2022 12:11:19 +0100
Subject: [PATCH 10/10] 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 {