From e3858001c8b1125cfc78e9c55315e86c4cfa7b24 Mon Sep 17 00:00:00 2001 From: LeonGaultier Date: Wed, 27 Oct 2021 03:31:27 +0000 Subject: [PATCH] 73_GardenaSmartBridge.pm: 74_GardenaSmartDevice.pm, bugfix and new features, see CHANGE file git-svn-id: https://svn.fhem.de/fhem/trunk@25124 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/CHANGED | 4 + fhem/FHEM/73_GardenaSmartBridge.pm | 120 ++++++++++++++++++--------- fhem/FHEM/74_GardenaSmartDevice.pm | 127 +++++++++++++++++++++++++---- 3 files changed, 198 insertions(+), 53 deletions(-) diff --git a/fhem/CHANGED b/fhem/CHANGED index 0c54f8e57..35ceecafc 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,9 @@ # 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: 73_GardenaSmartBridge.pm 74_GardenaSmartDevice.pm: + add stop/resume schedule, add wintermode, fix notifys, + fixing connection / interval / 404 error, add mower timestamp to + readings, increase slider to 720 and many many more backand stuff - change: 88_HMCCU: Update to version 5.0 - bugfix: 73_AutoShuttersControl: fix regex to detect position event with dot's diff --git a/fhem/FHEM/73_GardenaSmartBridge.pm b/fhem/FHEM/73_GardenaSmartBridge.pm index 12363aa12..88bdc0b73 100644 --- a/fhem/FHEM/73_GardenaSmartBridge.pm +++ b/fhem/FHEM/73_GardenaSmartBridge.pm @@ -63,6 +63,8 @@ use warnings; use POSIX; use FHEM::Meta; +#use Data::Dumper; + use HttpUtils; my $missingModul = ''; @@ -205,6 +207,7 @@ sub Initialize { $hash->{AttrFn} = \&Attr; $hash->{AttrList} = 'debugJSON:0,1 ' + . 'debugDEVICE:0,1 ' . 'disable:1 ' . 'interval ' . 'disabledForIntervals ' @@ -239,7 +242,7 @@ sub Define { . '/v1'; $hash->{VERSION} = version->parse($VERSION)->normal; $hash->{INTERVAL} = 60; - $hash->{NOTIFYDEV} = "global,$name"; + $hash->{NOTIFYDEV} = "global,$name"; CommandAttr( undef, $name . ' room GardenaSmart' ) if ( AttrVal( $name, 'room', 'none' ) eq 'none' ); @@ -258,7 +261,7 @@ sub Undef { my $hash = shift; my $name = shift; - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices"); delete $modules{GardenaSmartBridge}{defptr}{BRIDGE} if ( defined( $modules{GardenaSmartBridge}{defptr}{BRIDGE} ) ); @@ -279,7 +282,7 @@ sub Attr { if ( $attrName eq 'disable' ) { if ( $cmd eq 'set' && $attrVal eq '1' ) { - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices"); readingsSingleUpdate( $hash, 'state', 'inactive', 1 ); Log3 $name, 3, "GardenaSmartBridge ($name) - disabled"; } @@ -304,13 +307,13 @@ sub Attr { if ( $cmd eq 'set' ) { return 'Interval must be greater than 0' if ( $attrVal == 0 ); - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices"); $hash->{INTERVAL} = $attrVal; Log3 $name, 3, "GardenaSmartBridge ($name) - set interval: $attrVal"; } elsif ( $cmd eq 'del' ) { - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices"); $hash->{INTERVAL} = 60; Log3 $name, 3, "GardenaSmartBridge ($name) - delete User interval and set default: 60"; @@ -352,6 +355,7 @@ sub Notify { @{$events} or grep /^DEFINED.$name$/, @{$events} or grep /^MODIFIED.$name$/, @{$events} or grep /^ATTR.$name.gardenaAccountEmail.+/, + @{$events} or grep /^DELETEATTR.$name.disable$/, @{$events} ) ) @@ -363,14 +367,14 @@ sub Notify { @{$events} ) ) + && $init_done ); getDevices($hash) if ( $devtype eq 'Global' && ( - grep /^DELETEATTR.$name.disable$/, - @{$events} or grep /^ATTR.$name.disable.0$/, + grep /^ATTR.$name.disable.0$/, @{$events} or grep /^DELETEATTR.$name.interval$/, @{$events} or grep /^ATTR.$name.interval.[0-9]+/, @{$events} @@ -383,11 +387,10 @@ sub Notify { && ( grep /^state:.Connected$/, @{$events} or grep /^lastRequestState:.request_error$/, - @{$events} + @{$events} ) ) { - InternalTimer( gettimeofday() + $hash->{INTERVAL}, "FHEM::GardenaSmartBridge::getDevices", $hash ); Log3 $name, 4, @@ -413,7 +416,7 @@ sub Get { my $list = ""; $list .= " debug_devices_list:" .join( ',', @{ $hash->{helper}{deviceList} }) - if ( AttrVal( $name, "debugJSON", "none") ne "none" + if ( AttrVal( $name, "debugDEVICE", "none") ne "none" && exists($hash->{helper}{deviceList}) ); return "Unknown argument $cmd,choose one of $list"; } @@ -481,6 +484,7 @@ sub Write { { url => $hash->{URL} . $uri, timeout => 15, + incrementalTimeout => 1, hash => $hash, device_id => $deviceId, data => $payload, @@ -517,8 +521,8 @@ sub ErrorHandling { my $dname = $dhash->{NAME}; Log3 $name, 4, "GardenaSmartBridge ($name) - Request: $data"; - - my $decode_json = eval { decode_json($data) }; + + my $decode_json = eval { decode_json($data) } if ( length($data) > 0 ); if ($@) { Log3 $name, 3, "GardenaSmartBridge ($name) - JSON error while request"; } @@ -708,10 +712,26 @@ 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; } - if (defined($hash->{helper}{debug_device})){ - Log3 $name, 5, "GardenaSmartBridge DEBUG Device"; + elsif (defined ($decode_json->{message}) + && $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' + ){ + 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'; @@ -731,6 +751,18 @@ sub ErrorHandling { $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'){ + $output .= 'N/A \n'; + } else { + $output .= "value: $dev_settings->{value} \n"; + } + } $hash->{helper}{debug_device_output} = $output; asyncOutput($param->{cl}, $hash->{helper}{debug_device_output}); return; @@ -771,6 +803,10 @@ sub ResponseProcessing { $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}; + + InternalTimer($hash->{helper}{token_expired}, + "FHEM::GardenaSmartBridge::getToken", $hash ); Write( $hash, undef, undef, undef ); Log3 $name, 3, "GardenaSmartBridge ($name) - fetch locations id"; @@ -931,7 +967,6 @@ sub WriteReadings { elsif ( $decode_json->{abilities}[0]{properties} [$properties]{name} eq 'wifi_status' ) { - #TODO: read valies if bridge connected to wifi readingsBulkUpdateIfChanged( $hash, 'wifi_status-ssid', $v->{ssid} ) if (ref($v->{ssid}) ne 'HASH'); @@ -967,8 +1002,7 @@ sub getDevices { my $hash = shift; my $name = $hash->{NAME}; - - RemoveInternalTimer($hash); + RemoveInternalTimer($hash, "FHEM::GardenaSmartBridge::getDevices"); if ( not IsDisabled($name) ) { @@ -978,12 +1012,17 @@ sub getDevices { for my $gardenaDev (@list){ push( @{ $hash->{helper}{deviceList} }, $gardenaDev ); } - Write( $hash, undef, undef, undef ); - Log3 $name, 4, - "GardenaSmartBridge ($name) - fetch device list and device states"; + if ( AttrVal( $name, 'gardenaAccountEmail', 'none' ) ne 'none' + && ( + defined( ReadPassword( $hash, $name ) ) + )) + { + Write( $hash, undef, undef, undef ); + Log3 $name, 4, + "GardenaSmartBridge ($name) - fetch device list and device states"; + } # fi gardenaAccountEmail } else { - readingsSingleUpdate( $hash, 'state', 'disabled', 1 ); Log3 $name, 3, "GardenaSmartBridge ($name) - device is disabled"; } @@ -1013,16 +1052,6 @@ sub getToken { if ( defined( $hash->{helper}{locations_id} ) && $hash->{helper}{locations_id} ); - # Write( - # $hash, - # '"sessions": {"email": "' - # . AttrVal( $name, 'gardenaAccountEmail', 'none' ) - # . '","password": "' - # . ReadPassword( $hash, $name ) . '"}', - # undef, - # undef - # ); - Write( $hash, '"data": {"type":"token", "attributes":{"username": "' @@ -1033,8 +1062,9 @@ sub getToken { 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"; @@ -1200,20 +1230,21 @@ sub createHttpValueStrings { $payload = '{}' if ( !defined($payload) ); if ( $payload eq '{}' ) { - $method = 'GET'; + $method = 'GET' if (defined( $hash->{helper}{session_id} ) ); $payload = ''; $uri .= '/locations?locatioId=null&user_id=' . $hash->{helper}{user_id} if ( exists( $hash->{helper}{user_id} ) && !defined( $hash->{helper}{locations_id} ) ); readingsSingleUpdate( $hash, 'state', 'fetch locationId', 1 ) - if ( !defined( $hash->{helper}{locations_id} ) ); - $uri .= '/auth/token' if ( !defined( $hash->{helper}{session_id} ) ); + if ( exists( $hash->{helper}{user_id} ) + && !defined( $hash->{helper}{locations_id} ) ); $uri .= '/devices' if (!defined($abilities) && defined( $hash->{helper}{locations_id} ) ); } - $uri = '/devices/'.InternalVal($hash->{helper}{debug_device}, 'DEVICEID', 0 ) if ( exists ($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} ) ); if ( defined( $hash->{helper}{locations_id} ) ) { @@ -1279,6 +1310,19 @@ sub createHttpValueStrings { . $abilities . '/properties/manual_watering_timer'; + } + elsif (defined($abilities) + && defined($payload) + && $abilities eq 'watering_button_config' ) + { + $method = 'PUT'; + + $uri .= + '/devices/' + . $deviceId + . '/abilities/watering' + . '/properties/button_config_time'; + } elsif (defined($abilities) && defined($payload) @@ -1463,7 +1507,7 @@ sub DeletePassword { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.4.0", + "version": "v2.4.6", "author": [ "Marko Oldenburg " ], diff --git a/fhem/FHEM/74_GardenaSmartDevice.pm b/fhem/FHEM/74_GardenaSmartDevice.pm index d56d9a948..801e015e7 100644 --- a/fhem/FHEM/74_GardenaSmartDevice.pm +++ b/fhem/FHEM/74_GardenaSmartDevice.pm @@ -209,6 +209,10 @@ sub Define { $hash->{helper}{STARTINGPOINTID} = ''; $hash->{helper}{schedules_paused_until_id} = ''; $hash->{helper}{eco_mode_id} = ''; + $hash->{helper}{button_config_time_id} = ''; + $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} = ''; @@ -306,7 +310,7 @@ 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":"2040-12-31T22: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}; } @@ -361,6 +365,18 @@ sub Set { . $aArg->[0] * 60 . ',"valve_id":1}}'; } + elsif ( lc $cmd eq 'manualbuttontime'){ + $service_id = $hash->{helper}{button_config_time_id}; + $payload= + '"properties":{"name":"button_config_time",' + .'"value":' + . $aArg->[0] * 60 + . ',"timestamp":"2021-05-26T19:06:23.680Z"' + . ',"at_bound":null,"unit":"seconds","ability":"' + . $service_id + .'"}'; + $abilities = 'watering_button_config'; + } elsif ( $cmd =~ m{\AcancelOverride}xms ) { my $valve_id = 1; @@ -377,14 +393,26 @@ 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')); + + $abilities = 'wateringcomputer_settings'; + $service_id = $hash->{helper}->{'schedules_paused_until_id'}; + $payload = '"settings":{"name":"schedules_paused_until"' + . ', "value":"' + . ($cmd eq 'resumeSchedule' ? '' : $duration ) + . '","device":"' + . $hash->{DEVICEID} + . '"}'; + } elsif ( lc $cmd eq 'on' || lc $cmd eq 'off' || lc $cmd eq 'on-for-timer' ) { my $val = ( - defined($aArg) && ref($aArg) eq 'ARRAY' + scalar(!@$aArg == 0) && ref($aArg) eq 'ARRAY' ? $aArg->[0] * 60 : lc $cmd ); - $payload = '"properties":{"value":"' . $val . '"}'; + $payload = '"properties":{"name":"power_timer", "value":"' . $val . '"}'; } ### Watering ic24 elsif ( $cmd =~ m{\AmanualDurationValve\d\z}xms ) { @@ -407,8 +435,8 @@ sub Set { } 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' : '2040-12-31T22:00:00.000Z')); - + my $duration = (( defined($aArg->[1]) ? ( ((Time::Piece->new)+(ONE_HOUR * $aArg->[1]) - (Time::Piece->new)->tzoffset )->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_' @@ -441,8 +469,16 @@ sub Set { $payload = '"name":"measure_soil_humidity"'; $abilities = 'humidity'; } - - + } + ## winter sleep + elsif ( lc $cmd eq 'winter_mode') { + $payload = '"settings":{"name":"winter_mode","value":"' + . $aArg->[0] + .'","device":"' + . $hash->{DEVICEID} + .'"}'; + $abilities = 'winter_settings'; + $service_id = $hash->{helper}->{'winter_mode_id'}; } else { @@ -452,7 +488,7 @@ 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' + $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 .= @@ -466,9 +502,10 @@ sub Set { if ( AttrVal( $name, 'model', 'unknown' ) eq 'sensor' && ReadingsVal($name, 'device_info-category', 'unknown') eq 'sensor' ); - $list .= 'on:noArg off:noArg on-for-timer:slider,0,1,60' + $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"; } @@ -547,6 +584,27 @@ 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 ( $hash->{helper}{$propertie->{name}.'_id'} ne + $decode_json->{abilities}[$abilities]{id} ) + { + $hash->{helper}{$propertie->{name}.'_id'} = + $decode_json->{abilities}[$abilities]{id}; + } + readingsBulkUpdateIfChanged( + $hash, + 'manualButtonTime', + (RigReadingsValue( $hash, $propertie->{value} / 60) ) + ); + next; + } + } + readingsBulkUpdateIfChanged( $hash, $decode_json->{abilities}[$abilities]{name} . '-' @@ -595,6 +653,21 @@ sub WriteReadings { || $decode_json->{abilities}[$abilities]{name} . '-' . $propertie->{name} eq 'light-light' ) ); + + readingsBulkUpdateIfChanged( + $hash, + $decode_json->{abilities}[$abilities]{name} . '-' + . $propertie->{name} + . '_timestamp', + 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' + ) + ); readingsBulkUpdateIfChanged( $hash, @@ -641,7 +714,8 @@ sub WriteReadings { 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 'eco_mode' + || $decode_json->{settings}[$settings]{name} eq 'winter_mode' ) ) { if ( $hash->{helper}{$decode_json->{settings}[$settings]{name}.'_id'} ne @@ -650,6 +724,12 @@ sub WriteReadings { $hash->{helper}{$decode_json->{settings}[$settings]{name}.'_id'} = $decode_json->{settings}[$settings]{id}; } + # save winter mode as reading + readingsBulkUpdateIfChanged( + $hash, + 'winter_mode', + $decode_json->{settings}[$settings]{value} + ) if ($decode_json->{settings}[$settings]{name} eq 'winter_mode'); } if ( ref( $decode_json->{settings}[$settings]{value} ) eq "ARRAY" @@ -1125,6 +1205,10 @@ sub SetPredefinedStartPoints { set + - +

irrigation control

  • resumeScheduleValve - start schedule irrigation on valve n
  • -
  • stopScheduleValve - stop schedule irrigation on valve n (Default: 2040-12-31T22:00:00.000Z) | optional params hours (now + hours)
  • +
  • stopScheduleValve - stop schedule irrigation on valve n (Default: 2038-01-18T00:00:00.000Z) | optional params hours (now + hours)
  • closeAllValves - close all valves
  • +

    water control

    +
  • manualButtonTime - set manual time for button press (in minutes) 0 disable button
  • +
  • stopSchedule - stop schedule for now + n hours (Default: 2038-01-18T00:00:00.000Z)
  • +
  • resumeSchedule - resume schedule
  • @@ -1273,6 +1361,10 @@ sub SetPredefinedStartPoints { set + +

    irrigation control

  • resumeScheduleValve - Startet Bew&aauml;sserung am Ventil n nach Zeitplan
  • -
  • stopScheduleValve - Setzt Bew&aauml;sserung am Ventil n aus (Default: 2040-12-31T22:00:00.000Z) | Optionaler Parameter Stunden (Jetzt + Stunden)
  • +
  • stopScheduleValve - Setzt Bew&aauml;sserung am Ventil n aus (Default: 2038-01-18T00:00:00.000Z) | Optionaler Parameter Stunden (Jetzt + Stunden)
  • closeAllValves - Stopt Bew&aauml;sserung an allen Ventilen
  • +

    water control

    +
  • manualButtonTime - setzt die Dauer für den manuellen Knopf (in Minuten) 0 Schaltet den Knopf aus
  • +
  • stopSchedule - Halte Zeitplan an für x Stunden - (Default: 2038-01-18T00:00:00.000Z)
  • +
  • resumeSchedule - Weiterführung des Zeitplans
  • @@ -1307,7 +1404,7 @@ sub SetPredefinedStartPoints { ], "release_status": "stable", "license": "GPL_2", - "version": "v2.4.0", + "version": "v2.4.2", "author": [ "Marko Oldenburg " ],