From b0ede0c95013d1d54d76fbbcda789d370f6219cf Mon Sep 17 00:00:00 2001 From: andi291 <> Date: Sun, 7 May 2017 15:58:02 +0000 Subject: [PATCH] 86_Robonect.pm: ABU 20170501 added setkey/getkey for username and password, added eval-error-logging, changed verbose 3 to verbose 4, tuned documentation git-svn-id: https://svn.fhem.de/fhem/trunk@14209 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/86_Robonect.pm | 235 ++++++++++++++++++++++++++++----------- 1 file changed, 173 insertions(+), 62 deletions(-) diff --git a/fhem/FHEM/86_Robonect.pm b/fhem/FHEM/86_Robonect.pm index 829f46bc6..9dd8bdca3 100644 --- a/fhem/FHEM/86_Robonect.pm +++ b/fhem/FHEM/86_Robonect.pm @@ -23,6 +23,16 @@ # ABU 20170406 fixed hybernate-check in timer # ABU 20170422 fixed doku # ABU 20170427 fixed numerich undefs +# ABU 20170428 do not delete private hash data in undef +# ABU 20170428 do not define defptr in define +# ABU 20170428 fixed in define section: removed me and my secret, removed IP-check for DNS-Support +# ABU 20170428 removed setting attributes in define (eventonchange and pollInterval) +# ABU 20170428 masked decode_json in eval and added error-path +# ABU 20170501 added setkey/getkey for username and password +# ABU 20170501 added eval-error-logging +# ABU 20170501 changed verbose 3 to verbose 4 +# ABU 20170501 tuned documentation + package main; @@ -40,6 +50,8 @@ my $START = "start"; my $STOP = "stop"; my $OFFLINE = "offline"; my $HYBERNATE = "winterschlaf"; +my $USER = "benutzername"; +my $PW = "passwort"; #available get cmds my %gets = ( @@ -48,13 +60,15 @@ my %gets = ( #available set cmds my %sets = ( - "feierabend" => "noArg", + $EOD => "noArg", $HOME => "noArg", $AUTO => "noArg", $MANUAL => "noArg", $START => "noArg", $STOP => "noArg", - $HYBERNATE => "on,off" + $HYBERNATE => "on,off", + $USER => "", + $PW => "" ); my %commands = ( @@ -151,29 +165,31 @@ sub Robonect_Define($$) Log3 ($name, 5, "define $name: enter $hash, attributes: $tempStr"); #too less arguments - return "wrong syntax - define Robonect [ ]" if (int(@a) < 3); + #return "wrong syntax - define Robonect [ ]" if (int(@a) < 3); + return "wrong syntax - define Robonect " if (int(@a) < 3); #check IP my $ip = $a[2]; #remove whitespaces $ip =~ s/^\s+|\s+$//g; + #removed IP-check - can also be a name #Syntax ok - if ($ip =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) - { - my @octs = split (".", $ip); - - foreach my $octet (@octs) - { - return "wrong syntax - $octet has an invalid range. Allowed is 0..255" if (($octet >= 256) or ($octet <= -1)); - } - } - else - { - return "wrong syntax - IP must be supplied correctly <0..254>.<0..254>.<0..254>.<0..254>"; - } + #if ($ip =~ m/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) + #{ + # my @octs = split (".", $ip); + # + # foreach my $octet (@octs) + # { + # return "wrong syntax - $octet has an invalid range. Allowed is 0..255" if (($octet >= 256) or ($octet <= -1)); + # } + #} + #else + #{ + # return "wrong syntax - IP must be supplied correctly <0..254>.<0..254>.<0..254>.<0..254>"; + #} #wrong syntax for IP - return "wrong syntax - IP must be supplied correctly <0..254>.<0..254>.<0..254>.<000..254>" if (int(@a) < 3); + #return "wrong syntax - IP must be supplied correctly <0..254>.<0..254>.<0..254>.<000..254>" if (int(@a) < 3); #assign name and port $hash->{NAME} = $name; @@ -186,12 +202,15 @@ sub Robonect_Define($$) #finally create decice #defptr is needed to supress FHEM input - $modules{Robonect}{defptr}{$name} = $hash; + #removed according Rudis recommendation + #$modules{Robonect}{defptr}{$name} = $hash; #default event-on-changed-reading for all readings - $attr{$name}{"event-on-change-reading"} = ".*"; - #defaul poll-interval - $attr{$name}{"pollInterval"} = 90; + #removed according Rudis recommendation + #$attr{$name}{"event-on-change-reading"} = ".*"; + #default poll-interval + #removed according Rudis recommendation + #$attr{$name}{"pollInterval"} = 90; Log3 ($name, 5, "exit define"); return undef; @@ -215,10 +234,11 @@ sub Robonect_Undef($$) #remove module. Refer to DevName, because module may be renamed delete $modules{KNX}{defptr}{$hash->{DEVNAME}}; + #removed according to Rudis recommendation #remove name - delete $hash->{NAME}; + #delete $hash->{NAME}; #remove backuped name - delete $hash->{DEVNAME}; + #delete $hash->{DEVNAME}; Log3 ($name, 5, "exit undef"); return undef; @@ -331,7 +351,7 @@ sub Robonect_Set($@) #if command is hybernate, do this if ($cmd eq lc($HYBERNATE)) { - Log3 ($name, 5, "got hybernate for set-command"); + Log3 ($name, 5, "set - got hybernate for set-command"); my $val = lc($a[2]); $val = "off" if (!defined ($val)); @@ -339,18 +359,30 @@ sub Robonect_Set($@) if ($val =~ m/on/) { readingsSingleUpdate($hash, $HYBERNATE, "on", 1); - Log3 ($name, 5, "activated hybernate"); + Log3 ($name, 5, "set - activated hybernate"); } elsif ($val =~ m/off/) { readingsSingleUpdate($hash, $HYBERNATE, "off", 1); - Log3 ($name, 5, "deactivated hybernate"); + Log3 ($name, 5, "set - deactivated hybernate"); } else { return "only on or off are supported for $HYBERNATE"; } } + #if command is user + if ($cmd eq lc($USER)) + { + setKeyValue("ROBONECT_USER_$name", $a[2]); + Log3 ($name, 5, "set - wrote username"); + } + #if command is password + if ($cmd eq lc($PW)) + { + setKeyValue("ROBONECT_PW_$name", $a[2]); + Log3 ($name, 5, "set - wrote password"); + } #else proceed with communication to mower #execute it elsif (defined ($decodedCmd)) @@ -516,7 +548,7 @@ sub Robonect_callback ($) #wenn ein Fehler bei der HTTP Abfrage aufgetreten ist if($err ne "") { - Log3 ($name, 3, "callback - error while requesting ".$param->{url}." - $err"); + Log3 ($name, 4, "callback - error while requesting ".$param->{url}." - $err"); $hash->{LAST_COMM_STATUS} = $err; #set reading with failure - notify only, when state has not changed readingsSingleUpdate($hash, "state", $OFFLINE, 1); @@ -524,15 +556,29 @@ sub Robonect_callback ($) #wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) elsif($data ne "") { - Log3 ($name, 3, "callback - url ".$param->{url}." returned: $data"); + Log3 ($name, 4, "callback - url ".$param->{url}." returned: $data"); #repair V5.0b $data =~ s/:"/,"/g; $data = "" if (!defined($data)); - my $answer = decode_json (encode_utf8($data)); + #execute in eval to be safe - therefore $answer may be undef + my $answer = undef; + eval '$answer = decode_json (encode_utf8($data))'; - Log3 ($name, 3, "callback - url ".$param->{url}." repaired: $data"); + #backup error from eval + my $evalErr = $@; + + if (not defined($answer)) + { + my $err = "callback - error while decoding content"; + $err = $err . ": " . $evalErr if (defined ($evalErr)); + Log3 ($name, 2, $err); + readingsSingleUpdate($hash, "fehler_aktuell", "cannot decode content", 1); + return undef; + } + + Log3 ($name, 4, "callback - url ".$param->{url}." repaired: $data"); my ($key, $value) = Robonect_decodeContent ($hash, $answer, "successful", undef, undef); @@ -543,8 +589,9 @@ sub Robonect_callback ($) #my %tmp = %$answer; #print "answer: ", %tmp, "\n"; - + #status-readings + #answer may be undefined due to eval if ($answer->{successful} =~ m/(true)|(1)/) { Log3 ($name, 5, "callback - update readings"); @@ -589,6 +636,7 @@ sub Robonect_callback ($) } #error? + #answer may be undefined due to eval my $errorOccured = $answer->{status}->{status}; if (defined ($errorOccured) and ($errorOccured =~ m/7/)) { @@ -707,8 +755,20 @@ sub Robonect_getCredentials ($) my $name = $hash->{NAME}; my $userName = undef; my $passWord = undef; + + #use username and password previously defined with set and stored in "registry" + my ($errUsr, $user) = getKeyValue("ROBONECT_USER_$name"); + Log3 ($name, 4, "credentials - Error while getting value USER: " . $errUsr) if (defined ($errUsr)); + my ($errPw, $password) = getKeyValue("ROBONECT_PW_$name"); + Log3 ($name, 4, "credentials - Error while getting value PASSWORD: " . $errPw) if (defined ($errPw)); + + if (defined ($user) and defined ($password)) + { + Log3 ($name, 5, "credentials - found with key-value"); + return $userName, $passWord; + } - #parse basicAuth + #parse basicAuth - overrules getKeyValue my $basicAuth = AttrVal ($name, "basicAuth", undef); if (defined ($basicAuth)) { @@ -728,7 +788,7 @@ sub Robonect_getCredentials ($) $userName = $plainAuth[0]; $passWord = $plainAuth[1]; - Log3 ($name, 5, "credentials - found plain data"); + Log3 ($name, 5, "credentials - found plain or decrypted data"); } else { @@ -736,7 +796,7 @@ sub Robonect_getCredentials ($) } } - #parse credential-File - overrules basicAuth + #parse credential-File - overrules basicAuth ang getKeyValue my $credentials = AttrVal ($name, "credentials", undef); if(defined($credentials)) { @@ -814,7 +874,7 @@ sub Robonect_getCmdList ($$$)

Define

    - define <name> Robonect <ip-adress> [<user> <password>] + define <name> Robonect <ip-adress or name>

    Setting Winterschlaf prevents communicating with the mower.

    @@ -822,23 +882,49 @@ sub Robonect_getCmdList ($$$)

    Example:

    -      define myMower Robonect 192.168.13.5 test tmySecret
    +      define myMower Robonect 192.168.13.5
    +	  define myMower Robonect myMowersDNSName
           

Set

    - Switch the mower to automatic-timer: - set <name> auto - Send the mower home - prevents further runs triggered by timer (persistent): - set <name> home - Sends the mower home for the actual timer-slot. The next timer-slot starts the mower again: - set <name> feierabend - Start the mower (only needed after a manual stop: - set <name> start - Stop the mower immediately: - set <name> stop + Set +
      +
    • auto
      + Sets the mower to automatic mode. The mower follows the internal timer, until another mode is chosen. The mower can be stopped with stop at any time. After using stop: be aware, that it continues + mowing only if the timer supplies an active slot AND start is executed before. +
    • +
    • manuell
      + This sets the mower to manual mode. The internal timer is ignored. Mowing starts with start and ends with stop. +
    • +
    • home
      + This sends the mower directly home. Until you switch to auto or manuell, no further mowing work is done. +
    • +
    • feierabend
      + This sends the mower home for the rest of the actual timeslot. At the next active timeslot mowing is continued automatically. +
    • +
    • start
      + Start mowing in manual mode or in automatic mode at active timer-slot. +
    • +
    • stop
      + Stops mowing immediately. The mower does not drive home. It stands there, until battery is empty. Use with care! +
    • +
    • winterschlaf <on, off>
      + If set to on, no polling is executet. Please use this during winter. +
    • +
    • user <user>
      + One alternative to store authentication: username for robonect-logon is stored in FhemUtils or database (not encrypted).
      +
    • password <password>
      + One alternative to store authentication: password for robonect-logon is stored in FhemUtils or database (not encrypted).
      +
+ +

Get

    Gets the actual state of the mower - normally not needed, because the status is polled cyclic.

    @@ -913,39 +999,64 @@ sub Robonect_getCmdList ($$$)

    Robonect

      -

      Robonect ist ein Nachrüstmodul für automower, die auf der Husky-G3-Steuerung basieren. Es wurde von Fabian H. entwickelt und kann unter www.robonect.de bezogen werden. Dieses Modul gibt Euch Zugriff auf die nötigsten Kommandos. Dieses Modul benötigt libjson-perl. Bitte NICHT VERGESSEN zu installieren!

      +

      Robonect ist ein Nachr¨stmodul für automower, die auf der Husky-G3-Steuerung basieren. Es wurde von Fabian H. entwickelt und kann unter www.robonect.de bezogen werden. Dieses Modul gibt Euch Zugriff auf die nötigsten Kommandos. Dieses Modul benötigt libjson-perl. Bitte NICHT VERGESSEN zu installieren!

      Define

        - define <name> Robonect <ip-adress> [<user> <password>] + define <name> Robonect <IP-Adresse oder Name> -

        Mit gesetztem Winterschlaf wird die Kommunikation zum Mäher unterbunden.

        +

        Mit gesetztem Winterschlaf wird die Kommunikation zum Mäher unterbunden.

        -

        Die Zugangsinformationen können im Klartext bei der Definition angegeben werden. Wahlweise auch per Attribut. Standardmäßig wird der Status vom RObonect alle 90s aktualisiert.

        +

        Die Zugangsinformationen können im Klartext bei der Definition angegeben werden. Wahlweise auch per Attribut. Standardmäßig wird der Status vom RObonect alle 90s aktualisiert.

        Beispiel:

        -      define myMower Robonect 192.168.13.5 test tmySecret
        +      define myMower Robonect 192.168.13.5
        +	  define myMower Robonect myMowersDNSName
               

      Set

        - Versetzt den Mäher in den timerbasierten Automatikmodus: - set <name> auto - Schickt den Mäher nach hause. Ein erneutes Starten per Timer wird verhindert (persistent): - set <name> home - Schickt den Mäher nach Hause. Beim nächsten Timerstart fährt der Mäher wieder regulär: - set <name> feierabend - Startet den Mäher (wird nur nach einem manuellen Stop benötigt): - set <name> start - Stoppt den Mäher: - set <name> stop + Set +
          +
        • auto
          + Dies versetzt den Mäher in den Automatikmodus. Der Mäher reagiert nur auf den internen Timer, bis eine andere Betriebsart gewählt wird. Der Mäher kann mit Stop jederzeit + angehalten werden. Es wird erst wieder begonnen zu mähen, wenn der Timer (wieder) ein aktives Fenster hat UND Start gesendet wurde. +
        • +
        • manuell
          + Dies versetzt den Mäher in den manuellen modus. Der interne Timer wird nicht beachtet. Der Mäher reagiert nur auf Start oder Stopp Befehle von FHEM. +
        • +
        • home
          + Dies schickt den Mäher direkt nach hause. Weiteres mähen wird verhindert, bis auf manuell oder auto umgeschalten wird. +
        • +
        • feierabend
          + Dies schickt den Mäher für den aktuellen Timerslot direkt nach hause. Beim nächsten aktiven Timerslot wird weitergemäht. +
        • +
        • start
          + Startet den Mähvorgang im manuellen Modus oder im Automatikmodus bei aktivem Zeitslot. +
        • +
        • stop
          + Beendet den Mähvorgang. Der Mäher fährt nicht nach Hause und beginnt nicht wieder zu mähen. Er bleibt stehen, bis die Batterie leer ist. Nur mit Bedacht benutzen! +
        • +
        • winterschlaf <on, off>
          + Wenn aktiviert, wird das Pollen unterbunden. Empfiehlt sich für die Winterpause. +
        • +
        • user <user>
          + Alternativ zur Angabe per Argument kann per Set-Befehl der Benutzername zur Anmeldung am Robonect hier einmalig eingegeben werden. Er wird im Klartext in FhemUtils oder der DB gespeichert.
          + Wenn angegeben, werden die Attribute zur Authentisierung ignoriert. +
        • +
        • password <password>
          + Alternativ zur Angabe per Argument kann per Set-Befehl das Passwort zur Anmeldung am Robonect hier einmalig eingegeben werden. Er wird im Klartext in FhemUtils oder der DB gespeichert.
          + Wenn angegeben, werden die Attribute zur Authentisierung ignoriert. +
        • +
      +

      Get

        -

        Holt den aktuellen Status des Mähers. Wird normalerweise nicht benötigt, da automatisch gepolled wird.

        +

        Holt den aktuellen Status des Mähers. Wird normalerweise nicht benötigt, da automatisch gepolled wird.

      Attributes