From 512fe63b1e7adf4cb0bd3ee800b022c88c31a04c Mon Sep 17 00:00:00 2001 From: Ellert <> Date: Fri, 13 Oct 2023 16:44:05 +0000 Subject: [PATCH] 74_AutomowerConnect: Commandref update Common.pm: add setter for work zone start and stay out zone enabling/disabling. Zone handling is for TESTING purpose implemented. git-svn-id: https://svn.fhem.de/fhem/trunk@28048 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 3 + fhem/FHEM/74_AutomowerConnect.pm | 34 ++++++- fhem/lib/FHEM/Devices/AMConnect/Common.pm | 109 ++++++++++++++++++---- 3 files changed, 125 insertions(+), 21 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 6f5deb54a..2912b989a 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,8 @@ # 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. + - feature: 74_AutomowerConnect: Commandref update, add Setter for work zone + start and stay out zone enabling/disabling. + Zone handling is for TESTING purpose - feature: 76_SolarForecast: new consumer attr key 'noshow', minor fixes - feature: 72_FRITZBOX - Log3 handling - feature: 72_FB_CALLMONITOR - Log3 handling diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm index a199ce5a6..8bd03fbf6 100644 --- a/fhem/FHEM/74_AutomowerConnect.pm +++ b/fhem/FHEM/74_AutomowerConnect.pm @@ -168,8 +168,9 @@ __END__ Starts immediately for <number of minutes>
  • StartInWorkArea
    - set <name> StartInWorkArea <workAreaId> [<number of minutes>]
    - Starts immediately in <workAreaId> for <number of minutes>
  • + set <name> StartInWorkArea <workAreaId|name> [<number of minutes>]
    + Testing: Starts immediately in <workAreaId|name> for <number of minutes>
    + Zone name must not include space.
  • chargingStationPositionToAttribute
    set <name> chargingStationPositionToAttribute
    @@ -183,6 +184,14 @@ __END__ set <name> cuttingHeight <1..9>
    Sets the cutting height. NOTE: Do not use for 550 EPOS and Ceora.
  • +
  • stayOutZone_enable
    + set <name> stayOutZone_enable <Id|name>
    + Testing: Enables stay out zone by Id or zone name. Zone name must not include space and contain at least one alphabetic character.
  • + +
  • stayOutZone_disable
    + set <name> stayOutZone_disable <Id|name>
    + Testing: Disables stay out zone by Id or zone name. Zone name must not include space and contain at least one alphabetic character.
  • +
  • getNewAccessToken
    set <name> getNewAccessToken
    For debug purpose only.
  • @@ -418,6 +427,10 @@ __END__ Set timeout for API call, default 5 s.
    The response time is meassured and logged if a timeout ist set to 60 s. +
  • testing
    + attr <name> testing 1
    + Enables commands taged as Testing

  • +

    @@ -531,8 +544,9 @@ __END__ Startet sofort für <number of minutes>
  • StartInWorkArea
    - set <name> StartInWorkArea <workAreaId> [<number of minutes>]
    - Startet sofort in <workAreaId> für <number of minutes>
  • + set <name> StartInWorkArea <workAreaId|zone name> [<number of minutes>]
    + Testing: Startet sofort in <workAreaId|name> für <number of minutes>
    + Der Name der Zone darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.
  • chargingStationPositionToAttribute
    set <name> chargingStationPositionToAttribute
    @@ -546,6 +560,14 @@ __END__ set <name> cuttingHeight <1..9>
    Setzt die Schnitthöhe. HINWEIS: Nicht für 550 EPOS und Ceora geeignet.
  • +
  • stayOutZone_enable
    + set <name> stayOutZone_enable <Id|zone name>
    + Testing: Enabled stayOutZone für die Id oder den Namen der Zone, er darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.
  • + +
  • stayOutZone_disable
    + set <name> stayOutZone_disable <Id|zone name>
    + Testing: Disabled stayOutZone für die Id oder den Namen der Zone, er darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.
  • +
  • getNewAccessToken
    set <name> getNewAccessToken
    Nur zur Fehlerbehebung.
  • @@ -785,6 +807,10 @@ __END__ Setzt den Timeout für Befehl senden, default 15 s.
    Wird ein Timeout auf 60 s gesetzt, wird die Antwortzeit gemessen und geloggt. +
  • testing
    + attr <name> testing 1
    + Macht Befehle verfügbar, die mit Testing markiert sind.

  • +

    diff --git a/fhem/lib/FHEM/Devices/AMConnect/Common.pm b/fhem/lib/FHEM/Devices/AMConnect/Common.pm index 30ae15fb8..ad318dae4 100644 --- a/fhem/lib/FHEM/Devices/AMConnect/Common.pm +++ b/fhem/lib/FHEM/Devices/AMConnect/Common.pm @@ -1053,6 +1053,7 @@ sub CMD { my $type = $hash->{TYPE}; my $iam = "$type $name CMD:"; my $timeout = AttrVal( $name, 'timeoutCMD', $hash->{helper}->{timeout_cmd} ); + my $method = 'POST'; $hash->{helper}{mower_commandSend} = $cmd[ 0 ] . ' ' . ( $cmd[ 1 ] ? $cmd[ 1 ] : '' ); if ( IsDisabled( $name ) ) { @@ -1073,15 +1074,21 @@ sub CMD { my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: ".$client_id."\r\nAuthorization: Bearer " . $token . "\r\nAuthorization-Provider: " . $provider . "\r\nContent-Type: application/vnd.api+json"; - if ($cmd[0] eq "ParkUntilFurtherNotice") { $json = '{"data":{"type":"'.$cmd[0].'"}}'; $post = 'actions' } - elsif ($cmd[0] eq "ParkUntilNextSchedule") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' } - elsif ($cmd[0] eq "ResumeSchedule") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' } - elsif ($cmd[0] eq "Pause") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' } - elsif ($cmd[0] eq "Park") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' } - elsif ($cmd[0] eq "Start") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' } - elsif ($cmd[0] eq "headlight") { $json = '{"data": {"type":"settings","attributes":{"'.$cmd[0].'": {"mode": "'.$cmd[1].'"}}}}'; $post = 'settings' } - elsif ($cmd[0] eq "cuttingHeight") { $json = '{"data": {"type":"settings","attributes":{"'.$cmd[0].'": '.$cmd[1].'}}}'; $post = 'settings' } - elsif ($cmd[0] eq "sendScheduleFromAttributeToMower" && AttrVal( $name, 'mowerSchedule', '')) { + if ($cmd[0] eq "ParkUntilFurtherNotice") { $json = '{"data":{"type":"'.$cmd[0].'"}}'; $post = 'actions' } + elsif ($cmd[0] eq "ParkUntilNextSchedule") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' } + elsif ($cmd[0] eq "ResumeSchedule") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' } + elsif ($cmd[0] eq "Pause") { $json = '{"data": {"type":"'.$cmd[0].'"}}'; $post = 'actions' } + elsif ($cmd[0] eq "Park") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' } + elsif ($cmd[0] eq "Start") { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"duration":'.$cmd[1].'}}}'; $post = 'actions' } + elsif ($cmd[0] eq "StartInWorkArea" && $cmd[2]) + { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"workAreaId":'.$cmd[1].',"duration":'.$cmd[2].'}}}'; $post = 'actions' } + elsif ($cmd[0] eq "StartInWorkArea" && !$cmd[2]) + { $json = '{"data": {"type":"'.$cmd[0].'","attributes":{"workAreaId":'.$cmd[1].'}}}'; $post = 'actions' } + elsif ($cmd[0] eq "headlight") { $json = '{"data": {"type":"settings","attributes":{"'.$cmd[0].'": {"mode": "'.$cmd[1].'"}}}}'; $post = 'settings' } + elsif ($cmd[0] eq "cuttingHeight") { $json = '{"data": {"type":"settings","attributes":{"'.$cmd[0].'": '.$cmd[1].'}}}'; $post = 'settings' } + elsif ($cmd[0] eq "stayOutZone_enable") { $json = '{"data": {"type":"stayOutZone","id":"'.$cmd[1].'","attributes":{"enable": true}}}'; $post = 'stayOutZones/' . $cmd[1]; $method = 'PATCH' } + elsif ($cmd[0] eq "stayOutZone_disable") { $json = '{"data": {"type":"stayOutZone","id":"'.$cmd[1].'","attributes":{"enable": false}}}'; $post = 'stayOutZones/' . $cmd[1]; $method = 'PATCH' } + elsif ($cmd[0] eq "sendScheduleFromAttributeToMower" && AttrVal( $name, 'mowerSchedule', '')) { my $perl = eval { decode_json (AttrVal( $name, 'mowerSchedule', '')) }; if ($@) { @@ -1102,7 +1109,7 @@ my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: ".$client_id."\r\nA url => APIURL . "/mowers/". $mower_id . "/".$post, timeout => $timeout, hash => $hash, - method => "POST", + method => $method, header => $header, data => $json, callback => \&CMDResponse, @@ -1121,7 +1128,7 @@ sub CMDResponse { my $iam = "$type $name CMDResponse:"; Log3 $name, 1, "$iam response time ". sprintf( "%.2f", ( gettimeofday() - $param->{t_begin} ) ) . ' s' if ( $param->{timeout} == 60 ); - Log3 $name, 1, "\ndebug $iam \n\$statuscode [$statuscode]\n\$err [$err],\n \$data [$data] \n\$param->url $param->{url}" if ( AttrVal($name, 'debug', '') ); + Log3 $name, 1, "\ndebug $iam \n\$statuscode >$statuscode<\n\$err >$err<,\n \$data >$data< \n\$param->url $param->{url}" if ( AttrVal($name, 'debug', '') ); if( !$err && $statuscode == 202 && $data ) { @@ -1168,7 +1175,7 @@ sub CMDResponse { readingsEndUpdate($hash, 1); - Log3 $name, 2, "\n$iam \n\$statuscode [$statuscode]\n\$err [$err],\n\$data [$data]\n\$param->url $param->{url}"; + Log3 $name, 2, "\n$iam \n\$statuscode >$statuscode<\n\$err >$err<,\n\$data >$data<\n\$param->url $param->{url}"; return undef; } @@ -1271,10 +1278,40 @@ sub Set { CMD($hash,$setName); return undef; + } elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName =~ /^(StartInWorkArea)$/ && AttrVal( $name, 'testing', '' ) ) { + + my $id = undef; + $id = name2id( $hash, $setVal, 'workAreas' ) if ( $setVal !~ /^(\d+)$/ ); + $setVal = $id // $setVal; + if ( $setVal =~ /^(\d+)$/ && ( $setVal2 =~ /^(\d+)$/ or !$setVal2 ) ) { # && $hash->{helper}{mower}{attributes}{capabilities}{workAreas} + + CMD($hash ,$setName, $setVal, $setVal2); + return undef; + + } + + Log3 $name, 2, "$iam $setName : no valid Id or zone name for $setVal ."; + + } elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName =~ /^stayOutZone_(enable|disable)$/ && AttrVal( $name, 'testing', '' ) ) { + + my $id = undef; + $id = name2id( $hash, $setVal, 'stayOutZones' ) if ( $setVal !~ /\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/ ); + $setVal = $id // $setVal; + if ( $setVal =~ /\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/ ) { # && $hash->{helper}{mower}{attributes}{capabilities}{stayOutZones} + + CMD($hash ,$setName, $setVal); + return undef; + + } + + Log3 $name, 2, "$iam $setName : no valid Id or zone name for $setVal ."; + } my $ret = " getNewAccessToken:noArg ParkUntilFurtherNotice:noArg ParkUntilNextSchedule:noArg Pause:noArg Start:selectnumbers,60,60,600,0,lin Park:selectnumbers,60,60,600,0,lin ResumeSchedule:noArg getUpdate:noArg client_secret "; $ret .= "chargingStationPositionToAttribute:noArg headlight:ALWAYS_OFF,ALWAYS_ON,EVENING_ONLY,EVENING_AND_NIGHT cuttingHeight:1,2,3,4,5,6,7,8,9 mowerScheduleToAttribute:noArg "; $ret .= "sendScheduleFromAttributeToMower:noArg defaultDesignAttributesToAttribute:noArg mapZonesTemplateToAttribute:noArg "; + $ret .= "StartInWorkArea " if ( $hash->{helper}{mower}{attributes}{capabilities}{workAreas} && AttrVal( $name, 'testing', '' ) ); + $ret .= "stayOutZone_enable stayOutZone_disable " if ( $hash->{helper}{mower}{attributes}{capabilities}{stayOutZones} && AttrVal( $name, 'testing', '' ) ); return "Unknown argument $setName, choose one of".$ret; } @@ -1423,10 +1460,10 @@ sub Attr { ########## } elsif ( $attrName eq 'numberOfWayPointsToDisplay' ) { - return "$iam $attrVal is invalid, min value is 100." if ( $attrVal < 100 ); my $icurr = scalar @{$hash->{helper}{areapos}}; if( $cmd eq "set" && $attrVal =~ /\d+/ ) { + return "$iam $attrVal is invalid, min value is 100." if ( $attrVal < 100 ); # reduce array $hash->{helper}{MOWING}{maxLength} = $attrVal; for ( my $i = $icurr; $i > $attrVal; $i-- ) { @@ -1586,6 +1623,36 @@ sub Attr { return undef; } +######################### +sub name2id { + my ( $hash, $zname, $ztype ) = @_; + $ztype = $ztype // 'workAreas'; + if ( $ztype eq 'workAreas' && defined ( $hash->{helper}{mower}{attributes}{workAreas} ) ) { + + my @ar = @{ $hash->{helper}{mower}{attributes}{workAreas} }; + for ( @ar ) { + + return $_->{workAreaId} if ( $_->{name} eq $zname ); + + } + + } elsif ( $ztype eq 'stayOutZones' && defined( $hash->{helper}{mower}{attributes}{stayOutZones} ) && defined ( $hash->{helper}{mower}{attributes}{stayOutZones}{zones} ) ) { + + if ( defined( $hash->{helper}{mower}{attributes}{stayOutZones}{dirty} ) && $hash->{helper}{mower}{attributes}{stayOutZones}{dirty} == 0) { + + my @ar = @{ $hash->{helper}{mower}{attributes}{stayOutZones}{zones} }; + for ( @ar ) { + + return $_->{Id} if ( $_->{name} eq $zname ); + + } + + } + + } + return undef; +} + ######################### sub AlignArray { my ($hash) = @_; @@ -2251,7 +2318,8 @@ sub listStatisticsData { $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . ' '; $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} / 3600 ) . ' h '; $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} / 3600 ) . ' h '; - $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} / 3600 ) . '1 h '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance} / 1000 ) . '1 km '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} / 3600 ) . '2 h '; $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} / 3600 ) . ' h '; my $prop = ''; @@ -2299,7 +2367,8 @@ sub listStatisticsData { } $ret .= ''; - $ret .= '

    1 totalRunningTime = totalCuttingTime + totalSearchingTime'; + $ret .= '

    1 totalDriveDistance = totalRunningTime * '. sprintf( "%.2f", $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance} / $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} ) if ( $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} ); + $ret .= '

    2 totalRunningTime = totalCuttingTime + totalSearchingTime'; $ret .= ''; return $ret; @@ -2352,11 +2421,17 @@ sub listMowerData { $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions}   ' . $hash->{helper}{mower}{attributes}{statistics}{numberOfCollisions} . ' '; $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalChargingTime} . ' s '; $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalCuttingTime} . ' s '; - $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '1 s '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance} . '1 m '; + $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} . '2 s '; $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime}   ' . $hash->{helper}{mower}{attributes}{statistics}{totalSearchingTime} . ' s '; + $ret .= ' $hash->{helper}{mower}{attributes}{capabilities}{headlights}   ' . $hash->{helper}{mower}{attributes}{capabilities}{headlights} . ' '; + $ret .= ' $hash->{helper}{mower}{attributes}{capabilities}{stayOutZones}   ' . $hash->{helper}{mower}{attributes}{capabilities}{stayOutZones} . ' '; + $ret .= ' $hash->{helper}{mower}{attributes}{capabilities}{workAreas}   ' . $hash->{helper}{mower}{attributes}{capabilities}{workAreas} . ' '; + $ret .= ' $hash->{helper}{mower}{attributes}{capabilities}{position}   ' . $hash->{helper}{mower}{attributes}{capabilities}{position} . ' '; $ret .= ''; - $ret .= '

    1 totalRunningTime = totalCuttingTime + totalSearchingTime'; + $ret .= '

    1 totalDriveDistance = totalRunningTime * '. sprintf( "%.2f", $hash->{helper}{mower}{attributes}{statistics}{totalDriveDistance} / $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} ) if ( $hash->{helper}{mower}{attributes}{statistics}{totalRunningTime} ); + $ret .= '

    2 totalRunningTime = totalCuttingTime + totalSearchingTime'; $ret .= ''; return $ret;