From aa188f7017d53072c01190084023adcb4c817c1d Mon Sep 17 00:00:00 2001 From: LeonGaultier Date: Wed, 7 Nov 2018 12:45:10 +0000 Subject: [PATCH] 46_Aqicn: fix commandref, change to packages git-svn-id: https://svn.fhem.de/fhem/trunk@17701 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 1 + fhem/FHEM/46_Aqicn.pm | 983 +++++++++++++++++++++++++----------------- 2 files changed, 593 insertions(+), 391 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 037ab7a0a..43ff81712 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - bugfix: 46_Aqicn: fix commandref, change to packages - bugfix: 88_HMCCU: fixed set toggle command - new: 98_livetracking: added module - bugfix: 38_netatmo: changed geocoding to openstreetmap diff --git a/fhem/FHEM/46_Aqicn.pm b/fhem/FHEM/46_Aqicn.pm index e348cd8fd..7c030fef9 100644 --- a/fhem/FHEM/46_Aqicn.pm +++ b/fhem/FHEM/46_Aqicn.pm @@ -1,5 +1,5 @@ ############################################################################### -# +# # Developed with Kate # # (c) 2017 Copyright: Marko Oldenburg (leongaultier at gmail dot com) @@ -35,7 +35,7 @@ # # if($@){ # Log3($SELF, 2, "$TYPE ($SELF) - error while request: $@"); -# +# # readingsSingleUpdate($hash, "state", "error", 1); # # return; @@ -47,483 +47,650 @@ ## ## - - package main; +use strict; +use warnings; -my $missingModul = ""; +my $version = "0.4.2"; + +sub Aqicn_Initialize($) { + + my ($hash) = @_; + + # Consumer + $hash->{GetFn} = "Aqicn::Get"; + $hash->{DefFn} = "Aqicn::Define"; + $hash->{UndefFn} = "Aqicn::Undef"; + $hash->{NotifyFn} = "Aqicn::Notify"; + + $hash->{AttrFn} = "Aqicn::Attr"; + $hash->{AttrList} = + "interval " . "disable:1 " . "language:de,en " . $readingFnAttributes; + + foreach my $d ( sort keys %{ $modules{Aqicn}{defptr} } ) { + + my $hash = $modules{Aqicn}{defptr}{$d}; + $hash->{VERSION} = $version; + } +} + +package Aqicn; use strict; use warnings; use HttpUtils; -eval "use Encode qw(encode encode_utf8 decode_utf8);1" or $missingModul .= "Encode "; + +use GPUtils qw(GP_Import) + ; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt + +my $missingModul = ""; +eval "use Encode qw(encode encode_utf8 decode_utf8);1" + or $missingModul .= "Encode "; eval "use JSON;1" or $missingModul .= "JSON "; - -my $version = "0.2.1"; - - - +## Import der FHEM Funktionen +BEGIN { + GP_Import( + qw(readingsSingleUpdate + readingsBulkUpdate + readingsBulkUpdateIfChanged + readingsBeginUpdate + readingsEndUpdate + defs + modules + Log3 + CommandAttr + AttrVal + IsDisabled + deviceEvents + init_done + gettimeofday + InternalTimer + RemoveInternalTimer + urlEncode + HttpUtils_NonblockingGet + makeDeviceName + asyncOutput) + ); +} ### Air Quality Index scale my %AQIS = ( - 1 => { 'i18nde' => 'Gut' ,'i18nen' => 'Good' ,'bgcolor' => '#009966' ,'font color' => '#FFFFFF'}, - 2 => { 'i18nde' => 'Moderat' ,'i18nen' => 'Moderate' ,'bgcolor' => '#ffde33' ,'font color' => '#000000'}, - 3 => { 'i18nde' => 'Ungesund für empfindliche Personengruppen' ,'i18nen' => 'Unhealthy for Sensitive Groups' ,'bgcolor' => '#ff9933' ,'font color' => '#000000'}, - 4 => { 'i18nde' => 'Ungesund' ,'i18nen' => 'Unhealthy' ,'bgcolor' => '#cc0033' ,'font color' => '#FFFFFF'}, - 5 => { 'i18nde' => 'Sehr ungesund' ,'i18nen' => 'Very Unhealthy' ,'bgcolor' => '#660099' ,'font color' => '#FFFFFF'}, - 6 => { 'i18nde' => 'Gefährlich' ,'i18nen' => 'Hazardous' ,'bgcolor' => '#7e0023' ,'font color' => '#FFFFFF'}, - ); - - - -# Declare functions -sub Aqicn_Attr(@); -sub Aqicn_Define($$); -sub Aqicn_Initialize($); -sub Aqicn_Get($$@); -sub Aqicn_Notify($$); -sub Aqicn_GetData($;$); -sub Aqicn_Undef($$); -sub Aqicn_ResponseProcessing($$$); -sub Aqicn_ReadingsProcessing_SearchStationResponse($$); -sub Aqicn_ReadingsProcessing_AqiResponse($); -sub Aqicn_ErrorHandling($$$); -sub Aqicn_WriteReadings($$); -sub Aqicn_Timer_GetData($); -sub Aqicn_AirPollutionLevel($); -sub Aqicn_HtmlStyle($); -sub Aqicn_i18n_de($); -sub Aqicn_i18n_en($); -sub Aqicn_HealthImplications($$); - - - - - - -my %paths = ( 'statussoe' => 'system_status/soe', - 'aggregates' => 'meters/aggregates', - 'siteinfo' => 'site_info', - 'sitemaster' => 'sitemaster', - 'powerwalls' => 'powerwalls', - 'registration' => 'customer/registration', - 'status' => 'status' + 1 => { + 'i18nde' => 'Gut', + 'i18nen' => 'Good', + 'bgcolor' => '#009966', + 'font color' => '#FFFFFF' + }, + 2 => { + 'i18nde' => 'Moderat', + 'i18nen' => 'Moderate', + 'bgcolor' => '#ffde33', + 'font color' => '#000000' + }, + 3 => { + 'i18nde' => 'Ungesund für empfindliche Personengruppen', + 'i18nen' => 'Unhealthy for Sensitive Groups', + 'bgcolor' => '#ff9933', + 'font color' => '#000000' + }, + 4 => { + 'i18nde' => 'Ungesund', + 'i18nen' => 'Unhealthy', + 'bgcolor' => '#cc0033', + 'font color' => '#FFFFFF' + }, + 5 => { + 'i18nde' => 'Sehr ungesund', + 'i18nen' => 'Very Unhealthy', + 'bgcolor' => '#660099', + 'font color' => '#FFFFFF' + }, + 6 => { + 'i18nde' => 'Gefährlich', + 'i18nen' => 'Hazardous', + 'bgcolor' => '#7e0023', + 'font color' => '#FFFFFF' + }, ); +my %paths = ( + 'statussoe' => 'system_status/soe', + 'aggregates' => 'meters/aggregates', + 'siteinfo' => 'site_info', + 'sitemaster' => 'sitemaster', + 'powerwalls' => 'powerwalls', + 'registration' => 'customer/registration', + 'status' => 'status' +); -sub Aqicn_Initialize($) { - - my ($hash) = @_; - - # Consumer - $hash->{GetFn} = "Aqicn_Get"; - $hash->{DefFn} = "Aqicn_Define"; - $hash->{UndefFn} = "Aqicn_Undef"; - $hash->{NotifyFn} = "Aqicn_Notify"; - - $hash->{AttrFn} = "Aqicn_Attr"; - $hash->{AttrList} = "interval ". - "disable:1 ". - "language:de,en ". - $readingFnAttributes; - - foreach my $d(sort keys %{$modules{Aqicn}{defptr}}) { - - my $hash = $modules{Aqicn}{defptr}{$d}; - $hash->{VERSION} = $version; - } -} - -sub Aqicn_Define($$) { +sub Define($$) { my ( $hash, $def ) = @_; - + my @a = split( "[ \t][ \t]*", $def ); - - - if( $a[2] =~ /^token=/ ) { + + if ( $a[2] =~ /^token=/ ) { $a[2] =~ m/token=([^\s]*)/; $hash->{TOKEN} = $1; - - } else { + + } + else { $hash->{UID} = $a[2]; } - - return "Cannot define a Aqicn device. Perl modul $missingModul is missing." if ( $missingModul ); - return "too few parameters: define Aqicn " if( @a != 3 ); - return "too few parameters: define Aqicn token=" if( not defined($hash->{TOKEN}) and not defined($modules{Aqicn}{defptr}{TOKEN}) ); - return "too few parameters: define Aqicn " if( not defined($hash->{UID}) and defined($modules{Aqicn}{defptr}{TOKEN}) ); - - - my $name = $a[0]; - $hash->{VERSION} = $version; - $hash->{NOTIFYDEV} = "global"; - - - - - - if( defined($hash->{TOKEN}) ) { - return "there is already a Aqicn Head Device, did you want to define a Aqicn station use: define Aqicn " if( $modules{Aqicn}{defptr}{TOKEN} ); + return "Cannot define a Aqicn device. Perl modul $missingModul is missing." + if ($missingModul); + return "too few parameters: define Aqicn " + if ( @a != 3 ); + return "too few parameters: define Aqicn token=" + if ( not defined( $hash->{TOKEN} ) + and not defined( $modules{Aqicn}{defptr}{TOKEN} ) ); + return "too few parameters: define Aqicn " + if ( not defined( $hash->{UID} ) + and defined( $modules{Aqicn}{defptr}{TOKEN} ) ); - $hash->{HOST} = 'api.waqi.info'; - $attr{$name}{room} = "AQICN" if( !defined( $attr{$name}{room} ) ); - - readingsSingleUpdate ( $hash, "state", "ready for search", 1 ); - - Log3 $name, 3, "Aqicn ($name) - defined Aqicn Head Device with API-Key $hash->{TOKEN}"; - $modules{Aqicn}{defptr}{TOKEN} = $hash; + my $name = $a[0]; - } elsif( defined($hash->{UID}) ) { + $hash->{VERSION} = $version; + $hash->{NOTIFYDEV} = "global"; - $attr{$name}{room} = "AQICN" if( !defined( $attr{$name}{room} ) ); - $hash->{INTERVAL} = 3600; - $hash->{HEADDEVICE} = $modules{Aqicn}{defptr}{TOKEN}->{NAME}; - - readingsSingleUpdate ( $hash, "state", "initialized", 1 ); - - Log3 $name, 3, "Aqicn ($name) - defined Aqicn Station Device with Station UID $hash->{UID}"; - - $modules{Aqicn}{defptr}{UID} = $hash; + if ( defined( $hash->{TOKEN} ) ) { + return +"there is already a Aqicn Head Device, did you want to define a Aqicn station use: define Aqicn " + if ( $modules{Aqicn}{defptr}{TOKEN} ); + + $hash->{HOST} = 'api.waqi.info'; + CommandAttr( undef, $name . ' room AQICN' ) + if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); + + readingsSingleUpdate( $hash, "state", "ready for search", 1 ); + + Log3 $name, 3, +"Aqicn ($name) - defined Aqicn Head Device with API-Key $hash->{TOKEN}"; + $modules{Aqicn}{defptr}{TOKEN} = $hash; + + } + elsif ( defined( $hash->{UID} ) ) { + + CommandAttr( undef, $name . ' room AQICN' ) + if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); + $hash->{INTERVAL} = 3600; + $hash->{HEADDEVICE} = $modules{Aqicn}{defptr}{TOKEN}->{NAME}; + + readingsSingleUpdate( $hash, "state", "initialized", 1 ); + + Log3 $name, 3, +"Aqicn ($name) - defined Aqicn Station Device with Station UID $hash->{UID}"; + + $modules{Aqicn}{defptr}{UID} = $hash; } return undef; } -sub Aqicn_Undef($$) { +sub Undef($$) { - my ( $hash, $arg ) = @_; - - my $name = $hash->{NAME}; + my ( $hash, $arg ) = @_; + my $name = $hash->{NAME}; + + if ( defined( $modules{Aqicn}{defptr}{TOKEN} ) and $hash->{TOKEN} ) { + return +"there is a Aqicn Station Device present, please delete all Station Device first" + unless ( not defined( $modules{Aqicn}{defptr}{UID} ) ); - if( defined($modules{Aqicn}{defptr}{TOKEN}) and $hash->{TOKEN} ) { - return "there is a Aqicn Station Device present, please delete all Station Device first" - unless( not defined($modules{Aqicn}{defptr}{UID}) ); - delete $modules{Aqicn}{defptr}{TOKEN}; - - } elsif( defined($modules{Aqicn}{defptr}{UID}) and $hash->{UID} ) { + + } + elsif ( defined( $modules{Aqicn}{defptr}{UID} ) and $hash->{UID} ) { delete $modules{Aqicn}{defptr}{UID}; } - - RemoveInternalTimer( $hash ); + + RemoveInternalTimer($hash); Log3 $name, 3, "Aqicn ($name) - Device $name deleted"; return undef; } -sub Aqicn_Attr(@) { +sub Attr(@) { my ( $cmd, $name, $attrName, $attrVal ) = @_; - my $hash = $defs{$name}; + my $hash = $defs{$name}; - - if( $attrName eq "disable" ) { - if( $cmd eq "set" and $attrVal eq "1" ) { + if ( $attrName eq "disable" ) { + if ( $cmd eq "set" and $attrVal eq "1" ) { RemoveInternalTimer($hash); - readingsSingleUpdate ( $hash, "state", "disabled", 1 ); + readingsSingleUpdate( $hash, "state", "disabled", 1 ); Log3 $name, 3, "Aqicn ($name) - disabled"; - - } elsif( $cmd eq "del" ) { + + } + elsif ( $cmd eq "del" ) { Log3 $name, 3, "Aqicn ($name) - enabled"; } } - - 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?)+$/); + + 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, "Aqicn ($name) - disabledForIntervals"; - readingsSingleUpdate ( $hash, "state", "disabled", 1 ); - - } elsif( $cmd eq "del" ) { + readingsSingleUpdate( $hash, "state", "disabled", 1 ); + + } + elsif ( $cmd eq "del" ) { Log3 $name, 3, "Aqicn ($name) - enabled"; readingsSingleUpdate( $hash, "state", "active", 1 ); } } - - if( $attrName eq "interval" ) { - if( $cmd eq "set" ) { - if( $attrVal < 30 ) { - Log3 $name, 3, "Aqicn ($name) - interval too small, please use something >= 30 (sec), default is 300 (sec)"; - return "interval too small, please use something >= 30 (sec), default is 300 (sec)"; - - } else { + + if ( $attrName eq "interval" ) { + if ( $cmd eq "set" ) { + if ( $attrVal < 30 ) { + Log3 $name, 3, +"Aqicn ($name) - interval too small, please use something >= 30 (sec), default is 300 (sec)"; + return +"interval too small, please use something >= 30 (sec), default is 300 (sec)"; + + } + else { RemoveInternalTimer($hash); $hash->{INTERVAL} = $attrVal; Log3 $name, 3, "Aqicn ($name) - set interval to $attrVal"; - Aqicn_Timer_GetData($hash); + Timer_GetData($hash); } - } elsif( $cmd eq "del" ) { + } + elsif ( $cmd eq "del" ) { RemoveInternalTimer($hash); $hash->{INTERVAL} = 300; Log3 $name, 3, "Aqicn ($name) - set interval to default"; - Aqicn_Timer_GetData($hash); + Timer_GetData($hash); } } - + return undef; } -sub Aqicn_Notify($$) { +sub Notify($$) { - my ($hash,$dev) = @_; + my ( $hash, $dev ) = @_; my $name = $hash->{NAME}; - return if (IsDisabled($name)); - + return if ( IsDisabled($name) ); + my $devname = $dev->{NAME}; my $devtype = $dev->{TYPE}; - my $events = deviceEvents($dev,1); - return if (!$events); + my $events = deviceEvents( $dev, 1 ); + return if ( !$events ); - - Aqicn_Timer_GetData($hash) if( (grep /^INITIALIZED$/,@{$events} - or grep /^DELETEATTR.$name.disable$/,@{$events} - or (grep /^DEFINED.$name$/,@{$events} and $init_done)) - and defined($hash->{UID}) ); + Timer_GetData($hash) + if ( + ( + grep /^INITIALIZED$/, + @{$events} + or grep /^REREADCFG$/, + @{$events} + or grep /^MODIFIED.$name$/, + @{$events} + or ( grep /^DEFINED.$name$/, @{$events} and $init_done ) + ) + and defined( $hash->{UID} ) + ); return; } -sub Aqicn_Get($$@) { - - my ($hash, $name, @aa) = @_; - my ($cmd, @args) = @aa; +sub Get($$@) { + my ( $hash, $name, @aa ) = @_; + my ( $cmd, @args ) = @aa; - if( $cmd eq 'update' ) { - - Aqicn_GetData($hash); + if ( $cmd eq 'update' ) { + + GetData($hash); return undef; - - } elsif( $cmd eq 'stationSearchByCity' ) { - return "usage: $cmd" if( @args == 0 ); - + + } + elsif ( $cmd eq 'stationSearchByCity' ) { + return "usage: $cmd" if ( @args == 0 ); + my $city = join( " ", @args ); my $ret; - $ret = Aqicn_GetData($hash,$city); + $ret = GetData( $hash, $city ); return $ret; - } else { - + } + else { + my $list = ''; - $list .= 'update:noArg' if( defined($hash->{UID}) ); - $list .= 'stationSearchByCity' if( defined($hash->{TOKEN}) ); - + $list .= 'update:noArg' if ( defined( $hash->{UID} ) ); + $list .= 'stationSearchByCity' if ( defined( $hash->{TOKEN} ) ); + return "Unknown argument $cmd, choose one of $list"; } } -sub Aqicn_Timer_GetData($) { +sub Timer_GetData($) { - my $hash = shift; - my $name = $hash->{NAME}; + my $hash = shift; + my $name = $hash->{NAME}; + if ( not IsDisabled($name) ) { + GetData($hash); - if( not IsDisabled($name) ) { - Aqicn_GetData($hash); - - } else { - readingsSingleUpdate($hash,'state','disabled',1); + } + else { + readingsSingleUpdate( $hash, 'state', 'disabled', 1 ); } - InternalTimer( gettimeofday()+$hash->{INTERVAL}, 'Aqicn_Timer_GetData', $hash ); - Log3 $name, 4, "Aqicn ($name) - Call InternalTimer Aqicn_Timer_GetData"; + InternalTimer( gettimeofday() + $hash->{INTERVAL}, + 'Aqicn::Timer_GetData', $hash ); + Log3 $name, 4, "Aqicn ($name) - Call InternalTimer Timer_GetData"; } -sub Aqicn_GetData($;$) { +sub GetData($;$) { - my ($hash,$cityName) = @_; - - my $name = $hash->{NAME}; - my $host = $modules{Aqicn}{defptr}{TOKEN}->{HOST}; - my $token = $modules{Aqicn}{defptr}{TOKEN}->{TOKEN}; + my ( $hash, $cityName ) = @_; + + my $name = $hash->{NAME}; + my $host = $modules{Aqicn}{defptr}{TOKEN}->{HOST}; + my $token = $modules{Aqicn}{defptr}{TOKEN}->{TOKEN}; my $uri; - - - if( $hash->{UID} ) { - my $uid = $hash->{UID}; - $uri = $host . '/feed/@' . $hash->{UID} . '/?token=' . $token; - readingsSingleUpdate($hash,'state','fetch data',1); - - } else { - $uri = $host . '/search/?token=' . $token . '&keyword=' . urlEncode($cityName); + + if ( $hash->{UID} ) { + my $uid = $hash->{UID}; + $uri = $host . '/feed/@' . $hash->{UID} . '/?token=' . $token; + readingsSingleUpdate( $hash, 'state', 'fetch data', 1 ); + + } + else { + $uri = + $host + . '/search/?token=' + . $token + . '&keyword=' + . urlEncode($cityName); } my $param = { - url => "https://".$uri, - timeout => 5, - method => 'GET', - hash => $hash, - doTrigger => 1, - callback => \&Aqicn_ErrorHandling, - }; - - $param->{cl} = $hash->{CL} if( $hash->{TOKEN} and ref($hash->{CL}) eq 'HASH' ); - + url => "https://" . $uri, + timeout => 5, + method => 'GET', + hash => $hash, + doTrigger => 1, + callback => \&ErrorHandling, + }; + + $param->{cl} = $hash->{CL} + if ( $hash->{TOKEN} and ref( $hash->{CL} ) eq 'HASH' ); + HttpUtils_NonblockingGet($param); Log3 $name, 4, "Aqicn ($name) - Send with URI: https://$uri"; } -sub Aqicn_ErrorHandling($$$) { +sub ErrorHandling($$$) { - my ($param,$err,$data) = @_; - - my $hash = $param->{hash}; - my $name = $hash->{NAME}; + my ( $param, $err, $data ) = @_; + my $hash = $param->{hash}; + my $name = $hash->{NAME}; Log3 $name, 4, "Aqicn ($name) - Recieve JSON data: $data"; + #Log3 $name, 3, "Aqicn ($name) - Recieve HTTP Code: $param->{code}"; #Log3 $name, 3, "Aqicn ($name) - Recieve Error: $err"; ### Begin Error Handling - - if( defined( $err ) ) { - if( $err ne "" ) { - if( $param->{cl} && $param->{cl}{canAsyncOutput} ) { + + if ( defined($err) ) { + if ( $err ne "" ) { + if ( $param->{cl} && $param->{cl}{canAsyncOutput} ) { asyncOutput( $param->{cl}, "Request Error: $err\r\n" ); } - readingsBeginUpdate( $hash ); - readingsBulkUpdate( $hash, 'state', $err, 1); + readingsBeginUpdate($hash); + readingsBulkUpdate( $hash, 'state', $err, 1 ); readingsBulkUpdate( $hash, 'lastRequestError', $err, 1 ); readingsEndUpdate( $hash, 1 ); - + Log3 $name, 3, "Aqicn ($name) - RequestERROR: $err"; return; } } - if( $data eq "" and exists( $param->{code} ) && $param->{code} ne 200 ) { + if ( $data eq "" and exists( $param->{code} ) && $param->{code} ne 200 ) { + #if( $param->{cl} && $param->{cl}{canAsyncOutput} ) { # asyncOutput( $param->{cl}, "Request Error: $param->{code}\r\n" ); #} - - readingsBeginUpdate( $hash ); + + readingsBeginUpdate($hash); readingsBulkUpdate( $hash, 'state', $param->{code}, 1 ); readingsBulkUpdate( $hash, 'lastRequestError', $param->{code}, 1 ); - Log3 $name, 3, "Aqicn ($name) - RequestERROR: ".$param->{code}; + Log3 $name, 3, "Aqicn ($name) - RequestERROR: " . $param->{code}; readingsEndUpdate( $hash, 1 ); - - Log3 $name, 5, "Aqicn ($name) - RequestERROR: received http code ".$param->{code}." without any data after requesting"; + + Log3 $name, 5, + "Aqicn ($name) - RequestERROR: received http code " + . $param->{code} + . " without any data after requesting"; return; } - if( ( $data =~ /Error/i ) and exists( $param->{code} ) ) { + if ( ( $data =~ /Error/i ) and exists( $param->{code} ) ) { + #if( $param->{cl} && $param->{cl}{canAsyncOutput} ) { # asyncOutput( $param->{cl}, "Request Error: $param->{code}\r\n" ); #} - - readingsBeginUpdate( $hash ); - - readingsBulkUpdate( $hash, 'state', $param->{code}, 1 ); + + readingsBeginUpdate($hash); + + readingsBulkUpdate( $hash, 'state', $param->{code}, 1 ); readingsBulkUpdate( $hash, "lastRequestError", $param->{code}, 1 ); readingsEndUpdate( $hash, 1 ); - - Log3 $name, 3, "Aqicn ($name) - statusRequestERROR: http error ".$param->{code}; + + Log3 $name, 3, + "Aqicn ($name) - statusRequestERROR: http error " . $param->{code}; return; ### End Error Handling } - + Log3 $name, 4, "Aqicn ($name) - Recieve JSON data: $data"; - - Aqicn_ResponseProcessing($hash,$data,$param); + + ResponseProcessing( $hash, $data, $param ); } -sub Aqicn_ResponseProcessing($$$) { +sub ResponseProcessing($$$) { - my ($hash,$json,$param) = @_; - - my $name = $hash->{NAME}; + my ( $hash, $json, $param ) = @_; + + my $name = $hash->{NAME}; my $decode_json; my $readings; - - $decode_json = eval{decode_json($json)}; - if($@){ + $decode_json = eval { decode_json($json) }; + if ($@) { Log3 $name, 4, "Aqicn ($name) - error while request: $@"; readingsBeginUpdate($hash); - readingsBulkUpdate($hash, 'JSON_Error', $@); - readingsBulkUpdate($hash, 'httpCode', $param->{code}); - readingsBulkUpdate($hash, 'state', 'JSON error'); - readingsEndUpdate($hash,1); + readingsBulkUpdate( $hash, 'JSON_Error', $@ ); + readingsBulkUpdate( $hash, 'httpCode', $param->{code} ); + readingsBulkUpdate( $hash, 'state', 'JSON error' ); + readingsEndUpdate( $hash, 1 ); return; } - - + #### Verarbeitung der Readings zum passenden - if( $hash->{TOKEN} ) { - Aqicn_ReadingsProcessing_SearchStationResponse($decode_json,$param); - readingsSingleUpdate($hash,'state','search finished',1); + if ( $hash->{TOKEN} ) { + ReadingsProcessing_SearchStationResponse( $decode_json, $param ); + readingsSingleUpdate( $hash, 'state', 'search finished', 1 ); return; - } elsif( $hash->{UID} ) { - $readings = Aqicn_ReadingsProcessing_AqiResponse($decode_json); } - - - Aqicn_WriteReadings($hash,$readings); + elsif ( $hash->{UID} ) { + $readings = ReadingsProcessing_AqiResponse($decode_json); + } + + WriteReadings( $hash, $readings ); } -sub Aqicn_WriteReadings($$) { +sub WriteReadings($$) { + + my ( $hash, $readings ) = @_; + + my $name = $hash->{NAME}; - my ($hash,$readings) = @_; - - my $name = $hash->{NAME}; - - Log3 $name, 4, "Aqicn ($name) - Write Readings"; - - + readingsBeginUpdate($hash); - while( my ($r,$v) = each %{$readings} ) { - readingsBulkUpdate($hash,$r,$v); + while ( my ( $r, $v ) = each %{$readings} ) { + readingsBulkUpdate( $hash, $r, $v ); } - - if( defined($readings->{'PM2.5-AQI'}) ) { - readingsBulkUpdateIfChanged($hash,'htmlStyle','
'.( ((AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en') ? "$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}: $readings->{'PM2.5-AQI'} " : " $AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}: $readings->{'PM2.5-AQI'}").'
'); - - readingsBulkUpdateIfChanged($hash,'state',( ((AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en') ? "$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}: $readings->{'PM2.5-AQI'}" : "$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}: $readings->{'PM2.5-AQI'}") ); - - readingsBulkUpdateIfChanged($hash,'APL',( ((AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en') ? "$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}" : "$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}") ); - - readingsBulkUpdateIfChanged($hash,'healthImplications',Aqicn_HealthImplications($hash,Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})) ); - } else { + if ( defined( $readings->{'PM2.5-AQI'} ) ) { - readingsBulkUpdateIfChanged($hash,'htmlStyle','
'.( ((AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en') ? "$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nde'}: $readings->{'AQI'} " : " $AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nen'}: $readings->{'AQI'}").'
'); - - readingsBulkUpdateIfChanged($hash,'state',( ((AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en') ? "$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nde'}: $readings->{'AQI'}" : "$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nen'}: $readings->{'AQI'}") ); - - readingsBulkUpdateIfChanged($hash,'APL',( ((AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en') ? "$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nde'}" : "$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nen'}") ); - - readingsBulkUpdateIfChanged($hash,'healthImplications',Aqicn_HealthImplications($hash,Aqicn_AirPollutionLevel($readings->{'AQI'})) ); + readingsBulkUpdateIfChanged( + $hash, + 'htmlStyle', + '
' + . ( + ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ) + ? "$AQIS{AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}: $readings->{'PM2.5-AQI'} " + : " $AQIS{AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}: $readings->{'PM2.5-AQI'}" + ) + . '
' + ); + + readingsBulkUpdateIfChanged( + $hash, 'state', + ( + ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ) + ? "$AQIS{AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}: $readings->{'PM2.5-AQI'}" + : "$AQIS{AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}: $readings->{'PM2.5-AQI'}" + ) + ); + + readingsBulkUpdateIfChanged( + $hash, 'APL', + ( + ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ) + ? "$AQIS{AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}" + : "$AQIS{AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}" + ) + ); + + readingsBulkUpdateIfChanged( + $hash, + 'healthImplications', + HealthImplications( + $hash, AirPollutionLevel( $readings->{'PM2.5-AQI'} ) + ) + ); } - - readingsEndUpdate($hash,1); + else { + + readingsBulkUpdateIfChanged( + $hash, + 'htmlStyle', + '
' + . ( + ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ) + ? "$AQIS{AirPollutionLevel($readings->{'AQI'})}{'i18nde'}: $readings->{'AQI'} " + : " $AQIS{AirPollutionLevel($readings->{'AQI'})}{'i18nen'}: $readings->{'AQI'}" + ) + . '
' + ); + + readingsBulkUpdateIfChanged( + $hash, 'state', + ( + ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ) + ? "$AQIS{AirPollutionLevel($readings->{'AQI'})}{'i18nde'}: $readings->{'AQI'}" + : "$AQIS{AirPollutionLevel($readings->{'AQI'})}{'i18nen'}: $readings->{'AQI'}" + ) + ); + + readingsBulkUpdateIfChanged( + $hash, 'APL', + ( + ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ) + ? "$AQIS{AirPollutionLevel($readings->{'AQI'})}{'i18nde'}" + : "$AQIS{AirPollutionLevel($readings->{'AQI'})}{'i18nen'}" + ) + ); + + readingsBulkUpdateIfChanged( + $hash, + 'healthImplications', + HealthImplications( + $hash, AirPollutionLevel( $readings->{'AQI'} ) + ) + ); + } + + readingsEndUpdate( $hash, 1 ); } ##### ##### ## my little Helper -sub Aqicn_ReadingsProcessing_SearchStationResponse($$) { - - my ($decode_json,$param) = @_; - - - if( $param->{cl} and $param->{cl}->{TYPE} eq 'FHEMWEB' ) { - +sub ReadingsProcessing_SearchStationResponse($$) { + + my ( $decode_json, $param ) = @_; + + if ( $param->{cl} and $param->{cl}->{TYPE} eq 'FHEMWEB' ) { + my $ret = ''; $ret .= '
'; $ret .= ''; $ret .= ''; @@ -534,144 +701,178 @@ sub Aqicn_ReadingsProcessing_SearchStationResponse($$) { $ret .= ""; $ret .= ''; - - - if( ref($decode_json->{data}) eq "ARRAY" and scalar(@{$decode_json->{data}}) > 0 ) { - - my $linecount=1; - foreach my $dataset (@{$decode_json->{data}}) { + if ( ref( $decode_json->{data} ) eq "ARRAY" + and scalar( @{ $decode_json->{data} } ) > 0 ) + { + + my $linecount = 1; + foreach my $dataset ( @{ $decode_json->{data} } ) { if ( $linecount % 2 == 0 ) { $ret .= ''; - } else { + } + else { $ret .= ''; } $dataset->{station}{name} =~ s/'//g; - $ret .= ""; + $ret .= + ""; $ret .= ""; $ret .= ""; $ret .= ""; - - + ###### create Links my $aHref; - + # create Google Map Link - $aHref="{station}{geo}[0]."+".$dataset->{station}{geo}[1]."\">Station on Google Maps"; - $ret .= ""; + $aHref = +"{station}{geo}[0] . "+" + . $dataset->{station}{geo}[1] + . "\">Station on Google Maps"; + $ret .= ""; # create define Link - my @headerHost = grep /Origin/, @FW_httpheader; - $headerHost[0] = 'Origin: no Hostname at FHEMWEB Header available' - unless( defined($headerHost[0]) ); - $headerHost[0] =~ m/Origin:.([^\s]*)/; - $headerHost[0] = $1; - $aHref="{station}{name})."+Aqicn+".$dataset->{uid}.$FW_CSRF."\">Create Station Device"; - $ret .= ""; + $aHref = + "{host} + . "/fhem?cmd=define+" + . makeDeviceName( $dataset->{station}{name} ) + . "+Aqicn+" + . $dataset->{uid} + . $::FW_CSRF + . "\">Create Station Device"; + $ret .= ""; $ret .= ''; $linecount++; } } - + $ret .= '
".encode_utf8($dataset->{station}{name})."" . encode_utf8( $dataset->{station}{name} ) . "$dataset->{'time'}{stime}$dataset->{station}{geo}[0]$dataset->{station}{geo}[1]".$aHref."" . $aHref . "".$aHref."" . $aHref . "
'; - asyncOutput( $param->{cl}, $ret ) if( $param->{cl} and $param->{cl}{canAsyncOutput} ); + asyncOutput( $param->{cl}, $ret ) + if ( $param->{cl} and $param->{cl}{canAsyncOutput} ); return; - - } elsif( $param->{cl} and $param->{cl}->{TYPE} eq 'telnet' ) { + + } + elsif ( $param->{cl} and $param->{cl}->{TYPE} eq 'telnet' ) { my $ret = ''; - - foreach my $dataset (@{$decode_json->{data}}) { - $ret .= encode_utf8($dataset->{station}{name}) . "| $dataset->{'time'}{stime} | $dataset->{station}{geo}[0] | $dataset->{station}{geo}[1] | define " . makeDeviceName($dataset->{station}{name}) . " Aqicn $dataset->{uid}\r\n"; + + foreach my $dataset ( @{ $decode_json->{data} } ) { + $ret .= + encode_utf8( $dataset->{station}{name} ) + . "| $dataset->{'time'}{stime} | $dataset->{station}{geo}[0] | $dataset->{station}{geo}[1] | define " + . makeDeviceName( $dataset->{station}{name} ) + . " Aqicn $dataset->{uid}\r\n"; } - asyncOutput( $param->{cl}, $ret ) if( $param->{cl} && $param->{cl}{canAsyncOutput} ); + asyncOutput( $param->{cl}, $ret ) + if ( $param->{cl} && $param->{cl}{canAsyncOutput} ); return; } } -sub Aqicn_ReadingsProcessing_AqiResponse($) { - - my ($decode_json) = @_; +sub ReadingsProcessing_AqiResponse($) { + + my ($decode_json) = @_; my %readings; + if ( ref( $decode_json->{data} ) eq "HASH" ) { - $readings{'CO-AQI'} = $decode_json->{data}{iaqi}{co}{v}; - $readings{'NO2-AQI'} = $decode_json->{data}{iaqi}{no2}{v}; - $readings{'PM10-AQI'} = $decode_json->{data}{iaqi}{pm10}{v}; - $readings{'PM2.5-AQI'} = $decode_json->{data}{iaqi}{pm25}{v}; - $readings{'AQI'} = $decode_json->{data}{aqi}; - $readings{'O3-AQI'} = $decode_json->{data}{iaqi}{o3}{v}; - $readings{'SO2-AQI'} = $decode_json->{data}{iaqi}{so2}{v}; - $readings{'temperature'} = $decode_json->{data}{iaqi}{t}{v}; - $readings{'pressure'} = $decode_json->{data}{iaqi}{p}{v}; - $readings{'humidity'} = $decode_json->{data}{iaqi}{h}{v}; - $readings{'status'} = $decode_json->{status}; - $readings{'pubDate'} = $decode_json->{data}{time}{s}; - $readings{'pubUnixTime'} = $decode_json->{data}{time}{v}; - $readings{'pubTimezone'} = $decode_json->{data}{time}{tz}; - $readings{'windSpeed'} = $decode_json->{data}{iaqi}{w}{v}; - $readings{'windDirection'} = $decode_json->{data}{iaqi}{wd}{v}; - $readings{'dewpoint'} = $decode_json->{data}{iaqi}{d}{v}; - $readings{'dominatPoll'} = $decode_json->{data}{dominentpol}; + $readings{'CO-AQI'} = $decode_json->{data}{iaqi}{co}{v}; + $readings{'NO2-AQI'} = $decode_json->{data}{iaqi}{no2}{v}; + $readings{'PM10-AQI'} = $decode_json->{data}{iaqi}{pm10}{v}; + $readings{'PM2.5-AQI'} = $decode_json->{data}{iaqi}{pm25}{v}; + $readings{'AQI'} = $decode_json->{data}{aqi}; + $readings{'O3-AQI'} = $decode_json->{data}{iaqi}{o3}{v}; + $readings{'SO2-AQI'} = $decode_json->{data}{iaqi}{so2}{v}; + $readings{'temperature'} = $decode_json->{data}{iaqi}{t}{v}; + $readings{'pressure'} = $decode_json->{data}{iaqi}{p}{v}; + $readings{'humidity'} = $decode_json->{data}{iaqi}{h}{v}; + $readings{'status'} = $decode_json->{status}; + $readings{'pubDate'} = $decode_json->{data}{time}{s}; + $readings{'pubUnixTime'} = $decode_json->{data}{time}{v}; + $readings{'pubTimezone'} = $decode_json->{data}{time}{tz}; + $readings{'windSpeed'} = $decode_json->{data}{iaqi}{w}{v}; + $readings{'windDirection'} = $decode_json->{data}{iaqi}{wd}{v}; + $readings{'dewpoint'} = $decode_json->{data}{iaqi}{d}{v}; + $readings{'dominatPoll'} = $decode_json->{data}{dominentpol}; + + } + else { + $readings{'status'} = 'no hash reference found'; + } return \%readings; } -sub Aqicn_AirPollutionLevel($) { +sub AirPollutionLevel($) { my $aqi = shift; + return 1 unless ( defined($aqi) ); my $apl; + if ( $aqi < 51 ) { $apl = 1 } + elsif ( $aqi < 101 ) { $apl = 2 } + elsif ( $aqi < 151 ) { $apl = 3 } + elsif ( $aqi < 201 ) { $apl = 4 } + elsif ( $aqi < 301 ) { $apl = 5 } + else { $apl = 6 } - if($aqi < 51) { $apl = 1} - elsif($aqi < 101) { $apl = 2} - elsif($aqi < 151) { $apl = 3} - elsif($aqi < 201) { $apl = 4} - elsif($aqi < 301) { $apl = 5} - else { $apl = 6} - return $apl; } -sub Aqicn_HealthImplications($$) { +sub HealthImplications($$) { - my ($hash,$apl) = @_; + my ( $hash, $apl ) = @_; - my $name = $hash->{NAME}; + my $name = $hash->{NAME}; my %HIen = ( - 1 => 'Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.', - 2 => 'Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.', - 3 => 'Members of sensitive groups may experience health effects. The general public is not likely to be affected.', - 4 => 'Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects', - 5 => 'Health warnings of emergency conditions. The entire population is more likely to be affected.', - 6 => 'Health alert: everyone may experience more serious health effects' - ); - - my %HIde = ( - 1 => 'Die Qualität der Luft gilt als zufriedenstellend und die Luftverschmutzung stellt ein geringes oder kein Risiko dar', - 2 => 'Die Luftqualität ist insgesamt akzeptabel. Bei manchen Schadstoffe besteht jedoch eventuell eine geringe Gesundheitsgefahr für einen sehr kleinen Personenkreis, der sehr empfindlich auf Luftverschmutzung ist.', - 3 => 'Bei Mitgliedern von empfindlichen Personengruppen können gesundheitliche Auswirkungen auftreten. Die allgemeine Öffentlichkeit ist wahrscheinlich nicht betroffen.', - 4 => 'Erste gesundheitliche Auswirkungen können sich bei allen Personen einstellen. Bei empfindlichen Personengruppen können ernstere gesundheitliche Auswirkungen auftreten.', - 5 => 'Gesundheitswarnung aufgrund einer Notfallsituation. Die gesamte Bevölkerung ist voraussichtlich betroffen.', - 6 => 'Gesundheitsalarm: Jeder muss mit dem Auftreten ernsterer Gesundheitsschäden rechnen' - ); - - - return ( (AttrVal('global','language','none') eq 'DE' or AttrVal($name,'language','none') eq 'de') and AttrVal($name,'language','none') ne 'en' ? $HIde{$apl} : $HIen{$apl} ); + 1 => +'Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.', + 2 => +'Air quality is acceptable; however, for some pollutants there may be a moderate health concern for a very small number of people who are unusually sensitive to air pollution.', + 3 => +'Members of sensitive groups may experience health effects. The general public is not likely to be affected.', + 4 => +'Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects', + 5 => +'Health warnings of emergency conditions. The entire population is more likely to be affected.', + 6 => 'Health alert: everyone may experience more serious health effects' + ); + + my %HIde = ( + 1 => +'Die Qualität der Luft gilt als zufriedenstellend und die Luftverschmutzung stellt ein geringes oder kein Risiko dar', + 2 => +'Die Luftqualität ist insgesamt akzeptabel. Bei manchen Schadstoffe besteht jedoch eventuell eine geringe Gesundheitsgefahr für einen sehr kleinen Personenkreis, der sehr empfindlich auf Luftverschmutzung ist.', + 3 => +'Bei Mitgliedern von empfindlichen Personengruppen können gesundheitliche Auswirkungen auftreten. Die allgemeine Öffentlichkeit ist wahrscheinlich nicht betroffen.', + 4 => +'Erste gesundheitliche Auswirkungen können sich bei allen Personen einstellen. Bei empfindlichen Personengruppen können ernstere gesundheitliche Auswirkungen auftreten.', + 5 => +'Gesundheitswarnung aufgrund einer Notfallsituation. Die gesamte Bevölkerung ist voraussichtlich betroffen.', + 6 => +'Gesundheitsalarm: Jeder muss mit dem Auftreten ernsterer Gesundheitsschäden rechnen' + ); + + return ( + ( + AttrVal( 'global', 'language', 'none' ) eq 'DE' + or AttrVal( $name, 'language', 'none' ) eq 'de' + ) + and AttrVal( $name, 'language', 'none' ) ne 'en' + ? $HIde{$apl} + : $HIen{$apl} + ); } - - - - 1; - =pod =item device @@ -688,11 +889,11 @@ sub Aqicn_HealthImplications($$) { Define

    - define <name> Aqicn + define <name> Aqicn token=<TOKEN-KEY>

    Example:

      - define aqicnMaster Aqicn
      + define aqicnMaster Aqicn token=12345678

    This statement creates the Aqicn Master Device.