From abb139d4135aa84ae90595e37bcd3212c55cedaa Mon Sep 17 00:00:00 2001
From: Ellert <>
Date: Fri, 25 Oct 2024 17:02:10 +0000
Subject: [PATCH] 74_AutomowerConnect: new statistics values upTime, downTime;
new setter getMessages, resetCuttingBladeUsageTime; added messages to
listErrorStack.
git-svn-id: https://svn.fhem.de/fhem/trunk@29292 2b470e98-0d58-463d-a4d8-8e2adae1ed80
---
fhem/CHANGED | 3 +
fhem/FHEM/74_AutomowerConnect.pm | 26 +++-
fhem/lib/FHEM/Devices/AMConnect/Common.pm | 151 +++++++++++++++++++---
3 files changed, 161 insertions(+), 19 deletions(-)
diff --git a/fhem/CHANGED b/fhem/CHANGED
index 9bde4ba0e..0e318d5f5 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: new statistics values upTime, downTime
+ new setter getMessages, resetCuttingBladeUsageTime
+ added messages to listErrorStack
- bugfix: 36_Shelly: ShellyPro3EM: div by zero when attr Periods is not set
- bugfix: 36_Shelly: diverse Korrekturen/Ergänzungen/siehe Forum
- bugfix: 76_SolarForecast: minor fix in Flowgraphic
diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm
index 4b5f0c07a..b8ef7fe71 100644
--- a/fhem/FHEM/74_AutomowerConnect.pm
+++ b/fhem/FHEM/74_AutomowerConnect.pm
@@ -194,7 +194,7 @@ __END__
dateTime
set <name> dateTime <timestamp / s>
- Syncronize the mower time to timestamp. The default (empty Input field) timestamp is for local time of the machine the mower is defined.
+ Syncronize the mower time to timestamp. The default (empty Input field) timestamp is for local time of the machine the mower is defined, see also mowerAutoSyncTime.
confirmError
set <name> confirmError
@@ -218,6 +218,10 @@ __END__
set <name> cuttingHeight <1..9>
Sets the cutting height. NOTE: Do not use for 550 EPOS and Ceora.
+ resetCuttingBladeUsageTime
+ set <name> resetCuttingBladeUsageTime
+ Resets the cutting blade usage time if the mower provides it.
+
cuttingHeightInWorkArea
set <name> cuttingHeightInWorkArea <Id|name> <0..100>
Testing: Sets the cutting height for Id or zone name from 0 to 100. Zone name must not include space and contain at least one alphabetic character.
@@ -234,6 +238,10 @@ __END__
set <name> getUpdate
For debug purpose only.
+
+ get <name> getMessages
+ Gets messages from mower if supported, see also errorStack.
+
headlight
set <name> headlight <ALWAYS_OFF|ALWAYS_ON|EVENIG_ONLY|EVENING_AND_NIGHT>
@@ -284,8 +292,9 @@ __END__
errorStack
get <name> errorStack
- Lists error stack.
-
+ Lists error stack and messages, see also getMessages.
+
+
@@ -715,6 +724,10 @@ __END__
set <name> cuttingHeight <1..9>
Setzt die Schnitthöhe. HINWEIS: Nicht für 550 EPOS und Ceora geeignet.
+ resetCuttingBladeUsageTime
+ set <name> resetCuttingBladeUsageTime
+ Setzt die Benutzungszeit der Messer zurück, falls der Mäher es unterstützt.
+
cuttingHeightInWorkArea
set <name> cuttingHeightInWorkArea <Id|name> <0..100>
Testing: Setzt die Schnitthöhe für Id oder Zonennamen von 0 bis 100. Der Zonenname darf keine Leerzeichen beinhalten und muss mindestens einen Buchstaben enthalten.
@@ -741,6 +754,10 @@ __END__
set <name> getUpdate
Nur zur Fehlerbehebung.
+ getMessages
+ get <name> getMessages
+ Läd die im Mäher gespeicherten Meldungen, sofern unterstützt, siehe auch errorStack.
+
headlight
set <name> headlight <ALWAYS_OFF|ALWAYS_ON|EVENIG_ONLY|EVENING_AND_NIGHT>
Setzt den Scheinwerfermode
@@ -791,7 +808,8 @@ __END__
errorStack
get <name> errorStack
- Listet die gespeicherten Fehler auf.
+ Listet die gespeicherten Fehler und die geladenen Meldungen auf, siehe auch getMessages.
+
diff --git a/fhem/lib/FHEM/Devices/AMConnect/Common.pm b/fhem/lib/FHEM/Devices/AMConnect/Common.pm
index 2f647a5b8..9ab3e8774 100644
--- a/fhem/lib/FHEM/Devices/AMConnect/Common.pm
+++ b/fhem/lib/FHEM/Devices/AMConnect/Common.pm
@@ -981,8 +981,7 @@ sub getMowerResponse {
#########################
sub getMowerWs {
-
- my ( $hash ) = @_;
+ my ( $hash, $endpoint ) = @_;
my $name = $hash->{NAME};
my $type = $hash->{TYPE};
my $iam = "$type $name getMowerWs:";
@@ -990,24 +989,100 @@ sub getMowerWs {
my $provider = ReadingsVal($name,".provider","");
my $client_id = $hash->{helper}->{client_id};
my $timeout = AttrVal( $name, 'timeoutGetMower', $hash->{helper}->{timeout_getmower} );
+ my $callback = \&getMowerResponseWs;
my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: " . $client_id . "\r\nAuthorization: Bearer " . $access_token . "\r\nAuthorization-Provider: " . $provider;
Log3 $name, 5, "$iam header [ $header ]";
readingsSingleUpdate( $hash, 'api_callsThisMonth' , ReadingsVal( $name, 'api_callsThisMonth', 0 ) + 1, 0) if ( $hash->{helper}{additional_polling} );
+ if ( $endpoint eq 'messages') { $callback = \&getEndpointResponse }
+
::HttpUtils_NonblockingGet( {
- url => APIURL . '/mowers/' . $hash->{helper}{mower}{id},
+ url => APIURL . '/mowers/' . $hash->{helper}{mower}{id} . ($endpoint ? '/' . $endpoint : ''),
timeout => $timeout,
hash => $hash,
method => "GET",
header => $header,
- callback => \&getMowerResponseWs,
+ callback => $callback,
+ endpoint => $endpoint,
t_begin => scalar gettimeofday()
} );
return undef;
}
+#########################
+sub getEndpointResponse {
+
+ my ( $param, $err, $data ) = @_;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+ my $type = $hash->{TYPE};
+ my $statuscode = $param->{code} // '';
+ my $endpoint = $param->{endpoint} // '';
+ my $iam = "$type $name getEndpointResponse:";
+
+ Log3 $name, 1, "$iam response time ". sprintf( "%.2f", ( gettimeofday() - $param->{t_begin} ) ) . ' s' if ( $param->{timeout} == 60 );
+ Log3 $name, 4, "$iam response calling \$endpoint >$endpoint<, \$statuscode >$statuscode<, \$err >$err<, \$param->url $param->{url} \n \$data >$data<";
+
+ if ( !$err && $statuscode == 200 && $data) {
+
+ if ( $data eq '' ) {
+
+ Log3 $name, 2, "$iam no mower data present";
+
+ } else {
+
+ my $result = eval { JSON::XS->new->allow_nonref(0)->utf8( not $unicodeEncoding )->decode( $data ) };
+
+ if ( $@ ) {
+
+ Log3( $name, 2, "$iam - JSON error while request: $@");
+
+ } elsif ( $endpoint eq 'messages' ) {
+
+ if ( defined $result->{data}{attributes}{messages} ) {
+
+ $hash->{helper}{endpoints}{$endpoint} = $result->{data};
+ $hash->{helper}->{mower_commandStatus} = 'OK - messages';
+ $hash->{helper}->{mower_commandSend} = 'messages';
+
+ } else {
+
+ $hash->{helper}->{mower_commandStatus} = 'OK - no messages recieved';
+ $hash->{helper}->{mower_commandSend} = 'messages';
+
+ }
+
+ }
+
+ readingsBeginUpdate($hash);
+
+ readingsBulkUpdateIfChanged( $hash, 'mower_commandStatus', $hash->{helper}{mower_commandStatus}, 1 );
+ readingsBulkUpdateIfChanged( $hash, 'mower_commandSend', $hash->{helper}{mower_commandSend}, 1 );
+
+ readingsEndUpdate($hash, 1);
+
+ }
+
+ } else {
+
+ readingsBeginUpdate($hash);
+
+ readingsBulkUpdateIfChanged( $hash, 'mower_commandStatus', "ERROR statuscode $statuscode", 1 );
+ readingsBulkUpdateIfChanged( $hash, 'mower_commandSend', $hash->{helper}{mower_commandSend}, 1 );
+
+ readingsEndUpdate($hash, 1);
+
+ Log3 $name, 1, "$iam \$statuscode >$statuscode<, \$err >$err<,\n \$data [$data] \n\$param->url $param->{url}";
+ DoTrigger($name, "MOWERAPI ERROR");
+
+ }
+
+ return undef;
+
+}
+
#########################
sub getMowerResponseWs {
@@ -1144,7 +1219,7 @@ sub Get {
my $ret = listInternalData($hash);
return $ret;
- } elsif ( $setName eq 'MowerData' ) {
+ } elsif ( $setName eq 'MowerData' ) {
my $ret = listMowerData($hash);
return $ret;
@@ -1313,11 +1388,25 @@ sub Set {
return undef;
##########
- } elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName =~ /confirmError/ && AttrVal( $name, 'testing', '' ) ) {
+ } elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/
+ && $setName =~ /confirmError/ && $hash->{helper}{mower}{attributes}{capabilities}{canConfirmError} && AttrVal( $name, 'testing', '' ) ) {
CMD($hash,$setName);
return undef;
+ ##########
+ } elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/
+ && $setName =~ /resetCuttingBladeUsageTime/ && defined( $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} ) ) {
+
+ CMD($hash,$setName);
+ return undef;
+
+ ##########
+ } elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/ && $setName =~ /getMessages/ ) {
+
+ getMowerWs( $hash, 'messages' );
+ return undef;
+
##########
} elsif ( ReadingsVal( $name, 'device_state', 'defined' ) !~ /defined|initialized|authentification|authenticated|update/
&& $setName =~ /^(StartInWorkArea|cuttingHeightInWorkArea)$/ && $hash->{helper}{mower}{attributes}{capabilities}{workAreas} && AttrVal( $name, 'testing', '' ) ) {
@@ -1355,7 +1444,7 @@ sub Set {
}
##########
- my $ret = " getNewAccessToken:noArg ParkUntilFurtherNotice:noArg ParkUntilNextSchedule:noArg Pause:noArg Start:selectnumbers,30,30,600,0,lin Park:selectnumbers,30,30,600,0,lin ResumeSchedule:noArg getUpdate:noArg client_secret dateTime ";
+ my $ret = " getNewAccessToken:noArg ParkUntilFurtherNotice:noArg ParkUntilNextSchedule:noArg Pause:noArg Start:selectnumbers,30,30,600,0,lin Park:selectnumbers,30,30,600,0,lin ResumeSchedule:noArg getUpdate:noArg client_secret dateTime getMessages:noArg ";
$ret .= "mowerScheduleToAttribute:noArg sendScheduleFromAttributeToMower:noArg ";
$ret .= "cuttingHeight:1,2,3,4,5,6,7,8,9 " if ( defined $hash->{helper}{mower}{attributes}{settings}{cuttingHeight} );
$ret .= "defaultDesignAttributesToAttribute:noArg mapZonesTemplateToAttribute:noArg chargingStationPositionToAttribute:noArg " if ( $hash->{helper}{mower}{attributes}{capabilities}{position} );
@@ -1380,7 +1469,8 @@ sub Set {
}
- $ret .= "confirmError:noArg " if ( AttrVal( $name, 'testing', '' ) );
+ $ret .= "confirmError:noArg " if ( $hash->{helper}{mower}{attributes}{capabilities}{canConfirmError} && AttrVal( $name, 'testing', '' ) );
+ $ret .= "resetCuttingBladeUsageTime " if ( defined( $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} ) );
return "Unknown argument $setName, choose one of".$ret;
}
@@ -1435,6 +1525,7 @@ my $header = "Accept: application/vnd.api+json\r\nX-Api-Key: ".$client_id."\r\nA
elsif ($cmd[0] eq "cuttingHeight") { $json = '{"data": {"type":"settings","attributes":{"'.$cmd[0].'": '.$cmd[1].'}}}'; $post = 'settings' }
elsif ($cmd[0] eq "stayOutZone") { $json = '{"data": {"type":"stayOutZone","id":"'.$cmd[1].'","attributes":{"enable": '.$cmd[2].'}}}'; $post = 'stayOutZones/' . $cmd[1]; $method = 'PATCH' }
elsif ($cmd[0] eq "confirmError") { $json = '{}'; $post = 'errors/confirm' }
+ elsif ($cmd[0] eq "resetCuttingBladeUsageTime") { $json = '{}'; $post = 'statistics/resetCuttingBladeUsageTime' }
elsif ($cmd[0] eq "sendScheduleFromAttributeToMower" && AttrVal( $name, 'mowerSchedule', '')) {
my $perl = eval { JSON::XS->new->decode (AttrVal( $name, 'mowerSchedule', '')) };
@@ -1490,6 +1581,7 @@ sub CMDResponse {
if( !$err && $statuscode == 202 && $data ) {
my $result = eval { JSON::XS->new->decode($data) };
+
if ($@) {
Log3( $name, 2, "$iam - JSON error while request: $@");
@@ -1497,6 +1589,7 @@ sub CMDResponse {
} else {
$hash->{helper}{CMDResponse} = $result;
+
if ($result->{data}) {
Log3 $name, 5, $data;
@@ -1533,6 +1626,8 @@ sub CMDResponse {
readingsEndUpdate($hash, 1);
Log3 $name, 2, "$iam \n\$statuscode >$statuscode<\n\$err >$err<,\n\$data >$data<\n\$param->{url} >$param->{url}<\n\$param->{data} >$param->{data}<";
+ DoTrigger($name, "MOWERAPI ERROR");
+
return undef;
}
@@ -2581,6 +2676,9 @@ sub listStatisticsData {
$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 |
';
+ $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} | ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} / 3600 ) . ' | h |
' if ( defined $hash->{helper}{mower}{attributes}{statistics}{cuttingBladeUsageTime} );
+ $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{upTime} | ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{upTime} / 3600 ) . ' | h |
' if ( defined $hash->{helper}{mower}{attributes}{statistics}{upTime} );
+ $ret .= ' $hash->{helper}{mower}{attributes}{statistics}{downTime} | ' . sprintf( "%.0f", $hash->{helper}{mower}{attributes}{statistics}{downTime} / 3600 ) . ' | h |
' if ( defined $hash->{helper}{mower}{attributes}{statistics}{downTime} );
my $prop = '';
for my $item ( @items ) {
@@ -2718,13 +2816,13 @@ sub listErrorStack {
my ( $hash ) = @_;
my $name = $hash->{NAME};
my $cnt = 0;
- my $ret = '';
+ my $ret = '';
if ( $::init_done && defined( $hash->{helper}{mower}{type} ) && @{ $hash->{helper}{errorstack} } ) {
- $ret .= '';
+ $ret .= '';
$ret .= 'Last Errors';
- $ret .= '';
+ $ret .= '';
for ( my $i = 0; $i < @{ $hash->{helper}{errorstack} }; $i++ ) {
@@ -2733,15 +2831,38 @@ sub listErrorStack {
}
$ret .= '
';
- $ret .= '';
-
- return $ret;
} else {
- return '';
+ $ret .= '';
}
+
+ if ( $::init_done && defined ( $hash->{helper}{endpoints}{messages}{attributes}{messages} ) && ref $hash->{helper}{endpoints}{messages}{attributes}{messages} eq 'ARRAY' && @{ $hash->{helper}{endpoints}{messages}{attributes}{messages} } > 0 ) {
+
+
+ my @msg = @{ $hash->{helper}{endpoints}{messages}{attributes}{messages} };
+ $ret .= '';
+ $ret .= 'Last Messages';
+
+ $ret .= '';
+
+
+ for ( my $i = 0; $i < @{ $hash->{helper}{endpoints}{messages}{attributes}{messages} }; $i++ ) {
+
+ $ret .= ' ' . FmtDateTimeGMT( $msg[$i]{time} ) . ' | ' . $msg[$i]{severity} . ' - ' . $errortable->{ $msg[$i]{code} } . ' | ' . ( defined $msg[$i]{longitude} ? $msg[$i]{longitude} : '-' ) . ' / ' . ( defined $msg[$i]{latitude} ? $msg[$i]{latitude} : '-' ) . ' |
';
+
+ }
+
+ } else {
+
+ $ret .= '';
+
+ }
+
+ $ret .= '';
+ return $ret;
+
}
#########################