#################################################################################################### # # 77_UWZ.pm # # (c) 2015-2016 Tobias D. Oestreicher # (c) 2017-2021 Marko Oldenburg # # Special thanks goes to comitters: # - Marko Oldenburg (fhemdevelopment at cooltux dot net) # - Hanjo (Forum) patch for sort by creation # - cb1 patch Replace Iconv with native perl encode() # - KölnSolar (Markus) new write UWZAsHtml with smaler Code # # Storm warnings from unwetterzentrale.de # inspired by 59_PROPLANTA.pm # # Copyright notice # # 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 # (at your option) 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 text file 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. # # This copyright notice MUST APPEAR in all copies of the script! # # # # $Id$ # #################################################################################################### # also a thanks goes to hexenmeister ############################################## package FHEM::UWZ; use strict; use feature qw/say switch/; use warnings; use POSIX; use GPUtils qw(GP_Import GP_Export); use FHEM::Meta; use Encode qw(encode_utf8); # use Data::Dumper; # Debug only no if $] >= 5.017011, warnings => 'experimental::lexical_subs', 'experimental::smartmatch'; my $missingModul; eval 'use LWP::UserAgent;1' or $missingModul .= 'LWP::UserAgent '; eval 'use LWP::Simple;1' or $missingModul .= 'LWP::Simple '; eval 'use HTTP::Request;1' or $missingModul .= 'HTTP::Request '; eval 'use HTML::Parser;1' or $missingModul .= 'HTML::Parser '; eval 'use Encode::Guess;1' or $missingModul .= 'Encode::Guess '; require 'Blocking.pm'; require 'HttpUtils.pm'; use vars qw($readingFnAttributes); use vars qw(%defs); # 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; } } } } } ## Import der FHEM Funktionen #-- Run before package compilation BEGIN { # Import from main context GP_Import( qw( readingsSingleUpdate readingsBulkUpdate readingsBeginUpdate readingsEndUpdate defs modules Log3 CommandAttr CommandDeleteReading readingFnAttributes AttrVal ReadingsVal IsDisabled gettimeofday InternalTimer RemoveInternalTimer BlockingCall BlockingKill init_done FW_httpheader HttpUtils_BlockingGet deviceEvents) ); } #-- Export to main context with different name GP_Export( qw( Initialize Start Run Aborted Done ) ); my @DEweekdays = qw(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag); my @DEmonths = ( 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember' ); my @NLweekdays = qw(zondag maandag dinsdag woensdag donderdag vrijdag zaterdag); my @NLmonths = ( 'januari', 'februari', 'maart', 'april', 'mei', 'juni', 'juli', 'augustus', 'september', 'oktober', 'november', 'december' ); my @FRweekdays = qw(dimanche lundi mardi mercredi jeudi vendredi samedi); my @FRmonths = ( 'janvier', 'février', 'mars', 'avril', 'mai', 'juin', 'juillet', 'août', 'september', 'octobre', 'novembre', 'decembre' ); my @ENweekdays = qw(sunday monday thuesday wednesday thursday friday saturday); my @ENmonths = ( 'January', 'February', 'March', 'April', 'Mäy', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ); # my $countrycode = 'DE'; # my $plz = '77777'; # my $uwz_alert_url = 'https://feed.alertspro.meteogroup.com/AlertsPro/AlertsProPollService.php?method=getWarning&language=de&areaID=UWZ' . $countrycode . $plz; ######################################## sub Log { my $hash = shift; my $loglevel = shift; my $text = shift; my $MODUL = 'UWZ'; my $xline = ( caller(0) )[2]; my $xsubroutine = ( caller(1) )[3]; my $sub = ( split( ':', $xsubroutine ) )[2]; $sub =~ s/UWZ_//; my $instName = ( ref($hash) eq 'HASH' ) ? $hash->{NAME} : $hash; Log3 $instName, $loglevel, "$MODUL $instName: $sub.$xline " . $text; return; } ######################################## sub Map2Movie { my $smap = shift; my $uwz_movie_url = "https://www.meteocentrale.ch/uploads/media/"; my $lmap; $smap = lc($smap); ## Euro $lmap->{'niederschlag-wolken'} = $uwz_movie_url . 'UWZ_EUROPE_COMPLETE_niwofi.mp4'; $lmap->{'stroemung'} = $uwz_movie_url . 'UWZ_EUROPE_COMPLETE_stfi.mp4'; $lmap->{'temperatur'} = $uwz_movie_url . 'UWZ_EUROPE_COMPLETE_theta_E.mp4'; ## DE $lmap->{'niederschlag-wolken-de'} = $uwz_movie_url . 'UWZ_EUROPE_GERMANY_COMPLETE_niwofi.mp4'; $lmap->{'stroemung-de'} = $uwz_movie_url . 'UWZ_EUROPE_GERMANY_COMPLETE_stfi.mp4'; ## CH $lmap->{'niederschlag-wolken-ch'} = $uwz_movie_url . 'UWZ_EUROPE_SWITZERLAND_COMPLETE_niwofi.mp4'; $lmap->{'stroemung-ch'} = $uwz_movie_url . 'UWZ_EUROPE_SWITZERLAND_COMPLETE_stfi.mp4'; ## AT $lmap->{'niederschlag-wolken-at'} = $uwz_movie_url . 'UWZ_EUROPE_AUSTRIA_COMPLETE_niwofi.mp4'; $lmap->{'stroemung-at'} = $uwz_movie_url . 'UWZ_EUROPE_AUSTRIA_COMPLETE_stfi.mp4'; ## NL $lmap->{'neerslag-wolken-nl'} = $uwz_movie_url . 'UWZ_EUROPE_BENELUX_COMPLETE_niwofi.mp4'; $lmap->{'stroming-nl'} = $uwz_movie_url . 'UWZ_EUROPE_BENELUX_COMPLETE_stfi.mp4'; ## FR $lmap->{'nuages-precipitations-fr'} = $uwz_movie_url . 'UWZ_EUROPE_FRANCE_COMPLETE_niwofi.mp4'; $lmap->{'courants-fr'} = $uwz_movie_url . 'UWZ_EUROPE_FRANCE_COMPLETE_stfi.mp4'; ## UK $lmap->{'clouds-precipitation-uk'} = $uwz_movie_url . 'UWZ_EUROPE_GREATBRITAIN_COMPLETE_niwofi.mp4'; $lmap->{'currents-uk'} = $uwz_movie_url . 'UWZ_EUROPE_GREATBRITAIN_COMPLETE_stfi.mp4'; ## IT $lmap->{'niederschlag-wolken-it'} = $uwz_movie_url . 'UWZ_EUROPE_ITALY_COMPLETE_niwofi.mp4'; $lmap->{'stroemung-it'} = $uwz_movie_url . 'UWZ_EUROPE_ITALY_COMPLETE_stfi.mp4'; return $lmap->{$smap}; } ######################################## sub Map2Image { my $smap = shift; my $uwz_de_url = 'https://www.unwetterzentrale.de/images/map/'; my $uwz_at_url = 'https://unwetter.wetteralarm.at/images/map/'; my $uwz_ch_url = 'https://alarm.meteocentrale.ch/images/map/'; my $uwz_en_url = 'https://warnings.severe-weather-centre.co.uk/images/map/'; my $uwz_li_url = 'https://alarm.meteocentrale.li/images/map/'; my $uwz_be_url = 'https://alarm.meteo-info.be/images/map/'; my $uwz_dk_url = 'https://alarm.vejrcentral.dk/images/map/'; my $uwz_fi_url = 'https://vaaratasot.saa-varoitukset.fi/images/map/'; my $uwz_fr_url = 'https://alerte.vigilance-meteo.fr/images/map/'; my $uwz_lu_url = 'https://alarm.meteozentral.lu/images/map/'; my $uwz_nl_url = 'https://alarm.noodweercentrale.nl/images/map/'; my $uwz_no_url = 'https://advarsler.vaer-sentral.no/images/map/'; my $uwz_pt_url = 'https://avisos.centrometeo.pt/images/map/'; my $uwz_se_url = 'https://varningar.vader-alarm.se/images/map/'; my $uwz_es_url = 'https://avisos.alertas-tiempo.es/images/map/'; my $uwz_it_url = 'https://allarmi.meteo-allerta.it/images/map/'; my $lmap; $smap = lc($smap); ## Euro $lmap->{'europa'} = $uwz_de_url . 'europe_index.png'; ## DE $lmap->{'deutschland'} = $uwz_de_url . 'deutschland_index.png'; $lmap->{'deutschland-small'} = $uwz_de_url . 'deutschland_preview.png'; $lmap->{'niedersachsen'} = $uwz_de_url . 'niedersachsen_index.png'; $lmap->{'bremen'} = $uwz_de_url . 'niedersachsen_index.png'; $lmap->{'bayern'} = $uwz_de_url . 'bayern_index.png'; $lmap->{'schleswig-holstein'} = $uwz_de_url . 'schleswig_index.png'; $lmap->{'hamburg'} = $uwz_de_url . 'schleswig_index.png'; $lmap->{'mecklenburg-vorpommern'} = $uwz_de_url . 'meckpom_index.png'; $lmap->{'sachsen'} = $uwz_de_url . 'sachsen_index.png'; $lmap->{'sachsen-anhalt'} = $uwz_de_url . 'sachsenanhalt_index.png'; $lmap->{'nordrhein-westfalen'} = $uwz_de_url . 'nrw_index.png'; $lmap->{'thueringen'} = $uwz_de_url . 'thueringen_index.png'; $lmap->{'rheinland-pfalz'} = $uwz_de_url . 'rlp_index.png'; $lmap->{'saarland'} = $uwz_de_url . 'rlp_index.png'; $lmap->{'baden-wuerttemberg'} = $uwz_de_url . 'badenwuerttemberg_index.png'; $lmap->{'hessen'} = $uwz_de_url . 'hessen_index.png'; $lmap->{'brandenburg'} = $uwz_de_url . 'brandenburg_index.png'; $lmap->{'berlin'} = $uwz_de_url . 'brandenburg_index.png'; ## AT $lmap->{'oesterreich'} = $uwz_at_url . 'oesterreich_index.png'; $lmap->{'burgenland'} = $uwz_at_url . 'burgenland_index.png'; $lmap->{'kaernten'} = $uwz_at_url . 'kaernten_index.png'; $lmap->{'niederoesterreich'} = $uwz_at_url . 'niederoesterreich_index.png'; $lmap->{'oberoesterreich'} = $uwz_at_url . 'oberoesterreich_index.png'; $lmap->{'salzburg'} = $uwz_at_url . 'salzburg_index.png'; $lmap->{'steiermark'} = $uwz_at_url . 'steiermark_index.png'; $lmap->{'tirol'} = $uwz_at_url . 'tirol_index.png'; $lmap->{'vorarlberg'} = $uwz_at_url . 'vorarlberg_index.png'; $lmap->{'wien'} = $uwz_at_url . 'wien_index.png'; ## CH $lmap->{'schweiz'} = $uwz_ch_url . 'schweiz_index.png'; $lmap->{'aargau'} = $uwz_ch_url . 'aargau_index.png'; $lmap->{'appenzell_ausserrhoden'} = $uwz_ch_url . 'appenzell_ausserrhoden_index.png'; $lmap->{'appenzell_innerrhoden'} = $uwz_ch_url . 'appenzell_innerrhoden_index.png'; $lmap->{'basel_landschaft'} = $uwz_ch_url . 'basel_landschaft_index.png'; $lmap->{'basel_stadt'} = $uwz_ch_url . 'basel_stadt_index.png'; $lmap->{'bern'} = $uwz_ch_url . 'bern_index.png'; $lmap->{'fribourg'} = $uwz_ch_url . 'fribourg_index.png'; $lmap->{'geneve'} = $uwz_ch_url . 'geneve_index.png'; $lmap->{'glarus'} = $uwz_ch_url . 'glarus_index.png'; $lmap->{'graubuenden'} = $uwz_ch_url . 'graubuenden_index.png'; $lmap->{'jura'} = $uwz_ch_url . 'jura_index.png'; $lmap->{'luzern'} = $uwz_ch_url . 'luzern_index.png'; $lmap->{'neuchatel'} = $uwz_ch_url . 'neuchatel_index.png'; $lmap->{'nidwalden'} = $uwz_ch_url . 'nidwalden_index.png'; $lmap->{'obwalden'} = $uwz_ch_url . 'obwalden_index.png'; $lmap->{'schaffhausen'} = $uwz_ch_url . 'schaffhausen_index.png'; $lmap->{'schwyz'} = $uwz_ch_url . 'schwyz_index.png'; $lmap->{'solothurn'} = $uwz_ch_url . 'solothurn_index.png'; $lmap->{'stgallen'} = $uwz_ch_url . 'stgallen_index.png'; $lmap->{'ticino'} = $uwz_ch_url . 'ticino_index.png'; $lmap->{'thurgau'} = $uwz_ch_url . 'thurgau_index.png'; $lmap->{'uri'} = $uwz_ch_url . 'uri_index.png'; $lmap->{'waadt'} = $uwz_ch_url . 'waadt_index.png'; $lmap->{'wallis'} = $uwz_ch_url . 'wallis_index.png'; $lmap->{'zug'} = $uwz_ch_url . 'zug_index.png'; $lmap->{'zuerich'} = $uwz_ch_url . 'zuerich_index.png'; ## LI $lmap->{'liechtenstein'} = $uwz_li_url . 'liechtenstein_index.png'; ## UK $lmap->{'unitedkingdom'} = $uwz_en_url . 'unitedkingdom_index.png'; $lmap->{'eastofengland'} = $uwz_en_url . 'eastofengland_index.png'; $lmap->{'eastmidlands'} = $uwz_en_url . 'eastmidlands-index.png'; $lmap->{'london'} = $uwz_en_url . 'london-index.png'; $lmap->{'northeastengland'} = $uwz_en_url . 'northeastengland-index.png'; $lmap->{'northernireland'} = $uwz_en_url . 'northernireland-index.png'; $lmap->{'northwestengland'} = $uwz_en_url . 'northwestengland-index.png'; $lmap->{'scotland'} = $uwz_en_url . 'scotland-index.png'; $lmap->{'southeastengland'} = $uwz_en_url . 'southeastengland-index.png'; $lmap->{'southwestengland'} = $uwz_en_url . 'southwestengland-index.png'; $lmap->{'wales'} = $uwz_en_url . 'wales-index.png'; $lmap->{'westmidlands'} = $uwz_en_url . 'westmidlands-index.png'; $lmap->{'yorkshireandthehumber'} = $uwz_en_url . 'yorkshireandthehumber-index.png'; ## BE $lmap->{'belgique'} = $uwz_be_url . 'belgique_index.png'; ## DK $lmap->{'denmark'} = $uwz_dk_url . 'denmark_index.png'; ## FI $lmap->{'finnland'} = $uwz_fi_url . 'finnland_index.png'; ## FR $lmap->{'france'} = $uwz_fr_url . 'france_index.png'; $lmap->{'alsace'} = $uwz_fr_url . 'alsace_index.png'; $lmap->{'aquitaine'} = $uwz_fr_url . 'aquitaine_index.png'; $lmap->{'basse-normandie'} = $uwz_fr_url . 'basse-normandie_index.png'; $lmap->{'bretagne'} = $uwz_fr_url . 'bretagne_index.png'; $lmap->{'champagne-ardenne'} = $uwz_fr_url . 'champagne-ardenne_index.png'; $lmap->{'franche-comte'} = $uwz_fr_url . 'franche-comte_index.png'; $lmap->{'haute-normandie'} = $uwz_fr_url . 'haute-normandie_index.png'; $lmap->{'ile-de-france'} = $uwz_fr_url . 'ile-de-france_index.png'; $lmap->{'languedoc-roussillon'} = $uwz_fr_url . 'languedoc-roussillon_index.png'; $lmap->{'limousin'} = $uwz_fr_url . 'limousin_index.png'; $lmap->{'lorraine'} = $uwz_fr_url . 'lorraine_index.png'; $lmap->{'bourgogne'} = $uwz_fr_url . 'bourgogne_index.png'; $lmap->{'centre'} = $uwz_fr_url . 'centre_index.png'; $lmap->{'midi-pyrenees'} = $uwz_fr_url . 'midi-pyrenees_index.png'; $lmap->{'nord-pas-de-calais'} = $uwz_fr_url . 'nord-pas-de-calais_index.png'; $lmap->{'pays-de-la-loire'} = $uwz_fr_url . 'pays-de-la-loire_index.png'; $lmap->{'picardie'} = $uwz_fr_url . 'picardie_index.png'; $lmap->{'poitou-charentes'} = $uwz_fr_url . 'poitou-charentes_index.png'; $lmap->{'provence-alpes-cote-dazur'} = $uwz_fr_url . 'provence-alpes-cote-dazur_index.png'; $lmap->{'rhone-alpes'} = $uwz_fr_url . 'rhone-alpes_index.png'; ## LU $lmap->{'letzebuerg'} = $uwz_lu_url . 'letzebuerg_index.png'; ## NL $lmap->{'nederland'} = $uwz_nl_url . 'nederland_index.png'; $lmap->{'drenthe'} = $uwz_nl_url . 'drenthe_index.png'; $lmap->{'flevoland'} = $uwz_nl_url . 'flevoland_index.png'; $lmap->{'friesland'} = $uwz_nl_url . 'friesland_index.png'; $lmap->{'gelderland'} = $uwz_nl_url . 'gelderland_index.png'; $lmap->{'groningen'} = $uwz_nl_url . 'groningen_index.png'; $lmap->{'limburg'} = $uwz_nl_url . 'limburg_index.png'; $lmap->{'noordbrabant'} = $uwz_nl_url . 'noordbrabant_index.png'; $lmap->{'noordholland'} = $uwz_nl_url . 'noordholland_index.png'; $lmap->{'overijssel'} = $uwz_nl_url . 'overijssel_index.png'; $lmap->{'utrecht'} = $uwz_nl_url . 'utrecht_index.png'; $lmap->{'zeeland'} = $uwz_nl_url . 'zeeland_index.png'; $lmap->{'zuidholland'} = $uwz_nl_url . 'zuidholland_index.png'; ## NO $lmap->{'norwegen'} = $uwz_no_url . 'norwegen_index.png'; ## PT $lmap->{'portugal'} = $uwz_pt_url . 'portugal_index.png'; ## SE $lmap->{'sverige'} = $uwz_se_url . 'sverige_index.png'; ## ES $lmap->{'espana'} = $uwz_es_url . 'espana_index.png'; ## IT $lmap->{'italia'} = $uwz_it_url . 'italia_index.png'; $lmap->{'valledaosta'} = $uwz_it_url . 'valledaosta_index.png'; $lmap->{'piemonte'} = $uwz_it_url . 'piemonte_index.png'; $lmap->{'lombardia'} = $uwz_it_url . 'lombardia_index.png'; $lmap->{'trentinoaltoadige'} = $uwz_it_url . 'trentinoaltoadige_index.png'; $lmap->{'friuliveneziagiulia'} = $uwz_it_url . 'friuliveneziagiulia_index.png'; $lmap->{'veneto'} = $uwz_it_url . 'veneto_index.png'; $lmap->{'liguria'} = $uwz_it_url . 'liguria_index.png'; $lmap->{'emiliaromagna'} = $uwz_it_url . 'emiliaromagna_index.png'; $lmap->{'toscana'} = $uwz_it_url . 'toscana_index.png'; $lmap->{'marche'} = $uwz_it_url . 'marche_index.png'; $lmap->{'umbria'} = $uwz_it_url . 'umbria_index.png'; $lmap->{'lazio'} = $uwz_it_url . 'lazio_index.png'; $lmap->{'molise'} = $uwz_it_url . 'molise_index.png'; $lmap->{'abruzzo'} = $uwz_it_url . 'abruzzo_index.png'; $lmap->{'campania'} = $uwz_it_url . 'campania_index.png'; $lmap->{'puglia'} = $uwz_it_url . 'puglia_index.png'; $lmap->{'basilicata'} = $uwz_it_url . 'basilicata_index.png'; $lmap->{'calabria'} = $uwz_it_url . 'calabria_index.png'; $lmap->{'sicilia'} = $uwz_it_url . 'sicilia_index.png'; $lmap->{'sardegna'} = $uwz_it_url . 'sardegna_index.png'; ## Isobaren $lmap->{'isobaren1'} = 'https://www.unwetterzentrale.de/images/icons/UWZ_ISO_00.jpg'; $lmap->{'isobaren2'} = 'https://www.wetteralarm.at/uploads/pics/UWZ_EURO_ISO_GER_00.jpg'; $lmap->{'isobaren3'} = 'https://www.severe-weather-centre.co.uk/uploads/pics/UWZ_EURO_ISO_ENG_00.jpg'; return $lmap->{$smap}; } ################################### sub Initialize { my $hash = shift; $hash->{DefFn} = \&Define; $hash->{UndefFn} = \&Undef; $hash->{SetFn} = \&Set; $hash->{NotifyFn} = \&Notify; $hash->{GetFn} = \&Get; $hash->{AttrFn} = \&Attr; $hash->{AttrList} = 'download:0,1 ' . 'savepath ' . 'maps ' . 'humanreadable:0,1 ' . 'htmlattr ' . 'htmltitle ' . 'htmltitleclass ' . 'htmlsequence:ascending,descending ' . 'lang ' . 'sort_readings_by:severity,start,creation ' . 'localiconbase ' . 'intervalAtWarnLevel ' . 'disable:1 ' . 'disabledForIntervals ' . $readingFnAttributes; $hash->{parseParams} = 1; return FHEM::Meta::InitMod( __FILE__, $hash ); } ################################### sub Define { my $hash = shift // return; my $aArg = shift // return; return $@ unless ( FHEM::Meta::SetInternals($hash) ); use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); return "Error: Perl moduls " . $missingModul . "are missing on this system" if ($missingModul); return "Wrong syntax: use define UWZ " if ( scalar( @{$aArg} ) != 5 && lc $aArg->[2] ne 'search' ); my $name = $hash->{NAME}; my $lang = ''; $hash->{VERSION} = version->parse($VERSION)->normal; $hash->{NOTIFYDEV} = 'global,' . $name; if ( ( lc $aArg->[2] ) ne 'search' ) { readingsSingleUpdate( $hash, 'state', 'Initializing', 1 ); $hash->{CountryCode} = $aArg->[2]; $hash->{PLZ} = $aArg->[3]; ## URL by CountryCode my $URL_language = 'en'; if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $URL_language = 'de'; } if ( $hash->{CountryCode} ~~ ['NL'] ) { $URL_language = 'nl'; } if ( $hash->{CountryCode} ~~ ['FR'] ) { $URL_language = 'fr'; } $hash->{URL} = 'https://feed.alertspro.meteogroup.com/AlertsPro/AlertsProPollService.php?method=getWarning&language=' . $URL_language . '&areaID=UWZ' . $aArg->[2] . $aArg->[3]; $hash->{fhem}{LOCAL} = 0; $hash->{INTERVAL} = $aArg->[4]; $hash->{INTERVALWARN} = 0; } else { readingsSingleUpdate( $hash, 'state', 'Search-Mode', 1 ); $hash->{CountryCode} = uc $aArg->[2]; } return; } ##################################### sub Undef { my $hash = shift; my $arg = shift; RemoveInternalTimer($hash); BlockingKill( $hash->{helper}{RUNNING_PID} ) if ( defined( $hash->{helper}{RUNNING_PID} ) ); return; } sub Attr { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; if ( $attrName eq "disabledForIntervals" ) { if ( $cmd eq "set" ) { return "check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'" unless ( $attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/ ); Log3( $name, 3, "UWZ ($name) - disabledForIntervals" ); } elsif ( $cmd eq "del" ) { Log3( $name, 3, "UWZ ($name) - enabled" ); readingsSingleUpdate( $hash, "state", "active", 1 ); } } return; } ##################################### sub Set { my $hash = shift // return; my $aArg = shift // return; my $name = shift @$aArg // return; my $cmd = shift @$aArg // return qq{"set $name" needs at least one argument}; my $usage = "Unknown argument $cmd, choose one of update:noArg " if ( ( lc $hash->{CountryCode} ) ne 'search' ); return $usage if ( scalar( @{$aArg} ) != 0 ); given ($cmd) { when ("?") { return $usage; } when ('update') { Log $hash, 4, 'set command: ' . $cmd; $hash->{fhem}{LOCAL} = 1; Start($hash); $hash->{fhem}{LOCAL} = 0; } default { return $usage; } } return; } sub Notify { my $hash = shift; my $dev = shift; my $name = $hash->{NAME}; my $devname = $dev->{NAME}; my $devtype = $dev->{TYPE}; my $events = deviceEvents( $dev, 1 ); return if ( !$events ); Start($hash) if ( ( ( ( grep /^DEFINED.$name$/, @{$events} or grep /^DELETEATTR.$name.disable$/, @{$events} or grep /^ATTR.$name.disable.0$/, @{$events} ) && $devname eq 'global' && $init_done ) || ( ( grep /^INITIALIZED$/, @{$events} or grep /^REREADCFG$/, @{$events} or grep /^MODIFIED.$name$/, @{$events} ) && $devname eq 'global' ) ) && ReadingsVal( $name, 'state', 'Search-Mode' ) ne 'Search-Mode' ); return; } sub Get { my $hash = shift // return; my $aArg = shift // return; my $name = shift @$aArg // return; my $cmd = shift @$aArg // return qq{"get $name" needs at least one argument}; if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { my $usage = "Unknown argument $cmd, choose one of Sturm:noArg Schneefall:noArg Regen:noArg Extremfrost:noArg Waldbrand:noArg Gewitter:noArg Glaette:noArg Hitze:noArg Glatteisregen:noArg Bodenfrost:noArg Hagel:noArg "; return $usage if ( scalar( @{$aArg} ) != 0 ); return $cmd =~ m{\ASturm}xms ? GetCurrent( $hash, 2 ) : $cmd =~ m{\ASchneefall}xms ? GetCurrent( $hash, 3 ) : $cmd =~ m{\ARegen}xms ? GetCurrent( $hash, 4 ) : $cmd =~ m{\AExtremfrost}xms ? GetCurrent( $hash, 5 ) : $cmd =~ m{\AWaldbrand}xms ? GetCurrent( $hash, 6 ) : $cmd =~ m{\AGewitter}xms ? GetCurrent( $hash, 7 ) : $cmd =~ m{\AGlaette}xms ? GetCurrent( $hash, 8 ) : $cmd =~ m{\AHitze}xms ? GetCurrent( $hash, 9 ) : $cmd =~ m{\AGlatteisregen}xms ? GetCurrent( $hash, 10 ) : $cmd =~ m{\ABodenfrost}xms ? GetCurrent( $hash, 11 ) : $cmd =~ m{\AHagel}xms ? GetCurrentHail($hash) : $usage; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { my $usage = "Unknown argument $cmd, choose one of storm:noArg sneeuw:noArg regen:noArg strenge-vorst:noArg bosbrand:noArg onweer:noArg gladheid:noArg hitte:noArg ijzel:noArg grondvorst:noArg hagel:noArg "; return $usage if ( scalar( @{$aArg} ) != 0 ); return $cmd =~ m{\Astorm}xms ? GetCurrent( $hash, 2 ) : $cmd =~ m{\Asneeuw}xms ? GetCurrent( $hash, 3 ) : $cmd =~ m{\Aregen}xms ? GetCurrent( $hash, 4 ) : $cmd =~ m{\Astrenge-vorst}xms ? GetCurrent( $hash, 5 ) : $cmd =~ m{\Abosbrand}xms ? GetCurrent( $hash, 6 ) : $cmd =~ m{\Aonweer}xms ? GetCurrent( $hash, 7 ) : $cmd =~ m{\Agladheid}xms ? GetCurrent( $hash, 8 ) : $cmd =~ m{\Ahitte}xms ? GetCurrent( $hash, 9 ) : $cmd =~ m{\Aijzel}xms ? GetCurrent( $hash, 10 ) : $cmd =~ m{\Agrondvorst}xms ? GetCurrent( $hash, 11 ) : $cmd =~ m{\Ahagel}xms ? GetCurrentHail($hash) : $usage; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { my $usage = "Unknown argument $cmd, choose one of tempete:noArg neige:noArg pluie:noArg strenge-vorst:noArg incendie-de-foret:noArg orage:noArg glissange:noArg canicule:noArg verglas:noArg grondvorst:noArg grele:noArg "; return $usage if ( scalar( @{$aArg} ) != 0 ); return $cmd =~ m{\Atempete}xms ? GetCurrent( $hash, 2 ) : $cmd =~ m{\Aneige}xms ? GetCurrent( $hash, 3 ) : $cmd =~ m{\Apluie}xms ? GetCurrent( $hash, 4 ) : $cmd =~ m{\Atempérature}xms ? GetCurrent( $hash, 5 ) : $cmd =~ m{\Afeu-de-forêt}xms ? GetCurrent( $hash, 6 ) : $cmd =~ m{\Aorage}xms ? GetCurrent( $hash, 7 ) : $cmd =~ m{\Aoute-glissante}xms ? GetCurrent( $hash, 8 ) : $cmd =~ m{\Achaleur}xms ? GetCurrent( $hash, 9 ) : $cmd =~ m{\Apluie-de-verglas}xms ? GetCurrent( $hash, 10 ) : $cmd =~ m{\Agelée}xms ? GetCurrent( $hash, 11 ) : $cmd =~ m{\Agrêle}xms ? GetCurrentHail($hash) : $usage; } elsif ( ( lc $hash->{CountryCode} ) eq 'search' ) { my $usage = "Unknown argument $cmd, choose one of SearchAreaID "; return $usage if ( scalar( @{$aArg} ) != 1 ); if ( $cmd =~ m{\ASearchAreaID}xms ) { UWZSearchLatLon( $name, $aArg->[0] ); } elsif ( $cmd =~ m{\AAreaID}xms ) { my @splitparam = split( /,/, $aArg->[0] ); UWZSearchAreaID( $splitparam[0], $splitparam[1] ); } else { return $usage; } } else { my $usage = "Unknown argument $cmd, choose one of storm:noArg snow:noArg rain:noArg extremfrost:noArg forest-fire:noArg thunderstorms:noArg glaze:noArg heat:noArg glazed-rain:noArg soil-frost:noArg hail:noArg "; return $usage if ( scalar( @{$aArg} ) != 0 ); return $cmd =~ m{\Astorm}xms ? GetCurrent( $hash, 2 ) : $cmd =~ m{\Asnow}xms ? GetCurrent( $hash, 3 ) : $cmd =~ m{\Arain}xms ? GetCurrent( $hash, 4 ) : $cmd =~ m{\Aextremfrost}xms ? GetCurrent( $hash, 5 ) : $cmd =~ m{\Aforest-fire}xms ? GetCurrent( $hash, 6 ) : $cmd =~ m{\Athunderstorms}xms ? GetCurrent( $hash, 7 ) : $cmd =~ m{\Aglaze}xms ? GetCurrent( $hash, 8 ) : $cmd =~ m{\Aheat}xms ? GetCurrent( $hash, 9 ) : $cmd =~ m{\Aglazed-rain}xms ? GetCurrent( $hash, 10 ) : $cmd =~ m{\Asoil-frost}xms ? GetCurrent( $hash, 11 ) : $cmd =~ m{\Ahail}xms ? GetCurrentHail($hash) : $usage; } } ##################################### sub GetCurrent { my $hash = shift; my $type = shift; my $name = $hash->{NAME}; my $out; my $curTimestamp = time(); if ( ReadingsVal( $name, 'WarnCount', 0 ) eq 0 ) { $out = 'inactive'; } else { for ( my $i = 0 ; $i < ReadingsVal( $name, 'WarnCount', 0 ) ; $i++ ) { if ( ( ReadingsVal( $name, 'Warn_' . $i . '_Start', '' ) le $curTimestamp ) && ( ReadingsVal( $name, 'Warn_' . $i . '_End', '' ) ge $curTimestamp ) && ( ReadingsVal( $name, 'Warn_' . $i . '_Type', '' ) eq $type ) ) { $out = 'active'; last; } else { $out = 'inactive'; } } } return $out; } ##################################### sub GetCurrentHail($) { my $hash = shift; my $name = $hash->{NAME}; my $out; my $curTimestamp = time(); if ( ReadingsVal( $name, "WarnCount", 0 ) eq 0 ) { $out = 'inactive'; } else { for ( my $i = 0 ; $i < ReadingsVal( $name, 'WarnCount', 0 ) ; $i++ ) { if ( ( ReadingsVal( $name, 'Warn_' . $i . '_Start', '' ) le $curTimestamp ) && ( ReadingsVal( $name, 'Warn_' . $i . '_End', '' ) ge $curTimestamp ) && ( ReadingsVal( $name, 'Warn_' . $i . '_Hail', '' ) eq 1 ) ) { $out = 'active'; last; } else { $out = 'inactive'; } } } return $out; } ##################################### sub JSONAcquire { my $hash = shift; my $URL = shift; my $name = $hash->{NAME}; return if ( !defined( $hash->{NAME} ) ); Log $hash, 4, "Start capturing of $URL"; my $param = { url => $URL, timeout => 5, hash => $hash, method => 'GET', header => '', }; my ( $err, $data ) = HttpUtils_BlockingGet($param); if ( $err ne '' ) { my $err_log = "Can't get $URL -- " . $err; readingsSingleUpdate( $hash, 'lastConnection', $err, 1 ); Log $hash, 1, 'Error: ' . $err_log; return encode_utf8( '{"Error": "' . $err . '"}' ); } Log $hash, 4, length($data) . ' characters captured: ' . encode_utf8($data); return encode_utf8($data); } ##################################### sub Start { my $hash = shift; my $name = $hash->{NAME}; return if ( !defined( $hash->{NAME} ) ); if ( !$hash->{fhem}{LOCAL} && $hash->{INTERVAL} > 0 ) { # set up timer if automatically call RemoveInternalTimer($hash); InternalTimer( gettimeofday() + $hash->{INTERVAL}, 'UWZ_Start', $hash ); return readingsSingleUpdate( $hash, 'state', 'disabled', 1 ) if ( IsDisabled($name) ); readingsSingleUpdate( $hash, 'currentIntervalMode', 'normal', 0 ); } ## URL by CountryCode my $URL_language = 'en'; if ( AttrVal( $hash->{NAME}, 'lang', undef ) ) { $URL_language = AttrVal( $hash->{NAME}, 'lang', '' ); } else { if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $URL_language = 'de'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $URL_language = 'nl'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $URL_language = 'fr'; } } $hash->{URL} = 'https://feed.alertspro.meteogroup.com/AlertsPro/AlertsProPollService.php?method=getWarning&language=' . $URL_language . '&areaID=UWZ' . $hash->{CountryCode} . $hash->{PLZ}; if ( !defined( $hash->{URL} ) ) { Log $hash, 3, 'missing URL'; return; } $hash->{helper}{RUNNING_PID} = BlockingCall( 'UWZ_Run', # callback worker task $name, # name of the device 'UWZ_Done', # callback result method 120, # timeout seconds 'UWZ_Aborted', # callback for abortion $hash ); # parameter for abortion return; } ##################################### sub Aborted { my $hash = shift; delete( $hash->{helper}{RUNNING_PID} ); return; } ##################################### # asyncronous callback by blocking sub Done { my $string = shift; return if ( !defined($string) ); # all term are separated by '|' , the first is the name of the instance my ( $name, %values ) = split( '\\|', $string ); my $hash = $defs{$name}; return if ( !defined( $hash->{NAME} ) ); # delete the marker for RUNNING_PID process delete( $hash->{helper}{RUNNING_PID} ); # UnWetterdaten speichern readingsBeginUpdate($hash); if ( defined $values{Error} ) { readingsBulkUpdate( $hash, 'lastConnection', $values{Error} ); readingsBulkUpdate( $hash, 'state', 'error at last run, please check >>lastConnection<< reading' ); } else { while ( my ( $rName, $rValue ) = each(%values) ) { readingsBulkUpdate( $hash, $rName, $rValue ); Log $hash, 5, "reading:$rName value:$rValue"; } if ( keys %values > 0 ) { my $newState; Log $hash, 4, "Delete old Readings"; for my $Counter ( $values{WarnCount} .. 9 ) { CommandDeleteReading( undef, "$hash->{NAME} Warn_${Counter}_.*" ); } if ( defined $values{WarnCount} ) { # Message by CountryCode $newState = 'Warnings: ' . $values{WarnCount}; $newState = 'Warnungen: ' . $values{WarnCount} if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ); $newState = 'Aantal waarschuwingen: ' . $values{WarnCount} if ( $hash->{CountryCode} ~~ ['NL'] ); $newState = 'Avertissements: ' . $values{WarnCount} if ( $hash->{CountryCode} ~~ ['FR'] ); # end Message by CountryCode } else { $newState = 'Error: Could not capture all data. Please check CountryCode and PLZ.'; } readingsBulkUpdate( $hash, 'state', $newState ); readingsBulkUpdate( $hash, 'lastConnection', keys(%values) . ' values captured in ' . $values{durationFetchReadings} . ' s' ); Log $hash, 4, keys(%values) . ' values captured'; } else { readingsBulkUpdate( $hash, 'lastConnection', 'no data found' ); Log $hash, 1, 'No data found. Check city name or URL.'; } } readingsEndUpdate( $hash, 1 ); if ( AttrVal( $name, 'intervalAtWarnLevel', '' ) ne '' && ReadingsVal( $name, 'WarnUWZLevel', 0 ) > 1 ) { IntervalAtWarnLevel($hash); Log $hash, 5, 'run Sub IntervalAtWarnLevel'; } return; } ##################################### sub Run { my $name = shift; my $ptext = $name; my $UWZ_download; my $UWZ_savepath; my $UWZ_humanreadable; return if ( !defined($name) ); my $hash = $defs{$name}; return if ( !defined( $hash->{NAME} ) ); my $readingStartTime = time(); my $attrdownload = AttrVal( $name, 'download', '' ); my $attrsavepath = AttrVal( $name, 'savepath', '' ); my $maps2fetch = AttrVal( $name, 'maps', '' ); ## begin redundant Reading switch my $attrhumanreadable = AttrVal( $name, 'humanreadable', '' ); ## end redundant Reading switch # preset download if ( $attrdownload eq '' ) { $UWZ_download = 0; } else { $UWZ_download = $attrdownload; } # preset savepath if ( $attrsavepath eq '' ) { $UWZ_savepath = '/tmp/'; } else { $UWZ_savepath = $attrsavepath; } # preset humanreadable if ( $attrhumanreadable eq '' ) { $UWZ_humanreadable = 0; } else { $UWZ_humanreadable = $attrhumanreadable; } if ( $UWZ_download == 1 ) { if ( !defined($maps2fetch) ) { $maps2fetch = 'deutschland'; } Log $hash, 4, 'Maps2Fetch : ' . $maps2fetch; my @maps = split( ' ', $maps2fetch ); my $uwz_de_url = 'https://www.unwetterzentrale.de/images/map/'; foreach my $smap (@maps) { Log $hash, 4, 'Download map : ' . $smap; my $img = Map2Image($smap); if ( !defined($img) ) { $img = $uwz_de_url . 'deutschland_index.png'; } my $code = getstore( $img, $UWZ_savepath . $smap . '.png' ); if ( $code == 200 ) { Log $hash, 4, 'Successfully downloaded map ' . $smap; } else { Log $hash, 3, 'Failed to download map (' . $img . ')'; } } } # acquire the json-response my $response = JSONAcquire( $hash, $hash->{URL} ); Log $hash, 5, length($response) . ' characters captured'; my $uwz_warnings = JSON->new->ascii->decode($response); my $message; my $uwz_warncount; if ( !exists $uwz_warnings->{Error} ) { my $enc = guess_encoding($uwz_warnings); $uwz_warncount = scalar( @{ $uwz_warnings->{'results'} } ); Log $hash, 4, 'There are ' . $uwz_warncount . ' warnings active'; my $sortby = AttrVal( $name, 'sort_readings_by', '' ); my @sorted; if ( $sortby eq 'creation' ) { Log $hash, 4, 'Sorting by creation'; @sorted = sort { $b->{payload}{creation} <=> $a->{payload}{creation} } @{ $uwz_warnings->{'results'} }; } elsif ( $sortby ne 'severity' ) { Log $hash, 4, 'Sorting by dtgStart'; @sorted = sort { $a->{dtgStart} <=> $b->{dtgStart} } @{ $uwz_warnings->{'results'} }; } else { Log $hash, 4, 'Sorting by severity'; @sorted = sort { $a->{severity} <=> $b->{severity} } @{ $uwz_warnings->{'results'} }; } my $i = 0; my %typenames = ( '1' => 'unknown', # <===== FIX HERE '2' => 'sturm', '3' => 'schnee', '4' => 'regen', '5' => 'temperatur', '6' => 'waldbrand', '7' => 'gewitter', '8' => 'strassenglaette', '9' => 'temperatur', # 9 = hitzewarnung '10' => 'glatteisregen', '11' => 'temperatur' ); # 11 = bodenfrost my %typenames_de_str = ( '1' => 'unknown', # <===== FIX HERE '2' => 'Sturm', '3' => 'Schnee', '4' => 'Regen', '5' => 'Temperatur', '6' => 'Waldbrand', '7' => 'Gewitter', '8' => 'Strassenglaette', '9' => 'Hitze', # 9 = hitzewarnung '10' => 'Glatteisregen', '11' => 'Bodenfrost' ); # 11 = bodenfrost my %typenames_nl_str = ( '1' => 'unknown', # <===== FIX HERE '2' => 'storm', '3' => 'sneeuw', '4' => 'regen', '5' => 'temperatuur', '6' => 'bosbrand', '7' => 'onweer', '8' => 'gladde wegen', '9' => 'hitte', # 9 = hitzewarnung '10' => 'ijzel', '11' => 'grondvorst' ); # 11 = bodenfrost my %typenames_fr_str = ( '1' => 'unknown', # <===== FIX HERE '2' => 'tempete', '3' => 'neige', '4' => 'pluie', '5' => 'températur', '6' => 'feu de forêt', '7' => 'orage', '8' => 'route glissante', '9' => 'chaleur', # 9 = hitzewarnung '10' => 'pluie de verglas', '11' => 'gelée' ); # 11 = bodenfrost my %typenames_en_str = ( '1' => 'unknown', # <===== FIX HERE '2' => 'storm', '3' => 'snow', '4' => 'rain', '5' => 'temperatur', '6' => 'forest fire', '7' => 'thunderstorms', '8' => 'slippery road', '9' => 'heat', # 9 = hitzewarnung '10' => 'black ice rain', '11' => 'soil frost' ); # 11 = bodenfrost my %severitycolor = ( '0' => 'green', '1' => 'unknown', # <===== FIX HERE '2' => 'unknown', # <===== FIX HERE '3' => 'unknown', # <===== FIX HERE '4' => 'orange', '5' => 'unknown', # <===== FIX HERE '6' => 'unknown', # <===== FIX HERE '7' => 'orange', '8' => 'gelb', '9' => 'gelb', # <===== FIX HERE '10' => 'orange', '11' => 'rot', '12' => 'violett' ); my @uwzmaxlevel; foreach my $single_warning (@sorted) { push @uwzmaxlevel, GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ); Log $hash, 4, 'Warn_' . $i . '_EventID: ' . $single_warning->{'payload'}{'id'}; $message .= 'Warn_' . $i . '_EventID|' . $single_warning->{'payload'}{'id'} . '|'; my $chopcreation = substr( $single_warning->{'payload'}{'creation'}, 0, 10 ); $chopcreation = $chopcreation; Log $hash, 4, 'Warn_' . $i . '_Creation: ' . $chopcreation; $message .= 'Warn_' . $i . '_Creation|' . $chopcreation . '|'; Log $hash, 4, 'Warn_' . $i . '_Type: ' . $single_warning->{'type'}; $message .= 'Warn_' . $i . '_Type|' . $single_warning->{'type'} . '|'; Log $hash, 4, 'Warn_' . $i . '_uwzLevel: ' . GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ); $message .= 'Warn_' . $i . '_uwzLevel|' . GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) . '|'; Log $hash, 4, 'Warn_' . $i . '_Severity: ' . $single_warning->{'severity'}; $message .= 'Warn_' . $i . '_Severity|' . $single_warning->{'severity'} . '|'; Log $hash, 4, 'Warn_' . $i . '_Start: ' . $single_warning->{'dtgStart'}; $message .= 'Warn_' . $i . '_Start|' . $single_warning->{'dtgStart'} . '|'; Log $hash, 4, 'Warn_' . $i . '_End: ' . $single_warning->{'dtgEnd'}; $message .= 'Warn_' . $i . '_End|' . $single_warning->{'dtgEnd'} . '|'; ## Begin of redundant Reading if ( $UWZ_humanreadable eq 1 ) { Log $hash, 4, 'Warn_' . $i . '_Start_Date: ' . strftime( "%d.%m.%Y", localtime( $single_warning->{'dtgStart'} ) ); $message .= 'Warn_' . $i . '_Start_Date|' . strftime( "%d.%m.%Y", localtime( $single_warning->{'dtgStart'} ) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_Start_Time: ' . strftime( "%H:%M", localtime( $single_warning->{'dtgStart'} ) ); $message .= 'Warn_' . $i . '_Start_Time|' . strftime( "%H:%M", localtime( $single_warning->{'dtgStart'} ) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_End_Date: ' . strftime( "%d.%m.%Y", localtime( $single_warning->{'dtgEnd'} ) ); $message .= 'Warn_' . $i . '_End_Date|' . strftime( "%d.%m.%Y", localtime( $single_warning->{'dtgEnd'} ) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_End_Time: ' . strftime( "%H:%M", localtime( $single_warning->{'dtgEnd'} ) ); $message .= 'Warn_' . $i . '_End_Time|' . strftime( "%H:%M", localtime( $single_warning->{'dtgEnd'} ) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_Creation_Date: ' . strftime( "%d.%m.%Y", localtime($chopcreation) ); $message .= 'Warn_' . $i . '_Creation_Date|' . strftime( "%d.%m.%Y", localtime($chopcreation) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_Creation_Time: ' . strftime( "%H:%M", localtime($chopcreation) ); $message .= 'Warn_' . $i . '_Creation_Time|' . strftime( "%H:%M", localtime($chopcreation) ) . '|'; # Begin Language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { Log $hash, 4, 'Warn_' . $i . '_Type_Str: ' . $typenames_de_str{ $single_warning->{'type'} }; $message .= 'Warn_' . $i . '_Type_Str|' . $typenames_de_str{ $single_warning->{'type'} } . '|'; my %uwzlevelname = ( '0' => 'Stufe Grün (keine Warnung)', '1' => 'Stufe Dunkelgrün (Wetterhinweise)', '2' => 'Stufe Gelb (Vorwarnung für Unwetterwarnung)', '3' => 'Warnstufe Orange (Unwetterwarnung)', '4' => 'Warnstufe Rot (Unwetterwarnung)', '5' => 'Warnstufe Violett (Unwetterwarnung)' ); Log $hash, 4, 'Warn_' . $i . '_uwzLevel_Str: ' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) }; $message .= 'Warn_' . $i . '_uwzLevel_Str|' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) } . '|'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { Log $hash, 4, 'Warn_' . $i . '_Type_Str: ' . $typenames_nl_str{ $single_warning->{'type'} }; $message .= 'Warn_' . $i . '_Type_Str|' . $typenames_nl_str{ $single_warning->{'type'} } . '|'; my %uwzlevelname = ( '0' => 'niveau groen (geen waarschuwingen)', '1' => 'niveau donkergroen (weermelding)', '2' => 'niveau geel (voorwaarschuwing)', '3' => 'waarschuwingsniveau oranje (waarschuwing voor matig noodweer)', '4' => 'waarschuwingsniveau rood (waarschuwing voor zwaar noodweer)', '5' => 'waarschuwingsniveau violet (waarschuwing voor zeer zwaar noodweer)' ); Log $hash, 4, 'Warn_' . $i . '_uwzLevel_Str: ' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) }; $message .= 'Warn_' . $i . '_uwzLevel_Str|' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) } . '|'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { Log $hash, 4, 'Warn_' . $i . '_Type_Str: ' . $typenames_nl_str{ $single_warning->{'type'} }; $message .= 'Warn_' . $i . '_Type_Str|' . $typenames_nl_str{ $single_warning->{'type'} } . '|'; my %uwzlevelname = ( '0' => 'niveau vert (aucune alerte)', '1' => 'niveau vert foncé (indication météo)', '2' => 'niveau jaune (pré-alerte)', '3' => 'niveau d\' alerte orange (alerte météo)', '4' => 'niveau d\' alerte rouge (alerte météo)', '5' => 'niveau d\' alerte violet (alerte météo)' ); Log $hash, 4, 'Warn_' . $i . '_uwzLevel_Str: ' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) }; $message .= 'Warn_' . $i . '_uwzLevel_Str|' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) } . '|'; } else { Log $hash, 4, 'Warn_' . $i . '_Type_Str: ' . $typenames_en_str{ $single_warning->{'type'} }; $message .= 'Warn_' . $i . '_Type_Str|' . $typenames_en_str{ $single_warning->{'type'} } . '|'; my %uwzlevelname = ( '0' => 'level green (no warnings)', '1' => 'level dark green (weather notice)', '2' => 'level yellow (severe weather watch)', '3' => 'Alert level Orange', '4' => 'Alert level Red', '5' => 'Alert level Violet' ); Log $hash, 4, 'Warn_' . $i . '_uwzLevel_Str: ' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) }; $message .= 'Warn_' . $i . '_uwzLevel_Str|' . $uwzlevelname{ GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) } . '|'; } } ## End of redundant Reading Log $hash, 4, 'Warn_' . $i . '_levelName: ' . $single_warning->{'payload'}{'levelName'}; $message .= 'Warn_' . $i . '_levelName|' . $single_warning->{'payload'}{'levelName'} . '|'; Log $hash, 4, 'Warn_' . $i . '_AltitudeMin: ' . $enc->decode( $single_warning->{'payload'}{'altMin'} ); $message .= 'Warn_' . $i . '_AltitudeMin|' . encode( 'UTF-8', decode( 'iso-8859-1', $single_warning->{'payload'}{'altMin'} ) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_AltitudeMax: ' . $enc->decode( $single_warning->{'payload'}{'altMax'} ); $message .= 'Warn_' . $i . '_AltitudeMax|' . encode( 'UTF-8', decode( 'iso-8859-1', $single_warning->{'payload'}{'altMax'} ) ) . '|'; my $uclang = 'EN'; if ( AttrVal( $name, 'lang', undef ) ) { $uclang = uc AttrVal( $name, 'lang', '' ); } else { # Begin Language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $uclang = 'DE'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $uclang = 'NL'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $uclang = 'FR'; } else { $uclang = 'EN'; } } Log $hash, 4, 'Warn_' . $i . '_LongText: ' . $enc->decode( $single_warning->{'payload'}{'translationsLongText'}{$uclang} ); $message .= 'Warn_' . $i . '_LongText|' . encode( 'UTF-8', decode( 'iso-8859-1', $single_warning->{'payload'}{'translationsLongText'} {$uclang} ) ) . '|'; Log $hash, 4, 'Warn_' . $i . '_ShortText: ' . $enc->decode( $single_warning->{'payload'}{'translationsShortText'}{$uclang} ); $message .= 'Warn_' . $i . '_ShortText|' . encode( 'UTF-8', decode( 'iso-8859-1', $single_warning->{'payload'}{'translationsShortText'} {$uclang} ) ) . '|'; ### if ( AttrVal( $name, 'localiconbase', undef ) ) { Log $hash, 4, 'Warn_' . $i . '_IconURL: ' . AttrVal( $name, 'localiconbase', undef ) . $typenames{ $single_warning->{'type'} } . '-' . $single_warning->{'severity'} . '.png'; $message .= 'Warn_' . $i . '_IconURL|' . AttrVal( $name, 'localiconbase', undef ) . $typenames{ $single_warning->{'type'} } . '-' . GetSeverityColor( $hash, GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) ) . '.png|'; } else { Log $hash, 4, 'Warn_' . $i . '_IconURL: https://www.unwetterzentrale.de/images/icons/' . $typenames{ $single_warning->{'type'} } . '-' . $single_warning->{'severity'} . '.gif'; $message .= 'Warn_' . $i . '_IconURL|https://www.unwetterzentrale.de/images/icons/' . $typenames{ $single_warning->{'type'} } . '-' . GetSeverityColor( $hash, GetUWZLevel( $hash, $single_warning->{'payload'}{'levelName'} ) ) . '.gif|'; } ### ## Hagel start my $hagelcount = 0; # Begin Language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $hagelcount = my @hagelmatch = $single_warning->{'payload'}{'translationsLongText'}{'DE'} =~ /Hagel/g; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $hagelcount = my @hagelmatch = $single_warning->{'payload'}{'translationsLongText'}{'NL'} =~ /hagel/g; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $hagelcount = my @hagelmatch = $single_warning->{'payload'}{'translationsLongText'}{'FR'} =~ /grêle/g; } else { $hagelcount = my @hagelmatch = $single_warning->{'payload'}{'translationsLongText'}{'EN'} =~ /Hail/g; } # end language by AttrVal if ( $hagelcount ne 0 ) { Log $hash, 4, 'Warn_' . $i . '_Hail: 1'; $message .= 'Warn_' . $i . '_Hail|1|'; } else { Log $hash, 4, 'Warn_' . $i . '_Hail: 0'; $message .= 'Warn_' . $i . '_Hail|0|'; } ## Hagel end $i++; } my $max = 0; for (@uwzmaxlevel) { $max = $_ if !$max || $_ > $max; } $message .= 'WarnUWZLevel|'; $message .= $max . '|'; Log $hash, 4, 'WarnUWZLevel_Color: ' . GetSeverityColor( $hash, $max ); $message .= 'WarnUWZLevel_Color|' . GetSeverityColor( $hash, $max ) . '|'; ## Begin of redundant Reading if ( $UWZ_humanreadable eq 1 ) { if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { my %uwzlevelname = ( '0' => 'Stufe Grün (keine Warnung)', '1' => 'Stufe Dunkelgrün (Wetterhinweise)', '2' => 'Stufe Gelb (Vorwarnung für Unwetterwarnung)', '3' => 'Warnstufe Orange (Unwetterwarnung)', '4' => 'Warnstufe Rot (Unwetterwarnung)', '5' => 'Warnstufe Violett (Unwetterwarnung)' ); Log $hash, 4, 'WarnUWZLevel_Str: ' . $uwzlevelname{$max}; $message .= 'WarnUWZLevel_Str|' . $uwzlevelname{$max} . '|'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { my %uwzlevelname = ( '0' => 'niveau groen (geen waarschuwingen)', '1' => 'niveau donkergroen (voorwaarschuwing)', '2' => 'niveau geel (voorwaarschuwing)', '3' => 'waarschuwingsniveau oranje (waarschuwing voor matig noodweer)', '4' => 'waarschuwingsniveau rood (waarschuwing voor zwaar noodweer)', '5' => 'waarschuwingsniveau violet (waarschuwing voor zeer zwaar noodweer)' ); Log $hash, 4, 'WarnUWZLevel_Str: ' . $uwzlevelname{$max}; $message .= 'WarnUWZLevel_Str|' . $uwzlevelname{$max} . '|'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { my %uwzlevelname = ( '0' => 'niveau vert (aucune alerte)', '1' => 'niveau vert foncé (indication météo)', '2' => 'niveau jaune (pré-alerte)', '3' => 'niveau d\' alerte orange (alerte météo)', '4' => 'niveau d\' alerte rouge (alerte météo)', '5' => 'niveau d\' alerte violet (alerte météo)' ); Log $hash, 4, 'WarnUWZLevel_Str: ' . $uwzlevelname{$max}; $message .= 'WarnUWZLevel_Str|' . $uwzlevelname{$max} . '|'; } else { my %uwzlevelname = ( '0' => 'level green (no warnings)', '1' => 'level dark green (weather notice)', '2' => 'level yellow (severe weather watch)', '3' => 'Alert level Orange', '4' => 'Alert level Red', '5' => 'Alert level Violet' ); Log $hash, 4, 'WarnUWZLevel_Str: ' . $uwzlevelname{$max}; $message .= 'WarnUWZLevel_Str|' . $uwzlevelname{$max} . '|'; } } $message .= 'durationFetchReadings|'; $message .= sprintf "%.2f", time() - $readingStartTime; Log $hash, 3, 'Done fetching data'; Log $hash, 4, 'Will return : ' . "$name|$message|WarnCount|$uwz_warncount"; } else { $message = 'Error|' . $uwz_warnings->{Error}; $uwz_warncount = -1; } return "$name|$message|WarnCount|$uwz_warncount"; } ##################################### sub UWZAsHtml { my $name = shift; my $ret = ''; my $hash = $defs{$name}; my $htmlsequence = AttrVal( $name, 'htmlsequence', 'none' ); my $htmltitle = AttrVal( $name, 'htmltitle', '' ); my $htmltitleclass = AttrVal( $name, 'htmltitleclass', '' ); my $attr; if ( AttrVal( $name, 'htmlattr', 'none' ) ne 'none' ) { $attr = AttrVal( $name, 'htmlattr', '' ); } else { $attr = 'width="100%"'; } if ( ReadingsVal( $name, 'WarnCount', 0 ) != 0 ) { $ret .= ''; $ret .= '
'; $ret .= ''; if ( $htmlsequence eq 'descending' ) { for ( my $i = ReadingsVal( $name, 'WarnCount', -1 ) - 1 ; $i >= 0 ; $i-- ) { $ret .= UWZHtmlFrame( $hash, 'Warn_' . $i, $attr, 1 ); } } else { for ( my $i = 0 ; $i < ReadingsVal( $name, 'WarnCount', 0 ) ; $i++ ) { $ret .= UWZHtmlFrame( $hash, 'Warn_' . $i, $attr, 1 ); } } $ret .= '
' . $htmltitle . '
'; $ret .= '
'; } else { $ret .= ''; $ret .= '
'; $ret .= ''; $ret .= ''; $ret .= '
' . $htmltitle . '
'; # language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $ret .= 'Keine Warnungen'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $ret .= 'Geen waarschuwingen'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $ret .= 'Aucune alerte'; } else { $ret .= 'No Warnings'; } # end language by AttrVal $ret .= '
'; $ret .= '
'; } return $ret; } ##################################### sub UWZAsHtmlLite { my $name = shift; my $ret = ''; my $hash = $defs{$name}; my $htmlsequence = AttrVal( $name, 'htmlsequence', 'none' ); my $htmltitle = AttrVal( $name, 'htmltitle', '' ); my $htmltitleclass = AttrVal( $name, 'htmltitleclass', '' ); my $attr; if ( AttrVal( $name, 'htmlattr', 'none' ) ne 'none' ) { $attr = AttrVal( $name, 'htmlattr', '' ); } else { $attr = 'width="100%"'; } if ( ReadingsVal( $name, 'WarnCount', '' ) != 0 ) { $ret .= ''; $ret .= '
'; $ret .= ''; if ( $htmlsequence eq 'descending' ) { for ( my $i = ReadingsVal( $name, 'WarnCount', '' ) - 1 ; $i >= 0 ; $i-- ) { $ret .= UWZHtmlFrame( $hash, 'Warn_' . $i, $attr, 0 ); } } else { for ( my $i = 0 ; $i < ReadingsVal( $name, 'WarnCount', '' ) ; $i++ ) { $ret .= UWZHtmlFrame( $hash, 'Warn_' . $i, $attr, 0 ); } } $ret .= '
' . $htmltitle . '
'; $ret .= '
'; } else { $ret .= ''; $ret .= '
'; $ret .= ''; $ret .= ''; $ret .= '
' . $htmltitle . '
'; # language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $ret .= 'Keine Warnungen'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $ret .= 'Geen waarschuwingen'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $ret .= 'Aucune alerte'; } else { $ret .= 'No Warnings'; } # end language by AttrVal $ret .= '
'; $ret .= '
'; } return $ret; } ##################################### sub UWZAsHtmlFP { my $name = shift; my $tablewidth = ReadingsVal( $name, 'WarnCount', '' ) * 80; my $htmlsequence = AttrVal( $name, 'htmlsequence', 'none' ); my $htmltitle = AttrVal( $name, 'htmltitle', '' ); my $htmltitleclass = AttrVal( $name, 'htmltitleclass', '' ); my $ret = ''; $ret .= ''; $ret .= ''; if ( $htmlsequence eq 'descending' ) { for ( my $i = ReadingsVal( $name, 'WarnCount', '' ) - 1 ; $i >= 0 ; $i-- ) { $ret .= ''; } } else { for ( my $i = 0 ; $i < ReadingsVal( $name, 'WarnCount', '' ) ; $i++ ) { $ret .= ''; } } $ret .= ''; $ret .= '
' . $htmltitle . '
'; return $ret; } ##################################### sub UWZAsHtmlMovie { my $name = shift; my $land = shift; my $url = Map2Movie($land); my $hash = $defs{$name}; my $ret = ''; $ret .= '
'; $ret .= ''; $ret .= '
'; if ( defined($url) ) { $ret .= ''; } else { # language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $ret .= 'unbekannte Landbezeichnung'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $ret .= 'Onbekende landcode'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $ret .= 'code de pays inconnu'; } else { $ret .= 'unknown movie setting'; } # end language by AttrVal } $ret .= '
'; return $ret; } ##################################### sub UWZAsHtmlKarteLand { my $name = shift; my $land = shift; my $url = Map2Image($land); my $hash = $defs{$name}; my $ret = ''; $ret .= '
'; $ret .= ''; $ret .= '
'; if ( defined($url) ) { $ret .= ''; } else { # language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $ret .= 'unbekannte Landbezeichnung'; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $ret .= 'onbekende landcode'; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $ret .= 'code de pays inconnu'; } else { $ret .= 'unknown map setting'; } # end language by AttrVal } $ret .= '
'; return $ret; } ##################################### sub UWZHtmlFrame { my ( $hash, $readingStart, $attr, $parm ) = @_; my $ret = ''; my $name = $hash->{NAME}; $ret .= ''; $ret .= '' . ReadingsVal( $name, $readingStart . '_ShortText', '' ) . '

'; $ret .= ReadingsVal( $name, $readingStart . '_LongText', '' ) . '

' if ($parm); $ret .= UWZHtmlTimestamp( $hash, $readingStart . '_Start', $attr ); $ret .= UWZHtmlTimestamp( $hash, $readingStart . '_End', $attr ); $ret .= ''; $ret .= ''; return $ret; } ##################################### sub UWZHtmlTimestamp { my @DEText = qw(Anfang: Ende: Uhr); my @NLText = qw(Begin: Einde: uur); my @FRText = ( "Valide à partir du:", "Jusqu\'au:", "heure" ); my @ENText = qw(Start: End: hour); my $hash = shift; my $reading = shift; my $attr = shift; my $ret; my $StartEnd = ''; my $name = $hash->{NAME}; if ( substr( $reading, 7, 1 ) eq 'S' ) { $StartEnd = 0; $ret .= ''; } else { $StartEnd = 1; } my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime( ReadingsVal( $name, $reading, '' ) ); if ( length($hour) == 1 ) { $hour = "0$hour"; } if ( length($min) == 1 ) { $min = "0$min"; } # language by AttrVal if ( $hash->{CountryCode} ~~ [ 'DE', 'AT', 'CH' ] ) { $ret .= ""; } elsif ( $hash->{CountryCode} ~~ ['NL'] ) { $ret .= ""; } elsif ( $hash->{CountryCode} ~~ ['FR'] ) { $ret .= ""; } else { $ret .= ""; } return $ret; } ##################################### sub GetSeverityColor { my $name = shift; my $uwzlevel = shift; my $alertcolor = ''; my %UWZSeverity = ( '0' => 'gruen', '1' => 'orange', '2' => 'gelb', '3' => 'orange', '4' => 'rot', '5' => 'violett' ); return $UWZSeverity{$uwzlevel}; } ##################################### sub GetUWZLevel { my $name = shift; my $warnname = shift; my @alert = split( /_/, $warnname ); if ( $alert[0] eq 'notice' ) { return '1'; } elsif ( $alert[1] eq 'forewarn' ) { return '2'; } else { my %UWZSeverity = ( 'green' => '0', 'yellow' => '2', 'orange' => '3', 'red' => '4', 'violet' => '5' ); return $UWZSeverity{ $alert[2] }; } return; } ##################################### sub IntervalAtWarnLevel { my $hash = shift; my $name = $hash->{NAME}; my $warnLevel = ReadingsVal( $name, 'WarnUWZLevel', 0 ); my @valuestring = split( ',', AttrVal( $name, 'intervalAtWarnLevel', '' ) ); my %warnLevelInterval; readingsSingleUpdate( $hash, 'currentIntervalMode', 'warn', 0 ); foreach (@valuestring) { my @values = split( '=', $_ ); $warnLevelInterval{ $values[0] } = $values[1]; } if ( defined( $warnLevelInterval{$warnLevel} ) && $hash->{INTERVALWARN} != $warnLevelInterval{$warnLevel} ) { $hash->{INTERVALWARN} = $warnLevelInterval{$warnLevel}; RemoveInternalTimer($hash); InternalTimer( gettimeofday() + $hash->{INTERVALWARN}, 'UWZ_Start', $hash ); Log $hash, 4, "restart internal timer with interval $hash->{INTERVALWARN}"; } else { RemoveInternalTimer($hash); InternalTimer( gettimeofday() + $hash->{INTERVALWARN}, "UWZ_Start", $hash ); Log $hash, 4, "restart internal timer with interval $hash->{INTERVALWARN}"; } return; } ##################################### ## ## UWZ Helper Functions ## ##################################### sub UWZSearchLatLon { my $name = shift; my $loc = shift; my $err_log = ''; my $url = 'https://alertspro.geoservice.meteogroup.de/weatherpro/SearchFeed.php?search=' . $loc; my $agent = LWP::UserAgent->new( env_proxy => 1, keep_alive => 1, protocols_allowed => ['http'], timeout => 10 ); my $request = HTTP::Request->new( GET => $url ); my $response = $agent->request($request); $err_log = 'Can\'t get ' . $url . ' -- ' . $response->status_line if ( !$response->is_success ); if ( $err_log ne '' ) { print 'Error|Error ' . $response->status_line; } use XML::Simple qw(:strict); use Encode qw(decode encode); my $uwzxmlparser = XML::Simple->new(); my $search = $uwzxmlparser->XMLin( $response->content, KeyAttr => { 'city' => 'id' }, ForceArray => ['city'] ); my $ret = '
$DEText[$StartEnd]" . "$DEweekdays[$wday], $mday $DEmonths[$mon] " . ( 1900 + $year ) . " $hour:$min " . "$DEText[2]$NLText[$StartEnd]" . "$NLweekdays[$wday], $mday $NLmonths[$mon] " . ( 1900 + $year ) . " $hour:$min " . "$NLText[2]$FRText[$StartEnd]" . "$FRweekdays[$wday], $mday $FRmonths[$mon] " . ( 1900 + $year ) . " $hour:$min " . "$FRText[2]$ENText[$StartEnd]" . "$ENweekdays[$wday], $mday $ENmonths[$mon] " . ( 1900 + $year ) . " $hour:$min " . "$ENText[2]
'; $ret .= '
'; $ret .= ''; $ret .= ''; $ret .= ''; $ret .= ''; $ret .= ''; $ret .= ''; $ret .= ''; foreach my $locres ( $search->{cities}->{city} ) { my $linecount = 1; while ( my ( $key, $value ) = each(%$locres) ) { if ( $linecount % 2 == 0 ) { $ret .= ''; } else { $ret .= ''; } $ret .= ''; $ret .= ''; $ret .= ''; $ret .= ''; my $aHref = 'Get AreaID'; $ret .= ''; $ret .= ''; $linecount++; } } $ret .= '
citycountrylatitudelongitude
' . encode( 'utf-8', $value->{'name'} ) . '' . $value->{'country-name'} . '' . $value->{'latitude'} . '' . $value->{'longitude'} . '' . $aHref . '
'; return $ret; } ##################################### sub UWZSearchAreaID { my $lat = shift; my $lon = shift; my $err_log = ''; my $url = 'https://feed.alertspro.meteogroup.com/AlertsPro/AlertsProPollService.php?method=lookupCoord&lat=' . $lat . '&lon=' . $lon; my $agent = LWP::UserAgent->new( env_proxy => 1, keep_alive => 1, protocols_allowed => ['http'], timeout => 10 ); my $request = HTTP::Request->new( GET => $url ); my $response = $agent->request($request); $err_log = "Can't get $url -- " . $response->status_line if ( !$response->is_success ); if ( $err_log ne '' ) { print "Error|Error " . $response->status_line; } use JSON; my @perl_scalar = @{ JSON->new->utf8->decode( $response->content ) }; my $AreaType = $perl_scalar[0]->{'AREA_TYPE'}; my $CC = substr $perl_scalar[0]->{'AREA_ID'}, 3, 2; my $AreaID = substr $perl_scalar[0]->{'AREA_ID'}, 5, 5; if ( $AreaType eq 'UWZ' ) { my $ret = 'Please use the following statement to define Unwetterzentrale for your location:

'; $ret .= '
'; $ret .= ''; $ret .= ''; $ret .= ""; $ret .= ''; $ret .= '
define Unwetterzentrale UWZ $CC $AreaID 3600
'; $ret .= '
'; $ret .= '
'; $ret .= 'You can also use weblinks to add weathermaps. For a list of possible Weblinks see Commandref. For example to add the Europe Map use:
'; $ret .= '
'; $ret .= ''; $ret .= ''; $ret .= ""; $ret .= ''; $ret .= '
define UWZ_Map_Europe weblink htmlCode { UWZAsHtmlKarteLand('Unwetterzentrale','europa') }
'; $ret .= '
'; $ret .= ''; return $ret; } else { return 'Sorry, nothing found or not implemented'; } return; } ##################################### 1; =pod =item device =item summary extracts thunderstorm warnings from unwetterzentrale.de =item summary_DE extrahiert Unwetterwarnungen von unwetterzentrale.de =begin html

UWZ

    This modul extracts thunderstorm warnings from www.unwetterzentrale.de.
    Therefore the same interface is used as the Android App Alerts Pro does. A maximum of 10 thunderstorm warnings will be served. Additional the module provides a few functions to create HTML-Templates which can be used with weblink.
    The following Perl-Modules are used within this module: HTTP::Request, LWP::UserAgent, JSON, Encode::Guess und HTML::Parse.

    Define

      define <Name> UWZ [CountryCode] [AreaID] [INTERVAL]


      Example:
      define Unwetterzentrale UWZ UK 08357 1800
      attr Unwetterzentrale download 1
      attr Unwetterzentrale humanreadable 1
      attr Unwetterzentrale maps eastofengland unitedkingdom

      define UnwetterDetails weblink htmlCode {FHEM::UWZ::UWZAsHtml("Unwetterzentrale")}
      define UnwetterMapE_UK weblink htmlCode {FHEM::UWZ::UWZAsHtmlKarteLand("Unwetterzentrale","eastofengland")}
      define UnwetterLite weblink htmlCode {FHEM::UWZ::UWZAsHtmlLite("Unwetterzentrale")} define UnwetterMovie weblink htmlCode {FHEM::UWZ::UWZAsHtmlMovie("Unwetterzentrale","clouds-precipitation-uk")}

       
    • [CountryCode]
      Possible values: DE, AT, CH, UK, ...
      (for other countries than germany use SEARCH for CountryCode to start device in search mode)

    • [AreaID]
      For Germany you can use the postalcode, other countries use SEARCH for CountryCode to start device in search mode.

    • [INTERVAL]
      Defines the refresh interval. The interval is defined in seconds, so an interval of 3600 means that every hour a refresh will be triggered onetimes.




    • Example Search-Mode:
      define Unwetterzentrale UWZ SEARCH

      now get the AreaID for your location (example shows london):
      get Unwetterzentrale SearchAreaID London

      now redefine your device with the outputted CountryCode and AreaID.

       

    Get

    • get <name> soil-frost
      give info about current soil frost (active|inactive).

    • get <name> extremfrost
      give info about current frost (active|inactive).

    • get <name> thunderstorm
      give info about current thunderstorm (active|inactive).

    • get <name> glaze
      give info about current glaze (active|inactive).

    • get <name> glazed-rain
      give info about current freezing rain (active|inactive).

    • get <name> hail
      give info about current hail (active|inactive).

    • get <name> heat
      give info about current heat (active|inactive).

    • get <name> rain
      give info about current rain (active|inactive).

    • get <name> snow
      give info about current snow (active|inactive).

    • get <name> storm
      give info about current storm (active|inactive).

    • get <name> forest-fire
      give info about current forest fire (active|inactive).


    Get (Search-Mode)

    • get <name> SearchAreaID <city>
      Get AreaID coresponnding to entered location.


    Set

    • set <name> update
      Executes an imediate update of thunderstorm warnings.


    Attributes

    • download
      Download maps during update (0|1).
    • savepath
      Define where to store the map png files (default: /tmp/).
    • maps
      Define the maps to download space seperated. For possible values see UWZAsHtmlKarteLand.
    • humanreadable
      Add additional Readings Warn_?_Start_Date, Warn_?_Start_Time, Warn_?_End_Date and Warn_?_End_Time containing the coresponding timetamp in a human readable manner. Additionally Warn_?_uwzLevel_Str and Warn_?_Type_Str will be added to device readings (0|1).
    • lang
      Overwrite requested language for short and long warn text. (de|en|it|fr|es|..).
    • sort_readings_by
      define how readings will be sortet (start|severity|creation).
    • htmlsequence
      define warn order of html output (ascending|descending).
    • htmltitle
      title / header for the html ouput
    • htmltitleclass
      css-Class of title / header for the html ouput
    • localiconbase
      define baseurl to host your own thunderstorm warn pics (filetype is png).
    • intervalAtWarnLevel
      define the interval per warnLevel. Example: 2=1800,3=900,4=300


    Readings

    • Warn_0|1|2|3...|9_... - active warnings
    • WarnCount - warnings count
    • WarnUWZLevel - total warn level
    • WarnUWZLevel_Color - total warn level color
    • WarnUWZLevel_Str - total warn level string
    • Warn_0_AltitudeMin - minimum altitude for warning
    • Warn_0_AltitudeMax - maximum altitude for warning
    • Warn_0_EventID - warning EventID
    • Warn_0_Creation - warning creation
    • Warn_0_Creation_Date - warning creation datum
    • Warn_0_Creation_Time - warning creation time
    • currentIntervalMode - default/warn, Interval is read from INTERVAL or INTERVALWARN Internal
    • Warn_0_Start - begin of warnperiod
    • Warn_0_Start_Date - start date of warnperiod
    • Warn_0_Start_Time - start time of warnperiod
    • Warn_0_End - end of warnperiod
    • Warn_0_End_Date - end date of warnperiod
    • Warn_0_End_Time - end time of warnperiod
    • Warn_0_Severity - Severity of thunderstorm (0 no thunderstorm, 4, 7, 11, .. heavy thunderstorm)
    • Warn_0_Hail - warning contains hail
    • Warn_0_Type - kind of thunderstorm
    • Warn_0_Type_Str - kind of thunderstorm (text)
      • 1 - unknown
      • 2 - storm
      • 3 - snow
      • 4 - rain
      • 5 - frost
      • 6 - forest fire
      • 7 - thunderstorm
      • 8 - glaze
      • 9 - heat
      • 10 - freezing rain
      • 11 - soil frost
    • Warn_0_uwzLevel - Severity of thunderstorm (0-5)
    • Warn_0_uwzLevel_Str - Severity of thunderstorm (text)
    • Warn_0_levelName - Level Warn Name
    • Warn_0_ShortText - short warn text
    • Warn_0_LongText - detailed warn text
    • Warn_0_IconURL - cumulated URL to display warn-icons from www.unwetterzentrale.de

    Weblinks

      With the additional implemented functions UWZAsHtml, UWZAsHtmlLite, UWZAsHtmlFP, UWZAsHtmlKarteLand and UWZAsHtmlMovie HTML-Code will be created to display warnings and weathermovies, using weblinks.


      Example:
    • define UnwetterDetailiert weblink htmlCode {FHEM::UWZ::UWZAsHtml("Unwetterzentrale")}

    • define UnwetterLite weblink htmlCode {FHEM::UWZ::UWZAsHtmlLite("Unwetterzentrale")}

    • define UnwetterFloorplan weblink htmlCode {FHEM::UWZ::UWZAsHtmlFP("Unwetterzentrale")}

    • define UnwetterKarteLand weblink htmlCode {FHEM::UWZ::UWZAsHtmlKarteLand("Unwetterzentrale","Bayern")}
      • The second parameter should be one of:
        • europa

        • deutschland
        • deutschland-small
        • niedersachsen
        • bremen
        • bayern
        • schleswig-holstein
        • hamburg
        • mecklenburg-vorpommern
        • sachsen
        • sachsen-anhalt
        • nordrhein-westfalen
        • thueringen
        • rheinland-pfalz
        • saarland
        • baden-wuerttemberg
        • hessen
        • brandenburg
        • berlin

        • oesterreich
        • burgenland
        • kaernten
        • niederoesterreich
        • oberoesterreich
        • salzburg
        • steiermark
        • tirol
        • vorarlberg
        • wien

        • schweiz
        • aargau
        • appenzell_ausserrhoden
        • appenzell_innerrhoden
        • basel_landschaft
        • basel_stadt
        • bern
        • fribourg
        • geneve
        • glarus
        • graubuenden
        • jura
        • luzern
        • neuchatel
        • nidwalden
        • obwalden
        • schaffhausen
        • schwyz
        • solothurn
        • stgallen
        • ticino
        • thurgau
        • uri
        • waadt
        • wallis
        • zug
        • zuerich

        • liechtenstein

        • belgique

        • denmark

        • finnland

        • france
        • alsace
        • aquitaine
        • basse-normandie
        • bretagne
        • champagne-ardenne
        • franche-comte
        • haute-normandie
        • ile-de-france
        • languedoc-roussillon
        • limousin
        • lorraine
        • bourgogne
        • centre
        • midi-pyrenees
        • nord-pas-de-calais
        • pays-de-la-loire
        • picardie
        • poitou-charentes
        • provence-alpes-cote-dazur
        • rhone-alpes

        • letzebuerg

        • nederland
        • drenthe
        • flevoland
        • friesland
        • gelderland
        • groningen
        • limburg
        • noordbrabant
        • noordholland
        • overijssel
        • utrecht
        • zeeland
        • zuidholland

        • norwegen

        • portugal

        • sverige

        • espana

        • unitedkingdom
        • eastofengland
        • eastmidlands
        • london
        • northeastengland
        • northernireland
        • northwestengland
        • scotland
        • southeastengland
        • southwestengland
        • wales
        • westmidlands
        • yorkshireandthehumber

        • isobaren1
        • isobaren2
        • isobaren3
    • define UnwetterKarteMovie weblink htmlCode {FHEM::UWZ::UWZAsHtmlMovie("Unwetterzentrale","currents")}
      • The second parameter should be one of:
        • niederschlag-wolken
        • stroemung
        • temperatur

        • niederschlag-wolken-de
        • stroemung-de

        • niederschlag-wolken-ch
        • stroemung-ch

        • niederschlag-wolken-at
        • stroemung-at

        • neerslag-wolken-nl
        • stroming-nl

        • nuages-precipitations-fr
        • courants-fr

        • clouds-precipitation-uk
        • currents-uk




=end html =begin html_DE

UWZ

    Das Modul extrahiert Unwetterwarnungen von www.unwetterzentrale.de.
    Hierfür wird die selbe Schnittstelle verwendet die auch die Android App Alerts Pro nutzt. Es werden maximal 10 Standortbezogene Unwetterwarnungen zur Verfügung gestellt. Weiterhin verfügt das Modul über HTML-Templates welche als weblink verwendet werden können.
    Es nutzt die Perl-Module HTTP::Request, LWP::UserAgent, JSON, Encode::Guess und HTML::Parse.

    Define

      define <Name> UWZ [Ländercode] [Postleitzahl] [INTERVAL]


      Beispiel:
      define Unwetterzentrale UWZ DE 86405 3600
       
    • [Ländercode]
      Mögliche Werte: DE, AT, CH, SEARCH, ...
      (für ander Länder als Deutschland bitte den SEARCH Parameter nutzen um die AreaID zu ermitteln.)

    • [Postleitzahl/AreaID]
      Die Postleitzahl/AreaID des Ortes für den Unwetterinformationen abgefragt werden sollen.

    • [INTERVAL]
      Definiert das Interval zur aktualisierung der Unwetterwarnungen. Das Interval wird in Sekunden angegeben, somit aktualisiert das Modul bei einem Interval von 3600 jede Stunde 1 mal.


    Get

    • get <name> Bodenfrost
      Gibt aus ob aktuell eine Bodenfrostwarnung besteht (active|inactive).

    • get <name> Extremfrost
      Gibt aus ob aktuell eine Extremfrostwarnung besteht (active|inactive).

    • get <name> Gewitter
      Gibt aus ob aktuell eine Gewitter Warnung besteht (active|inactive).

    • get <name> Glaette
      Gibt aus ob aktuell eine Glaettewarnung besteht (active|inactive).

    • get <name> Glatteisregen
      Gibt aus ob aktuell eine Glatteisregen Warnung besteht (active|inactive).

    • get <name> Hagel
      Gibt aus ob aktuell eine Hagel Warnung besteht (active|inactive).

    • get <name> Hitze
      Gibt aus ob aktuell eine Hitze Warnung besteht (active|inactive).

    • get <name> Regen
      Gibt aus ob aktuell eine Regen Warnung besteht (active|inactive).

    • get <name> Schneefall
      Gibt aus ob aktuell eine Schneefall Warnung besteht (active|inactive).

    • get <name> Sturm
      Gibt aus ob aktuell eine Sturm Warnung besteht (active|inactive).

    • get <name> Waldbrand
      Gibt aus ob aktuell eine Waldbrand Warnung besteht (active|inactive).


    Get (Search-Mode)

    • get <name> SearchAreaID <gesuchte_stadt>
      Gibt die AreaID zum eingegebenen Ort aus.


    Set

    • set <name> update
      Startet sofort ein neues Auslesen der Unwetterinformationen.


    Attribute

    • download
      Download Unwetterkarten während des updates (0|1).
    • savepath
      Pfad zum speichern der Karten (default: /tmp/).
    • maps
      Leerzeichen separierte Liste der zu speichernden Karten. Für mögliche Karten siehe UWZAsHtmlKarteLand.
    • humanreadable
      Anzeige weiterer Readings Warn_?_Start_Date, Warn_?_Start_Time, Warn_?_End_Date, Warn_?_End_Time. Diese Readings enthalten aus dem Timestamp kalkulierte Datums/Zeit Angaben. Weiterhin werden folgende Readings aktivier: Warn_?_Type_Str und Warn_?_uwzLevel_Str welche den Unwettertyp als auch das Unwetter-Warn-Level als Text ausgeben. (0|1)
    • lang
      Umschalten der angeforderten Sprache für kurz und lange warn text. (de|en|it|fr|es|..).
    • sort_readings_by
      Sortierreihenfolge der Warnmeldungen. (start|severity|creation).
    • htmlsequence
      Anzeigereihenfolge der html warnungen. (ascending|descending).
    • htmltitle
      Titel / Ueberschrift der HTML Ausgabe
    • htmltitleclass
      css-Class des Titels der HTML Ausgabe
    • localiconbase
      BaseURL angeben um Warn Icons lokal zu hosten. (Dateityp ist png).
    • intervalAtWarnLevel
      konfiguriert den Interval je nach WarnLevel. Beispiel: 2=1800,3=900,4=300


    Readings

    • Warn_0|1|2|3...|9_... - aktive Warnmeldungen
    • WarnCount - Anzahl der aktiven Warnmeldungen
    • WarnUWZLevel - Gesamt Warn Level
    • WarnUWZLevel_Color - Gesamt Warn Level Farbe
    • WarnUWZLevel_Str - Gesamt Warn Level Text
    • Warn_0_AltitudeMin - minimum Höhe für Warnung
    • Warn_0_AltitudeMax - maximum Höhe für Warnung
    • Warn_0_EventID - EventID der Warnung
    • Warn_0_Creation - Warnungs Erzeugung
    • Warn_0_Creation_Date - Warnungs Erzeugungs Datum
    • Warn_0_Creation_Time - Warnungs Erzeugungs Zeit
    • currentIntervalMode - default/warn, aktuell Verwendeter Interval. Internal INTERVAL oder INTERVALWARN
    • Warn_0_Start - Begin der Warnung
    • Warn_0_Start_Date - Startdatum der Warnung
    • Warn_0_Start_Time - Startzeit der Warnung
    • Warn_0_End - Warn Ende
    • Warn_0_End_Date - Enddatum der Warnung
    • Warn_0_End_Time - Endzeit der Warnung
    • Warn_0_Severity - Schwere des Unwetters (0 kein Unwetter, 12 massives Unwetter)
    • Warn_0_Hail - Hagelwarnung (1|0)
    • Warn_0_Type - Art des Unwetters
    • Warn_0_Type_Str - Art des Unwetters (text)
      • 1 - unbekannt
      • 2 - Sturm/Orkan
      • 3 - Schneefall
      • 4 - Regen
      • 5 - Extremfrost
      • 6 - Waldbrandgefahr
      • 7 - Gewitter
      • 8 - Glätte
      • 9 - Hitze
      • 10 - Glatteisregen
      • 11 - Bodenfrost
    • Warn_0_uwzLevel - Unwetterwarnstufe (0-5)
    • Warn_0_uwzLevel_Str - Unwetterwarnstufe (text)
    • Warn_0_levelName - Level Warn Name
    • Warn_0_ShortText - Kurzbeschreibung der Warnung
    • Warn_0_LongText - Ausführliche Unwetterbeschreibung
    • Warn_0_IconURL - Kumulierte URL um Warnungs-Icon von www.unwetterzentrale.de anzuzeigen

    Weblinks

      Über die Funktionen UWZAsHtml, UWZAsHtmlLite, UWZAsHtmlFP, UWZAsHtmlKarteLand, UWZAsHtmlMovie wird HTML-Code zur Warnanzeige und Wetterfilme über weblinks erzeugt.


      Beispiele:
    • define UnwetterDetailiert weblink htmlCode {FHEM::UWZ::UWZAsHtml("Unwetterzentrale")}

    • define UnwetterLite weblink htmlCode {FHEM::UWZ::UWZAsHtmlLite("Unwetterzentrale")}

    • define UnwetterFloorplan weblink htmlCode {FHEM::UWZ::UWZAsHtmlFP("Unwetterzentrale")}

    • define UnwetterKarteLand weblink htmlCode {FHEM::UWZ::UWZAsHtmlKarteLand("Unwetterzentrale","Bayern")}
      • Der zweite Parameter kann einer der folgenden sein:
        • europa

        • deutschland
        • deutschland-small
        • niedersachsen
        • bremen
        • bayern
        • schleswig-holstein
        • hamburg
        • mecklenburg-vorpommern
        • sachsen
        • sachsen-anhalt
        • nordrhein-westfalen
        • thueringen
        • rheinland-pfalz
        • saarland
        • baden-wuerttemberg
        • hessen
        • brandenburg
        • berlin

        • oesterreich
        • burgenland
        • kaernten
        • niederoesterreich
        • oberoesterreich
        • salzburg
        • steiermark
        • tirol
        • vorarlberg
        • wien

        • schweiz
        • aargau
        • appenzell_ausserrhoden
        • appenzell_innerrhoden
        • basel_landschaft
        • basel_stadt
        • bern
        • fribourg
        • geneve
        • glarus
        • graubuenden
        • jura
        • luzern
        • neuchatel
        • nidwalden
        • obwalden
        • schaffhausen
        • schwyz
        • solothurn
        • stgallen
        • ticino
        • thurgau
        • uri
        • waadt
        • wallis
        • zug
        • zuerich

        • liechtenstein

        • belgique

        • denmark

        • finnland

        • france
        • alsace
        • aquitaine
        • basse-normandie
        • bretagne
        • champagne-ardenne
        • franche-comte
        • haute-normandie
        • ile-de-france
        • languedoc-roussillon
        • limousin
        • lorraine
        • bourgogne
        • centre
        • midi-pyrenees
        • nord-pas-de-calais
        • pays-de-la-loire
        • picardie
        • poitou-charentes
        • provence-alpes-cote-dazur
        • rhone-alpes

        • letzebuerg

        • nederland
        • drenthe
        • flevoland
        • friesland
        • gelderland
        • groningen
        • limburg
        • noordbrabant
        • noordholland
        • overijssel
        • utrecht
        • zeeland
        • zuidholland

        • norwegen

        • portugal

        • sverige

        • espana

        • unitedkingdom
        • eastofengland
        • eastmidlands
        • london
        • northeastengland
        • northernireland
        • northwestengland
        • scotland
        • southeastengland
        • southwestengland
        • wales
        • westmidlands
        • yorkshireandthehumber

        • isobaren1
        • isobaren2
        • isobaren3
    • define UnwetterKarteMovie weblink htmlCode {FHEM::UWZ::UWZAsHtmlMovie("Unwetterzentrale","niederschlag-wolken-de")}
      • Der zweite Parameter kann einer der folgenden sein:
        • niederschlag-wolken
        • stroemung
        • temperatur

        • niederschlag-wolken-de
        • stroemung-de

        • niederschlag-wolken-ch
        • stroemung-ch

        • niederschlag-wolken-at
        • stroemung-at

        • neerslag-wolken-nl
        • stroming-nl

        • nuages-precipitations-fr
        • courants-fr

        • clouds-precipitation-uk
        • currents-uk




=end html_DE =for :application/json;q=META.json 77_UWZ.pm { "abstract": "Module to extracts thunderstorm warnings from unwetterzentrale.de", "x_lang": { "de": { "abstract": "Modul zum anzeigen von Unwetterwarnungen von unwetterzentrale.de" } }, "keywords": [ "fhem-mod-device", "fhem-core", "Unwetter", "Wetter", "Warnungen" ], "release_status": "stable", "license": "GPL_2", "version": "v3.1.0", "author": [ "Marko Oldenburg " ], "x_fhem_maintainer": [ "CoolTux" ], "x_fhem_maintainer_github": [ "LeonGaultier" ], "prereqs": { "runtime": { "requires": { "FHEM": 5.00918799, "perl": 5.016, "Meta": 0, "JSON": 0, "Date::Parse": 0 }, "recommends": { }, "suggests": { } } } } =end :application/json;q=META.json =cut