46_Aqicn: fix commandref, change to packages

git-svn-id: https://svn.fhem.de/fhem/trunk@17701 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
LeonGaultier 2018-11-07 12:45:10 +00:00
parent 5bddb23abe
commit aa188f7017
2 changed files with 593 additions and 391 deletions

View File

@ -1,5 +1,6 @@
# Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # 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. # 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 - bugfix: 88_HMCCU: fixed set toggle command
- new: 98_livetracking: added module - new: 98_livetracking: added module
- bugfix: 38_netatmo: changed geocoding to openstreetmap - bugfix: 38_netatmo: changed geocoding to openstreetmap

View File

@ -47,89 +47,26 @@
## ##
## ##
package main; package main;
my $missingModul = "";
use strict; use strict;
use warnings; use warnings;
use HttpUtils; my $version = "0.4.2";
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";
### 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'
);
sub Aqicn_Initialize($) { sub Aqicn_Initialize($) {
my ($hash) = @_; my ($hash) = @_;
# Consumer # Consumer
$hash->{GetFn} = "Aqicn_Get"; $hash->{GetFn} = "Aqicn::Get";
$hash->{DefFn} = "Aqicn_Define"; $hash->{DefFn} = "Aqicn::Define";
$hash->{UndefFn} = "Aqicn_Undef"; $hash->{UndefFn} = "Aqicn::Undef";
$hash->{NotifyFn} = "Aqicn_Notify"; $hash->{NotifyFn} = "Aqicn::Notify";
$hash->{AttrFn} = "Aqicn_Attr"; $hash->{AttrFn} = "Aqicn::Attr";
$hash->{AttrList} = "interval ". $hash->{AttrList} =
"disable:1 ". "interval " . "disable:1 " . "language:de,en " . $readingFnAttributes;
"language:de,en ".
$readingFnAttributes;
foreach my $d ( sort keys %{ $modules{Aqicn}{defptr} } ) { foreach my $d ( sort keys %{ $modules{Aqicn}{defptr} } ) {
@ -138,56 +75,156 @@ sub Aqicn_Initialize($) {
} }
} }
sub Aqicn_Define($$) { package Aqicn;
use strict;
use warnings;
use HttpUtils;
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 ";
## 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'
},
);
my %paths = (
'statussoe' => 'system_status/soe',
'aggregates' => 'meters/aggregates',
'siteinfo' => 'site_info',
'sitemaster' => 'sitemaster',
'powerwalls' => 'powerwalls',
'registration' => 'customer/registration',
'status' => 'status'
);
sub Define($$) {
my ( $hash, $def ) = @_; my ( $hash, $def ) = @_;
my @a = split( "[ \t][ \t]*", $def ); my @a = split( "[ \t][ \t]*", $def );
if ( $a[2] =~ /^token=/ ) { if ( $a[2] =~ /^token=/ ) {
$a[2] =~ m/token=([^\s]*)/; $a[2] =~ m/token=([^\s]*)/;
$hash->{TOKEN} = $1; $hash->{TOKEN} = $1;
} else { }
else {
$hash->{UID} = $a[2]; $hash->{UID} = $a[2];
} }
return "Cannot define a Aqicn device. Perl modul $missingModul is missing." if ( $missingModul ); return "Cannot define a Aqicn device. Perl modul $missingModul is missing."
return "too few parameters: define <name> Aqicn <OPTION-PARAMETER>" if( @a != 3 ); if ($missingModul);
return "too few parameters: define <name> Aqicn token=<TOKEN-KEY>" if( not defined($hash->{TOKEN}) and not defined($modules{Aqicn}{defptr}{TOKEN}) ); return "too few parameters: define <name> Aqicn <OPTION-PARAMETER>"
return "too few parameters: define <name> Aqicn <STATION-UID>" if( not defined($hash->{UID}) and defined($modules{Aqicn}{defptr}{TOKEN}) ); if ( @a != 3 );
return "too few parameters: define <name> Aqicn token=<TOKEN-KEY>"
if ( not defined( $hash->{TOKEN} )
and not defined( $modules{Aqicn}{defptr}{TOKEN} ) );
return "too few parameters: define <name> Aqicn <STATION-UID>"
if ( not defined( $hash->{UID} )
and defined( $modules{Aqicn}{defptr}{TOKEN} ) );
my $name = $a[0]; my $name = $a[0];
$hash->{VERSION} = $version; $hash->{VERSION} = $version;
$hash->{NOTIFYDEV} = "global"; $hash->{NOTIFYDEV} = "global";
if ( defined( $hash->{TOKEN} ) ) { if ( defined( $hash->{TOKEN} ) ) {
return "there is already a Aqicn Head Device, did you want to define a Aqicn station use: define <name> Aqicn <STATION-UID>" if( $modules{Aqicn}{defptr}{TOKEN} ); return
"there is already a Aqicn Head Device, did you want to define a Aqicn station use: define <name> Aqicn <STATION-UID>"
if ( $modules{Aqicn}{defptr}{TOKEN} );
$hash->{HOST} = 'api.waqi.info'; $hash->{HOST} = 'api.waqi.info';
$attr{$name}{room} = "AQICN" if( !defined( $attr{$name}{room} ) ); CommandAttr( undef, $name . ' room AQICN' )
if ( AttrVal( $name, 'room', 'none' ) eq 'none' );
readingsSingleUpdate( $hash, "state", "ready for search", 1 ); readingsSingleUpdate( $hash, "state", "ready for search", 1 );
Log3 $name, 3, "Aqicn ($name) - defined Aqicn Head Device with API-Key $hash->{TOKEN}"; Log3 $name, 3,
"Aqicn ($name) - defined Aqicn Head Device with API-Key $hash->{TOKEN}";
$modules{Aqicn}{defptr}{TOKEN} = $hash; $modules{Aqicn}{defptr}{TOKEN} = $hash;
} elsif( defined($hash->{UID}) ) { }
elsif ( defined( $hash->{UID} ) ) {
$attr{$name}{room} = "AQICN" if( !defined( $attr{$name}{room} ) ); CommandAttr( undef, $name . ' room AQICN' )
if ( AttrVal( $name, 'room', 'none' ) eq 'none' );
$hash->{INTERVAL} = 3600; $hash->{INTERVAL} = 3600;
$hash->{HEADDEVICE} = $modules{Aqicn}{defptr}{TOKEN}->{NAME}; $hash->{HEADDEVICE} = $modules{Aqicn}{defptr}{TOKEN}->{NAME};
readingsSingleUpdate( $hash, "state", "initialized", 1 ); readingsSingleUpdate( $hash, "state", "initialized", 1 );
Log3 $name, 3, "Aqicn ($name) - defined Aqicn Station Device with Station UID $hash->{UID}"; Log3 $name, 3,
"Aqicn ($name) - defined Aqicn Station Device with Station UID $hash->{UID}";
$modules{Aqicn}{defptr}{UID} = $hash; $modules{Aqicn}{defptr}{UID} = $hash;
} }
@ -195,20 +232,21 @@ sub Aqicn_Define($$) {
return undef; return undef;
} }
sub Aqicn_Undef($$) { sub Undef($$) {
my ( $hash, $arg ) = @_; my ( $hash, $arg ) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if ( defined( $modules{Aqicn}{defptr}{TOKEN} ) and $hash->{TOKEN} ) { if ( defined( $modules{Aqicn}{defptr}{TOKEN} ) and $hash->{TOKEN} ) {
return "there is a Aqicn Station Device present, please delete all Station Device first" return
"there is a Aqicn Station Device present, please delete all Station Device first"
unless ( not defined( $modules{Aqicn}{defptr}{UID} ) ); unless ( not defined( $modules{Aqicn}{defptr}{UID} ) );
delete $modules{Aqicn}{defptr}{TOKEN}; 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}; delete $modules{Aqicn}{defptr}{UID};
} }
@ -218,31 +256,33 @@ sub Aqicn_Undef($$) {
return undef; return undef;
} }
sub Aqicn_Attr(@) { sub Attr(@) {
my ( $cmd, $name, $attrName, $attrVal ) = @_; my ( $cmd, $name, $attrName, $attrVal ) = @_;
my $hash = $defs{$name}; my $hash = $defs{$name};
if ( $attrName eq "disable" ) { if ( $attrName eq "disable" ) {
if ( $cmd eq "set" and $attrVal eq "1" ) { if ( $cmd eq "set" and $attrVal eq "1" ) {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
readingsSingleUpdate( $hash, "state", "disabled", 1 ); readingsSingleUpdate( $hash, "state", "disabled", 1 );
Log3 $name, 3, "Aqicn ($name) - disabled"; Log3 $name, 3, "Aqicn ($name) - disabled";
} elsif( $cmd eq "del" ) { }
elsif ( $cmd eq "del" ) {
Log3 $name, 3, "Aqicn ($name) - enabled"; Log3 $name, 3, "Aqicn ($name) - enabled";
} }
} }
if ( $attrName eq "disabledForIntervals" ) { if ( $attrName eq "disabledForIntervals" ) {
if ( $cmd eq "set" ) { if ( $cmd eq "set" ) {
return "check disabledForIntervals Syntax HH:MM-HH:MM or 'HH:MM-HH:MM HH:MM-HH:MM ...'" 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?)+$/ ); unless ( $attrVal =~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/ );
Log3 $name, 3, "Aqicn ($name) - disabledForIntervals"; Log3 $name, 3, "Aqicn ($name) - disabledForIntervals";
readingsSingleUpdate( $hash, "state", "disabled", 1 ); readingsSingleUpdate( $hash, "state", "disabled", 1 );
} elsif( $cmd eq "del" ) { }
elsif ( $cmd eq "del" ) {
Log3 $name, 3, "Aqicn ($name) - enabled"; Log3 $name, 3, "Aqicn ($name) - enabled";
readingsSingleUpdate( $hash, "state", "active", 1 ); readingsSingleUpdate( $hash, "state", "active", 1 );
} }
@ -251,27 +291,31 @@ sub Aqicn_Attr(@) {
if ( $attrName eq "interval" ) { if ( $attrName eq "interval" ) {
if ( $cmd eq "set" ) { if ( $cmd eq "set" ) {
if ( $attrVal < 30 ) { if ( $attrVal < 30 ) {
Log3 $name, 3, "Aqicn ($name) - interval too small, please use something >= 30 (sec), default is 300 (sec)"; Log3 $name, 3,
return "interval too small, please use something >= 30 (sec), default is 300 (sec)"; "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 { }
else {
RemoveInternalTimer($hash); RemoveInternalTimer($hash);
$hash->{INTERVAL} = $attrVal; $hash->{INTERVAL} = $attrVal;
Log3 $name, 3, "Aqicn ($name) - set interval to $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); RemoveInternalTimer($hash);
$hash->{INTERVAL} = 300; $hash->{INTERVAL} = 300;
Log3 $name, 3, "Aqicn ($name) - set interval to default"; Log3 $name, 3, "Aqicn ($name) - set interval to default";
Aqicn_Timer_GetData($hash); Timer_GetData($hash);
} }
} }
return undef; return undef;
} }
sub Aqicn_Notify($$) { sub Notify($$) {
my ( $hash, $dev ) = @_; my ( $hash, $dev ) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
@ -282,34 +326,43 @@ sub Aqicn_Notify($$) {
my $events = deviceEvents( $dev, 1 ); my $events = deviceEvents( $dev, 1 );
return if ( !$events ); return if ( !$events );
Timer_GetData($hash)
Aqicn_Timer_GetData($hash) if( (grep /^INITIALIZED$/,@{$events} if (
or grep /^DELETEATTR.$name.disable$/,@{$events} (
or (grep /^DEFINED.$name$/,@{$events} and $init_done)) grep /^INITIALIZED$/,
and defined($hash->{UID}) ); @{$events}
or grep /^REREADCFG$/,
@{$events}
or grep /^MODIFIED.$name$/,
@{$events}
or ( grep /^DEFINED.$name$/, @{$events} and $init_done )
)
and defined( $hash->{UID} )
);
return; return;
} }
sub Aqicn_Get($$@) { sub Get($$@) {
my ( $hash, $name, @aa ) = @_; my ( $hash, $name, @aa ) = @_;
my ( $cmd, @args ) = @aa; my ( $cmd, @args ) = @aa;
if ( $cmd eq 'update' ) { if ( $cmd eq 'update' ) {
Aqicn_GetData($hash); GetData($hash);
return undef; return undef;
} elsif( $cmd eq 'stationSearchByCity' ) { }
elsif ( $cmd eq 'stationSearchByCity' ) {
return "usage: $cmd" if ( @args == 0 ); return "usage: $cmd" if ( @args == 0 );
my $city = join( " ", @args ); my $city = join( " ", @args );
my $ret; my $ret;
$ret = Aqicn_GetData($hash,$city); $ret = GetData( $hash, $city );
return $ret; return $ret;
} else { }
else {
my $list = ''; my $list = '';
$list .= 'update:noArg' if ( defined( $hash->{UID} ) ); $list .= 'update:noArg' if ( defined( $hash->{UID} ) );
@ -319,24 +372,25 @@ sub Aqicn_Get($$@) {
} }
} }
sub Aqicn_Timer_GetData($) { sub Timer_GetData($) {
my $hash = shift; my $hash = shift;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
if ( not IsDisabled($name) ) { if ( not IsDisabled($name) ) {
Aqicn_GetData($hash); GetData($hash);
} else { }
else {
readingsSingleUpdate( $hash, 'state', 'disabled', 1 ); readingsSingleUpdate( $hash, 'state', 'disabled', 1 );
} }
InternalTimer( gettimeofday()+$hash->{INTERVAL}, 'Aqicn_Timer_GetData', $hash ); InternalTimer( gettimeofday() + $hash->{INTERVAL},
Log3 $name, 4, "Aqicn ($name) - Call InternalTimer Aqicn_Timer_GetData"; 'Aqicn::Timer_GetData', $hash );
Log3 $name, 4, "Aqicn ($name) - Call InternalTimer Timer_GetData";
} }
sub Aqicn_GetData($;$) { sub GetData($;$) {
my ( $hash, $cityName ) = @_; my ( $hash, $cityName ) = @_;
@ -345,14 +399,19 @@ sub Aqicn_GetData($;$) {
my $token = $modules{Aqicn}{defptr}{TOKEN}->{TOKEN}; my $token = $modules{Aqicn}{defptr}{TOKEN}->{TOKEN};
my $uri; my $uri;
if ( $hash->{UID} ) { if ( $hash->{UID} ) {
my $uid = $hash->{UID}; my $uid = $hash->{UID};
$uri = $host . '/feed/@' . $hash->{UID} . '/?token=' . $token; $uri = $host . '/feed/@' . $hash->{UID} . '/?token=' . $token;
readingsSingleUpdate( $hash, 'state', 'fetch data', 1 ); readingsSingleUpdate( $hash, 'state', 'fetch data', 1 );
} else { }
$uri = $host . '/search/?token=' . $token . '&keyword=' . urlEncode($cityName); else {
$uri =
$host
. '/search/?token='
. $token
. '&keyword='
. urlEncode($cityName);
} }
my $param = { my $param = {
@ -361,24 +420,25 @@ sub Aqicn_GetData($;$) {
method => 'GET', method => 'GET',
hash => $hash, hash => $hash,
doTrigger => 1, doTrigger => 1,
callback => \&Aqicn_ErrorHandling, callback => \&ErrorHandling,
}; };
$param->{cl} = $hash->{CL} if( $hash->{TOKEN} and ref($hash->{CL}) eq 'HASH' ); $param->{cl} = $hash->{CL}
if ( $hash->{TOKEN} and ref( $hash->{CL} ) eq 'HASH' );
HttpUtils_NonblockingGet($param); HttpUtils_NonblockingGet($param);
Log3 $name, 4, "Aqicn ($name) - Send with URI: https://$uri"; Log3 $name, 4, "Aqicn ($name) - Send with URI: https://$uri";
} }
sub Aqicn_ErrorHandling($$$) { sub ErrorHandling($$$) {
my ( $param, $err, $data ) = @_; my ( $param, $err, $data ) = @_;
my $hash = $param->{hash}; my $hash = $param->{hash};
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 4, "Aqicn ($name) - Recieve JSON data: $data"; 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 HTTP Code: $param->{code}";
#Log3 $name, 3, "Aqicn ($name) - Recieve Error: $err"; #Log3 $name, 3, "Aqicn ($name) - Recieve Error: $err";
@ -402,6 +462,7 @@ sub Aqicn_ErrorHandling($$$) {
} }
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} ) { #if( $param->{cl} && $param->{cl}{canAsyncOutput} ) {
# asyncOutput( $param->{cl}, "Request Error: $param->{code}\r\n" ); # asyncOutput( $param->{cl}, "Request Error: $param->{code}\r\n" );
#} #}
@ -415,12 +476,16 @@ sub Aqicn_ErrorHandling($$$) {
readingsEndUpdate( $hash, 1 ); 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; return;
} }
if ( ( $data =~ /Error/i ) and exists( $param->{code} ) ) { if ( ( $data =~ /Error/i ) and exists( $param->{code} ) ) {
#if( $param->{cl} && $param->{cl}{canAsyncOutput} ) { #if( $param->{cl} && $param->{cl}{canAsyncOutput} ) {
# asyncOutput( $param->{cl}, "Request Error: $param->{code}\r\n" ); # asyncOutput( $param->{cl}, "Request Error: $param->{code}\r\n" );
#} #}
@ -432,7 +497,8 @@ sub Aqicn_ErrorHandling($$$) {
readingsEndUpdate( $hash, 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; return;
### End Error Handling ### End Error Handling
@ -440,10 +506,10 @@ sub Aqicn_ErrorHandling($$$) {
Log3 $name, 4, "Aqicn ($name) - Recieve JSON data: $data"; 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 ( $hash, $json, $param ) = @_;
@ -451,7 +517,6 @@ sub Aqicn_ResponseProcessing($$$) {
my $decode_json; my $decode_json;
my $readings; my $readings;
$decode_json = eval { decode_json($json) }; $decode_json = eval { decode_json($json) };
if ($@) { if ($@) {
Log3 $name, 4, "Aqicn ($name) - error while request: $@"; Log3 $name, 4, "Aqicn ($name) - error while request: $@";
@ -463,30 +528,27 @@ sub Aqicn_ResponseProcessing($$$) {
return; return;
} }
#### Verarbeitung der Readings zum passenden #### Verarbeitung der Readings zum passenden
if ( $hash->{TOKEN} ) { if ( $hash->{TOKEN} ) {
Aqicn_ReadingsProcessing_SearchStationResponse($decode_json,$param); ReadingsProcessing_SearchStationResponse( $decode_json, $param );
readingsSingleUpdate( $hash, 'state', 'search finished', 1 ); readingsSingleUpdate( $hash, 'state', 'search finished', 1 );
return; return;
} elsif( $hash->{UID} ) { }
$readings = Aqicn_ReadingsProcessing_AqiResponse($decode_json); elsif ( $hash->{UID} ) {
$readings = ReadingsProcessing_AqiResponse($decode_json);
} }
WriteReadings( $hash, $readings );
Aqicn_WriteReadings($hash,$readings);
} }
sub Aqicn_WriteReadings($$) { sub WriteReadings($$) {
my ( $hash, $readings ) = @_; my ( $hash, $readings ) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
Log3 $name, 4, "Aqicn ($name) - Write Readings"; Log3 $name, 4, "Aqicn ($name) - Write Readings";
readingsBeginUpdate($hash); readingsBeginUpdate($hash);
while ( my ( $r, $v ) = each %{$readings} ) { while ( my ( $r, $v ) = each %{$readings} ) {
readingsBulkUpdate( $hash, $r, $v ); readingsBulkUpdate( $hash, $r, $v );
@ -494,22 +556,128 @@ sub Aqicn_WriteReadings($$) {
if ( defined( $readings->{'PM2.5-AQI'} ) ) { if ( defined( $readings->{'PM2.5-AQI'} ) ) {
readingsBulkUpdateIfChanged($hash,'htmlStyle','<div style="background-color: '.$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'bgcolor'}.';"><font color="'.$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'font color'}.'">'.( ((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'}").'</div>'); readingsBulkUpdateIfChanged(
$hash,
'htmlStyle',
'<div style="background-color: '
. $AQIS{ AirPollutionLevel( $readings->{'PM2.5-AQI'} ) }
{'bgcolor'}
. ';"><font color="'
. $AQIS{ AirPollutionLevel( $readings->{'PM2.5-AQI'} ) }
{'font color'} . '">'
. (
(
(
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'}"
)
. '</div>'
);
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, '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{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nde'}" : "$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'i18nen'}") ); 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',Aqicn_HealthImplications($hash,Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})) ); readingsBulkUpdateIfChanged(
} else { $hash,
'healthImplications',
HealthImplications(
$hash, AirPollutionLevel( $readings->{'PM2.5-AQI'} )
)
);
}
else {
readingsBulkUpdateIfChanged($hash,'htmlStyle','<div style="background-color: '.$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'bgcolor'}.';"><font color="'.$AQIS{Aqicn_AirPollutionLevel($readings->{'PM2.5-AQI'})}{'font color'}.'">'.( ((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'}").'</div>'); readingsBulkUpdateIfChanged(
$hash,
'htmlStyle',
'<div style="background-color: '
. $AQIS{ AirPollutionLevel( $readings->{'AQI'} ) }{'bgcolor'}
. ';"><font color="'
. $AQIS{ AirPollutionLevel( $readings->{'PM2.5-AQI'} ) }
{'font color'} . '">'
. (
(
(
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'}"
)
. '</div>'
);
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, '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{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nde'}" : "$AQIS{Aqicn_AirPollutionLevel($readings->{'AQI'})}{'i18nen'}") ); 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',Aqicn_HealthImplications($hash,Aqicn_AirPollutionLevel($readings->{'AQI'})) ); readingsBulkUpdateIfChanged(
$hash,
'healthImplications',
HealthImplications(
$hash, AirPollutionLevel( $readings->{'AQI'} )
)
);
} }
readingsEndUpdate( $hash, 1 ); readingsEndUpdate( $hash, 1 );
@ -517,11 +685,10 @@ sub Aqicn_WriteReadings($$) {
##### #####
##### #####
## my little Helper ## my little Helper
sub Aqicn_ReadingsProcessing_SearchStationResponse($$) { sub ReadingsProcessing_SearchStationResponse($$) {
my ( $decode_json, $param ) = @_; my ( $decode_json, $param ) = @_;
if ( $param->{cl} and $param->{cl}->{TYPE} eq 'FHEMWEB' ) { if ( $param->{cl} and $param->{cl}->{TYPE} eq 'FHEMWEB' ) {
my $ret = '<html><table><tr><td>'; my $ret = '<html><table><tr><td>';
@ -534,39 +701,47 @@ sub Aqicn_ReadingsProcessing_SearchStationResponse($$) {
$ret .= "<td></td>"; $ret .= "<td></td>";
$ret .= '</tr>'; $ret .= '</tr>';
if ( ref( $decode_json->{data} ) eq "ARRAY"
and scalar( @{ $decode_json->{data} } ) > 0 )
if( ref($decode_json->{data}) eq "ARRAY" and scalar(@{$decode_json->{data}}) > 0 ) { {
my $linecount = 1; my $linecount = 1;
foreach my $dataset ( @{ $decode_json->{data} } ) { foreach my $dataset ( @{ $decode_json->{data} } ) {
if ( $linecount % 2 == 0 ) { if ( $linecount % 2 == 0 ) {
$ret .= '<tr class="even">'; $ret .= '<tr class="even">';
} else { }
else {
$ret .= '<tr class="odd">'; $ret .= '<tr class="odd">';
} }
$dataset->{station}{name} =~ s/'//g; $dataset->{station}{name} =~ s/'//g;
$ret .= "<td>".encode_utf8($dataset->{station}{name})."</td>"; $ret .=
"<td>" . encode_utf8( $dataset->{station}{name} ) . "</td>";
$ret .= "<td>$dataset->{'time'}{stime}</td>"; $ret .= "<td>$dataset->{'time'}{stime}</td>";
$ret .= "<td>$dataset->{station}{geo}[0]</td>"; $ret .= "<td>$dataset->{station}{geo}[0]</td>";
$ret .= "<td>$dataset->{station}{geo}[1]</td>"; $ret .= "<td>$dataset->{station}{geo}[1]</td>";
###### create Links ###### create Links
my $aHref; my $aHref;
# create Google Map Link # create Google Map Link
$aHref="<a target=\"_blank\" href=\"https://www.google.de/maps/search/".$dataset->{station}{geo}[0]."+".$dataset->{station}{geo}[1]."\">Station on Google Maps</a>"; $aHref =
"<a target=\"_blank\" href=\"https://www.google.de/maps/search/"
. $dataset->{station}{geo}[0] . "+"
. $dataset->{station}{geo}[1]
. "\">Station on Google Maps</a>";
$ret .= "<td>" . $aHref . "</td>"; $ret .= "<td>" . $aHref . "</td>";
# create define Link # create define Link
my @headerHost = grep /Origin/, @FW_httpheader; $aHref =
$headerHost[0] = 'Origin: no Hostname at FHEMWEB Header available' "<a href=\""
unless( defined($headerHost[0]) ); . $::FW_httpheader->{host}
$headerHost[0] =~ m/Origin:.([^\s]*)/; . "/fhem?cmd=define+"
$headerHost[0] = $1; . makeDeviceName( $dataset->{station}{name} )
$aHref="<a href=\"".$headerHost[0]."/fhem?cmd=define+".makeDeviceName($dataset->{station}{name})."+Aqicn+".$dataset->{uid}.$FW_CSRF."\">Create Station Device</a>"; . "+Aqicn+"
. $dataset->{uid}
. $::FW_CSRF
. "\">Create Station Device</a>";
$ret .= "<td>" . $aHref . "</td>"; $ret .= "<td>" . $aHref . "</td>";
$ret .= '</tr>'; $ret .= '</tr>';
$linecount++; $linecount++;
@ -576,27 +751,35 @@ sub Aqicn_ReadingsProcessing_SearchStationResponse($$) {
$ret .= '</table></td></tr>'; $ret .= '</table></td></tr>';
$ret .= '</table></html>'; $ret .= '</table></html>';
asyncOutput( $param->{cl}, $ret ) if( $param->{cl} and $param->{cl}{canAsyncOutput} ); asyncOutput( $param->{cl}, $ret )
if ( $param->{cl} and $param->{cl}{canAsyncOutput} );
return; return;
} elsif( $param->{cl} and $param->{cl}->{TYPE} eq 'telnet' ) { }
elsif ( $param->{cl} and $param->{cl}->{TYPE} eq 'telnet' ) {
my $ret = ''; my $ret = '';
foreach my $dataset ( @{ $decode_json->{data} } ) { 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"; $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; return;
} }
} }
sub Aqicn_ReadingsProcessing_AqiResponse($) { sub ReadingsProcessing_AqiResponse($) {
my ($decode_json) = @_; my ($decode_json) = @_;
my %readings; my %readings;
if ( ref( $decode_json->{data} ) eq "HASH" ) {
$readings{'CO-AQI'} = $decode_json->{data}{iaqi}{co}{v}; $readings{'CO-AQI'} = $decode_json->{data}{iaqi}{co}{v};
$readings{'NO2-AQI'} = $decode_json->{data}{iaqi}{no2}{v}; $readings{'NO2-AQI'} = $decode_json->{data}{iaqi}{no2}{v};
@ -617,16 +800,21 @@ sub Aqicn_ReadingsProcessing_AqiResponse($) {
$readings{'dewpoint'} = $decode_json->{data}{iaqi}{d}{v}; $readings{'dewpoint'} = $decode_json->{data}{iaqi}{d}{v};
$readings{'dominatPoll'} = $decode_json->{data}{dominentpol}; $readings{'dominatPoll'} = $decode_json->{data}{dominentpol};
}
else {
$readings{'status'} = 'no hash reference found';
}
return \%readings; return \%readings;
} }
sub Aqicn_AirPollutionLevel($) { sub AirPollutionLevel($) {
my $aqi = shift; my $aqi = shift;
return 1 unless ( defined($aqi) );
my $apl; my $apl;
if ( $aqi < 51 ) { $apl = 1 } if ( $aqi < 51 ) { $apl = 1 }
elsif ( $aqi < 101 ) { $apl = 2 } elsif ( $aqi < 101 ) { $apl = 2 }
elsif ( $aqi < 151 ) { $apl = 3 } elsif ( $aqi < 151 ) { $apl = 3 }
@ -637,41 +825,54 @@ sub Aqicn_AirPollutionLevel($) {
return $apl; return $apl;
} }
sub Aqicn_HealthImplications($$) { sub HealthImplications($$) {
my ( $hash, $apl ) = @_; my ( $hash, $apl ) = @_;
my $name = $hash->{NAME}; my $name = $hash->{NAME};
my %HIen = ( 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.', 1 =>
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.', '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.', 2 =>
4 => 'Everyone may begin to experience health effects; members of sensitive groups may experience more serious health effects', '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.',
5 => 'Health warnings of emergency conditions. The entire population is more likely to be affected.', 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' 6 => 'Health alert: everyone may experience more serious health effects'
); );
my %HIde = ( my %HIde = (
1 => 'Die Qualität der Luft gilt als zufriedenstellend und die Luftverschmutzung stellt ein geringes oder kein Risiko dar', 1 =>
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.', 'Die Qualität der Luft gilt als zufriedenstellend und die Luftverschmutzung stellt ein geringes oder kein Risiko dar',
3 => 'Bei Mitgliedern von empfindlichen Personengruppen können gesundheitliche Auswirkungen auftreten. Die allgemeine Öffentlichkeit ist wahrscheinlich nicht betroffen.', 2 =>
4 => 'Erste gesundheitliche Auswirkungen können sich bei allen Personen einstellen. Bei empfindlichen Personengruppen können ernstere gesundheitliche Auswirkungen auftreten.', '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.',
5 => 'Gesundheitswarnung aufgrund einer Notfallsituation. Die gesamte Bevölkerung ist voraussichtlich betroffen.', 3 =>
6 => 'Gesundheitsalarm: Jeder muss mit dem Auftreten ernsterer Gesundheitsschäden rechnen' '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 (
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} ); (
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; 1;
=pod =pod
=item device =item device
@ -688,11 +889,11 @@ sub Aqicn_HealthImplications($$) {
<a name="Aqicndefine"></a> <a name="Aqicndefine"></a>
<b>Define</b> <b>Define</b>
<ul><br> <ul><br>
<code>define &lt;name&gt; Aqicn</code> <code>define &lt;name&gt; Aqicn token=&ltTOKEN-KEY&gt</code>
<br><br> <br><br>
Example: Example:
<ul><br> <ul><br>
<code>define aqicnMaster Aqicn</code><br> <code>define aqicnMaster Aqicn token=12345678</code><br>
</ul> </ul>
<br> <br>
This statement creates the Aqicn Master Device.<br> This statement creates the Aqicn Master Device.<br>