diff --git a/fhem/CHANGED b/fhem/CHANGED index 35b2c989a..1f6f50818 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: GardenaSmart: all modules change to new API and code PBP conform - feature: 37_echodevice.pm A1WAR447VT003J Yamaha MusicCast 20 CHANGE: get status - feature: 10_SOMFY: new attr autoStoreRollingCode - store rc in uniqueID diff --git a/fhem/FHEM/73_GardenaSmartBridge.pm b/fhem/FHEM/73_GardenaSmartBridge.pm index 2402efb7f..4ac306463 100644 --- a/fhem/FHEM/73_GardenaSmartBridge.pm +++ b/fhem/FHEM/73_GardenaSmartBridge.pm @@ -54,22 +54,23 @@ ## package FHEM::GardenaSmartBridge; -use GPUtils qw(GP_Import) - ; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt +use GPUtils qw(GP_Import GP_Export); + +# use Data::Dumper; #only for Debugging use strict; use warnings; use POSIX; use FHEM::Meta; +use Data::Dumper; use HttpUtils; -our $VERSION = '1.6.9'; my $missingModul = ''; eval "use Encode qw(encode encode_utf8 decode_utf8);1" or $missingModul .= "Encode "; -# eval "use JSON;1" or $missingModul .= 'JSON '; +# eval "use JSON;1" || $missingModul .= 'JSON '; eval "use IO::Socket::SSL;1" or $missingModul .= 'IO::Socket::SSL '; # try to use JSON::MaybeXS wrapper @@ -176,42 +177,30 @@ BEGIN { ); } -# _Export - Export references to main context using a different naming schema -sub _Export { - no strict qw/refs/; ## no critic - my $pkg = caller(0); - my $main = $pkg; - $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/g; - foreach (@_) { - *{ $main . $_ } = *{ $pkg . '::' . $_ }; - } -} - #-- Export to main context with different name -_Export( +GP_Export( qw( Initialize ) ); -sub Initialize($) { - - my ($hash) = @_; +sub Initialize { + my $hash = shift; # Provider - $hash->{WriteFn} = 'FHEM::GardenaSmartBridge::Write'; + $hash->{WriteFn} = \&Write; $hash->{Clients} = ':GardenaSmartDevice:'; $hash->{MatchList} = { '1:GardenaSmartDevice' => '^{"id":".*' }; # Consumer - $hash->{SetFn} = 'FHEM::GardenaSmartBridge::Set'; - $hash->{DefFn} = 'FHEM::GardenaSmartBridge::Define'; - $hash->{UndefFn} = 'FHEM::GardenaSmartBridge::Undef'; - $hash->{DeleteFn} = 'FHEM::GardenaSmartBridge::Delete'; - $hash->{RenameFn} = 'FHEM::GardenaSmartBridge::Rename'; - $hash->{NotifyFn} = 'FHEM::GardenaSmartBridge::Notify'; + $hash->{SetFn} = \&Set; + $hash->{DefFn} = \&Define; + $hash->{UndefFn} = \&Undef; + $hash->{DeleteFn} = \&Delete; + $hash->{RenameFn} = \&Rename; + $hash->{NotifyFn} = \&Notify; - $hash->{AttrFn} = 'FHEM::GardenaSmartBridge::Attr'; + $hash->{AttrFn} = \&Attr; $hash->{AttrList} = 'debugJSON:0,1 ' . 'disable:1 ' @@ -220,38 +209,33 @@ sub Initialize($) { . 'gardenaAccountEmail ' . 'gardenaBaseURL ' . $readingFnAttributes; - - foreach my $d ( sort keys %{ $modules{GardenaSmartBridge}{defptr} } ) { - - my $hash = $modules{GardenaSmartBridge}{defptr}{$d}; - $hash->{VERSION} = $VERSION; - } + $hash->{parseParams} = 1; return FHEM::Meta::InitMod( __FILE__, $hash ); } -sub Define($$) { - - my ( $hash, $def ) = @_; - - my @a = split( '[ \t][ \t]*', $def ); +sub Define { + my $hash = shift; + my $a = shift; return $@ unless ( FHEM::Meta::SetInternals($hash) ); + use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); + return 'too few parameters: define GardenaSmartBridge' - if ( @a != 2 ); + if ( scalar( @{$a} ) != 2 ); return 'Cannot define Gardena Bridge device. Perl modul ' . ${missingModul} . ' is missing.' if ($missingModul); - my $name = $a[0]; + my $name = shift @$a; $hash->{BRIDGE} = 1; $hash->{URL} = AttrVal( $name, 'gardenaBaseURL', 'https://sg-api.dss.husqvarnagroup.net' ) . '/sg-1'; - $hash->{VERSION} = $VERSION; + $hash->{VERSION} = version->parse($VERSION)->normal; $hash->{INTERVAL} = 60; $hash->{NOTIFYDEV} = "global,$name"; @@ -265,35 +249,34 @@ sub Define($$) { $modules{GardenaSmartBridge}{defptr}{BRIDGE} = $hash; - return undef; + return; } -sub Undef($$) { - - my ( $hash, $name ) = @_; +sub Undef { + my $hash = shift; + my $name = shift; RemoveInternalTimer($hash); delete $modules{GardenaSmartBridge}{defptr}{BRIDGE} if ( defined( $modules{GardenaSmartBridge}{defptr}{BRIDGE} ) ); - return undef; + return; } -sub Delete($$) { - - my ( $hash, $name ) = @_; +sub Delete { + my $hash = shift; + my $name = shift; setKeyValue( $hash->{TYPE} . '_' . $name . '_passwd', undef ); - return undef; + return; } -sub Attr(@) { - +sub Attr { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; if ( $attrName eq 'disable' ) { - if ( $cmd eq 'set' and $attrVal eq '1' ) { + if ( $cmd eq 'set' && $attrVal eq '1' ) { RemoveInternalTimer($hash); readingsSingleUpdate( $hash, 'state', 'inactive', 1 ); Log3 $name, 3, "GardenaSmartBridge ($name) - disabled"; @@ -307,7 +290,7 @@ sub Attr(@) { 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 ( $attrVal !~ /^((\d{2}:\d{2})-(\d{2}:\d{2})\s?)+$/ ); Log3 $name, 3, "GardenaSmartBridge ($name) - disabledForIntervals"; } elsif ( $cmd eq 'del' ) { @@ -319,7 +302,7 @@ sub Attr(@) { if ( $cmd eq 'set' ) { RemoveInternalTimer($hash); return 'Interval must be greater than 0' - unless ( $attrVal > 0 ); + if ( $attrVal == 0 ); $hash->{INTERVAL} = $attrVal; Log3 $name, 3, "GardenaSmartBridge ($name) - set interval: $attrVal"; @@ -342,12 +325,13 @@ sub Attr(@) { } } - return undef; + return; } -sub Notify($$) { +sub Notify { + my $hash = shift; + my $dev = shift; - my ( $hash, $dev ) = @_; my $name = $hash->{NAME}; return if ( IsDisabled($name) ); @@ -360,7 +344,7 @@ sub Notify($$) { if ( ( $devtype eq 'Global' - and ( + && ( grep /^INITIALIZED$/, @{$events} or grep /^REREADCFG$/, @{$events} or grep /^DEFINED.$name$/, @@ -370,11 +354,11 @@ sub Notify($$) { ) ) - or ( + || ( $devtype eq 'GardenaSmartBridge' - and ( + && ( grep /^gardenaAccountPassword.+/, - @{$events} or ReadingsVal( '$devname', 'token', '' ) eq 'none' + @{$events} || ReadingsVal( '$devname', 'token', '' ) eq 'none' ) ) ); @@ -382,19 +366,19 @@ sub Notify($$) { getDevices($hash) if ( $devtype eq 'Global' - and ( + && ( grep /^DELETEATTR.$name.disable$/, @{$events} or grep /^ATTR.$name.disable.0$/, @{$events} or grep /^DELETEATTR.$name.interval$/, @{$events} or grep /^ATTR.$name.interval.[0-9]+/, @{$events} ) - and $init_done + && $init_done ); if ( $devtype eq 'GardenaSmartBridge' - and ( + && ( grep /^state:.Connected$/, @{$events} or grep /^lastRequestState:.request_error$/, @{$events} @@ -411,9 +395,12 @@ sub Notify($$) { return; } -sub Set($@) { +sub Set { + my $hash = shift; + my $a = shift; - my ( $hash, $name, $cmd, @args ) = @_; + my $name = shift @$a; + my $cmd = shift @$a // return qq{"set $name" needs at least one argument}; if ( lc $cmd eq 'getdevicesstate' ) { getDevices($hash); @@ -423,44 +410,39 @@ sub Set($@) { return "please set Attribut gardenaAccountEmail first" if ( AttrVal( $name, 'gardenaAccountEmail', 'none' ) eq 'none' ); return "please set gardenaAccountPassword first" - if ( not defined( ReadPassword($hash,$name) ) ); + if ( not defined( ReadPassword( $hash, $name ) ) ); return "token is up to date" if ( defined( $hash->{helper}{session_id} ) ); getToken($hash); - } elsif ( lc $cmd eq 'gardenaaccountpassword' ) { return "please set Attribut gardenaAccountEmail first" if ( AttrVal( $name, 'gardenaAccountEmail', 'none' ) eq 'none' ); - return "usage: $cmd " if ( @args != 1 ); - - my $passwd = join( ' ', @args ); - StorePassword( $hash, $name, $passwd ); + return "usage: $cmd " if ( scalar( @{$a} ) != 0 ); + StorePassword( $hash, $name, $a->[0] ); } elsif ( lc $cmd eq 'deleteaccountpassword' ) { - return "usage: $cmd " if ( @args != 0 ); + return "usage: $cmd " if ( scalar( @{$a} ) != 0 ); DeletePassword($hash); - } else { my $list = "getDevicesState:noArg getToken:noArg" - if ( defined( ReadPassword($hash,$name) ) ); + if ( defined( ReadPassword( $hash, $name ) ) ); $list .= " gardenaAccountPassword" - if ( not defined( ReadPassword($hash,$name) ) ); + if ( not defined( ReadPassword( $hash, $name ) ) ); $list .= " deleteAccountPassword:noArg" - if ( defined( ReadPassword($hash,$name) ) ); + if ( defined( ReadPassword( $hash, $name ) ) ); return "Unknown argument $cmd, choose one of $list"; } - return undef; + return; } -sub Write($@) { - +sub Write { my ( $hash, $payload, $deviceId, $abilities ) = @_; my $name = $hash->{NAME}; @@ -489,18 +471,21 @@ sub Write($@) { # Log3($name, 3, # "GardenaSmartBridge ($name) - Send with URL: $hash->{URL}$uri, HEADER: $header, DATA: $payload, METHOD: $method"); + + return; } -sub ErrorHandling($$$) { - - my ( $param, $err, $data ) = @_; +sub ErrorHandling { + my $param = shift; + my $err = shift; + my $data = shift; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $dhash = $hash; $dhash = $modules{GardenaSmartDevice}{defptr}{ $param->{'device_id'} } - unless ( not defined( $param->{'device_id'} ) ); + if ( defined( $param->{'device_id'} ) ); my $dname = $dhash->{NAME}; @@ -526,7 +511,7 @@ sub ErrorHandling($$$) { } elsif ($err =~ /Keine Route zum Zielrechner/ - or $err =~ /no route to target/ ) + || $err =~ /no route to target/ ) { Log3 $dname, 5, @@ -551,7 +536,7 @@ sub ErrorHandling($$$) { } } - if ( $data eq "" and exists( $param->{code} ) and $param->{code} != 200 ) { + if ( $data eq "" && exists( $param->{code} ) && $param->{code} != 200 ) { readingsBeginUpdate($dhash); readingsBulkUpdate( $dhash, "state", $param->{code}, 1 ) @@ -560,7 +545,7 @@ sub ErrorHandling($$$) { readingsBulkUpdateIfChanged( $dhash, "lastRequestState", "request_error", 1 ); - if ( $param->{code} == 401 and $hash eq $dhash ) { + if ( $param->{code} == 401 && $hash eq $dhash ) { if ( ReadingsVal( $dname, 'token', 'none' ) eq 'none' ) { readingsBulkUpdate( $dhash, "state", "no token available", 1 ); @@ -572,9 +557,9 @@ sub ErrorHandling($$$) { "GardenaSmartBridge ($dname) - RequestERROR: " . $param->{code}; } - elsif ( $param->{code} == 204 - and $dhash ne $hash - and defined( $dhash->{helper}{deviceAction} ) ) + elsif ($param->{code} == 204 + && $dhash ne $hash + && defined( $dhash->{helper}{deviceAction} ) ) { readingsBulkUpdate( $dhash, "state", "the command is processed", @@ -607,9 +592,9 @@ sub ErrorHandling($$$) { if ( $data =~ /Error/ - or ( defined($decode_json) - and ref($decode_json) eq 'HASH' - and defined( $decode_json->{errors} ) ) + || ( defined($decode_json) + && ref($decode_json) eq 'HASH' + && defined( $decode_json->{errors} ) ) ) { readingsBeginUpdate($dhash); @@ -621,7 +606,7 @@ sub ErrorHandling($$$) { if ( $param->{code} == 400 ) { if ($decode_json) { if ( ref( $decode_json->{errors} ) eq "ARRAY" - and defined( $decode_json->{errors} ) ) + && defined( $decode_json->{errors} ) ) { readingsBulkUpdate( $dhash, @@ -660,7 +645,7 @@ sub ErrorHandling($$$) { } elsif ( $param->{code} == 404 ) { - if ( defined( $dhash->{helper}{deviceAction} ) and $dhash ne $hash ) + if ( defined( $dhash->{helper}{deviceAction} ) && $dhash ne $hash ) { readingsBulkUpdate( $dhash, "state", "device Id not found", 1 ); readingsBulkUpdate( $dhash, "lastRequestState", @@ -701,11 +686,13 @@ sub ErrorHandling($$$) { if ( defined( $hash->{helper}{locations_id} ) ); ResponseProcessing( $hash, $data ) if ( ref($decode_json) eq 'HASH' ); + + return; } -sub ResponseProcessing($$) { - - my ( $hash, $json ) = @_; +sub ResponseProcessing { + my $hash = shift; + my $json = shift; my $name = $hash->{NAME}; @@ -722,7 +709,9 @@ sub ResponseProcessing($$) { } } - if ( defined( $decode_json->{sessions} ) and $decode_json->{sessions} ) { + # print Dumper $decode_json; + + if ( defined( $decode_json->{sessions} ) && $decode_json->{sessions} ) { $hash->{helper}{session_id} = $decode_json->{sessions}{token}; $hash->{helper}{user_id} = $decode_json->{sessions}{user_id}; @@ -734,13 +723,12 @@ sub ResponseProcessing($$) { return; } - elsif ( not defined( $hash->{helper}{locations_id} ) - and defined( $decode_json->{locations} ) - and ref( $decode_json->{locations} ) eq "ARRAY" - and scalar( @{ $decode_json->{locations} } ) > 0 ) + elsif ( !defined( $hash->{helper}{locations_id} ) + && defined( $decode_json->{locations} ) + && ref( $decode_json->{locations} ) eq 'ARRAY' + && scalar( @{ $decode_json->{locations} } ) > 0 ) { - - foreach my $location ( @{ $decode_json->{locations} } ) { + for my $location ( @{ $decode_json->{locations} } ) { $hash->{helper}{locations_id} = $location->{id}; @@ -753,11 +741,10 @@ sub ResponseProcessing($$) { Write( $hash, undef, undef, undef ); return; - } - elsif ( defined( $decode_json->{devices} ) - and ref( $decode_json->{devices} ) eq "ARRAY" - and scalar( @{ $decode_json->{devices} } ) > 0 ) + elsif (defined( $decode_json->{devices} ) + && ref( $decode_json->{devices} ) eq 'ARRAY' + && scalar( @{ $decode_json->{devices} } ) > 0 ) { my @buffer = split( '"devices":\[', $json ); @@ -779,19 +766,18 @@ sub ResponseProcessing($$) { . " Tail: " . $tail; - unless ( not defined($tail) and not($tail) ) { - + if ( defined($tail) and $tail ) { $decode_json = eval { decode_json($json) }; if ($@) { - Log3 $name, 3, + Log3 $name, 5, "GardenaSmartBridge ($name) - JSON error while request: $@"; } Dispatch( $hash, $json, undef ) - unless ( $decode_json->{category} eq 'gateway' ); + if ( $decode_json->{category} ne 'gateway' ); WriteReadings( $hash, $decode_json ) if ( defined( $decode_json->{category} ) - and $decode_json->{category} eq 'gateway' ); + && $decode_json->{category} eq 'gateway' ); } ( $json, $tail ) = ParseJSON( $hash, $tail ); @@ -809,17 +795,22 @@ sub ResponseProcessing($$) { } Log3 $name, 3, "GardenaSmartBridge ($name) - no Match for processing data"; + + return; } -sub WriteReadings($$) { +sub WriteReadings { + my $hash = shift; + my $decode_json = shift; + + # print Dumper $decode_json; - my ( $hash, $decode_json ) = @_; my $name = $hash->{NAME}; - if ( defined( $decode_json->{id} ) - and $decode_json->{id} - and defined( $decode_json->{name} ) - and $decode_json->{name} ) + if ( defined( $decode_json->{id} ) + && $decode_json->{id} + && defined( $decode_json->{name} ) + && $decode_json->{name} ) { readingsBeginUpdate($hash); if ( $decode_json->{id} eq $hash->{helper}{locations_id} ) { @@ -839,9 +830,9 @@ sub WriteReadings($$) { readingsBulkUpdateIfChanged( $hash, 'zones', scalar( @{ $decode_json->{zones} } ) ); } - elsif ( $decode_json->{id} ne $hash->{helper}{locations_id} - and ref( $decode_json->{abilities} ) eq 'ARRAY' - and ref( $decode_json->{abilities}[0]{properties} ) eq 'ARRAY' ) + elsif ($decode_json->{id} ne $hash->{helper}{locations_id} + && ref( $decode_json->{abilities} ) eq 'ARRAY' + && ref( $decode_json->{abilities}[0]{properties} ) eq 'ARRAY' ) { my $properties = scalar( @{ $decode_json->{abilities}[0]{properties} } ); @@ -861,20 +852,20 @@ sub WriteReadings($$) { {name} . '-' . $t, $v ) - unless ( + if ( $decode_json->{abilities}[0]{properties}[$properties] - {name} eq 'ethernet_status' - or $decode_json->{abilities}[0]{properties}[$properties] - {name} eq 'wifi_status' ); + {name} ne 'ethernet_status' + || $decode_json->{abilities}[0]{properties} + [$properties]{name} ne 'wifi_status' ); if ( ( $decode_json->{abilities}[0]{properties} [$properties]{name} eq 'ethernet_status' - or $decode_json->{abilities}[0]{properties} + || $decode_json->{abilities}[0]{properties} [$properties]{name} eq 'wifi_status' ) - and ref($v) eq 'HASH' + && ref($v) eq 'HASH' ) { if ( $decode_json->{abilities}[0]{properties} @@ -914,15 +905,17 @@ sub WriteReadings($$) { } Log3 $name, 4, "GardenaSmartBridge ($name) - readings would be written"; + + return; } #################################### #################################### #### my little helpers Sub's ####### -sub getDevices($) { - +sub getDevices { my $hash = shift; + my $name = $hash->{NAME}; RemoveInternalTimer($hash); @@ -938,11 +931,13 @@ sub getDevices($) { readingsSingleUpdate( $hash, 'state', 'disabled', 1 ); Log3 $name, 3, "GardenaSmartBridge ($name) - device is disabled"; } + + return; } -sub getToken($) { - +sub getToken { my $hash = shift; + my $name = $hash->{NAME}; return readingsSingleUpdate( $hash, 'state', @@ -950,35 +945,39 @@ sub getToken($) { if ( AttrVal( $name, 'gardenaAccountEmail', 'none' ) eq 'none' ); return readingsSingleUpdate( $hash, 'state', 'please set gardena account password first', 1 ) - if ( not defined( ReadPassword($hash,$name) ) ); + if ( !defined( ReadPassword( $hash, $name ) ) ); readingsSingleUpdate( $hash, 'state', 'get token', 1 ); delete $hash->{helper}{session_id} if ( defined( $hash->{helper}{session_id} ) - and $hash->{helper}{session_id} ); + && $hash->{helper}{session_id} ); delete $hash->{helper}{user_id} - if ( defined( $hash->{helper}{user_id} ) and $hash->{helper}{user_id} ); + if ( defined( $hash->{helper}{user_id} ) && $hash->{helper}{user_id} ); delete $hash->{helper}{locations_id} if ( defined( $hash->{helper}{locations_id} ) - and $hash->{helper}{locations_id} ); + && $hash->{helper}{locations_id} ); Write( $hash, '"sessions": {"email": "' . AttrVal( $name, 'gardenaAccountEmail', 'none' ) . '","password": "' - . ReadPassword($hash,$name) . '"}', + . ReadPassword( $hash, $name ) . '"}', undef, undef ); Log3 $name, 3, "GardenaSmartBridge ($name) - send credentials to fetch Token and locationId"; + + return; } -sub StorePassword($@) { +sub StorePassword { + my $hash = shift; + my $name = shift; + my $password = shift; - my ( $hash, $name, $password ) = @_; my $index = $hash->{TYPE} . "_" . $name . "_passwd"; my $key = getUniqueId() . $index; my $enc_pwd = ""; @@ -1002,11 +1001,12 @@ sub StorePassword($@) { return "password successfully saved"; } -sub ReadPassword($$) { +sub ReadPassword { + my $hash = shift; + my $name = shift; - my ( $hash, $name ) = @_; - my $index = $hash->{TYPE} . "_" . $name . "_passwd"; - my $key = getUniqueId() . $index; + my $index = $hash->{TYPE} . "_" . $name . "_passwd"; + my $key = getUniqueId() . $index; my ( $password, $err ); Log3 $name, 4, "GardenaSmartBridge ($name) - Read password from file"; @@ -1045,22 +1045,25 @@ sub ReadPassword($$) { Log3 $name, 3, "GardenaSmartBridge ($name) - No password in file"; return undef; } + + return; } -sub Rename(@) { +sub Rename { + my $new = shift; + my $old = shift; - my ( $new, $old ) = @_; my $hash = $defs{$new}; - - StorePassword( $hash, $new, ReadPassword($hash,$old) ); + + StorePassword( $hash, $new, ReadPassword( $hash, $old ) ); setKeyValue( $hash->{TYPE} . "_" . $old . "_passwd", undef ); - return undef; + return; } -sub ParseJSON($$) { - - my ( $hash, $buffer ) = @_; +sub ParseJSON { + my $hash = shift; + my $buffer = shift; my $name = $hash->{NAME}; my $open = 0; @@ -1069,14 +1072,14 @@ sub ParseJSON($$) { my $tail = ''; if ($buffer) { - foreach my $c ( split //, $buffer ) { - if ( $open == $close and $open > 0 ) { + for my $c ( split //, $buffer ) { + if ( $open == $close && $open > 0 ) { $tail .= $c; Log3 $name, 5, "GardenaSmartBridge ($name) - $open == $close and $open > 0"; } - elsif ( ( $open == $close ) and ( $c ne '{' ) ) { + elsif ( ( $open == $close ) && ( $c ne '{' ) ) { Log3 $name, 5, "GardenaSmartBridge ($name) - Garbage character before message: " @@ -1111,9 +1114,9 @@ sub ParseJSON($$) { return ( $msg, $tail ); } -sub createHttpValueStrings($@) { - +sub createHttpValueStrings { my ( $hash, $payload, $deviceId, $abilities ) = @_; + my $session_id = $hash->{helper}{session_id}; my $header = "Content-Type: application/json"; my $uri = ''; @@ -1121,25 +1124,25 @@ sub createHttpValueStrings($@) { $header .= "\r\nX-Session: $session_id" if ( defined( $hash->{helper}{session_id} ) ); $payload = '{' . $payload . '}' if ( defined($payload) ); - $payload = '{}' if ( not defined($payload) ); + $payload = '{}' if ( !defined($payload) ); if ( $payload eq '{}' ) { $method = 'GET'; $uri .= '/locations/?user_id=' . $hash->{helper}{user_id} - if ( exists($hash->{helper}{user_id}) - and not defined( $hash->{helper}{locations_id} ) ); + if ( exists( $hash->{helper}{user_id} ) + && !defined( $hash->{helper}{locations_id} ) ); readingsSingleUpdate( $hash, 'state', 'fetch locationId', 1 ) - if ( not defined( $hash->{helper}{locations_id} ) ); - $uri .= '/sessions' if ( not defined( $hash->{helper}{session_id} ) ); + if ( !defined( $hash->{helper}{locations_id} ) ); + $uri .= '/sessions' if ( !defined( $hash->{helper}{session_id} ) ); $uri .= '/devices' - if ( not defined($abilities) - and defined( $hash->{helper}{locations_id} ) ); + if (!defined($abilities) + && defined( $hash->{helper}{locations_id} ) ); } - $uri .= '/sessions' if ( not defined( $hash->{helper}{session_id} ) ); + $uri .= '/sessions' if ( !defined( $hash->{helper}{session_id} ) ); if ( defined( $hash->{helper}{locations_id} ) ) { - if ( defined($abilities) and $abilities eq 'mower_settings' ) { + if ( defined($abilities) && $abilities eq 'mower_settings' ) { $method = 'PUT'; my $dhash = $modules{GardenaSmartDevice}{defptr}{$deviceId}; @@ -1148,14 +1151,14 @@ sub createHttpValueStrings($@) { . $deviceId . '/settings/' . $dhash->{helper}{STARTINGPOINTID} - if ( defined($abilities) - and defined($payload) - and $abilities eq 'mower_settings' ); + if ( defined($abilities) + && defined($payload) + && $abilities eq 'mower_settings' ); } - elsif ( defined($abilities) - and defined($payload) - and $abilities eq 'watering' ) + elsif (defined($abilities) + && defined($payload) + && $abilities eq 'watering' ) { my $valve_id; $method = 'PUT'; @@ -1172,9 +1175,9 @@ sub createHttpValueStrings($@) { . $valve_id; } - elsif ( defined($abilities) - and defined($payload) - and $abilities eq 'manual_watering' ) + elsif (defined($abilities) + && defined($payload) + && $abilities eq 'manual_watering' ) { my $valve_id; $method = 'PUT'; @@ -1187,9 +1190,9 @@ sub createHttpValueStrings($@) { . '/properties/manual_watering_timer'; } - elsif ( defined($abilities) - and defined($payload) - and $abilities eq 'power' ) + elsif (defined($abilities) + && defined($payload) + && $abilities eq 'power' ) { my $valve_id; $method = 'PUT'; @@ -1205,7 +1208,7 @@ sub createHttpValueStrings($@) { else { $uri .= '/devices/' . $deviceId . '/abilities/' . $abilities . '/command' - if ( defined($abilities) and defined($payload) ); + if ( defined($abilities) && defined($payload) ); } $uri .= '?locationId=' . $hash->{helper}{locations_id}; @@ -1215,13 +1218,12 @@ sub createHttpValueStrings($@) { $abilities ); } -sub DeletePassword($) { - +sub DeletePassword { my $hash = shift; setKeyValue( $hash->{TYPE} . "_" . $hash->{NAME} . "_passwd", undef ); - return undef; + return; } 1; @@ -1373,6 +1375,7 @@ sub DeletePassword($) { ], "release_status": "stable", "license": "GPL_2", + "version": "v2.0.0", "author": [ "Marko Oldenburg " ], diff --git a/fhem/FHEM/74_GardenaSmartDevice.pm b/fhem/FHEM/74_GardenaSmartDevice.pm index 1a993cc0a..0250165c9 100644 --- a/fhem/FHEM/74_GardenaSmartDevice.pm +++ b/fhem/FHEM/74_GardenaSmartDevice.pm @@ -55,18 +55,13 @@ ## unserer packagename package FHEM::GardenaSmartDevice; - -use GPUtils qw(GP_Import) - ; # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt - -my $missingModul = ""; +use GPUtils qw(GP_Import GP_Export); use strict; use warnings; use POSIX; use FHEM::Meta; use Time::Local; -our $VERSION = '1.6.7'; # try to use JSON::MaybeXS wrapper # for chance of better performance + open code @@ -163,70 +158,51 @@ BEGIN { ); } -# _Export - Export references to main context using a different naming schema -sub _Export { - no strict qw/refs/; ## no critic - my $pkg = caller(0); - my $main = $pkg; - $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/g; - foreach (@_) { - *{ $main . $_ } = *{ $pkg . '::' . $_ }; - } -} - #-- Export to main context with different name -_Export( +GP_Export( qw( Initialize ) ); -sub Initialize($) { - - my ($hash) = @_; +sub Initialize { + my $hash = shift; $hash->{Match} = '^{"id":".*'; - $hash->{SetFn} = "FHEM::GardenaSmartDevice::Set"; - $hash->{DefFn} = "FHEM::GardenaSmartDevice::Define"; - $hash->{UndefFn} = "FHEM::GardenaSmartDevice::Undef"; - $hash->{ParseFn} = "FHEM::GardenaSmartDevice::Parse"; + $hash->{SetFn} = \&Set; + $hash->{DefFn} = \&Define; + $hash->{UndefFn} = \&Undef; + $hash->{ParseFn} = \&Parse; - $hash->{AttrFn} = "FHEM::GardenaSmartDevice::Attr"; + $hash->{AttrFn} = \&Attr; $hash->{AttrList} = "readingValueLanguage:de,en " . "model:watering_computer,sensor,mower,ic24,power,electronic_pressure_pump " . "IODev " . $readingFnAttributes; - - foreach my $d ( sort keys %{ $modules{GardenaSmartDevice}{defptr} } ) { - - my $hash = $modules{GardenaSmartDevice}{defptr}{$d}; - $hash->{VERSION} = $VERSION; - } + $hash->{parseParams} = 1; return FHEM::Meta::InitMod( __FILE__, $hash ); } -sub Define($$) { - - my ( $hash, $def ) = @_; - my @a = split( "[ \t]+", $def ); +sub Define { + my $hash = shift; + my $a = shift; return $@ unless ( FHEM::Meta::SetInternals($hash) ); + use version 0.60; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); + return "too few parameters: define GardenaSmartDevice " - if ( @a < 3 ); - return -"Cannot define Gardena Bridge device. Perl modul $missingModul is missing." - if ($missingModul); + if ( scalar( @{$a} ) < 3 ); - my $name = $a[0]; - my $deviceId = $a[2]; - my $category = $a[3]; + my $name = $a->[0]; + my $deviceId = $a->[2]; + my $category = $a->[3]; $hash->{DEVICEID} = $deviceId; - $hash->{VERSION} = $VERSION; + $hash->{VERSION} = version->parse($VERSION)->normal; $hash->{helper}{STARTINGPOINTID} = ''; CommandAttr( undef, @@ -251,9 +227,9 @@ sub Define($$) { return "GardenaSmartDevice device $name on GardenaSmartBridge $iodev already defined." - if ( defined($d) - and $d->{IODev} == $hash->{IODev} - and $d->{NAME} ne $name ); + if ( defined($d) + && $d->{IODev} == $hash->{IODev} + && $d->{NAME} ne $name ); CommandAttr( undef, $name . ' room GardenaSmart' ) if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); @@ -267,100 +243,107 @@ sub Define($$) { $modules{GardenaSmartDevice}{defptr}{$deviceId} = $hash; - return undef; + return; } -sub Undef($$) { +sub Undef { + my $hash = shift; + my $arg = shift; - my ( $hash, $arg ) = @_; my $name = $hash->{NAME}; my $deviceId = $hash->{DEVICEID}; delete $modules{GardenaSmartDevice}{defptr}{$deviceId}; - return undef; + return; } -sub Attr(@) { +sub Attr { my ( $cmd, $name, $attrName, $attrVal ) = @_; my $hash = $defs{$name}; - return undef; + return; } -sub Set($@) { +sub Set { + my $hash = shift; + my $a = shift; - my ( $hash, $name, $cmd, @args ) = @_; + my $name = shift @$a; + my $cmd = shift @$a // return qq{"set $name" needs at least one argument}; my $payload; my $abilities = ''; ### mower if ( lc $cmd eq 'parkuntilfurthernotice' ) { - $payload = '"name":"park_until_further_notice"'; - } elsif ( lc $cmd eq 'parkuntilnexttimer' ) { - $payload = '"name":"park_until_next_timer"'; } elsif ( lc $cmd eq 'startresumeschedule' ) { - $payload = '"name":"start_resume_schedule"'; } elsif ( lc $cmd eq 'startoverridetimer' ) { - - my $duration = join( " ", @args ); $payload = '"name":"start_override_timer","parameters":{"duration":' - . $duration * 60 . '}'; + . $a->[0] * 60 . '}'; } elsif ( lc $cmd eq 'startpoint' ) { my $err; - ( $err, $payload, $abilities ) = - SetPredefinedStartPoints( $hash, @args ); + ( $err, $payload, $abilities ) = SetPredefinedStartPoints( $hash, @$a ); return $err if ( defined($err) ); } ### electronic_pressure_pump elsif ( lc $cmd eq 'pumptimer' ) { - - my $duration = join( " ", @args ); - $payload = '"name":"pump_manual_watering_timer","parameters":{"duration":' - . $duration . '}'; + . $a->[0] . '}'; } ### watering_computer elsif ( lc $cmd eq 'manualoverride' ) { - - my $duration = join( " ", @args ); - $payload = '"name":"manual_override","parameters":{"duration":' - . $duration . '}'; - + $payload = + '"properties":{"name":"watering_timer_1' + . '","value":{"state":"manual","duration":' + . $a->[0] * 60 + . ',"valve_id":1}}'; } - elsif ( lc $cmd eq 'canceloverride' ) { + elsif ( $cmd =~ m{\AcancelOverride}xms ) { - $payload = '"name":"cancel_override"'; + my $valve_id = 1; + if ( $cmd =~ m{\AcancelOverrideValve(\d)\z}xms ) { + $valve_id = $1; + } + + $payload = + '"properties":{"name":"watering_timer_' + . $valve_id + . '","value":{"state":"idle","duration":' + . 0 + . ',"valve_id":' + . $valve_id . '}}'; } - elsif ( lc $cmd eq 'on' or lc $cmd eq 'off' or lc $cmd eq 'on-for-timer' ) { + elsif ( lc $cmd eq 'on' || lc $cmd eq 'off' || lc $cmd eq 'on-for-timer' ) { + my $val = ( + defined($a) && ref($a) eq 'ARRAY' + ? $a->[0] * 60 + : lc $cmd + ); - my $val = ( defined( $args[0] ) ? join( " ", @args ) * 60 : lc $cmd ); $payload = '"properties":{"value":"' . $val . '"}'; } ### Watering ic24 - elsif ( $cmd =~ /manualDurationValve/ ) { - + elsif ( $cmd =~ m{\AmanualDurationValve\d\z}xms ) { my $valve_id; - my $duration = join( " ", @args ); - if ( $cmd =~ m#(\d)$# ) { + if ( $cmd =~ m{\AmanualDurationValve(\d)\z}xms ) { $valve_id = $1; } @@ -368,14 +351,14 @@ sub Set($@) { '"properties":{"name":"watering_timer_' . $valve_id . '","value":{"state":"manual","duration":' - . $duration * 60 + . $a->[0] * 60 . ',"valve_id":' . $valve_id . '}}'; } ### Sensors elsif ( lc $cmd eq 'refresh' ) { - my $sensname = join( " ", @args ); + my $sensname = $a->[0]; if ( lc $sensname eq 'temperature' ) { $payload = '"name":"measure_ambient_temperature"'; $abilities = 'ambient_temperature'; @@ -395,18 +378,12 @@ sub Set($@) { else { my $list = ''; - $list .= -'parkUntilFurtherNotice:noArg parkUntilNextTimer:noArg startResumeSchedule:noArg startOverrideTimer:slider,0,1,60 startpoint' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ); - $list .= 'manualOverride:slider,0,1,59 cancelOverride:noArg' + $list .= 'manualOverride:slider,1,1,59 cancelOverride:noArg' if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); -# $list .= 'pumpTimer:slider,0,1,59' -# if ( AttrVal( $name, 'model', 'unknown' ) eq 'electronic_pressure_pump' ); - $list .= -'manualDurationValve1:slider,1,1,59 manualDurationValve2:slider,1,1,59 manualDurationValve3:slider,1,1,59 manualDurationValve4:slider,1,1,59 manualDurationValve5:slider,1,1,59 manualDurationValve6:slider,1,1,59' +'manualDurationValve1:slider,1,1,59 manualDurationValve2:slider,1,1,59 manualDurationValve3:slider,1,1,59 manualDurationValve4:slider,1,1,59 manualDurationValve5:slider,1,1,59 manualDurationValve6:slider,1,1,59 cancelOverrideValve1:noArg cancelOverrideValve2:noArg cancelOverrideValve3:noArg cancelOverrideValve4:noArg cancelOverrideValve5:noArg cancelOverrideValve6:noArg' if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); $list .= 'refresh:temperature,light,humidity' @@ -420,11 +397,10 @@ sub Set($@) { $abilities = 'mower' if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' ) - and $abilities ne 'mower_settings'; - $abilities = 'outlet' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); + && $abilities ne 'mower_settings'; $abilities = 'watering' - if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' ); + if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' + || AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); $abilities = 'power' if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' ); $abilities = 'manual_watering' @@ -437,12 +413,12 @@ sub Set($@) { Log3 $name, 4, "GardenaSmartBridge ($name) - IOWrite: $payload $hash->{DEVICEID} $abilities IODevHash=$hash->{IODev}"; - return undef; + return; } -sub Parse($$) { - - my ( $io_hash, $json ) = @_; +sub Parse { + my $io_hash = shift; + my $json = shift; my $name = $io_hash->{NAME}; @@ -481,11 +457,13 @@ sub Parse($$) { . " GardenaSmartDevice $decode_json->{id} $decode_json->{category}"; } } + + return; } -sub WriteReadings($$) { - - my ( $hash, $decode_json ) = @_; +sub WriteReadings { + my $hash = shift; + my $decode_json = shift; my $name = $hash->{NAME}; my $abilities = scalar( @{ $decode_json->{abilities} } ); @@ -497,11 +475,10 @@ sub WriteReadings($$) { if ( ref( $decode_json->{abilities}[$abilities]{properties} ) eq "ARRAY" - and - scalar( @{ $decode_json->{abilities}[$abilities]{properties} } ) > - 0 ) + && scalar( @{ $decode_json->{abilities}[$abilities]{properties} } ) + > 0 ) { - foreach my $propertie ( + for my $propertie ( @{ $decode_json->{abilities}[$abilities]{properties} } ) { readingsBulkUpdateIfChanged( @@ -511,21 +488,21 @@ sub WriteReadings($$) { RigRadingsValue( $hash, $propertie->{value} ) ) if ( defined( $propertie->{value} ) - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'radio-quality' - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'battery-level' - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'internal_temperature-temperature' - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'ambient_temperature-temperature' - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'soil_temperature-temperature' - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'humidity-humidity' - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} ne 'light-light' - and ref( $propertie->{value} ) ne "HASH" ); + && ref( $propertie->{value} ) ne "HASH" ); readingsBulkUpdate( $hash, @@ -535,21 +512,21 @@ sub WriteReadings($$) { ) if ( defined( $propertie->{value} ) - and ( $decode_json->{abilities}[$abilities]{name} . '-' + && ( $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'radio-quality' - or $decode_json->{abilities}[$abilities]{name} . '-' + || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'battery-level' - or $decode_json->{abilities}[$abilities]{name} . '-' + || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'internal_temperature-temperature' - or $decode_json->{abilities}[$abilities]{name} . '-' + || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'ambient_temperature-temperature' - or $decode_json->{abilities}[$abilities]{name} . '-' + || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'soil_temperature-temperature' - or $decode_json->{abilities}[$abilities]{name} . '-' + || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'humidity-humidity' - or $decode_json->{abilities}[$abilities]{name} . '-' + || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'light-light' ) ); @@ -560,7 +537,7 @@ sub WriteReadings($$) { join( ',', @{ $propertie->{value} } ) ) if ( defined( $propertie->{value} ) - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'ic24-valves_connected' ); readingsBulkUpdateIfChanged( @@ -570,7 +547,7 @@ sub WriteReadings($$) { join( ',', @{ $propertie->{value} } ) ) if ( defined( $propertie->{value} ) - and $decode_json->{abilities}[$abilities]{name} . '-' + && $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'ic24-valves_master_config' ); if ( ref( $propertie->{value} ) eq "HASH" ) { @@ -593,7 +570,7 @@ sub WriteReadings($$) { do { if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY" - and $decode_json->{settings}[$settings]{name} eq 'starting_points' ) + && $decode_json->{settings}[$settings]{name} eq 'starting_points' ) { #save the startingpointid needed to update the startingpoints if ( $hash->{helper}{STARTINGPOINTID} ne @@ -608,7 +585,7 @@ sub WriteReadings($$) { . encode_json( $decode_json->{settings}[$settings]{value} ) . '}'; my $startpoint_cnt = 0; - foreach my $startingpoint ( + for my $startingpoint ( @{ $decode_json->{settings}[$settings]{value} } ) { $startpoint_cnt++; @@ -629,9 +606,9 @@ sub WriteReadings($$) { readingsBulkUpdate( $hash, 'state', ( - ReadingsVal( $name, 'outlet-valve_open', 0 ) == 1 - ? RigRadingsValue( $hash, 'open' ) - : RigRadingsValue( $hash, 'closed' ) + ReadingsVal( $name, 'watering-watering_timer_1_state', 0 ) eq 'idle' + ? RigRadingsValue( $hash, 'closed' ) + : RigRadingsValue( $hash, 'open' ) ) ) if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ); @@ -664,17 +641,19 @@ sub WriteReadings($$) { readingsEndUpdate( $hash, 1 ); Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written}"; + + return; } ################################## ################################## #### my little helpers ########### -sub ReadingLangGerman($$) { - - my ( $hash, $readingValue ) = @_; - my $name = $hash->{NAME}; +sub ReadingLangGerman { + my $hash = shift; + my $readingValue = shift; + my $name = $hash->{NAME}; my %langGermanMapp = ( 'ok_cutting' => 'mähen', 'paused' => 'pausiert', @@ -777,9 +756,9 @@ sub ReadingLangGerman($$) { if ( defined( $langGermanMapp{$readingValue} ) - and ( AttrVal( 'global', 'language', 'none' ) eq 'DE' - or AttrVal( $name, 'readingValueLanguage', 'none' ) eq 'de' ) - and AttrVal( $name, 'readingValueLanguage', 'none' ) ne 'en' + && ( AttrVal( 'global', 'language', 'none' ) eq 'DE' + || AttrVal( $name, 'readingValueLanguage', 'none' ) eq 'de' ) + && AttrVal( $name, 'readingValueLanguage', 'none' ) ne 'en' ) { return $langGermanMapp{$readingValue}; @@ -787,11 +766,13 @@ sub ReadingLangGerman($$) { else { return $readingValue; } + + return; } -sub RigRadingsValue($$) { - - my ( $hash, $readingValue ) = @_; +sub RigRadingsValue { + my $hash = shift; + my $readingValue = shift; my $rigReadingValue; @@ -805,9 +786,9 @@ sub RigRadingsValue($$) { return $rigReadingValue; } -sub Zulu2LocalString($) { - +sub Zulu2LocalString { my $t = shift; + my ( $datehour, $datemin, $rest ) = split( /:/, $t, 3 ); my ( $year, $month, $day, $hour, $min ) = @@ -840,18 +821,23 @@ sub Zulu2LocalString($) { ) ); } + + return; } -sub SetPredefinedStartPoints($@) { +sub SetPredefinedStartPoints { + my $hash = shift; + my $a = shift; + + my ( $startpoint_state, $startpoint_num, @morestartpoints ) = @$a; - my ( $hash, $startpoint_state, $startpoint_num, @morestartpoints ) = @_; my $name = $hash->{NAME}; my $payload; my $abilities; - if ( defined($startpoint_state) and defined($startpoint_num) ) { + if ( defined($startpoint_state) && defined($startpoint_num) ) { if ( defined( $hash->{helper}{STARTINGPOINTS} ) - and $hash->{helper}{STARTINGPOINTS} ne '' ) + && $hash->{helper}{STARTINGPOINTS} ne '' ) { # add needed parameters to saved settings config and change the value in request my $decode_json_settings = @@ -869,8 +855,8 @@ sub SetPredefinedStartPoints($@) { #set more startpoints if ( defined scalar(@morestartpoints) - and ( scalar(@morestartpoints) == 2 - or scalar(@morestartpoints) == 4 ) + && ( scalar(@morestartpoints) == 2 + || scalar(@morestartpoints) == 4 ) ) { if ( scalar(@morestartpoints) == 2 ) { @@ -1221,6 +1207,7 @@ sub SetPredefinedStartPoints($@) { ], "release_status": "stable", "license": "GPL_2", + "version": "v2.0.0", "author": [ "Marko Oldenburg " ],