testing #67
@@ -1,8 +1,8 @@
 | 
			
		||||
###############################################################################
 | 
			
		||||
#
 | 
			
		||||
# Developed with Kate
 | 
			
		||||
# Developed with VSCodium and richterger perl plugin.
 | 
			
		||||
#
 | 
			
		||||
#  (c) 2017-2021 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net)
 | 
			
		||||
#  (c) 2017-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net)
 | 
			
		||||
#  All rights reserved
 | 
			
		||||
#
 | 
			
		||||
#   Special thanks goes to comitters:
 | 
			
		||||
@@ -57,22 +57,20 @@
 | 
			
		||||
package FHEM::GardenaSmartBridge;
 | 
			
		||||
use GPUtils qw(GP_Import GP_Export);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
use POSIX;
 | 
			
		||||
use FHEM::Meta;
 | 
			
		||||
 | 
			
		||||
#use Data::Dumper;
 | 
			
		||||
 | 
			
		||||
use HttpUtils;
 | 
			
		||||
 | 
			
		||||
my $missingModul = '';
 | 
			
		||||
eval "use Encode qw(encode encode_utf8 decode_utf8);1"
 | 
			
		||||
eval { use Encode qw /encode_utf8 decode_utf8/; 1 }
 | 
			
		||||
  or $missingModul .= "Encode ";
 | 
			
		||||
 | 
			
		||||
# eval "use JSON;1" || $missingModul .= 'JSON ';
 | 
			
		||||
eval "use IO::Socket::SSL;1" or $missingModul .= 'IO::Socket::SSL ';
 | 
			
		||||
eval { use IO::Socket::SSL; 1 }
 | 
			
		||||
  or $missingModul .= 'IO::Socket::SSL ';
 | 
			
		||||
 | 
			
		||||
# try to use JSON::MaybeXS wrapper
 | 
			
		||||
#   for chance of better performance + open code
 | 
			
		||||
@@ -80,15 +78,11 @@ eval {
 | 
			
		||||
    require JSON::MaybeXS;
 | 
			
		||||
    import JSON::MaybeXS qw( decode_json encode_json );
 | 
			
		||||
    1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if ($@) {
 | 
			
		||||
    $@ = undef;
 | 
			
		||||
} or do {
 | 
			
		||||
 | 
			
		||||
    # try to use JSON wrapper
 | 
			
		||||
    #   for chance of better performance
 | 
			
		||||
    eval {
 | 
			
		||||
 | 
			
		||||
        # JSON preference order
 | 
			
		||||
        local $ENV{PERL_JSON_BACKEND} =
 | 
			
		||||
          'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP'
 | 
			
		||||
@@ -97,10 +91,7 @@ if ($@) {
 | 
			
		||||
        require JSON;
 | 
			
		||||
        import JSON qw( decode_json encode_json );
 | 
			
		||||
        1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if ($@) {
 | 
			
		||||
        $@ = undef;
 | 
			
		||||
    } or do {
 | 
			
		||||
 | 
			
		||||
        # In rare cases, Cpanel::JSON::XS may
 | 
			
		||||
        #   be installed but JSON|JSON::MaybeXS not ...
 | 
			
		||||
@@ -108,10 +99,7 @@ if ($@) {
 | 
			
		||||
            require Cpanel::JSON::XS;
 | 
			
		||||
            import Cpanel::JSON::XS qw(decode_json encode_json);
 | 
			
		||||
            1;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if ($@) {
 | 
			
		||||
            $@ = undef;
 | 
			
		||||
        } or do {
 | 
			
		||||
 | 
			
		||||
            # In rare cases, JSON::XS may
 | 
			
		||||
            #   be installed but JSON not ...
 | 
			
		||||
@@ -119,10 +107,7 @@ if ($@) {
 | 
			
		||||
                require JSON::XS;
 | 
			
		||||
                import JSON::XS qw(decode_json encode_json);
 | 
			
		||||
                1;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if ($@) {
 | 
			
		||||
                $@ = undef;
 | 
			
		||||
            } or do {
 | 
			
		||||
 | 
			
		||||
                # Fallback to built-in JSON which SHOULD
 | 
			
		||||
                #   be available since 5.014 ...
 | 
			
		||||
@@ -130,20 +115,17 @@ if ($@) {
 | 
			
		||||
                    require JSON::PP;
 | 
			
		||||
                    import JSON::PP qw(decode_json encode_json);
 | 
			
		||||
                    1;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if ($@) {
 | 
			
		||||
                    $@ = undef;
 | 
			
		||||
                } or do {
 | 
			
		||||
 | 
			
		||||
                    # Fallback to JSON::backportPP in really rare cases
 | 
			
		||||
                    require JSON::backportPP;
 | 
			
		||||
                    import JSON::backportPP qw(decode_json encode_json);
 | 
			
		||||
                    1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
## Import der FHEM Funktionen
 | 
			
		||||
#-- Run before package compilation
 | 
			
		||||
@@ -237,9 +219,7 @@ sub Define {
 | 
			
		||||
    my $name = shift @$aArg;
 | 
			
		||||
    $hash->{BRIDGE} = 1;
 | 
			
		||||
    $hash->{URL} =
 | 
			
		||||
      AttrVal( $name, 'gardenaBaseURL',
 | 
			
		||||
        'https://smart.gardena.com' )
 | 
			
		||||
      . '/v1';
 | 
			
		||||
      AttrVal( $name, 'gardenaBaseURL', 'https://smart.gardena.com' ) . '/v1';
 | 
			
		||||
    $hash->{VERSION}   = version->parse($VERSION)->normal;
 | 
			
		||||
    $hash->{INTERVAL}  = 60;
 | 
			
		||||
    $hash->{NOTIFYDEV} = "global,$name";
 | 
			
		||||
@@ -282,7 +262,8 @@ sub Attr {
 | 
			
		||||
 | 
			
		||||
    if ( $attrName eq 'disable' ) {
 | 
			
		||||
        if ( $cmd eq 'set' && $attrVal eq '1' ) {
 | 
			
		||||
            RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices");
 | 
			
		||||
            RemoveInternalTimer( $hash,
 | 
			
		||||
                "FHEM::GardenaSmartBridge::getDevices" );
 | 
			
		||||
            readingsSingleUpdate( $hash, 'state', 'inactive', 1 );
 | 
			
		||||
            Log3 $name, 3, "GardenaSmartBridge ($name) - disabled";
 | 
			
		||||
        }
 | 
			
		||||
@@ -307,13 +288,15 @@ sub Attr {
 | 
			
		||||
        if ( $cmd eq 'set' ) {
 | 
			
		||||
            return 'Interval must be greater than 0'
 | 
			
		||||
              if ( $attrVal == 0 );
 | 
			
		||||
            RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices");
 | 
			
		||||
            RemoveInternalTimer( $hash,
 | 
			
		||||
                "FHEM::GardenaSmartBridge::getDevices" );
 | 
			
		||||
            $hash->{INTERVAL} = $attrVal;
 | 
			
		||||
            Log3 $name, 3,
 | 
			
		||||
              "GardenaSmartBridge ($name) - set interval: $attrVal";
 | 
			
		||||
        }
 | 
			
		||||
        elsif ( $cmd eq 'del' ) {
 | 
			
		||||
            RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices");
 | 
			
		||||
            RemoveInternalTimer( $hash,
 | 
			
		||||
                "FHEM::GardenaSmartBridge::getDevices" );
 | 
			
		||||
            $hash->{INTERVAL} = 60;
 | 
			
		||||
            Log3 $name, 3,
 | 
			
		||||
"GardenaSmartBridge ($name) - delete User interval and set default: 60";
 | 
			
		||||
@@ -360,13 +343,8 @@ sub Notify {
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        || (
 | 
			
		||||
            $devtype eq 'GardenaSmartBridge'
 | 
			
		||||
            && (
 | 
			
		||||
                grep /^gardenaAccountPassword.+/,
 | 
			
		||||
                @{$events}
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        || ( $devtype eq 'GardenaSmartBridge'
 | 
			
		||||
            && ( grep /^gardenaAccountPassword.+/, @{$events} ) )
 | 
			
		||||
        && $init_done
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
@@ -399,6 +377,7 @@ sub Notify {
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub Get {
 | 
			
		||||
    my $hash = shift // return;
 | 
			
		||||
    my $aArg = shift // return;
 | 
			
		||||
@@ -411,11 +390,12 @@ sub Get {
 | 
			
		||||
        my $device = shift @$aArg;
 | 
			
		||||
        $hash->{helper}{debug_device} = $device;
 | 
			
		||||
        Write( $hash, undef, undef, undef, undef );
 | 
			
		||||
        return undef;
 | 
			
		||||
    } else {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        my $list = "";
 | 
			
		||||
        $list .= " debug_devices_list:"
 | 
			
		||||
          .join( ',', @{ $hash->{helper}{deviceList} }) 
 | 
			
		||||
        $list .=
 | 
			
		||||
          " debug_devices_list:" . join( ',', @{ $hash->{helper}{deviceList} } )
 | 
			
		||||
          if ( AttrVal( $name, "debugDEVICE", "none" ) ne "none"
 | 
			
		||||
            && exists( $hash->{helper}{deviceList} ) );
 | 
			
		||||
        return "Unknown argument $cmd,choose one of $list";
 | 
			
		||||
@@ -427,7 +407,8 @@ sub Set {
 | 
			
		||||
    my $aArg = shift // return;
 | 
			
		||||
 | 
			
		||||
    my $name = shift @$aArg // return;
 | 
			
		||||
    my $cmd  = shift @$aArg // return qq{"set $name" needs at least one argument};
 | 
			
		||||
    my $cmd  = shift @$aArg
 | 
			
		||||
      // return qq{"set $name" needs at least one argument};
 | 
			
		||||
 | 
			
		||||
#     Das Argument für das Passwort, also das Passwort an sich darf keine = enthalten!!!
 | 
			
		||||
 | 
			
		||||
@@ -478,7 +459,8 @@ sub Write {
 | 
			
		||||
    my ( $session_id, $header, $uri, $method );
 | 
			
		||||
 | 
			
		||||
    ( $payload, $session_id, $header, $uri, $method, $deviceId, $service_id ) =
 | 
			
		||||
      createHttpValueStrings( $hash, $payload, $deviceId, $abilities, $service_id );
 | 
			
		||||
      createHttpValueStrings( $hash, $payload, $deviceId, $abilities,
 | 
			
		||||
        $service_id );
 | 
			
		||||
 | 
			
		||||
    HttpUtils_NonblockingGet(
 | 
			
		||||
        {
 | 
			
		||||
@@ -702,6 +684,11 @@ sub ErrorHandling {
 | 
			
		||||
              . $param->{code};
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( !defined( $hash->{helper}{session_id} ) ) {
 | 
			
		||||
            readingsSingleUpdate( $hash, 'token', 'none', 1 );
 | 
			
		||||
            InternalTimer( gettimeofday() + 5,
 | 
			
		||||
                "FHEM::GardenaSmartBridge::getToken", $hash );
 | 
			
		||||
        }
 | 
			
		||||
        readingsEndUpdate( $dhash, 1 );
 | 
			
		||||
 | 
			
		||||
        Log3 $dname, 5,
 | 
			
		||||
@@ -712,32 +699,33 @@ sub ErrorHandling {
 | 
			
		||||
        delete $dhash->{helper}{deviceAction}
 | 
			
		||||
          if ( defined( $dhash->{helper}{deviceAction} ) );
 | 
			
		||||
 | 
			
		||||
        readingsSingleUpdate( $hash, 'token', 'none', 1 )
 | 
			
		||||
          if ( !defined( $hash->{helper}{session_id} ) );
 | 
			
		||||
 | 
			
		||||
        getToken($hash) 
 | 
			
		||||
          if ( !defined( $hash->{helper}{session_id} ) );
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( defined( $decode_json->{message} )
 | 
			
		||||
          && $decode_json->{message} eq 'Unauthorized') {
 | 
			
		||||
        && $decode_json->{message} eq 'Unauthorized' )
 | 
			
		||||
    {
 | 
			
		||||
        Log3 $name, 3,
 | 
			
		||||
          "GardenaSmartBridge ($name) - Unauthorized -> fetch new token ";
 | 
			
		||||
 | 
			
		||||
        getToken($hash);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( defined( $hash->{helper}{debug_device} )
 | 
			
		||||
    	&& $hash->{helper}{debug_device} ne 'none'
 | 
			
		||||
    	){
 | 
			
		||||
        && $hash->{helper}{debug_device} ne 'none' )
 | 
			
		||||
    {
 | 
			
		||||
        Log3 $name, 4, "GardenaSmartBridge DEBUG Device";
 | 
			
		||||
        delete $hash->{helper}{debug_device};
 | 
			
		||||
 | 
			
		||||
        my @device_spec = ( "name", "id", "category" );
 | 
			
		||||
        my $devJson     = $decode_json->{devices};
 | 
			
		||||
        my $output = '.:{ DEBUG OUTPUT for ' . $devJson->{name} . ' }:. \n';
 | 
			
		||||
 | 
			
		||||
        for my $spec (@device_spec) {
 | 
			
		||||
            $output .= "$spec : $devJson->{$spec} \n";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #settings
 | 
			
		||||
        $output .= '\n=== Settings \n';
 | 
			
		||||
        my $i = 0;
 | 
			
		||||
@@ -745,26 +733,35 @@ sub ErrorHandling {
 | 
			
		||||
            $output .= "[" . $i++ . "]id: $dev_settings->{id} \n";
 | 
			
		||||
            $output .= "name: $dev_settings->{name} ";
 | 
			
		||||
            if (   ref( $dev_settings->{value} ) eq 'ARRAY'
 | 
			
		||||
          || ref ($dev_settings->{value}) eq 'HASH'){
 | 
			
		||||
                || ref( $dev_settings->{value} ) eq 'HASH' )
 | 
			
		||||
            {
 | 
			
		||||
                $output .= 'N/A \n';
 | 
			
		||||
        } else {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                $output .= "value: $dev_settings->{value} \n";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $output .= '\n=== Abilities \n';
 | 
			
		||||
        $i = 0;
 | 
			
		||||
 | 
			
		||||
        for my $dev_settings ( @{ $devJson->{abilities} } ) {
 | 
			
		||||
            $output .= "[" . $i++ . "]id: $dev_settings->{id} \n";
 | 
			
		||||
            $output .= "name: $dev_settings->{name} ";
 | 
			
		||||
 | 
			
		||||
            if (   ref( $dev_settings->{value} ) eq 'ARRAY'
 | 
			
		||||
          || ref ($dev_settings->{value}) eq 'HASH'){
 | 
			
		||||
                || ref( $dev_settings->{value} ) eq 'HASH' )
 | 
			
		||||
            {
 | 
			
		||||
                $output .= 'N/A \n';
 | 
			
		||||
        } else {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                $output .= "value: $dev_settings->{value} \n";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $hash->{helper}{debug_device_output} = $output;
 | 
			
		||||
        asyncOutput( $param->{cl}, $hash->{helper}{debug_device_output} );
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    readingsSingleUpdate( $hash, 'state', 'Connected', 1 )
 | 
			
		||||
@@ -794,16 +791,18 @@ sub ResponseProcessing {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    # print Dumper $decode_json;
 | 
			
		||||
 | 
			
		||||
    if ( defined( $decode_json->{data} ) && $decode_json->{data} 
 | 
			
		||||
    if (   defined( $decode_json->{data} )
 | 
			
		||||
        && $decode_json->{data}
 | 
			
		||||
        && ref( $decode_json->{data} ) eq 'HASH'
 | 
			
		||||
        && !defined( $hash->{helper}->{user_id})) {
 | 
			
		||||
        && !defined( $hash->{helper}->{user_id} ) )
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        $hash->{helper}{session_id} = $decode_json->{data}{id};
 | 
			
		||||
        $hash->{helper}{user_id} = $decode_json->{data}{attributes}->{user_id};
 | 
			
		||||
        $hash->{helper}{refresh_token}  = $decode_json->{data}{attributes}->{refresh_token};
 | 
			
		||||
        $hash->{helper}{token_expired}  = gettimeofday() + $decode_json->{data}{attributes}->{expires_in};
 | 
			
		||||
        $hash->{helper}{refresh_token} =
 | 
			
		||||
          $decode_json->{data}{attributes}->{refresh_token};
 | 
			
		||||
        $hash->{helper}{token_expired} =
 | 
			
		||||
          gettimeofday() + $decode_json->{data}{attributes}->{expires_in};
 | 
			
		||||
 | 
			
		||||
        InternalTimer( $hash->{helper}{token_expired},
 | 
			
		||||
            "FHEM::GardenaSmartBridge::getToken", $hash );
 | 
			
		||||
@@ -838,27 +837,63 @@ sub ResponseProcessing {
 | 
			
		||||
        && ref( $decode_json->{devices} ) eq 'ARRAY'
 | 
			
		||||
        && scalar( @{ $decode_json->{devices} } ) > 0 )
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        my @buffer = split( '"devices":\[', $json );
 | 
			
		||||
 | 
			
		||||
        my ( $json, $tail ) = ParseJSON( $hash, $buffer[1] );
 | 
			
		||||
        require SubProcess;
 | 
			
		||||
 | 
			
		||||
        while ($json) {
 | 
			
		||||
        my $subprocess =
 | 
			
		||||
          SubProcess->new( { onRun => \&ResponseSubprocessing } );
 | 
			
		||||
        $subprocess->{buffer} = $buffer[1];
 | 
			
		||||
 | 
			
		||||
            Log3 $name, 5,
 | 
			
		||||
                "GardenaSmartBridge ($name) - Decoding JSON message. Length: "
 | 
			
		||||
              . length($json)
 | 
			
		||||
              . " Content: "
 | 
			
		||||
              . $json;
 | 
			
		||||
            Log3 $name, 5,
 | 
			
		||||
                "GardenaSmartBridge ($name) - Vor Sub: Laenge JSON: "
 | 
			
		||||
              . length($json)
 | 
			
		||||
              . " Content: "
 | 
			
		||||
              . $json
 | 
			
		||||
              . " Tail: "
 | 
			
		||||
              . $tail;
 | 
			
		||||
        my $pid = $subprocess->run();
 | 
			
		||||
 | 
			
		||||
            if ( defined($tail) and $tail ) {
 | 
			
		||||
        if ( !defined($pid) ) {
 | 
			
		||||
            Log3( $name, 1,
 | 
			
		||||
qq{GardenaSmartBridge ($name) - Cannot execute parse json asynchronously}
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            CleanSubprocess($hash);
 | 
			
		||||
            readingsSingleUpdate( $hash, 'state',
 | 
			
		||||
                'Cannot execute parse json asynchronously', 1 );
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Log3( $name, 4,
 | 
			
		||||
qq{GardenaSmartBridge ($name) - execute parse json asynchronously (PID="$pid")}
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $hash->{".fhem"}{subprocess} = $subprocess;
 | 
			
		||||
 | 
			
		||||
        InternalTimer( gettimeofday() + 1,
 | 
			
		||||
            "FHEM::GardenaSmartBridge::PollChild", $hash );
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Log3 $name, 3, "GardenaSmartBridge ($name) - no Match for processing data";
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub ResponseProcessingFinalFromSubProcessing {
 | 
			
		||||
    my $hash     = shift;
 | 
			
		||||
    my $response = shift;
 | 
			
		||||
 | 
			
		||||
    my $name = $hash->{NAME};
 | 
			
		||||
 | 
			
		||||
    my @response = split '\|,', $response;
 | 
			
		||||
 | 
			
		||||
    Log3( $name, 4,
 | 
			
		||||
        qq{GardenaSmartBridge ($name) - got result from asynchronous parsing} );
 | 
			
		||||
 | 
			
		||||
    my $decode_json;
 | 
			
		||||
 | 
			
		||||
    Log3( $name, 4, qq{GardenaSmartBridge ($name) - asynchronous finished.} );
 | 
			
		||||
 | 
			
		||||
    if ( scalar(@response) > 0 ) {
 | 
			
		||||
        for my $json (@response) {
 | 
			
		||||
 | 
			
		||||
            #################
 | 
			
		||||
            $decode_json = eval { decode_json($json) };
 | 
			
		||||
            if ($@) {
 | 
			
		||||
                Log3 $name, 5,
 | 
			
		||||
@@ -871,32 +906,117 @@ sub ResponseProcessing {
 | 
			
		||||
              if ( defined( $decode_json->{category} )
 | 
			
		||||
                && $decode_json->{category} eq 'gateway' );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
            ( $json, $tail ) = ParseJSON( $hash, $tail );
 | 
			
		||||
 | 
			
		||||
            Log3 $name, 5,
 | 
			
		||||
                "GardenaSmartBridge ($name) - Nach Sub: Laenge JSON: "
 | 
			
		||||
              . length($json)
 | 
			
		||||
              . " Content: "
 | 
			
		||||
              . $json
 | 
			
		||||
              . " Tail: "
 | 
			
		||||
              . $tail;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    Log3 $name, 3, "GardenaSmartBridge ($name) - no Match for processing data";
 | 
			
		||||
sub PollChild {
 | 
			
		||||
    my $hash = shift;
 | 
			
		||||
 | 
			
		||||
    my $name = $hash->{NAME};
 | 
			
		||||
 | 
			
		||||
    if ( defined( $hash->{".fhem"}{subprocess} ) ) {
 | 
			
		||||
        my $subprocess = $hash->{".fhem"}{subprocess};
 | 
			
		||||
        my $response   = $subprocess->readFromChild();
 | 
			
		||||
 | 
			
		||||
        if ( defined($response) ) {
 | 
			
		||||
            ResponseProcessingFinalFromSubProcessing( $hash, $response );
 | 
			
		||||
            $subprocess->wait();
 | 
			
		||||
            CleanSubprocess($hash);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Log3( $name, 4,
 | 
			
		||||
qq{GardenaSmartBridge ($name) - still waiting ($subprocess->{lasterror}).}
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        InternalTimer( gettimeofday() + 1,
 | 
			
		||||
            "FHEM::GardenaSmartBridge::PollChild", $hash );
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# ResponseSubprocessin muss in eine async ausgelagert werden
 | 
			
		||||
######################################
 | 
			
		||||
# Begin Childprozess
 | 
			
		||||
######################################
 | 
			
		||||
sub ResponseSubprocessing {
 | 
			
		||||
    my $subprocess = shift;
 | 
			
		||||
    my $buffer     = $subprocess->{buffer};
 | 
			
		||||
    my @response   = ();
 | 
			
		||||
 | 
			
		||||
    my ( $json, $tail ) = ParseJSON($buffer);
 | 
			
		||||
 | 
			
		||||
    while ($json) {
 | 
			
		||||
        if ( defined($tail) and $tail ) {
 | 
			
		||||
            push @response, $json;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ( $json, $tail ) = ParseJSON($tail);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $subprocess->writeToParent( join '|', @response );
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub ParseJSON {
 | 
			
		||||
    my $buffer = shift;
 | 
			
		||||
 | 
			
		||||
    my $open  = 0;
 | 
			
		||||
    my $close = 0;
 | 
			
		||||
    my $msg   = '';
 | 
			
		||||
    my $tail  = '';
 | 
			
		||||
 | 
			
		||||
    if ($buffer) {
 | 
			
		||||
        for my $c ( split //, $buffer ) {
 | 
			
		||||
            if ( $open == $close && $open > 0 ) {
 | 
			
		||||
                $tail .= $c;
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
 | 
			
		||||
                if ( $c eq '{' ) {
 | 
			
		||||
 | 
			
		||||
                    $open++;
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                elsif ( $c eq '}' ) {
 | 
			
		||||
 | 
			
		||||
                    $close++;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $msg .= $c;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( $open != $close ) {
 | 
			
		||||
 | 
			
		||||
            $tail = $msg;
 | 
			
		||||
            $msg  = '';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return ( $msg, $tail );
 | 
			
		||||
}
 | 
			
		||||
######################################
 | 
			
		||||
# End Childprozess
 | 
			
		||||
######################################
 | 
			
		||||
 | 
			
		||||
sub CleanSubprocess {
 | 
			
		||||
    my $hash = shift;
 | 
			
		||||
 | 
			
		||||
    my $name = $hash->{NAME};
 | 
			
		||||
 | 
			
		||||
    delete( $hash->{".fhem"}{subprocess} );
 | 
			
		||||
    Log3( $name, 4, qq{GardenaSmartBridge ($name) - clean Subprocess} );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub WriteReadings {
 | 
			
		||||
    my $hash        = shift;
 | 
			
		||||
    my $decode_json = shift;
 | 
			
		||||
 | 
			
		||||
    #     print Dumper $decode_json;
 | 
			
		||||
 | 
			
		||||
    my $name = $hash->{NAME};
 | 
			
		||||
 | 
			
		||||
    if (   defined( $decode_json->{id} )
 | 
			
		||||
@@ -941,7 +1061,8 @@ sub WriteReadings {
 | 
			
		||||
                          {name} . '-' . $t,
 | 
			
		||||
                        $v
 | 
			
		||||
                      )
 | 
			
		||||
                      if ($decode_json->{abilities}[0]{properties}[$properties]{name} !~ /ethernet_status|wifi_status/ );
 | 
			
		||||
                      if ( $decode_json->{abilities}[0]{properties}[$properties]
 | 
			
		||||
                        {name} !~ /ethernet_status|wifi_status/ );
 | 
			
		||||
                    if (
 | 
			
		||||
                        (
 | 
			
		||||
                            $decode_json->{abilities}[0]{properties}
 | 
			
		||||
@@ -1013,9 +1134,7 @@ sub getDevices {
 | 
			
		||||
            push( @{ $hash->{helper}{deviceList} }, $gardenaDev );
 | 
			
		||||
        }
 | 
			
		||||
        if ( AttrVal( $name, 'gardenaAccountEmail', 'none' ) ne 'none'
 | 
			
		||||
          && (
 | 
			
		||||
            defined( ReadPassword( $hash, $name ) ) 
 | 
			
		||||
          )) 
 | 
			
		||||
            && ( defined( ReadPassword( $hash, $name ) ) ) )
 | 
			
		||||
        {
 | 
			
		||||
            Write( $hash, undef, undef, undef );
 | 
			
		||||
            Log3 $name, 4,
 | 
			
		||||
@@ -1044,27 +1163,29 @@ sub getToken {
 | 
			
		||||
    readingsSingleUpdate( $hash, 'state', 'get token', 1 );
 | 
			
		||||
 | 
			
		||||
    delete $hash->{helper}{session_id}
 | 
			
		||||
      if ( defined( $hash->{helper}{session_id} )
 | 
			
		||||
        && $hash->{helper}{session_id} );
 | 
			
		||||
      if ( exists( $hash->{helper}{session_id} ) );
 | 
			
		||||
    delete $hash->{helper}{user_id}
 | 
			
		||||
      if ( defined( $hash->{helper}{user_id} ) && $hash->{helper}{user_id} );
 | 
			
		||||
      if ( exists( $hash->{helper}{user_id} ) );
 | 
			
		||||
    delete $hash->{helper}{locations_id}
 | 
			
		||||
      if ( defined( $hash->{helper}{locations_id} )
 | 
			
		||||
        && $hash->{helper}{locations_id} );
 | 
			
		||||
      if ( exists( $hash->{helper}{locations_id} ) );
 | 
			
		||||
 | 
			
		||||
    Write(
 | 
			
		||||
        $hash,
 | 
			
		||||
        '"data": {"type":"token", "attributes":{"username": "'
 | 
			
		||||
          . AttrVal( $name, 'gardenaAccountEmail', 'none' )
 | 
			
		||||
          . '","password": "'
 | 
			
		||||
            . ReadPassword( $hash, $name ) . '", "client_id":"smartgarden-jwt-client"}}',
 | 
			
		||||
          . ReadPassword( $hash, $name )
 | 
			
		||||
          . '", "client_id":"smartgarden-jwt-client"}}',
 | 
			
		||||
        undef,
 | 
			
		||||
        undef
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    Log3 $name, 4, '"data": {"type":"token", "attributes":{"username": "'     
 | 
			
		||||
               .AttrVal( $name, 'gardenaAccountEmail', 'none' ) . '","password": "'
 | 
			
		||||
               .ReadPassword( $hash, $name ) . '", "client_id":"smartgarden-jwt-client"}}';
 | 
			
		||||
    Log3 $name, 4,
 | 
			
		||||
        '"data": {"type":"token", "attributes":{"username": "'
 | 
			
		||||
      . AttrVal( $name, 'gardenaAccountEmail', 'none' )
 | 
			
		||||
      . '","password": "'
 | 
			
		||||
      . ReadPassword( $hash, $name )
 | 
			
		||||
      . '", "client_id":"smartgarden-jwt-client"}}';
 | 
			
		||||
    Log3 $name, 3,
 | 
			
		||||
"GardenaSmartBridge ($name) - send credentials to fetch Token and locationId";
 | 
			
		||||
 | 
			
		||||
@@ -1115,7 +1236,7 @@ sub ReadPassword {
 | 
			
		||||
 | 
			
		||||
        Log3 $name, 3,
 | 
			
		||||
"GardenaSmartBridge ($name) - unable to read password from file: $err";
 | 
			
		||||
        return undef;
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1141,7 +1262,7 @@ sub ReadPassword {
 | 
			
		||||
    else {
 | 
			
		||||
 | 
			
		||||
        Log3 $name, 3, "GardenaSmartBridge ($name) - No password in file";
 | 
			
		||||
        return undef;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
@@ -1159,59 +1280,6 @@ sub Rename {
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub ParseJSON {
 | 
			
		||||
    my $hash   = shift;
 | 
			
		||||
    my $buffer = shift;
 | 
			
		||||
 | 
			
		||||
    my $name  = $hash->{NAME};
 | 
			
		||||
    my $open  = 0;
 | 
			
		||||
    my $close = 0;
 | 
			
		||||
    my $msg   = '';
 | 
			
		||||
    my $tail  = '';
 | 
			
		||||
 | 
			
		||||
    if ($buffer) {
 | 
			
		||||
        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 ) && ( $c ne '{' ) ) {
 | 
			
		||||
 | 
			
		||||
                Log3 $name, 5,
 | 
			
		||||
"GardenaSmartBridge ($name) - Garbage character before message: "
 | 
			
		||||
                  . $c;
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
 | 
			
		||||
                if ( $c eq '{' ) {
 | 
			
		||||
 | 
			
		||||
                    $open++;
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                elsif ( $c eq '}' ) {
 | 
			
		||||
 | 
			
		||||
                    $close++;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $msg .= $c;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( $open != $close ) {
 | 
			
		||||
 | 
			
		||||
            $tail = $msg;
 | 
			
		||||
            $msg  = '';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Log3 $name, 5,
 | 
			
		||||
      "GardenaSmartBridge ($name) - return msg: $msg and tail: $tail";
 | 
			
		||||
    return ( $msg, $tail );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub createHttpValueStrings {
 | 
			
		||||
    my ( $hash, $payload, $deviceId, $abilities, $service_id ) = @_;
 | 
			
		||||
 | 
			
		||||
@@ -1243,7 +1311,9 @@ sub createHttpValueStrings {
 | 
			
		||||
            && defined( $hash->{helper}{locations_id} ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $uri = '/devices/'.InternalVal($hash->{helper}{debug_device}, 'DEVICEID', 0 ) if ( defined ($hash->{helper}{debug_device})  
 | 
			
		||||
    $uri =
 | 
			
		||||
      '/devices/' . InternalVal( $hash->{helper}{debug_device}, 'DEVICEID', 0 )
 | 
			
		||||
      if ( defined( $hash->{helper}{debug_device} )
 | 
			
		||||
        && defined( $hash->{helper}{locations_id} ) );
 | 
			
		||||
    $uri = '/auth/token' if ( !defined( $hash->{helper}{session_id} ) );
 | 
			
		||||
 | 
			
		||||
@@ -1253,11 +1323,7 @@ sub createHttpValueStrings {
 | 
			
		||||
            $method = 'PUT';
 | 
			
		||||
            my $dhash = $modules{GardenaSmartDevice}{defptr}{$deviceId};
 | 
			
		||||
 | 
			
		||||
            $uri .=
 | 
			
		||||
                '/devices/'
 | 
			
		||||
              . $deviceId
 | 
			
		||||
              . '/settings/'
 | 
			
		||||
              . $service_id
 | 
			
		||||
            $uri .= '/devices/' . $deviceId . '/settings/' . $service_id
 | 
			
		||||
              if ( defined($abilities)
 | 
			
		||||
                && defined($payload)
 | 
			
		||||
                && $abilities =~ /.*_settings/ );
 | 
			
		||||
@@ -1293,7 +1359,11 @@ sub createHttpValueStrings {
 | 
			
		||||
              . $deviceId
 | 
			
		||||
              . '/abilities/'
 | 
			
		||||
              . $abilities
 | 
			
		||||
              . ( defined($valve_id) ? '/properties/watering_timer_'. $valve_id : '/command')
 | 
			
		||||
              . (
 | 
			
		||||
                defined($valve_id)
 | 
			
		||||
                ? '/properties/watering_timer_' . $valve_id
 | 
			
		||||
                : '/command'
 | 
			
		||||
              );
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        elsif (defined($abilities)
 | 
			
		||||
@@ -1507,9 +1577,9 @@ sub DeletePassword {
 | 
			
		||||
  ],
 | 
			
		||||
  "release_status": "stable",
 | 
			
		||||
  "license": "GPL_2",
 | 
			
		||||
  "version": "v2.4.6",
 | 
			
		||||
  "version": "v2.4.7",
 | 
			
		||||
  "author": [
 | 
			
		||||
    "Marko Oldenburg <leongaultier@gmail.com>"
 | 
			
		||||
    "Marko Oldenburg <fhemdevelopment@cooltux.net>"
 | 
			
		||||
  ],
 | 
			
		||||
  "x_fhem_maintainer": [
 | 
			
		||||
    "CoolTux"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
###############################################################################
 | 
			
		||||
#
 | 
			
		||||
# Developed with Kate
 | 
			
		||||
# Developed with VSCodium and richterger perl plugin.
 | 
			
		||||
#
 | 
			
		||||
#  (c) 2017-2021 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net)
 | 
			
		||||
#  (c) 2017-2022 Copyright: Marko Oldenburg (fhemdevelopment at cooltux dot net)
 | 
			
		||||
#  All rights reserved
 | 
			
		||||
#
 | 
			
		||||
#   Special thanks goes to comitters:
 | 
			
		||||
@@ -72,15 +72,11 @@ eval {
 | 
			
		||||
    require JSON::MaybeXS;
 | 
			
		||||
    import JSON::MaybeXS qw( decode_json encode_json );
 | 
			
		||||
    1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if ($@) {
 | 
			
		||||
    $@ = undef;
 | 
			
		||||
} or do {
 | 
			
		||||
 | 
			
		||||
    # try to use JSON wrapper
 | 
			
		||||
    #   for chance of better performance
 | 
			
		||||
    eval {
 | 
			
		||||
 | 
			
		||||
        # JSON preference order
 | 
			
		||||
        local $ENV{PERL_JSON_BACKEND} =
 | 
			
		||||
          'Cpanel::JSON::XS,JSON::XS,JSON::PP,JSON::backportPP'
 | 
			
		||||
@@ -89,10 +85,7 @@ if ($@) {
 | 
			
		||||
        require JSON;
 | 
			
		||||
        import JSON qw( decode_json encode_json );
 | 
			
		||||
        1;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if ($@) {
 | 
			
		||||
        $@ = undef;
 | 
			
		||||
    } or do {
 | 
			
		||||
 | 
			
		||||
        # In rare cases, Cpanel::JSON::XS may
 | 
			
		||||
        #   be installed but JSON|JSON::MaybeXS not ...
 | 
			
		||||
@@ -100,10 +93,7 @@ if ($@) {
 | 
			
		||||
            require Cpanel::JSON::XS;
 | 
			
		||||
            import Cpanel::JSON::XS qw(decode_json encode_json);
 | 
			
		||||
            1;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        if ($@) {
 | 
			
		||||
            $@ = undef;
 | 
			
		||||
        } or do {
 | 
			
		||||
 | 
			
		||||
            # In rare cases, JSON::XS may
 | 
			
		||||
            #   be installed but JSON not ...
 | 
			
		||||
@@ -111,10 +101,7 @@ if ($@) {
 | 
			
		||||
                require JSON::XS;
 | 
			
		||||
                import JSON::XS qw(decode_json encode_json);
 | 
			
		||||
                1;
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if ($@) {
 | 
			
		||||
                $@ = undef;
 | 
			
		||||
            } or do {
 | 
			
		||||
 | 
			
		||||
                # Fallback to built-in JSON which SHOULD
 | 
			
		||||
                #   be available since 5.014 ...
 | 
			
		||||
@@ -122,20 +109,17 @@ if ($@) {
 | 
			
		||||
                    require JSON::PP;
 | 
			
		||||
                    import JSON::PP qw(decode_json encode_json);
 | 
			
		||||
                    1;
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                if ($@) {
 | 
			
		||||
                    $@ = undef;
 | 
			
		||||
                } or do {
 | 
			
		||||
 | 
			
		||||
                    # Fallback to JSON::backportPP in really rare cases
 | 
			
		||||
                    require JSON::backportPP;
 | 
			
		||||
                    import JSON::backportPP qw(decode_json encode_json);
 | 
			
		||||
                    1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
                };
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
## Import der FHEM Funktionen
 | 
			
		||||
#-- Run before package compilation
 | 
			
		||||
@@ -181,7 +165,7 @@ sub Initialize {
 | 
			
		||||
    $hash->{AttrFn} = \&Attr;
 | 
			
		||||
    $hash->{AttrList} =
 | 
			
		||||
        "readingValueLanguage:de,en "
 | 
			
		||||
      . "model:watering_computer,sensor,mower,ic24,power,electronic_pressure_pump "
 | 
			
		||||
      . "model:watering_computer,sensor,sensor2,mower,ic24,power,electronic_pressure_pump "
 | 
			
		||||
      . "IODev "
 | 
			
		||||
      . $readingFnAttributes;
 | 
			
		||||
    $hash->{parseParams} = 1;
 | 
			
		||||
@@ -213,6 +197,7 @@ sub Define {
 | 
			
		||||
    $hash->{helper}{winter_mode_id}            = '';
 | 
			
		||||
 | 
			
		||||
    $hash->{helper}{_id} = '';
 | 
			
		||||
 | 
			
		||||
    # IrrigationControl valve control max 6
 | 
			
		||||
    $hash->{helper}{schedules_paused_until_1_id} = '';
 | 
			
		||||
    $hash->{helper}{schedules_paused_until_2_id} = '';
 | 
			
		||||
@@ -287,12 +272,19 @@ sub Set {
 | 
			
		||||
    my $aArg = shift // return;
 | 
			
		||||
 | 
			
		||||
    my $name = shift @$aArg;
 | 
			
		||||
    my $cmd                 = shift @$aArg // return qq{"set $name" needs at least one argument};
 | 
			
		||||
    my $cmd  = shift @$aArg
 | 
			
		||||
      // return qq{"set $name" needs at least one argument};
 | 
			
		||||
 | 
			
		||||
    my $payload;
 | 
			
		||||
    my $abilities;
 | 
			
		||||
    my $service_id;
 | 
			
		||||
    my $mainboard_version   = ReadingsVal( $name, 'mower_type-mainboard_version', 0.0 );
 | 
			
		||||
    my $mainboard_version =
 | 
			
		||||
      ReadingsVal( $name, 'mower_type-mainboard_version', 0.0 );
 | 
			
		||||
    
 | 
			
		||||
    my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
 | 
			
		||||
        $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
 | 
			
		||||
 | 
			
		||||
    my $timezone_offset = $Sommerzeit ? 0 : ( Time::Piece->new )->tzoffset;
 | 
			
		||||
 | 
			
		||||
    #set default abilitie ... overwrite in cmd to change
 | 
			
		||||
    $abilities = 'mower'
 | 
			
		||||
@@ -310,7 +302,9 @@ sub Set {
 | 
			
		||||
    if ( lc $cmd eq 'parkuntilfurthernotice' ) {
 | 
			
		||||
        $payload = '"name":"park_until_further_notice"';
 | 
			
		||||
        if ( $mainboard_version > 10.30 ) {
 | 
			
		||||
          $payload = ' "settings":{"name":"schedules_paused_until","value":"2038-01-18T00:00:00.000Z","device":"'.$hash->{DEVICEID}.'"}';
 | 
			
		||||
            $payload =
 | 
			
		||||
' "settings":{"name":"schedules_paused_until","value":"2038-01-18T00:00:00.000Z","device":"'
 | 
			
		||||
              . $hash->{DEVICEID} . '"}';
 | 
			
		||||
            $abilities  = 'mower_settings';
 | 
			
		||||
            $service_id = $hash->{helper}{schedules_paused_until_id};
 | 
			
		||||
        }
 | 
			
		||||
@@ -325,7 +319,9 @@ sub Set {
 | 
			
		||||
    elsif ( lc $cmd eq 'startresumeschedule' ) {
 | 
			
		||||
        $payload = '"name":"start_resume_schedule"';
 | 
			
		||||
        if ( $mainboard_version > 10.30 ) {
 | 
			
		||||
          $payload = ' "settings":{"name":"schedules_paused_until","value":"","device":"'.$hash->{DEVICEID}.'"}';
 | 
			
		||||
            $payload =
 | 
			
		||||
' "settings":{"name":"schedules_paused_until","value":"","device":"'
 | 
			
		||||
              . $hash->{DEVICEID} . '"}';
 | 
			
		||||
            $abilities  = 'mower_settings';
 | 
			
		||||
            $service_id = $hash->{helper}{schedules_paused_until_id};
 | 
			
		||||
        }
 | 
			
		||||
@@ -334,21 +330,28 @@ sub Set {
 | 
			
		||||
        $payload = '"name":"start_override_timer","parameters":{"duration":'
 | 
			
		||||
          . $aArg->[0] * 60 . '}';
 | 
			
		||||
        if ( $mainboard_version > 10.30 ) {
 | 
			
		||||
          $payload = '"properties":{"name":"mower_timer","value":'.$aArg->[0] * 60 .'}';
 | 
			
		||||
            $payload = '"properties":{"name":"mower_timer","value":'
 | 
			
		||||
              . $aArg->[0] * 60 . '}';
 | 
			
		||||
            $abilities = 'mower_timer';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( lc $cmd eq 'startpoint' ) {
 | 
			
		||||
        my $err;
 | 
			
		||||
        ( $err, $payload, $abilities ) = SetPredefinedStartPoints( $hash, $aArg );
 | 
			
		||||
        ( $err, $payload, $abilities ) =
 | 
			
		||||
          SetPredefinedStartPoints( $hash, $aArg );
 | 
			
		||||
        $service_id = $hash->{helper}{STARTINGPOINTID};
 | 
			
		||||
        return $err if ( defined($err) );
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( lc $cmd eq 'eco' ) {
 | 
			
		||||
        $payload = '"settings": {"name": "eco_mode", "value": '.$aArg->[0].', "device": "'.$hash->{DEVICEID}.'"}';
 | 
			
		||||
        $payload =
 | 
			
		||||
            '"settings": {"name": "eco_mode", "value": '
 | 
			
		||||
          . $aArg->[0]
 | 
			
		||||
          . ', "device": "'
 | 
			
		||||
          . $hash->{DEVICEID} . '"}';
 | 
			
		||||
        $abilities  = 'mower_settings' if ( $mainboard_version > 10.30 );
 | 
			
		||||
        $service_id = $hash->{helper}{eco_mode_id};
 | 
			
		||||
 | 
			
		||||
#$abilities['service_id'] = $hash->{helper}{SCHEDULESID}  if ( $mainboard_version > 10.30 );
 | 
			
		||||
    }
 | 
			
		||||
    ### electronic_pressure_pump
 | 
			
		||||
@@ -373,8 +376,7 @@ sub Set {
 | 
			
		||||
          . $aArg->[0] * 60
 | 
			
		||||
          . ',"timestamp":"2021-05-26T19:06:23.680Z"'
 | 
			
		||||
          . ',"at_bound":null,"unit":"seconds","ability":"'
 | 
			
		||||
        . $service_id
 | 
			
		||||
        .'"}';
 | 
			
		||||
          . $service_id . '"}';
 | 
			
		||||
        $abilities = 'watering_button_config';
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( $cmd =~ m{\AcancelOverride}xms ) {
 | 
			
		||||
@@ -393,17 +395,30 @@ sub Set {
 | 
			
		||||
          . ',"valve_id":'
 | 
			
		||||
          . $valve_id . '}}';
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( $cmd =~ /.*Schedule/ ){
 | 
			
		||||
      my $duration = (( defined($aArg->[0]) ? ( ((Time::Piece->new)+(ONE_HOUR *  $aArg->[0]) - (Time::Piece->new)->tzoffset )->datetime ).'.000Z' : '2038-01-18T00:00:00.000Z'));
 | 
			
		||||
    elsif ( $cmd =~ /.*Schedule$/ ) {
 | 
			
		||||
        my $duration = (
 | 
			
		||||
            (
 | 
			
		||||
                defined( $aArg->[0] )
 | 
			
		||||
                ? (
 | 
			
		||||
                    (
 | 
			
		||||
                        ( Time::Piece->new ) +
 | 
			
		||||
                          ( ONE_HOUR * $aArg->[0] ) -
 | 
			
		||||
                          $timezone_offset
 | 
			
		||||
                    )->datetime
 | 
			
		||||
                  )
 | 
			
		||||
                  . '.000Z'
 | 
			
		||||
                : '2038-01-18T00:00:00.000Z'
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $abilities  = 'wateringcomputer_settings';
 | 
			
		||||
        $service_id = $hash->{helper}->{'schedules_paused_until_id'};
 | 
			
		||||
      $payload = '"settings":{"name":"schedules_paused_until"'
 | 
			
		||||
        $payload =
 | 
			
		||||
            '"settings":{"name":"schedules_paused_until"'
 | 
			
		||||
          . ', "value":"'
 | 
			
		||||
          . ( $cmd eq 'resumeSchedule' ? '' : $duration )
 | 
			
		||||
          . '","device":"'
 | 
			
		||||
                  . $hash->{DEVICEID}
 | 
			
		||||
                  . '"}';
 | 
			
		||||
          . $hash->{DEVICEID} . '"}';
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( lc $cmd eq 'on' || lc $cmd eq 'off' || lc $cmd eq 'on-for-timer' ) {
 | 
			
		||||
        my $val = (
 | 
			
		||||
@@ -412,7 +427,8 @@ sub Set {
 | 
			
		||||
            : lc $cmd
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $payload = '"properties":{"name":"power_timer", "value":"' . $val . '"}';
 | 
			
		||||
        $payload =
 | 
			
		||||
          '"properties":{"name":"power_timer", "value":"' . $val . '"}';
 | 
			
		||||
    }
 | 
			
		||||
    ### Watering ic24
 | 
			
		||||
    elsif ( $cmd =~ m{\AmanualDurationValve\d\z}xms ) {
 | 
			
		||||
@@ -433,29 +449,46 @@ sub Set {
 | 
			
		||||
    elsif ( $cmd eq 'closeAllValves' ) {
 | 
			
		||||
        $payload = '"name":"close_all_valves","parameters":{}';
 | 
			
		||||
    }
 | 
			
		||||
    elsif ( $cmd =~ /.*ScheduleValve/ ){
 | 
			
		||||
    elsif ( $cmd =~ /.*ScheduleValve$/ ) {
 | 
			
		||||
        my $valve_id = $aArg->[0];
 | 
			
		||||
      my $duration = (( defined($aArg->[1]) ? ( ((Time::Piece->new)+(ONE_HOUR *  $aArg->[1]) - (Time::Piece->new)->tzoffset )->datetime ).'.000Z' : '2038-01-18T00:00:00.000Z'));
 | 
			
		||||
        my $duration = (
 | 
			
		||||
            (
 | 
			
		||||
                defined( $aArg->[1] )
 | 
			
		||||
                ? (
 | 
			
		||||
                    (
 | 
			
		||||
                        ( Time::Piece->new ) +
 | 
			
		||||
                          ( ONE_HOUR * $aArg->[1] ) -
 | 
			
		||||
                          $timezone_offset
 | 
			
		||||
                    )->datetime
 | 
			
		||||
                  )
 | 
			
		||||
                  . '.000Z'
 | 
			
		||||
                : '2038-01-18T00:00:00.000Z'
 | 
			
		||||
            )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        $abilities = 'irrigation_settings';
 | 
			
		||||
      $service_id = $hash->{helper}->{'schedules_paused_until_'.$valve_id.'_id'};
 | 
			
		||||
      $payload = '"settings":{"name":"schedules_paused_until_'
 | 
			
		||||
        $service_id =
 | 
			
		||||
          $hash->{helper}->{ 'schedules_paused_until_' . $valve_id . '_id' };
 | 
			
		||||
        $payload =
 | 
			
		||||
            '"settings":{"name":"schedules_paused_until_'
 | 
			
		||||
          . $valve_id
 | 
			
		||||
          . '", "value":"'
 | 
			
		||||
          . ( $cmd eq 'resumeScheduleValve' ? '' : $duration )
 | 
			
		||||
          . '","device":"'
 | 
			
		||||
                  . $hash->{DEVICEID}
 | 
			
		||||
                  . '"}';
 | 
			
		||||
          . $hash->{DEVICEID} . '"}';
 | 
			
		||||
    }
 | 
			
		||||
    ### Sensors
 | 
			
		||||
    elsif ( lc $cmd eq 'refresh' ) {
 | 
			
		||||
 | 
			
		||||
        my $sensname = $aArg->[0];
 | 
			
		||||
        if ( lc $sensname eq 'temperature' ) {
 | 
			
		||||
            if ( ReadingsVal( $name, 'device_info-category', 'sensor' ) eq 'sensor') {
 | 
			
		||||
            if ( ReadingsVal( $name, 'device_info-category', 'sensor' ) eq
 | 
			
		||||
                'sensor' )
 | 
			
		||||
            {
 | 
			
		||||
                $payload   = '"name":"measure_ambient_temperature"';
 | 
			
		||||
                $abilities = 'ambient_temperature';
 | 
			
		||||
            } else {
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                $payload   = '"name":"measure_soil_temperature"';
 | 
			
		||||
                $abilities = 'soil_temperature';
 | 
			
		||||
            }
 | 
			
		||||
@@ -472,11 +505,11 @@ sub Set {
 | 
			
		||||
    }
 | 
			
		||||
    ## winter sleep
 | 
			
		||||
    elsif ( lc $cmd eq 'winter_mode' ) {
 | 
			
		||||
        $payload = '"settings":{"name":"winter_mode","value":"'
 | 
			
		||||
        $payload =
 | 
			
		||||
            '"settings":{"name":"winter_mode","value":"'
 | 
			
		||||
          . $aArg->[0]
 | 
			
		||||
          . '","device":"'
 | 
			
		||||
        . $hash->{DEVICEID}
 | 
			
		||||
        .'"}';
 | 
			
		||||
          . $hash->{DEVICEID} . '"}';
 | 
			
		||||
        $abilities  = 'winter_settings';
 | 
			
		||||
        $service_id = $hash->{helper}->{'winter_mode_id'};
 | 
			
		||||
    }
 | 
			
		||||
@@ -488,7 +521,8 @@ sub Set {
 | 
			
		||||
'parkUntilFurtherNotice:noArg parkUntilNextTimer:noArg startResumeSchedule:noArg startOverrideTimer:slider,0,1,240 startpoint'
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' );
 | 
			
		||||
 | 
			
		||||
        $list .= 'manualOverride:slider,1,1,59 cancelOverride:noArg resumeSchedule:noArg stopSchedule manualButtonTime:slider,0,2,100'
 | 
			
		||||
        $list .=
 | 
			
		||||
'manualOverride:slider,1,1,59 cancelOverride:noArg resumeSchedule:noArg stopSchedule manualButtonTime:slider,0,2,100'
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' );
 | 
			
		||||
 | 
			
		||||
        $list .=
 | 
			
		||||
@@ -496,14 +530,17 @@ sub Set {
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) eq 'ic24' );
 | 
			
		||||
 | 
			
		||||
        $list .= 'refresh:temperature,humidity'
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) eq 'sensor' );
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) =~ /sensor.?/ );
 | 
			
		||||
 | 
			
		||||
        # add light for old sensors
 | 
			
		||||
        $list .= ',light'
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) eq 'sensor'
 | 
			
		||||
            && ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor' );
 | 
			
		||||
            && ReadingsVal( $name, 'device_info-category', 'unknown' ) eq
 | 
			
		||||
            'sensor' );
 | 
			
		||||
 | 
			
		||||
        $list .= 'on:noArg off:noArg on-for-timer:slider,0,1,720'
 | 
			
		||||
          if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' );
 | 
			
		||||
 | 
			
		||||
        # all devices has abilitie to fall a sleep
 | 
			
		||||
        $list .= ' winter_mode:awake,hibernate';
 | 
			
		||||
        return "Unknown argument $cmd, choose one of $list";
 | 
			
		||||
@@ -584,12 +621,13 @@ sub WriteReadings {
 | 
			
		||||
            for my $propertie (
 | 
			
		||||
                @{ $decode_json->{abilities}[$abilities]{properties} } )
 | 
			
		||||
            {
 | 
			
		||||
              if (   exists($decode_json->{abilities}[$abilities]{name})
 | 
			
		||||
                  && ( 
 | 
			
		||||
                      $decode_json->{abilities}[$abilities]{name} eq 'watering' )
 | 
			
		||||
                ) {
 | 
			
		||||
                  if ( $propertie->{name} eq 'button_config_time'  ) 
 | 
			
		||||
                if (
 | 
			
		||||
                    exists( $decode_json->{abilities}[$abilities]{name} )
 | 
			
		||||
                    && ( $decode_json->{abilities}[$abilities]{name} eq
 | 
			
		||||
                        'watering' )
 | 
			
		||||
                  )
 | 
			
		||||
                {
 | 
			
		||||
                    if ( $propertie->{name} eq 'button_config_time' ) {
 | 
			
		||||
                        if ( $hash->{helper}{ $propertie->{name} . '_id' } ne
 | 
			
		||||
                            $decode_json->{abilities}[$abilities]{id} )
 | 
			
		||||
                        {
 | 
			
		||||
@@ -599,7 +637,11 @@ sub WriteReadings {
 | 
			
		||||
                        readingsBulkUpdateIfChanged(
 | 
			
		||||
                            $hash,
 | 
			
		||||
                            'manualButtonTime',
 | 
			
		||||
                              (RigReadingsValue( $hash, $propertie->{value} / 60) )
 | 
			
		||||
                            (
 | 
			
		||||
                                RigReadingsValue(
 | 
			
		||||
                                    $hash, $propertie->{value} / 60
 | 
			
		||||
                                )
 | 
			
		||||
                            )
 | 
			
		||||
                        );
 | 
			
		||||
                        next;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -659,14 +701,15 @@ sub WriteReadings {
 | 
			
		||||
                    $decode_json->{abilities}[$abilities]{name} . '-'
 | 
			
		||||
                      . $propertie->{name}
 | 
			
		||||
                      . '_timestamp',
 | 
			
		||||
                      Time::Piece->strptime(RigReadingsValue( $hash, $propertie->{timestamp} ), "%Y-%m-%d %H:%M:%S")->strftime('%s')
 | 
			
		||||
                    Time::Piece->strptime(
 | 
			
		||||
                        RigReadingsValue( $hash, $propertie->{timestamp} ),
 | 
			
		||||
                        "%Y-%m-%d %H:%M:%S" )->strftime('%s')
 | 
			
		||||
 | 
			
		||||
                  )
 | 
			
		||||
                  if (
 | 
			
		||||
                    defined( $propertie->{value} )
 | 
			
		||||
                    && (  $decode_json->{abilities}[$abilities]{name} . '-'
 | 
			
		||||
                        . $propertie->{name} eq 'mower_timer-mower_timer'
 | 
			
		||||
                  )
 | 
			
		||||
                        . $propertie->{name} eq 'mower_timer-mower_timer' )
 | 
			
		||||
                  );
 | 
			
		||||
 | 
			
		||||
                readingsBulkUpdateIfChanged(
 | 
			
		||||
@@ -706,30 +749,43 @@ sub WriteReadings {
 | 
			
		||||
        $abilities--;
 | 
			
		||||
    } while ( $abilities >= 0 );
 | 
			
		||||
 | 
			
		||||
    my $winter_mode;
 | 
			
		||||
 | 
			
		||||
    do {
 | 
			
		||||
#Log3 $name, 1, "Settings pro Device : ".$decode_json->{settings}[$settings]{name};
 | 
			
		||||
#Log3 $name, 1, " - KEIN ARRAY" if ( ref( $decode_json->{settings}[$settings]{value} ) ne "ARRAY");
 | 
			
		||||
#Log3 $name, 1, " - IST ARRAY" if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY");
 | 
			
		||||
 | 
			
		||||
        if (   exists($decode_json->{settings}[$settings]{name})
 | 
			
		||||
          && ( 
 | 
			
		||||
            $decode_json->{settings}[$settings]{name} =~ /schedules_paused_until_?\d?$/
 | 
			
		||||
        if (
 | 
			
		||||
            exists( $decode_json->{settings}[$settings]{name} )
 | 
			
		||||
            && ( $decode_json->{settings}[$settings]{name} =~
 | 
			
		||||
                   /schedules_paused_until_?\d?$/
 | 
			
		||||
                || $decode_json->{settings}[$settings]{name} eq 'eco_mode'
 | 
			
		||||
                || $decode_json->{settings}[$settings]{name} eq 'winter_mode' )
 | 
			
		||||
          )
 | 
			
		||||
        {
 | 
			
		||||
            if ( $hash->{helper}{$decode_json->{settings}[$settings]{name}.'_id'} ne
 | 
			
		||||
            if ( $hash->{helper}
 | 
			
		||||
                { $decode_json->{settings}[$settings]{name} . '_id' } ne
 | 
			
		||||
                $decode_json->{settings}[$settings]{id} )
 | 
			
		||||
            {
 | 
			
		||||
                $hash->{helper}{$decode_json->{settings}[$settings]{name}.'_id'} =
 | 
			
		||||
                $hash->{helper}
 | 
			
		||||
                  { $decode_json->{settings}[$settings]{name} . '_id' } =
 | 
			
		||||
                  $decode_json->{settings}[$settings]{id};
 | 
			
		||||
            }
 | 
			
		||||
            # check watering controler single schedules pause until
 | 
			
		||||
            if ( $decode_json->{settings}[$settings]{name} eq 'schedules_paused_until' ) {
 | 
			
		||||
              readingsBulkUpdateIfChanged( $hash, 'scheduling-schedules_paused_until',
 | 
			
		||||
                    $decode_json->{settings}[$settings]{value} );
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            # save winter mode as reading
 | 
			
		||||
            readingsBulkUpdateIfChanged(
 | 
			
		||||
                    $hash,
 | 
			
		||||
                    'winter_mode',
 | 
			
		||||
                    $decode_json->{settings}[$settings]{value}
 | 
			
		||||
                ) if ($decode_json->{settings}[$settings]{name} eq 'winter_mode');
 | 
			
		||||
 | 
			
		||||
            if ( $decode_json->{settings}[$settings]{name} eq 'winter_mode' ) {
 | 
			
		||||
                readingsBulkUpdateIfChanged( $hash, 'winter_mode',
 | 
			
		||||
                    $decode_json->{settings}[$settings]{value} );
 | 
			
		||||
 | 
			
		||||
                $winter_mode = $decode_json->{settings}[$settings]{value};
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY"
 | 
			
		||||
@@ -763,36 +819,111 @@ sub WriteReadings {
 | 
			
		||||
        $settings--;
 | 
			
		||||
    } while ( $settings >= 0 );
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    my $online_state = ReadingsVal($name , 'device_info-connection_status', 'unknown');
 | 
			
		||||
      
 | 
			
		||||
    if ( $winter_mode ne 'hibernate' ) {
 | 
			
		||||
        setState($hash);
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        readingsBulkUpdate( $hash, 'state',
 | 
			
		||||
        $online_state eq 'online' ?
 | 
			
		||||
          ReadingsVal( $name, 'mower-status', 'readingsValError') : 'offline'
 | 
			
		||||
        )
 | 
			
		||||
            RigReadingsValue( $hash, 'hibernate' ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    readingsEndUpdate( $hash, 1 );
 | 
			
		||||
 | 
			
		||||
    Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written";
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub setState {
 | 
			
		||||
    my $hash = shift;
 | 
			
		||||
    my $name = $hash->{NAME};
 | 
			
		||||
 | 
			
		||||
    my $online_state =
 | 
			
		||||
      ReadingsVal( $name, 'device_info-connection_status', 'unknown' );
 | 
			
		||||
 | 
			
		||||
    #online state mower
 | 
			
		||||
    readingsBulkUpdate( $hash, 'state',
 | 
			
		||||
        $online_state eq 'online'
 | 
			
		||||
        ? ReadingsVal( $name, 'mower-status', 'readingsValError' )
 | 
			
		||||
        : 'offline' )
 | 
			
		||||
      if ( AttrVal( $name, 'model', 'unknown' ) eq 'mower' );
 | 
			
		||||
    readingsBulkUpdate(
 | 
			
		||||
        $hash, 'state',
 | 
			
		||||
        (
 | 
			
		||||
                ReadingsVal( $name, 'watering-watering_timer_1_duration', 0 )
 | 
			
		||||
                  =~ m{\A[1-9]([0-9]+)?\z}xms
 | 
			
		||||
            ? RigReadingsValue( $hash, 'open' )
 | 
			
		||||
 | 
			
		||||
    #online state water control
 | 
			
		||||
    # zeitplan   -> dauert pausiert wenn 2038-01-18T00:00:00.000Z
 | 
			
		||||
 | 
			
		||||
    # watering-watering_timer_1_state idle | scheduled | manual
 | 
			
		||||
    # watering-watering_timer_1_duration  0    in sec
 | 
			
		||||
    # scheduling-scheduled_watering_next_start  XXX 
 | 
			
		||||
    # scheduling-scheduled_watering_end   | end  < NOW && duration = 0 => abbruch manuell
 | 
			
		||||
    # scheduling-schedules_paused_until
 | 
			
		||||
 | 
			
		||||
   
 | 
			
		||||
    # 1. Ventil geschlossen, Zeitplan pausiert.
 | 
			
		||||
    #   App zeigt: nichts (wenn vorher ein Zeitplan abgebrochen wurde, steht da "Unterbrochen   xx:yy - zz:aa")
 | 
			
		||||
    # 2. Ventil geschlossen, Zeitplan aktiv.
 | 
			
		||||
    #   App zeigt: "Nächste Bewässerung heute um xx:yy Uhr" (wenn vorher ein Zeitplan abgebrochen wurde, steht da vorher auch "Unterbrochen   xx:yy - zz:aa")
 | 
			
		||||
    # 3. Ventil manuell geoeffnet, späterer Zeitplan aktiv.
 | 
			
		||||
    #   Wird bewässert   xx Minuten verbleibend" und "Nächste Bewässerung   heute um xx:yy Uhr"
 | 
			
		||||
    # 4. Ventil manuell geoeffnet, Zeitpläne deaktiviert.
 | 
			
		||||
    #   App zeigt: "Wird bewässert   xx Minuten verbleibend"
 | 
			
		||||
    if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' ){
 | 
			
		||||
 | 
			
		||||
         my $state_string = ReadingsVal( $name, 'watering-watering_timer_1_duration', 0 ) =~
 | 
			
		||||
              m{\A[1-9]([0-9]+)?\z}xms
 | 
			
		||||
            # offen
 | 
			
		||||
            ? 
 | 
			
		||||
              ( ReadingsVal($name, 'scheduling-schedules_paused_until', '' ) eq '' )
 | 
			
		||||
              # leer ( zeitplan aktiv ... ) 
 | 
			
		||||
              ? sprintf( (RigReadingsValue($hash, 'will be irrigated %.f minutes remaining.').' '.RigReadingsValue($hash, 'next watering: %s')), (ReadingsVal( $name, 'watering-watering_timer_1_duration', 0 )/60), RigReadingsValue($hash, ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '')) ) 
 | 
			
		||||
              # zeitplan pausiert
 | 
			
		||||
              : 
 | 
			
		||||
                ( ReadingsVal($name, 'scheduling-schedules_paused_until', '') eq '2038-01-18T00:00:00.000Z')
 | 
			
		||||
                # pause bis  dauerhaft
 | 
			
		||||
                ? sprintf( (RigReadingsValue($hash, 'will be irrigated %.f minutes remaining.').' '.RigReadingsValue($hash , 'schedule permanently paused')), (ReadingsVal( $name, 'watering-watering_timer_1_duration', 0 )/60) )
 | 
			
		||||
                # naechter termin
 | 
			
		||||
                : sprintf( RigReadingsValue($hash , 'paused until %s'), RigReadingsValue($hash, ReadingsVal($name, 'scheduling-schedules_paused_until', '')) )
 | 
			
		||||
            # zu
 | 
			
		||||
            :
 | 
			
		||||
              ( ReadingsVal($name, 'scheduling-schedules_paused_until', '' ) eq '' )
 | 
			
		||||
              # zeitplan aktiv
 | 
			
		||||
              ? sprintf( (RigReadingsValue($hash, 'closed') .'. '.RigReadingsValue($hash, 'next watering: %s')),  RigReadingsValue($hash, ReadingsVal($name, 'scheduling-scheduled_watering_next_start', '') ) )
 | 
			
		||||
              # zeitplan pausiert
 | 
			
		||||
              : RigReadingsValue($hash, 'closed')
 | 
			
		||||
        )
 | 
			
		||||
    ) if ( AttrVal( $name, 'model', 'unknown' ) eq 'watering_computer' );
 | 
			
		||||
            ;
 | 
			
		||||
      # state offline | override
 | 
			
		||||
      $state_string = 'offline' if ($online_state eq 'offline');
 | 
			
		||||
 
 | 
			
		||||
      readingsBulkUpdate(
 | 
			
		||||
        $hash, 'state',  RigReadingsValue( $hash, $state_string ) );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ( AttrVal( $name, 'model', 'unknown' ) eq 'sensor' ) {
 | 
			
		||||
      my $state_string = ( ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor') ?  'T: ' .ReadingsVal( $name, 'ambient_temperature-temperature', 'readingsValError' ) . '°C, ' :  'T: ' .ReadingsVal( $name, 'soil_temperature-temperature', 'readingsValError' ) . '°C, ' ;
 | 
			
		||||
      $state_string .=  'H: '. ReadingsVal( $name, 'humidity-humidity', 'readingsValError' ). '%';
 | 
			
		||||
      $state_string .= ', L: ' . ReadingsVal( $name, 'light-light', 'readingsValError' ) . 'lux' if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor');
 | 
			
		||||
    if ( AttrVal( $name, 'model', 'unknown' ) =~ /sensor.?/ ) {
 | 
			
		||||
        my $state_string =
 | 
			
		||||
          ( ReadingsVal( $name, 'device_info-category', 'unknown' ) eq
 | 
			
		||||
              'sensor' )
 | 
			
		||||
          ? 'T: '
 | 
			
		||||
          . ReadingsVal( $name, 'ambient_temperature-temperature',
 | 
			
		||||
            'readingsValError' )
 | 
			
		||||
          . '°C, '
 | 
			
		||||
          : 'T: '
 | 
			
		||||
          . ReadingsVal( $name, 'soil_temperature-temperature',
 | 
			
		||||
            'readingsValError' )
 | 
			
		||||
          . '°C, ';
 | 
			
		||||
        $state_string .= 'H: '
 | 
			
		||||
          . ReadingsVal( $name, 'humidity-humidity', 'readingsValError' ) . '%';
 | 
			
		||||
        $state_string .= ', L: '
 | 
			
		||||
          . ReadingsVal( $name, 'light-light', 'readingsValError' ) . 'lux'
 | 
			
		||||
          if ( ReadingsVal( $name, 'device_info-category', 'unknown' ) eq
 | 
			
		||||
            'sensor' );
 | 
			
		||||
 | 
			
		||||
# if ( $online_state eq 'offline') {
 | 
			
		||||
#   readingsBulkUpdate( $hash, 'humidity-humidity', '-1' );
 | 
			
		||||
#   readingsBulkUpdate( $hash, 'ambient_temperature-temperature', '-1' ) if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor');
 | 
			
		||||
#   readingsBulkUpdate( $hash, 'light-light', '-1' ) if (ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor');
 | 
			
		||||
# }
 | 
			
		||||
      readingsBulkUpdate($hash, 'state', $online_state eq 'online' ? $state_string : 'offline' )
 | 
			
		||||
        #online state sensor I II
 | 
			
		||||
        readingsBulkUpdate( $hash, 'state',
 | 
			
		||||
            $online_state eq 'online' ? RigReadingsValue( $hash, $state_string) : RigReadingsValue( $hash, 'offline') );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    readingsBulkUpdate(
 | 
			
		||||
@@ -810,10 +941,6 @@ sub WriteReadings {
 | 
			
		||||
        ReadingsVal( $name, 'power-power_timer', 'no info from power-timer' ) )
 | 
			
		||||
      if ( AttrVal( $name, 'model', 'unknown' ) eq 'power' );
 | 
			
		||||
 | 
			
		||||
    readingsEndUpdate( $hash, 1 );
 | 
			
		||||
 | 
			
		||||
    Log3 $name, 4, "GardenaSmartDevice ($name) - readings was written";
 | 
			
		||||
 | 
			
		||||
    return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -837,8 +964,7 @@ sub ReadingLangGerman {
 | 
			
		||||
        'parked_timer'                   => 'geparkt nach Zeitplan',
 | 
			
		||||
        'parked_park_selected'           => 'geparkt',
 | 
			
		||||
        'off_disabled'                   => 'der Mäher ist ausgeschaltet',
 | 
			
		||||
        'off_hatch_open' =>
 | 
			
		||||
          'deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich',
 | 
			
		||||
        'off_hatch_open'                 => 'deaktiviert. Abdeckung ist offen oder PIN-Code erforderlich',
 | 
			
		||||
        'unknown'                        => 'unbekannter Status',
 | 
			
		||||
        'error'                          => 'Fehler',
 | 
			
		||||
        'error_at_power_up'              => 'Neustart ...',
 | 
			
		||||
@@ -880,10 +1006,8 @@ sub ReadingLangGerman {
 | 
			
		||||
        'guide_2_not_found'              => 'SK 2 nicht gefunden',
 | 
			
		||||
        'guide_3_not_found'              => 'SK 3 nicht gefunden',
 | 
			
		||||
        'difficult_finding_home'         => 'Problem die Ladestation zu finden',
 | 
			
		||||
        'guide_calibration_accomplished' =>
 | 
			
		||||
          'Kalibrierung des Suchkabels beendet',
 | 
			
		||||
        'guide_calibration_failed' =>
 | 
			
		||||
          'Kalibrierung des Suchkabels fehlgeschlagen',
 | 
			
		||||
        'guide_calibration_accomplished' => 'Kalibrierung des Suchkabels beendet',
 | 
			
		||||
        'guide_calibration_failed'       => 'Kalibrierung des Suchkabels fehlgeschlagen',
 | 
			
		||||
        'temporary_battery_problem'      => 'kurzzeitiges Batterieproblem',
 | 
			
		||||
        'battery_problem'                => 'Batterieproblem',
 | 
			
		||||
        'alarm_mower_switched_off'       => 'Alarm! Mäher ausgeschalten',
 | 
			
		||||
@@ -923,7 +1047,13 @@ sub ReadingLangGerman {
 | 
			
		||||
        'closed'                         => 'geschlossen',
 | 
			
		||||
        'included'                       => 'inbegriffen',
 | 
			
		||||
        'active'                         => 'aktiv',
 | 
			
		||||
        'inactive'                    => 'nicht aktiv'
 | 
			
		||||
        'inactive'                       => 'nicht aktiv',
 | 
			
		||||
        'hibernate'                      => 'Winterschlaf',
 | 
			
		||||
        'awake'                          => 'Aufgewacht',
 | 
			
		||||
        'schedule permanently paused'    => 'Zeitplan dauerhaft pausiert',
 | 
			
		||||
        'paused until %s'                => 'pausiert bis %s',        
 | 
			
		||||
        'will be irrigated %.f minutes remaining.'=> 'Wird bewässert. %.f Minuten verbleibend.',
 | 
			
		||||
        'next watering: %s'              => 'Nächste Bewässerung: %s',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
@@ -1049,6 +1179,7 @@ sub SetPredefinedStartPoints {
 | 
			
		||||
 | 
			
		||||
            $payload   = '"settings": ' . encode_json($decode_json_settings);
 | 
			
		||||
            $abilities = 'mower_settings';
 | 
			
		||||
 | 
			
		||||
            #$abilities['service_id'] = $hash->{helper}{STARTINGPOINTID};
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
@@ -1404,9 +1535,9 @@ sub SetPredefinedStartPoints {
 | 
			
		||||
  ],
 | 
			
		||||
  "release_status": "stable",
 | 
			
		||||
  "license": "GPL_2",
 | 
			
		||||
  "version": "v2.4.2",
 | 
			
		||||
  "version": "v2.4.3",
 | 
			
		||||
  "author": [
 | 
			
		||||
    "Marko Oldenburg <leongaultier@gmail.com>"
 | 
			
		||||
    "Marko Oldenburg <fhemdevelopment@cooltux.net>"
 | 
			
		||||
  ],
 | 
			
		||||
  "x_fhem_maintainer": [
 | 
			
		||||
    "CoolTux"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
UPD 2021-09-27_10:50:10 48067 FHEM/73_GardenaSmartBridge.pm
 | 
			
		||||
UPD 2021-09-27_10:50:10 56217 FHEM/74_GardenaSmartDevice.pm
 | 
			
		||||
UPD 2022-03-29_13:42:09 49520 FHEM/73_GardenaSmartBridge.pm
 | 
			
		||||
UPD 2022-04-12_08:40:29 61278 FHEM/74_GardenaSmartDevice.pm
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user