diff --git a/fhem/CHANGED b/fhem/CHANGED index 5cdeacae1..9e2cd449e 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 + - change: 74_AutomowerConnect: fix ws buffer handling if multiple JSON + data sets received, calculate nextStarttime if no planner event + add websocket statistics - feature: 76_SolarForecast: Major release 1.50.0 - change: 49_SSCam: set compatible version to 9.2.3 - feature: 76_SolarForecast: version 1.47.5 diff --git a/fhem/FHEM/74_AutomowerConnect.pm b/fhem/FHEM/74_AutomowerConnect.pm index 199223892..17dbd6933 100644 --- a/fhem/FHEM/74_AutomowerConnect.pm +++ b/fhem/FHEM/74_AutomowerConnect.pm @@ -83,6 +83,7 @@ sub Initialize() { 'numberOfWayPointsToDisplay ' . 'addPollingMinInterval ' . 'addPositionPolling:1,0 ' . + 'calculateReadings:nextStart ' . $::readingFnAttributes; $::data{FWEXT}{AutomowerConnect}{SCRIPT} = 'automowerconnect.js'; @@ -486,6 +487,10 @@ __END__ }'
+
  • calculateReadings
    + attr <name> calculateReadings <nextStart>
    + Calcutates nextStart if not provided by planner event.
  • +
  • addPollingMinInterval
    attr <name> addPollingMinInterval <interval in seconds>
    Set minimum intervall for additional polling triggered by any websocket event, default 0 (no polling). Gets periodically mower data. Make sure to be within API limits (10000 calls per month).
  • @@ -1014,6 +1019,10 @@ __END__ }'
    +
  • calculateReadings
    + attr <name> calculateReadings <nextStart>
    + Berechnet nextStart wenn der Wert nicht per Plannerevent geliefert wird.
  • +
  • addPollingMinInterval
    attr <name> addPollingMinInterval <interval in seconds>
    Setzt das Mindestintervall für zusätzliches Polling der API nach einem websocket event, default 0 (kein Polling). Liest periodisch zusätzlich Mäherdaten von der API. Es muss sichergestellt werden, das die API Begrenzung (10000 Anfragen pro Monat) eingehalten wird.
  • diff --git a/fhem/lib/FHEM/Devices/AMConnect/Common.pm b/fhem/lib/FHEM/Devices/AMConnect/Common.pm index d581d9e17..57dab4010 100644 --- a/fhem/lib/FHEM/Devices/AMConnect/Common.pm +++ b/fhem/lib/FHEM/Devices/AMConnect/Common.pm @@ -35,6 +35,7 @@ use Time::Local; use DevIo; use Storable qw(dclone retrieve store); use DateTime; +use List::Util qw( min ); my $EMPTY = q{}; my $missingModul = $EMPTY; ## no critic (ProhibitConditionalUseStatements) @@ -335,16 +336,32 @@ EOF hullArea => 0 }, wsbuf => { - events_changed => 0, - event_duplicates => 0, + sum_changed => 0, + sum_duplicates => 0, 'position-event-v2' => $EMPTY, 'mower-event-v2' => $EMPTY, 'battery-event-v2' => $EMPTY, 'planner-event-v2' => $EMPTY, 'cuttingHeight-event-v2'=> $EMPTY, - 'headLights-event-v2' => $EMPTY, + 'headlights-event-v2' => $EMPTY, 'calendar-event-v2' => $EMPTY, - 'message-event-v2' => $EMPTY + 'message-event-v2' => $EMPTY, + position_changed => 0, + mower_changed => 0, + battery_changed => 0, + planner_changed => 0, + cuttingHeight_changed => 0, + headlights_changed => 0, + calendar_changed => 0, + message_changed => 0, + position_duplicates => 0, + mower_duplicates => 0, + battery_duplicates => 0, + planner_duplicates => 0, + cuttingHeight_duplicates=> 0, + headlights_duplicates => 0, + calendar_duplicates => 0, + message_duplicates => 0 } } ); @@ -2595,9 +2612,17 @@ sub fillReadings { readingsBulkUpdateIfChanged( $hash, $pref.'_restrictedReason', $hash->{helper}{mower}{attributes}{$pref}{restrictedReason} ); readingsBulkUpdateIfChanged( $hash, $pref.'_overrideAction', $hash->{helper}{mower}{attributes}{$pref}{override}{action} ) if ( $hash->{helper}{mower}{attributes}{$pref}{override}{action} ); - $tstamp = $hash->{helper}{mower}{attributes}{$pref}{nextStartTimestamp}; - $timestamp = FmtDateTimeGMT( $tstamp/1000 ); - readingsBulkUpdateIfChanged($hash, $pref.'_nextStart', $tstamp ? $timestamp : '-' ); + if ( AttrVal( $name, 'calculateReadings', $EMPTY ) =~ /nextStart/ ) { + + readingsBulkUpdateIfChanged( $hash, $pref.'_nextStart', calculateNextStart( $hash ) ); + + } else { + + $tstamp = $hash->{helper}{mower}{attributes}{$pref}{nextStartTimestamp}; + $timestamp = FmtDateTimeGMT( $tstamp/1000 ); + readingsBulkUpdateIfChanged( $hash, $pref.'_nextStart', $tstamp ? $timestamp : '-' ); + + } $pref = 'statistics'; my $noCol = $hash->{helper}{statistics}{currentDayCollisions}; @@ -2984,6 +3009,22 @@ sub listInternalData { ## no critic (ProhibitExcessComplexity [complexity core m $ret .= 'NOT_APPLICABLE with error time stamp  lasterror/positions  ' . $ernr . ' - '; $ret .= ''; + $ret .= '

    '; + $ret .= ''; + + $ret .= ''; + my @evt = qw(battery calendar cuttingHeight headlights message mower planner position sum); + + for my $key (@evt) { + + my $hc = $hash->{helper}{wsbuf}{$key . '_changed'}; + my $hd = $hash->{helper}{wsbuf}{$key . '_duplicates'}; + $ret .= ''; + + } + + $ret .= '
    Websocket Events
    Events  Changed  Unchanged  Sum 
    ' . $key . '  ' . $hc . '  ' . $hd . ' ' . ( $hc + $hd ) . ' 
    '; + $ret .= '

    '; $ret .= ''; @@ -3000,11 +3041,11 @@ sub listInternalData { ## no critic (ProhibitExcessComplexity [complexity core m $ret .= ''; $ret .= ''; -$ret .= '
    Rest API Data
    Token Expires ' . FmtDateTime( ReadingsVal($name, '.expires', '0') ) . '
    Access Token' . ReadingsVal($name, '.access_token', '0') . '
    '; + $ret .= ''; $ret .= '

    '; $ret .= ''; -my $mapdesign = $hash->{helper}{mapdesign}; + my $mapdesign = $hash->{helper}{mapdesign}; $mapdesign =~ s/\n/
    /g; $ret .= ''; @@ -3188,6 +3229,37 @@ sub makeStatusTimeStamp { } +######################### +sub calculateNextStart { + my ( $hash ) = @_; + return "-" if ( $hash->{helper}{mower}{attributes}{mower}{mode} !~ /MAIN_AREA/ ) || $hash->{helper}{mower}{attributes}{mower}{state} =~ /IN_OPERATION/; + + my $nt = gettimeofday(); + my @lt = gmtime( $nt ); + my $wday = $lt[ 6 ]; + my $mn = $nt - ( $lt[ 2 ] * 3600 + $lt[ 1 ] * 60 + $lt[ 0 ] ); # Midnight + my @days = qw( sunday monday tuesday wednesday thursday friday saturday sunday monday tuesday wednesday thursday friday saturday ); + my @cal = @{ $hash->{helper}{mower}{attributes}{calendar}{tasks} }; + my @times =(); + + for ( my $i = 0; $i < @cal; $i++ ) { + + my $calt = $mn + $cal [ $i ]->{start} * 60; + + for ( my $wd = $lt [ 6 ]; $wd < $lt [ 6 ] + 7 ; $wd++ ) { + + my $nx = $calt + 86400 * ( $wd - $lt [ 6 ] ); + push @times, $nx if ( $cal [ $i ]->{$days [ $wd ]} && $nx > $nt ); + + } + + } + + # my $nextTime = POSIX::strftime( "%F %H:%M:00", localtime( min( @times ) ) ); + my $nextTime = FmtDateTimeGMT( min( @times ) ); + return $nextTime; +} + ############################################################## # # WEBSOCKET @@ -3283,14 +3355,19 @@ sub wsRead { ## no critic (ProhibitExcessComplexity [complexity core maintenanc my ( @bufj ) = split('\}\{', $buforig ); # split in case buffer contains more than one event string - if ( @bufj > 1 ) { + if ( @bufj > 1 ) { # complete JSON strings due to splitting - for ( my $i = 0; $i < @bufj; $i++ ) { # complete JSON strings due to splitting + my $i = 0; + $bufj[$i] = $bufj[$i].'}'; - $bufj[$i] = $i % 2 ? '{'.$bufj[$i] : $bufj[$i].'}'; + for ( my $i = 1; $i < @bufj - 1; $i++ ) { + + $bufj[$i] = '{'.$bufj[$i].'}'; } + $bufj[$i] = '{'.$bufj[$i]; + } for my $buf (@bufj) { # process each buffer part @@ -3303,7 +3380,7 @@ sub wsRead { ## no critic (ProhibitExcessComplexity [complexity core maintenanc if ( $buf ne $hash->{helper}{wsbuf}{$evt} ) { # handle changed events $hash->{helper}{wsbuf}{$evt} = $buf; - $hash->{helper}{wsbuf}{events_changed}++ ; + $hash->{helper}{wsbuf}{sum_changed}++ ; $hash->{helper}{wsbuf}{$evn.'_changed'}++ ; my $result = eval { JSON::XS->new->decode( $buf ) }; @@ -3413,7 +3490,7 @@ sub wsRead { ## no critic (ProhibitExcessComplexity [complexity core maintenanc } # headlights-event-v2 - elsif ( $result->{type} =~ /^hea/ ) { #no headlight event 430x + elsif ( $result->{type} =~ /^hea/ ) { #no headlights event 430x $hash->{helper}{mower}{attributes}{settings}{headlight}{mode} = $result->{attributes}{headlight}{mode}; @@ -3449,7 +3526,7 @@ sub wsRead { ## no critic (ProhibitExcessComplexity [complexity core maintenanc } else { # handle duplicates - $hash->{helper}{wsbuf}{event_duplicates}++; + $hash->{helper}{wsbuf}{sum_duplicates}++; $hash->{helper}{wsbuf}{$evn.'_duplicates'}++ ; } # end handle duplicates/changed
    Default mapDesignAttributes
    ' . $mapdesign . '