testing #29
| @@ -586,7 +586,9 @@ sub Weather_WriteReadings { | |||||||
|     else { |     else { | ||||||
|         Weather_DeleteAlertsReadings($hash); |         Weather_DeleteAlertsReadings($hash); | ||||||
|         readingsBulkUpdate( $hash, 'warnCount', |         readingsBulkUpdate( $hash, 'warnCount', | ||||||
|             scalar( @{ $dataRef->{alerts} } ) ); |             scalar( @{ $dataRef->{alerts} } ) ) | ||||||
|  |           if ( defined( $dataRef->{alerts} ) | ||||||
|  |             && ref( $dataRef->{alerts} ) eq 'ARRAY' ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ### state |     ### state | ||||||
| @@ -785,7 +787,7 @@ sub Weather_Define { | |||||||
|     # evaluate API options |     # evaluate API options | ||||||
|     my ( $api, $apioptions ) = split( ',', $API, 2 ); |     my ( $api, $apioptions ) = split( ',', $API, 2 ); | ||||||
|     $apioptions = "" unless ( defined($apioptions) ); |     $apioptions = "" unless ( defined($apioptions) ); | ||||||
|     eval { require $api . '.pm'; }; |     eval { require 'FHEM/APIs/Weather/' . $api . '.pm'; }; | ||||||
|     return "$name: cannot load API $api: $@" if ($@); |     return "$name: cannot load API $api: $@" if ($@); | ||||||
| 
 | 
 | ||||||
|     $hash->{NOTIFYDEV}          = "global"; |     $hash->{NOTIFYDEV}          = "global"; | ||||||
| @@ -815,7 +817,7 @@ sub Weather_Define { | |||||||
|     readingsSingleUpdate( $hash, 'state', 'Initialized', 1 ); |     readingsSingleUpdate( $hash, 'state', 'Initialized', 1 ); | ||||||
|     Weather_LanguageInitialize( $hash->{LANG} ); |     Weather_LanguageInitialize( $hash->{LANG} ); | ||||||
| 
 | 
 | ||||||
|     my $apistring = $api . '::Weather'; |     my $apistring = 'FHEM::APIs::Weather::' . $api; | ||||||
|     $hash->{fhem}->{api} = $apistring->new( |     $hash->{fhem}->{api} = $apistring->new( | ||||||
|         { |         { | ||||||
|             devName    => $hash->{NAME}, |             devName    => $hash->{NAME}, | ||||||
| @@ -1540,7 +1542,7 @@ sub Weather_CheckOptions { | |||||||
|   ], |   ], | ||||||
|   "release_status": "stable", |   "release_status": "stable", | ||||||
|   "license": "GPL_2", |   "license": "GPL_2", | ||||||
|   "version": "v2.2.6", |   "version": "v2.2.11", | ||||||
|   "author": [ |   "author": [ | ||||||
|     "Marko Oldenburg <fhemdevelopment@cooltux.net>" |     "Marko Oldenburg <fhemdevelopment@cooltux.net>" | ||||||
|   ], |   ], | ||||||
| @@ -1,963 +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/2.5/onecall?lat=[lat]&lon=[long]&APPID=[API]   Forecast |  | ||||||
| # https://openweathermap.org/weather-conditions     Icons und Conditions ID's |  | ||||||
|  |  | ||||||
| package 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/; |  | ||||||
|  |  | ||||||
| # 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 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( OpenWeatherMapAPI->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__ |  | ||||||
| @@ -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; |  | ||||||
							
								
								
									
										4
									
								
								controls_Weather.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								controls_Weather.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -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-30_12:10:48 36045 lib/FHEM/APIs/Weather/wundergroundAPI.pm | ||||||
| @@ -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. | 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. | 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. | 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. | Für beide Methodenaufrufe **muss** eine entsprechende Methode existieren. Das ist Pflicht. | ||||||
| 
 | 
 | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1027
									
								
								lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1027
									
								
								lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user