fixes: #30 #32
							
								
								
									
										483
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										483
									
								
								CHANGELOG.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,483 @@ | |||||||
|  | ### style: change version (HEAD -> dev) | ||||||
|  | >Sat, 7 Jan 2023 13:33:17 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | change version numbers | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### docs: change commandref | ||||||
|  | >Sat, 7 Jan 2023 13:21:27 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | change commandref with id tags | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix uninitialized value in multiplication | ||||||
|  | >Thu, 5 Jan 2023 19:40:21 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | when wind_guest not given | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change old snow and rain value to zero | ||||||
|  | >Thu, 5 Jan 2023 17:02:46 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### add hook example add support for onecall endpoint with api version 2.5 | ||||||
|  | >Thu, 5 Jan 2023 08:18:05 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix regex for sun and moon set and rise | ||||||
|  | >Mon, 2 Jan 2023 09:27:41 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix Undefined subroutine | ||||||
|  | >Fri, 30 Dec 2022 13:17:40 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | Undefined subroutine &FHEM::APIs::Weather::wundergroundAPI::strftimeWrapper | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix | ||||||
|  | >Fri, 30 Dec 2022 12:17:37 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix sub | ||||||
|  | >Fri, 30 Dec 2022 12:14:33 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix undefined value as an ARRAY reference | ||||||
|  | >Fri, 30 Dec 2022 12:11:19 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix some regex formating | ||||||
|  | >Thu, 29 Dec 2022 04:41:10 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change versions and add control file | ||||||
|  | >Wed, 28 Dec 2022 09:24:12 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### remove celvin substration | ||||||
|  | >Wed, 28 Dec 2022 09:10:14 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix "PERL WARNING: Use of uninitialized value" | ||||||
|  | >Sun, 25 Dec 2022 09:20:58 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | no {wind}->{gust} field in $data | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix undefined value | ||||||
|  | >Sat, 24 Dec 2022 12:57:41 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | Can't use an undefined value as an ARRAY reference at ./FHEM/59_Weather.pm line 589 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change versions | ||||||
|  | >Sat, 24 Dec 2022 10:20:09 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### move and rename API.de API description | ||||||
|  | >Sat, 24 Dec 2022 04:48:42 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | change README.m API.de description | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### add directory structure move API files remove YahooWeatherAPI | ||||||
|  | >Sat, 24 Dec 2022 04:39:45 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### move API files to lib/FHEM/APIs/Weather | ||||||
|  | >Thu, 22 Dec 2022 18:45:07 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix forecast number for weblink | ||||||
|  | >Wed, 21 Dec 2022 09:06:54 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### expand notify fn grep syntax | ||||||
|  | >Tue, 20 Dec 2022 18:42:36 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change code for better def modify | ||||||
|  | >Tue, 20 Dec 2022 18:04:45 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix IsDisabled | ||||||
|  | >Tue, 20 Dec 2022 15:29:49 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | change condition for is disabled in GetUpdate fn | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change versions of API modules add Copyright year range | ||||||
|  | >Tue, 20 Dec 2022 14:43:12 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix remove old data | ||||||
|  | >Tue, 20 Dec 2022 13:24:15 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix Weather_CheckOptions func | ||||||
|  | >Tue, 20 Dec 2022 12:53:44 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### add warnCount reading | ||||||
|  | >Mon, 19 Dec 2022 15:29:06 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix negativ integer value round fix alerts integration | ||||||
|  | >Mon, 19 Dec 2022 11:24:27 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix onecall update, remove weather endpoint | ||||||
|  | >Sun, 18 Dec 2022 10:58:24 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change wind_chill to decimal | ||||||
|  | >Wed, 14 Dec 2022 21:35:21 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change pressure value to decimal | ||||||
|  | >Wed, 14 Dec 2022 20:42:07 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix integer declaration for temperature values | ||||||
|  | >Wed, 14 Dec 2022 20:10:58 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### Subject line (try to keep under 50 characters) | ||||||
|  | >Wed, 14 Dec 2022 19:51:18 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | Multi-line description of commit, | ||||||
|  | feel free to be detailed. | ||||||
|  |  | ||||||
|  | [Ticket: X] | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix older entrys | ||||||
|  | >Wed, 14 Dec 2022 19:24:14 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### add numericPrecision=decimal option | ||||||
|  | >Wed, 14 Dec 2022 16:21:14 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix delete Readings | ||||||
|  | >Wed, 14 Dec 2022 11:26:32 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix delete Reading Counter | ||||||
|  | >Wed, 14 Dec 2022 11:08:36 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix forecast exclude and change commandref | ||||||
|  | >Wed, 14 Dec 2022 10:38:04 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix empty deklaration | ||||||
|  | >Tue, 13 Dec 2022 19:04:01 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### fix litte bugs in API modul | ||||||
|  | >Tue, 13 Dec 2022 14:00:46 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | add first code for daily and hourly forcast delete count | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### better formart | ||||||
|  | >Sat, 26 Nov 2022 06:43:20 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### remove double dewpoint reading | ||||||
|  | >Fri, 25 Nov 2022 20:02:39 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### full api support weather and onecall | ||||||
|  | >Fri, 25 Nov 2022 14:09:45 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### change OpenWeatherMapAPI Code and extend 59_Weather.pm Modul | ||||||
|  | >Thu, 24 Nov 2022 19:22:40 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### new OpenWeatherMapAPI onecall v3 Support | ||||||
|  | >Sun, 20 Nov 2022 21:10:22 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (oldenburg@b1-systems.de) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### new API Call and Data | ||||||
|  | >Thu, 17 Nov 2022 19:58:56 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ### add Onecall to Endpoint | ||||||
|  | >Sat, 12 Feb 2022 12:56:04 +0100 | ||||||
|  |  | ||||||
|  | >Author: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | >Commiter: Marko Oldenburg (fhemdevelopment@cooltux.net) | ||||||
|  |  | ||||||
|  | for daily output | ||||||
|  |  | ||||||
|  | [Ticket: no] | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,709 +0,0 @@ | |||||||
| # $Id:  $ |  | ||||||
| ############################################################################### |  | ||||||
| # |  | ||||||
| # Developed with Kate |  | ||||||
| # |  | ||||||
| #  (c) 2019 Copyright: Marko Oldenburg (leongaultier at gmail dot com) |  | ||||||
| #  All rights reserved |  | ||||||
| # |  | ||||||
| #   Special thanks goes to: |  | ||||||
| # |  | ||||||
| # |  | ||||||
| #  This script is free software; you can redistribute it and/or modify |  | ||||||
| #  it under the terms of the GNU General Public License as published by |  | ||||||
| #  the Free Software Foundation; either version 2 of the License,or |  | ||||||
| #  any later version. |  | ||||||
| # |  | ||||||
| #  The GNU General Public License can be found at |  | ||||||
| #  http://www.gnu.org/copyleft/gpl.html. |  | ||||||
| #  A copy is found in the textfile GPL.txt and important notices to the license |  | ||||||
| #  from the author is found in LICENSE.txt distributed with these scripts. |  | ||||||
| # |  | ||||||
| #  This script is distributed in the hope that it will be useful, |  | ||||||
| #  but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
| #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
| #  GNU General Public License for more details. |  | ||||||
| # |  | ||||||
| # |  | ||||||
| ############################################################################### |  | ||||||
|  |  | ||||||
| ### 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://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 = $main::packages{OpenWeatherMapAPI}{META}{version}; |  | ||||||
|  |  | ||||||
| package OpenWeatherMapAPI::Weather; |  | ||||||
| use strict; |  | ||||||
| use warnings; |  | ||||||
|  |  | ||||||
| use POSIX; |  | ||||||
| use HttpUtils; |  | ||||||
|  |  | ||||||
| # 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; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| if ($@) { |  | ||||||
|     $@ = undef; |  | ||||||
|  |  | ||||||
|     # 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; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     if ($@) { |  | ||||||
|         $@ = undef; |  | ||||||
|  |  | ||||||
|         # 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; |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         if ($@) { |  | ||||||
|             $@ = undef; |  | ||||||
|  |  | ||||||
|             # In rare cases, JSON::XS may |  | ||||||
|             #   be installed but JSON not ... |  | ||||||
|             eval { |  | ||||||
|                 require JSON::XS; |  | ||||||
|                 import JSON::XS qw(decode_json encode_json); |  | ||||||
|                 1; |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             if ($@) { |  | ||||||
|                 $@ = undef; |  | ||||||
|  |  | ||||||
|                 # 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; |  | ||||||
|                 }; |  | ||||||
|  |  | ||||||
|                 if ($@) { |  | ||||||
|                     $@ = undef; |  | ||||||
|  |  | ||||||
|                     # 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 "; |  | ||||||
|  |  | ||||||
| # use Data::Dumper;    # for Debug only |  | ||||||
| ## API URL |  | ||||||
| use constant URL => 'https://api.openweathermap.org/data/2.5/'; |  | ||||||
| ## URL . 'weather?' for current data |  | ||||||
| ## URL . 'forecast?' 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, |  | ||||||
|     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, $argsRef ) = @_; |  | ||||||
|     my $apioptions = parseApiOptions( $argsRef->{apioptions} ); |  | ||||||
|  |  | ||||||
|     my $self = { |  | ||||||
|         devName => $argsRef->{devName}, |  | ||||||
|         key     => ( |  | ||||||
|             ( defined( $argsRef->{apikey} ) and $argsRef->{apikey} ) |  | ||||||
|             ? $argsRef->{apikey} |  | ||||||
|             : 'none' |  | ||||||
|         ), |  | ||||||
|         lang      => $argsRef->{language}, |  | ||||||
|         lat       => ( split( ',', $argsRef->{location} ) )[0], |  | ||||||
|         long      => ( split( ',', $argsRef->{location} ) )[1], |  | ||||||
|         fetchTime => 0, |  | ||||||
|         endpoint  => 'none', |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $self->{cachemaxage} = ( |  | ||||||
|         defined( $apioptions->{cachemaxage} ) |  | ||||||
|         ? $apioptions->{cachemaxage} |  | ||||||
|         : 900 |  | ||||||
|     ); |  | ||||||
|     $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 setFetchTime { |  | ||||||
|     my $self = shift; |  | ||||||
|  |  | ||||||
|     $self->{fetchTime} = time(); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub setRetrieveData { |  | ||||||
|     my $self = shift; |  | ||||||
|  |  | ||||||
|     _RetrieveDataFromOpenWeatherMap($self); |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub setLocation { |  | ||||||
|     my ( $self, $lat, $long ) = @_; |  | ||||||
|  |  | ||||||
|     $self->{lat}  = $lat; |  | ||||||
|     $self->{long} = $long; |  | ||||||
|  |  | ||||||
|     return 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub getFetchTime { |  | ||||||
|     my $self = shift; |  | ||||||
|  |  | ||||||
|     return $self->{fetchTime}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub getWeather { |  | ||||||
|     my $self = shift; |  | ||||||
|  |  | ||||||
|     return $self->{cached}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub _RetrieveDataFromOpenWeatherMap { |  | ||||||
|     my $self = shift; |  | ||||||
|  |  | ||||||
|     # retrieve data from cache |  | ||||||
|     if (    ( time() - $self->{fetchTime} ) < $self->{cachemaxage} |  | ||||||
|         and $self->{cached}->{lat} == $self->{lat} |  | ||||||
|         and $self->{cached}->{long} == $self->{long} ) |  | ||||||
|     { |  | ||||||
|         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' ? 'weather' : 'forecast' ), |  | ||||||
|         callback => \&_RetrieveDataFinished, |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     $self->{endpoint} = $paramRef->{endpoint}; |  | ||||||
|  |  | ||||||
|     if (   $self->{lat} eq 'error' |  | ||||||
|         or $self->{long} eq 'error' |  | ||||||
|         or $self->{key} eq 'none' |  | ||||||
|         or $missingModul ) |  | ||||||
|     { |  | ||||||
|         _RetrieveDataFinished( |  | ||||||
|             $paramRef, |  | ||||||
| 'The given location is invalid. (wrong latitude or longitude?) put both as an attribute in the global device or set define option location=[LAT],[LONG]', |  | ||||||
|             undef |  | ||||||
|         ) if ( $self->{lat} eq 'error' or $self->{long} eq 'error' ); |  | ||||||
|  |  | ||||||
|         _RetrieveDataFinished( $paramRef, |  | ||||||
|             'No given api key. (define  myWeather Weather apikey=[KEY])', |  | ||||||
|             undef ) |  | ||||||
|           if ( $self->{key} eq 'none' ); |  | ||||||
|  |  | ||||||
|         _RetrieveDataFinished( $paramRef, |  | ||||||
|             'Perl modul ' . $missingModul . ' is missing.', undef ) |  | ||||||
|           if ($missingModul); |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         $paramRef->{url} = |  | ||||||
|             URL |  | ||||||
|           . $paramRef->{endpoint} . '?' . 'lat=' |  | ||||||
|           . $self->{lat} . '&' . 'lon=' |  | ||||||
|           . $self->{long} . '&' |  | ||||||
|           . 'APPID=' |  | ||||||
|           . $self->{key} . '&' . 'lang=' |  | ||||||
|           . $self->{lang}; |  | ||||||
|  |  | ||||||
|         main::HttpUtils_NonblockingGet($paramRef); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub _RetrieveDataFinished { |  | ||||||
|     my $paramRef = 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 ); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub _ProcessingRetrieveData { |  | ||||||
|     my $self     = shift; |  | ||||||
|     my $response = shift; |  | ||||||
|  |  | ||||||
|     if (    $self->{cached}->{status} eq 'ok' |  | ||||||
|         and defined($response) |  | ||||||
|         and $response ) |  | ||||||
|     { |  | ||||||
|         if ( $response =~ m/^{.*}$/ ) { |  | ||||||
|             my $data = eval { decode_json($response) }; |  | ||||||
|  |  | ||||||
|             if ($@) { |  | ||||||
|                 _ErrorHandling( $self, |  | ||||||
|                     'OpenWeatherMap Weather decode JSON err ' . $@ ); |  | ||||||
|             } |  | ||||||
|             elsif ( defined( $data->{cod} ) |  | ||||||
|                 and $data->{cod} |  | ||||||
|                 and $data->{cod} != 200 |  | ||||||
|                 and defined( $data->{message} ) |  | ||||||
|                 and $data->{message} ) |  | ||||||
|             { |  | ||||||
|                 _ErrorHandling( $self, $data->{cod} . ': ' . $data->{message} ); |  | ||||||
|             } |  | ||||||
|             else { |  | ||||||
|                 ### Debug |  | ||||||
|                 #                 print '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} ) ); |  | ||||||
|  |  | ||||||
|                 if ( $self->{endpoint} eq 'weather' ) { |  | ||||||
|                     $self->{cached}->{country} = $data->{sys}->{country}; |  | ||||||
|                     $self->{cached}->{city}    = encode_utf8( $data->{name} ); |  | ||||||
|                     $self->{cached}->{license}{text} = 'none'; |  | ||||||
|                     $self->{cached}->{current} = { |  | ||||||
|                         'temperature' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{temp} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'temp_c' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{temp} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'low_c' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{temp_min} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'high_c' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{temp_max} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'tempLow' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{temp_min} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'tempHigh' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{temp_max} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'tempFeelsLike_c' => int( |  | ||||||
|                             sprintf( "%.1f", |  | ||||||
|                                 ( $data->{main}->{feels_like} - 273.15 ) ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'humidity' => $data->{main}->{humidity}, |  | ||||||
|                         'condition' => |  | ||||||
|                           encode_utf8( $data->{weather}->[0]->{description} ), |  | ||||||
|                         'pressure' => int( |  | ||||||
|                             sprintf( "%.1f", $data->{main}->{pressure} ) + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'wind' => int( |  | ||||||
|                             sprintf( "%.1f", ( $data->{wind}->{speed} * 3.6 ) ) |  | ||||||
|                               + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'wind_speed' => int( |  | ||||||
|                             sprintf( "%.1f", ( $data->{wind}->{speed} * 3.6 ) ) |  | ||||||
|                               + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'wind_gust' => int( |  | ||||||
|                             sprintf( "%.1f", ( $data->{wind}->{gust} * 3.6 ) ) |  | ||||||
|                               + 0.5 |  | ||||||
|                         ), |  | ||||||
|                         'wind_direction' => $data->{wind}->{deg}, |  | ||||||
|                         'cloudCover'     => $data->{clouds}->{all}, |  | ||||||
|                         'code'       => $codes{ $data->{weather}->[0]->{id} }, |  | ||||||
|                         'iconAPI'    => $data->{weather}->[0]->{icon}, |  | ||||||
|                         'sunsetTime' => strftimeWrapper( |  | ||||||
|                             "%a, %e %b %Y %H:%M", |  | ||||||
|                             localtime( $data->{sys}->{sunset} ) |  | ||||||
|                         ), |  | ||||||
|                         'sunriseTime' => strftimeWrapper( |  | ||||||
|                             "%a, %e %b %Y %H:%M", |  | ||||||
|                             localtime( $data->{sys}->{sunrise} ) |  | ||||||
|                         ), |  | ||||||
|                         'pubDate' => strftimeWrapper( |  | ||||||
|                             "%a, %e %b %Y %H:%M", |  | ||||||
|                             localtime( $data->{dt} ) |  | ||||||
|                         ), |  | ||||||
|                     }; |  | ||||||
|  |  | ||||||
|                     $self->{cached}->{current}->{'visibility'} = |  | ||||||
|                       int( sprintf( "%.1f", $data->{visibility} ) + 0.5 ) |  | ||||||
|                       if ( exists $data->{visibility} ); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 if ( $self->{endpoint} eq 'forecast' ) { |  | ||||||
|                     if ( ref( $data->{list} ) eq "ARRAY" |  | ||||||
|                         and scalar( @{ $data->{list} } ) > 0 ) |  | ||||||
|                     { |  | ||||||
|                         ## löschen des alten Datensatzes |  | ||||||
|                         delete $self->{cached}->{forecast}; |  | ||||||
|  |  | ||||||
|                         my $i = 0; |  | ||||||
|                         for ( @{ $data->{list} } ) { |  | ||||||
|                             push( |  | ||||||
|                                 @{ $self->{cached}->{forecast}->{hourly} }, |  | ||||||
|                                 { |  | ||||||
|                                     'pubDate' => strftimeWrapper( |  | ||||||
|                                         "%a, %e %b %Y %H:%M", |  | ||||||
|                                         localtime( |  | ||||||
|                                             ( $data->{list}->[$i]->{dt} ) - 3600 |  | ||||||
|                                         ) |  | ||||||
|                                     ), |  | ||||||
|                                     'day_of_week' => strftime( |  | ||||||
|                                         "%a, %H:%M", |  | ||||||
|                                         localtime( |  | ||||||
|                                             ( $data->{list}->[$i]->{dt} ) - 3600 |  | ||||||
|                                         ) |  | ||||||
|                                     ), |  | ||||||
|                                     'temperature' => int( |  | ||||||
|                                         sprintf( |  | ||||||
|                                             "%.1f", |  | ||||||
|                                             ( |  | ||||||
|                                                 $data->{list}->[$i]->{main} |  | ||||||
|                                                   ->{temp} - 273.15 |  | ||||||
|                                             ) |  | ||||||
|                                         ) + 0.5 |  | ||||||
|                                     ), |  | ||||||
|                                     'temp_c' => int( |  | ||||||
|                                         sprintf( |  | ||||||
|                                             "%.1f", |  | ||||||
|                                             ( |  | ||||||
|                                                 $data->{list}->[$i]->{main} |  | ||||||
|                                                   ->{temp} - 273.15 |  | ||||||
|                                             ) |  | ||||||
|                                         ) + 0.5 |  | ||||||
|                                     ), |  | ||||||
|                                     'low_c' => int( |  | ||||||
|                                         sprintf( |  | ||||||
|                                             "%.1f", |  | ||||||
|                                             ( |  | ||||||
|                                                 $data->{list}->[$i]->{main} |  | ||||||
|                                                   ->{temp_min} - 273.15 |  | ||||||
|                                             ) |  | ||||||
|                                         ) + 0.5 |  | ||||||
|                                     ), |  | ||||||
|                                     'high_c' => int( |  | ||||||
|                                         sprintf( |  | ||||||
|                                             "%.1f", |  | ||||||
|                                             ( |  | ||||||
|                                                 $data->{list}->[$i]->{main} |  | ||||||
|                                                   ->{temp_max} - 273.15 |  | ||||||
|                                             ) |  | ||||||
|                                         ) + 0.5 |  | ||||||
|                                     ), |  | ||||||
|                                     'tempLow' => int( |  | ||||||
|                                         sprintf( |  | ||||||
|                                             "%.1f", |  | ||||||
|                                             ( |  | ||||||
|                                                 $data->{list}->[$i]->{main} |  | ||||||
|                                                   ->{temp_min} - 273.15 |  | ||||||
|                                             ) |  | ||||||
|                                         ) + 0.5 |  | ||||||
|                                     ), |  | ||||||
|                                     'tempHigh' => int( |  | ||||||
|                                         sprintf( |  | ||||||
|                                             "%.1f", |  | ||||||
|                                             ( |  | ||||||
|                                                 $data->{list}->[$i]->{main} |  | ||||||
|                                                   ->{temp_max} - 273.15 |  | ||||||
|                                             ) |  | ||||||
|                                         ) + 0.5 |  | ||||||
|                                     ), |  | ||||||
|                                     'humidity' => |  | ||||||
|                                       $data->{list}->[$i]->{main}->{humidity}, |  | ||||||
|                                     'condition' => encode_utf8( |  | ||||||
|                                         $data->{list}->[$i]->{weather}->[0] |  | ||||||
|                                           ->{description} |  | ||||||
|                                     ), |  | ||||||
|                                     'pressure' => int( |  | ||||||
|                                         sprintf( "%.1f", |  | ||||||
|                                             $data->{list}->[$i]->{main} |  | ||||||
|                                               ->{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++; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         else { _ErrorHandling( $self, 'OpenWeatherMap ' . $response ); } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     $self->{endpoint} = 'none' if ( $self->{endpoint} eq 'forecast' ); |  | ||||||
|  |  | ||||||
|     _RetrieveDataFromOpenWeatherMap($self) |  | ||||||
|       if ( $self->{endpoint} eq 'weather' ); |  | ||||||
|  |  | ||||||
|     _CallWeatherCallbackFn($self) if ( $self->{endpoint} eq 'none' ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| sub _CallWeatherCallbackFn { |  | ||||||
|     my $self = shift; |  | ||||||
|  |  | ||||||
|     #     print 'Dumperausgabe: ' . Dumper $self; |  | ||||||
|     ### Aufruf der callbackFn |  | ||||||
|     main::Weather_RetrieveCallbackFn( $self->{devName} ); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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'; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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 $string = POSIX::strftime(@_); |  | ||||||
|  |  | ||||||
|     $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; |  | ||||||
|  |  | ||||||
|     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": "v1.0.3", |  | ||||||
|   "author": [ |  | ||||||
|     "Marko Oldenburg <leongaultier@gmail.com>" |  | ||||||
|   ], |  | ||||||
|   "x_fhem_maintainer": [ |  | ||||||
|     "CoolTux" |  | ||||||
|   ], |  | ||||||
|   "x_fhem_maintainer_github": [ |  | ||||||
|     "LeonGaultier" |  | ||||||
|   ], |  | ||||||
|   "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 |  | ||||||
| @@ -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 2023-01-07_13:28:16 57768 FHEM/59_Weather.pm | ||||||
|  | UPD 2023-01-07_13:28:44 49884 lib/FHEM/APIs/Weather/DarkSkyAPI.pm | ||||||
|  | UPD 2023-01-07_13:28:59 33258 lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm | ||||||
|  | UPD 2023-01-07_13:29:12 36090 lib/FHEM/APIs/Weather/wundergroundAPI.pm | ||||||
| @@ -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. | ||||||
| 
 | 
 | ||||||
							
								
								
									
										36
									
								
								hooks/commit-msg
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										36
									
								
								hooks/commit-msg
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | # | ||||||
|  | # An example hook script to check the commit log message. | ||||||
|  | # Called by "git commit" with one argument, the name of the file | ||||||
|  | # that has the commit message.  The hook should exit with non-zero | ||||||
|  | # status after issuing an appropriate message if it wants to stop the | ||||||
|  | # commit.  The hook is allowed to edit the commit message file. | ||||||
|  | # | ||||||
|  | # To enable this hook, rename this file to "commit-msg". | ||||||
|  |  | ||||||
|  | # Uncomment the below to add a Signed-off-by line to the message. | ||||||
|  | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg | ||||||
|  | # hook is more suited to it. | ||||||
|  | # | ||||||
|  | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') | ||||||
|  | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" | ||||||
|  |  | ||||||
|  | # This example catches duplicate Signed-off-by lines. | ||||||
|  |  | ||||||
|  | commit_msg=$(cat "${1:?Missing commit message file}") | ||||||
|  |  | ||||||
|  | test "" = "$(grep '^Signed-off-by: ' "$1" | | ||||||
|  | 	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || { | ||||||
|  | 	echo >&2 Duplicate Signed-off-by lines. | ||||||
|  | 	exit 1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | if ! echo "$commit_msg" | grep -Eq "^(build|chore|ci|docs|feat|feat!|fix|perf|refactor|revert|style|test)(\(.+\))?: .*$" ; then  | ||||||
|  |  | ||||||
|  |   echo "Invalid commit message"  | ||||||
|  |  | ||||||
|  |     exit 1  | ||||||
|  |  | ||||||
|  | fi  | ||||||
|  |  | ||||||
|  | echo "Commit message is valid!" | ||||||
							
								
								
									
										18
									
								
								hooks/post-commit
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										18
									
								
								hooks/post-commit
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | set -eu | ||||||
|  |  | ||||||
|  | # destination of the final changelog file | ||||||
|  | OUTPUT_FILE=CHANGELOG.md | ||||||
|  |  | ||||||
|  | # generate the changelog | ||||||
|  | git --no-pager log --no-merges --format="### %s%d%n>%aD%n%n>Author: %aN (%aE)%n%n>Commiter: %cN (%cE)%n%n%b%n%N%n" > $OUTPUT_FILE | ||||||
|  |  | ||||||
|  | # prevent recursion! | ||||||
|  | # since a 'commit --amend' will trigger the post-commit script again | ||||||
|  | # we have to check if the changelog file has changed or not | ||||||
|  | res=$(git status --porcelain | grep -c ".\$OUTPUT_FILE$") | ||||||
|  | if [ "$res" -gt 0 ]; then | ||||||
|  |   git add $OUTPUT_FILE | ||||||
|  |   git commit --amend | ||||||
|  |   echo "Populated Changelog in $OUTPUT_FILE" | ||||||
|  | fi | ||||||
							
								
								
									
										41
									
								
								hooks/pre-commit
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										41
									
								
								hooks/pre-commit
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | #!/usr/bin/perl -w | ||||||
|  |  | ||||||
|  | use File::Basename; | ||||||
|  | use POSIX qw(strftime); | ||||||
|  | use strict; | ||||||
|  |  | ||||||
|  | my @filenames = (   'FHEM/59_Weather.pm', | ||||||
|  | 		    'lib/FHEM/APIs/Weather/DarkSkyAPI.pm', | ||||||
|  | 		    'lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm', | ||||||
|  | 		    'lib/FHEM/APIs/Weather/wundergroundAPI.pm', | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  | my $controlsfile    = 'controls_Weather.txt'; | ||||||
|  |  | ||||||
|  | open(FH, ">$controlsfile") || return("Can't open $controlsfile: $!"); | ||||||
|  |  | ||||||
|  | for my  $filename (@filenames) { | ||||||
|  |     my @statOutput = stat($filename); | ||||||
|  |    | ||||||
|  |     if (scalar @statOutput != 13) { | ||||||
|  |         printf 'error: stat has unexpected return value for ' . $filename . "\n"; | ||||||
|  |         next; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     my $mtime = $statOutput[9]; | ||||||
|  |     my $date = POSIX::strftime("%Y-%m-%d", localtime($mtime)); | ||||||
|  |     my $time = POSIX::strftime("%H:%M:%S", localtime($mtime)); | ||||||
|  |     my $filetime = $date."_".$time; | ||||||
|  |  | ||||||
|  |     my $filesize = $statOutput[7]; | ||||||
|  |  | ||||||
|  |     printf FH 'UPD ' . $filetime . ' ' . $filesize . ' ' .$filename . "\n"; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | close(FH); | ||||||
|  |  | ||||||
|  | system("git add $controlsfile"); | ||||||
|  |  | ||||||
|  | print 'Create controls File succesfully' . "\n"; | ||||||
|  |  | ||||||
|  | exit 0; | ||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										1062
									
								
								lib/FHEM/APIs/Weather/OpenWeatherMapAPI.pm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1062
									
								
								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