diff --git a/fhem/MAINTAINER.txt b/fhem/MAINTAINER.txt
index 944d046bc..55b01fa37 100644
--- a/fhem/MAINTAINER.txt
+++ b/fhem/MAINTAINER.txt
@@ -601,8 +601,9 @@ FHEM/lib/SWAP/* justme1968 Sonstige Systeme
FHEM/lib/UPnP/* Reinerlein Multimedia
lib/FHEM/Core/Timer/Helper.pm sidey79 FHEM Development
-lib/FHEM/SynoModules/API.pm DS_Starter Sonstiges
-lib/FHEM/SynoModules/SMUtils.pm DS_Starter Sonstiges
+lib/FHEM/SynoModules/API.pm DS_Starter Sonstiges
+lib/FHEM/SynoModules/SMUtils.pm DS_Starter Sonstiges
+lib/FHEM/SynoModules/ErrCodes.pm DS_Starter Sonstiges
contrib/sacha_gloor/* rudolfkoenig Sonstiges
contrib/70_ONKYO_AVR_PULL.pm loredo (deprecated)
diff --git a/fhem/lib/FHEM/SynoModules/ErrCodes.pm b/fhem/lib/FHEM/SynoModules/ErrCodes.pm
new file mode 100644
index 000000000..e9f4d1908
--- /dev/null
+++ b/fhem/lib/FHEM/SynoModules/ErrCodes.pm
@@ -0,0 +1,243 @@
+########################################################################################################################
+# $Id$
+#########################################################################################################################
+# ErrCodes.pm
+#
+# (c) 2020 by Heiko Maaz
+# e-mail: Heiko dot Maaz at t-online dot de
+#
+# This Module provides Synology API Error Codes.
+#
+# This script is part of fhem.
+#
+# Fhem is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# Fhem is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with fhem. If not, see .
+#
+#########################################################################################################################
+
+package FHEM::SynoModules::ErrCodes;
+
+use strict;
+use warnings;
+use utf8;
+use Carp qw(croak carp);
+
+use version; our $VERSION = version->declare('1.0.0');
+
+use Exporter ('import');
+our @EXPORT_OK = qw(expErrorsAuth expErrors);
+our %EXPORT_TAGS = (all => [@EXPORT_OK]);
+
+my %hterr = ( # Hash der TYPE Error Code Spezifikationen
+ SSCam => {fnerrauth => "_errauthsscam", fnerr => "_errsscam" },
+ SSCal => {fnerrauth => "_errauthsscal", fnerr => "_errsscal" },
+);
+
+# Standard Rückgabewert wenn keine Message zum Error Code gefunden wurde
+my $nofound = qq{Message not found for error code:};
+
+##############################################################################
+# Error Code Hashes
+##############################################################################
+## SSCam ##
+my %errauthsscam = ( # Authentification Error Codes der Surveillance Station API
+ 100 => "Unknown error",
+ 101 => "The account parameter is not specified",
+ 102 => "API does not exist",
+ 400 => "Invalid user or password",
+ 401 => "Guest or disabled account",
+ 402 => "Permission denied - DSM-Session: make sure user is member of Admin-group, SVS-Session: make sure SVS package is started, make sure FHEM-Server IP won't be blocked in DSM automated blocking list",
+ 403 => "One time password not specified",
+ 404 => "One time password authenticate failed",
+ 405 => "method not allowd - maybe the password is too long",
+ 406 => "OTP code enforced",
+ 407 => "Max Tries (if auto blocking is set to true) - make sure FHEM-Server IP won't be blocked in DSM automated blocking list",
+ 408 => "Password Expired Can not Change",
+ 409 => "Password Expired",
+ 410 => "Password must change (when first time use or after reset password by admin)",
+ 411 => "Account Locked (when account max try exceed)",
+);
+
+my %errsscam = ( # Standard Error Codes der Surveillance Station API
+ 100 => "Unknown error",
+ 101 => "Invalid parameters",
+ 102 => "API does not exist",
+ 103 => "Method does not exist",
+ 104 => "This API version is not supported",
+ 105 => "Insufficient user privilege",
+ 106 => "Connection time out",
+ 107 => "Multiple login detected",
+ 117 => "need manager rights in SurveillanceStation for operation",
+ 400 => "Execution failed",
+ 401 => "Parameter invalid",
+ 402 => "Camera disabled",
+ 403 => "Insufficient license",
+ 404 => "Codec activation failed",
+ 405 => "CMS server connection failed",
+ 407 => "CMS closed",
+ 410 => "Service is not enabled",
+ 412 => "Need to add license",
+ 413 => "Reach the maximum of platform",
+ 414 => "Some events not exist",
+ 415 => "message connect failed",
+ 417 => "Test Connection Error",
+ 418 => "Object is not exist",
+ 419 => "Visualstation name repetition",
+ 439 => "Too many items selected",
+ 502 => "Camera disconnected",
+ 600 => "Presetname and PresetID not found in Hash",
+);
+
+## SSCal ##
+my %errauthsscal = ( # Authentification Error Codes der Calendar API
+ 400 => "No such account or the password is incorrect",
+ 401 => "Account disabled",
+ 402 => "Permission denied",
+ 403 => "2-step verification code required",
+ 404 => "Failed to authenticate 2-step verification code",
+);
+
+my %errsscal = ( # Standard Error Codes der Calendar API
+ 100 => "Unknown error",
+ 101 => "No parameter of API, method or version",
+ 102 => "The requested API does not exist - may be the Synology Calendar package is stopped",
+ 103 => "The requested method does not exist",
+ 104 => "The requested version does not support the functionality",
+ 105 => "The logged in session does not have permission",
+ 106 => "Session timeout",
+ 107 => "Session interrupted by duplicate login",
+ 114 => "Missing required parameters",
+ 117 => "Unknown internal error",
+ 119 => "session id not valid",
+ 120 => "Invalid parameter",
+ 160 => "Insufficient application privilege",
+ 400 => "Invalid parameter of file operation",
+ 401 => "Unknown error of file operation",
+ 402 => "System is too busy",
+ 403 => "The user does not have permission to execute this operation",
+ 404 => "The group does not have permission to execute this operation",
+ 405 => "The user/group does not have permission to execute this operation",
+ 406 => "Cannot obtain user/group information from the account server",
+ 407 => "Operation not permitted",
+ 408 => "No such file or directory",
+ 409 => "File system not supported",
+ 410 => "Failed to connect internet-based file system (ex: CIFS)",
+ 411 => "Read-only file system",
+ 412 => "Filename too long in the non-encrypted file system",
+ 413 => "Filename too long in the encrypted file system",
+ 414 => "File already exists",
+ 415 => "Disk quota exceeded",
+ 416 => "No space left on device",
+ 417 => "Input/output error",
+ 418 => "Illegal name or path",
+ 419 => "Illegal file name",
+ 420 => "Illegal file name on FAT file system",
+ 421 => "Device or resource busy",
+ 599 => "No such task of the file operation",
+ 800 => "malformed or unsupported URL",
+ 805 => "empty API data received - may be the Synology cal Server package is stopped",
+ 806 => "couldn't get Synology cal API information",
+ 810 => "The credentials couldn't be retrieved",
+ 900 => "malformed JSON string received from Synology Calendar Server",
+ 910 => "Wrong timestamp definition. Check attributes \"cutOlderDays\", \"cutLaterDays\". ",
+);
+
+##############################################################################
+# Auflösung Errorcodes bei Login / Logout
+##############################################################################
+sub expErrorsAuth {
+ my $hash = shift // carp "got no hash value !" && return;
+ my $errorcode = shift // carp "got no error code to analyse" && return;
+ my $type = $hash->{TYPE};
+
+ no strict "refs"; ## no critic 'NoStrict'
+ if($hterr{$type} && defined &{$hterr{$type}{fnerrauth}}) {
+ my $error = &{$hterr{$type}{fnerrauth}} ($errorcode);
+ return $error;
+ }
+ use strict "refs";
+
+ carp qq{No resolution function of authentication errors for module type "$type" defined};
+
+return q{};
+}
+
+##############################################################################
+# Auflösung Standard Errorcodes
+##############################################################################
+sub expErrors {
+ my $hash = shift // carp "got no hash value !" && return;
+ my $errorcode = shift // carp "got no error code to analyse" && return;
+ my $type = $hash->{TYPE};
+
+ no strict "refs"; ## no critic 'NoStrict'
+ if($hterr{$type} && defined &{$hterr{$type}{fnerr}}) {
+ my $error = &{$hterr{$type}{fnerr}} ($errorcode);
+ return $error;
+ }
+ use strict "refs";
+
+ carp qq{No resolution function of authentication errors for module type "$type" defined};
+
+return q{};
+}
+
+##############################################################################
+# Liefert Fehlertext für einen
+# Authentification Error Code der Surveillance Station API
+##############################################################################
+sub _errauthsscam { ## no critic "not used"
+ my $errorcode = shift;
+
+ my $error = $errauthsscam{"$errorcode"} // $nofound." ".$errorcode;
+
+return $error;
+}
+
+##############################################################################
+# Liefert Fehlertext für einen
+# Standard Error Code der Surveillance Station API
+##############################################################################
+sub _errsscam { ## no critic "not used"
+ my $errorcode = shift;
+
+ my $error = $errsscam{"$errorcode"} // $nofound." ".$errorcode;
+
+return $error;
+}
+
+##############################################################################
+# Liefert Fehlertext für einen
+# Authentification Error Code der Calendar API
+##############################################################################
+sub _errauthsscal { ## no critic "not used"
+ my $errorcode = shift;
+
+ my $error = $errauthsscal{"$errorcode"} // $nofound." ".$errorcode;
+
+return $error;
+}
+
+##############################################################################
+# Liefert Fehlertext für einen
+# Standard Error Code der Calendar API
+##############################################################################
+sub _errsscal { ## no critic "not used"
+ my $errorcode = shift;
+
+ my $error = $errsscal{"$errorcode"} // $nofound." ".$errorcode;
+
+return $error;
+}
+
+1;
\ No newline at end of file
diff --git a/fhem/lib/FHEM/SynoModules/SMUtils.pm b/fhem/lib/FHEM/SynoModules/SMUtils.pm
index 52d731f55..0196decfb 100644
--- a/fhem/lib/FHEM/SynoModules/SMUtils.pm
+++ b/fhem/lib/FHEM/SynoModules/SMUtils.pm
@@ -32,24 +32,31 @@ use warnings;
use utf8;
use MIME::Base64;
eval "use JSON;1;" or my $nojsonmod = 1; ## no critic 'eval'
+use Data::Dumper;
-# use lib qw(/opt/fhem/FHEM); # für Syntaxcheck mit: perl -c /opt/fhem/lib/FHEM/SynoModules/SMUtils.pm
+# use lib qw(/opt/fhem/FHEM /opt/fhem/lib); # für Syntaxcheck mit: perl -c /opt/fhem/lib/FHEM/SynoModules/SMUtils.pm
+
+use FHEM::SynoModules::ErrCodes qw(:all); # Error Code Modul
use GPUtils qw( GP_Import GP_Export );
use Carp qw(croak carp);
-use version; our $VERSION = version->declare('1.2.0');
+use version; our $VERSION = version->declare('1.3.0');
use Exporter ('import');
-our @EXPORT_OK = qw(
- getClHash
- trim
- sortVersion
- setVersionInfo
- jboolmap
- setCredentials
- getCredentials
- evaljson
- );
+our @EXPORT_OK = qw(
+ getClHash
+ trim
+ sortVersion
+ setVersionInfo
+ jboolmap
+ setCredentials
+ getCredentials
+ evaljson
+ login
+ logout
+ setActiveToken
+ delActiveToken
+ );
our %EXPORT_TAGS = (all => [@EXPORT_OK]);
@@ -62,12 +69,15 @@ BEGIN {
Log3
defs
modules
+ CancelDelayedShutdown
devspec2array
setKeyValue
getKeyValue
+ readingsSingleUpdate
readingsBeginUpdate
readingsBulkUpdate
readingsEndUpdate
+ HttpUtils_NonblockingGet
)
);
};
@@ -77,7 +87,7 @@ BEGIN {
# Identifikation ob über FHEMWEB ausgelöst oder nicht -> erstellen $hash->CL
###############################################################################
sub getClHash {
- my $hash = shift // carp "got no hash value !" && return;
+ my $hash = shift // carp "got no hash value" && return;
my $nobgd = shift;
my $name = $hash->{NAME};
my $ret;
@@ -159,8 +169,8 @@ return @sorted;
# Die Verwendung von Meta.pm und Packages wird berücksichtigt
#############################################################################################
sub setVersionInfo {
- my $hash = shift // carp "got no hash value !" && return;
- my $notes = shift // carp "got no vNotesIntern value !" && return;
+ my $hash = shift // carp "got no hash value" && return;
+ my $notes = shift // carp "got no vNotesIntern value" && return;
my $name = $hash->{NAME};
my $v = (sortVersion("desc",keys %{$notes}))[0];
@@ -196,7 +206,7 @@ return;
# JSON Boolean Test und Mapping
###############################################################################
sub jboolmap {
- my $bool = shift // carp "got no value to check if bool !" && return;
+ my $bool = shift // carp "got no value to check if bool" && return;
my $is_boolean = JSON::is_bool($bool);
@@ -213,10 +223,10 @@ return $bool;
# $ao = "SMTPcredentials" -> Credentials für Mailversand
######################################################################################
sub setCredentials {
- my $hash = shift // carp "got no hash value !" && return;
- my $ao = shift // carp "got no credentials type !" && return;
- my $user = shift // carp "got no user name !" && return;
- my $pass = shift // carp "got no password !" && return;
+ my $hash = shift // carp "got no hash value" && return;
+ my $ao = shift // carp "got no credentials type" && return;
+ my $user = shift // carp "got no user name" && return;
+ my $pass = shift // carp "got no password" && return;
my $name = $hash->{NAME};
my $success;
@@ -251,9 +261,9 @@ return ($success);
# $ao = "SMTPcredentials" -> Credentials für Mailversand
######################################################################################
sub getCredentials {
- my $hash = shift // carp "got no hash value !" && return;
+ my $hash = shift // carp "got no hash value" && return;
my $boot = shift;
- my $ao = shift // carp "got no credentials type !" && return;
+ my $ao = shift // carp "got no credentials type" && return;
my $name = $hash->{NAME};
my ($success, $username, $passwd, $index, $retcode, $credstr);
my (@key,$len,$i);
@@ -322,8 +332,8 @@ return ($success, $username, $passwd);
# Test ob JSON-String vorliegt
###############################################################################
sub evaljson {
- my $hash = shift // carp "got no hash value !" && return;
- my $myjson = shift // carp "got no string for JSON test !" && return;
+ my $hash = shift // carp "got no hash value" && return;
+ my $myjson = shift // carp "got no string for JSON test" && return;
my $OpMode = $hash->{OPMODE};
my $name = $hash->{NAME};
@@ -357,4 +367,275 @@ sub evaljson {
return ($success,$myjson);
}
+####################################################################################
+# Login wenn keine oder ungültige Session-ID vorhanden ist
+# $apiref = Referenz zum API Hash
+# $fret = Rückkehrfunktion nach erfolgreichen Login
+####################################################################################
+sub login {
+ my $hash = shift // carp "got no hash value" && return;
+ my $apiref = shift // carp "got no API reference" && return;
+ my $fret = shift // carp "got no return function reference" && return;
+ my $name = $hash->{NAME};
+ my $serveraddr = $hash->{SERVERADDR};
+ my $serverport = $hash->{SERVERPORT};
+ my $apiauth = $apiref->{AUTH}{NAME};
+ my $apiauthpath = $apiref->{AUTH}{PATH};
+ my $apiauthver = $apiref->{AUTH}{VER};
+ my $proto = $hash->{PROTOCOL};
+ my $type = $hash->{TYPE};
+
+ my ($url,$param,$urlwopw);
+
+ delete $hash->{HELPER}{SID};
+
+ Log3($name, 4, "$name - --- Begin Function login ---");
+
+ my ($success, $username, $password) = getCredentials($hash,0,"credentials"); # Credentials abrufen
+
+ if (!$success) {
+ Log3($name, 2, "$name - Credentials couldn't be retrieved successfully - make sure you've set it with \"set $name credentials \"");
+ delActiveToken($hash) if($type eq "SSCam");
+ return;
+ }
+
+ my $lrt = AttrVal($name,"loginRetries",3);
+
+ if($hash->{HELPER}{LOGINRETRIES} >= $lrt) { # Max Versuche erreicht -> login wird abgebrochen, Freigabe Funktionstoken
+ delActiveToken($hash) if($type eq "SSCam");
+ Log3($name, 2, "$name - ERROR - Login or privilege of user $username unsuccessful");
+ return;
+ }
+
+ my $timeout = AttrVal($name,"timeout",60); # Kompatibilität zu Modulen die das Attr "timeout" verwenden
+ my $httptimeout = AttrVal($name,"httptimeout",$timeout);
+ $httptimeout = 60 if($httptimeout < 60);
+ Log3($name, 4, "$name - HTTP-Call login will be done with httptimeout-Value: $httptimeout s");
+
+ my $sid = AttrVal($name, "noQuotesForSID", 0) ? "sid" : qq{"sid"}; # sid in Quotes einschliessen oder nicht -> bei Problemen mit 402 - Permission denied
+
+ if (AttrVal($name,"session","DSM") eq "DSM") {
+ $url = "$proto://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthver&method=Login&account=$username&passwd=$password&format=$sid";
+ $urlwopw = "$proto://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthver&method=Login&account=$username&passwd=*****&format=$sid";
+
+ } else {
+ $url = "$proto://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthver&method=Login&account=$username&passwd=$password&session=SurveillanceStation&format=$sid";
+ $urlwopw = "$proto://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthver&method=Login&account=$username&passwd=*****&session=SurveillanceStation&format=$sid";
+ }
+
+ my $printurl = AttrVal($name, "showPassInLog", 0) ? $url : $urlwopw;
+
+ Log3($name, 4, "$name - Call-Out now: $printurl");
+ $hash->{HELPER}{LOGINRETRIES}++;
+
+ $param = {
+ url => $url,
+ timeout => $httptimeout,
+ hash => $hash,
+ user => $username,
+ funcret => $fret,
+ apiref => $apiref,
+ method => "GET",
+ header => "Accept: application/json",
+ callback => \&loginReturn
+ };
+
+ HttpUtils_NonblockingGet ($param);
+
+return;
+}
+
+sub loginReturn {
+ my $param = shift;
+ my $err = shift;
+ my $myjson = shift;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+ my $username = $param->{user};
+ my $fret = $param->{funcret};
+ my $apiref = $param->{apiref};
+ my $type = $hash->{TYPE};
+
+ my $success;
+
+ if ($err ne "") { # ein Fehler bei der HTTP Abfrage ist aufgetreten
+ Log3($name, 2, "$name - error while requesting ".$param->{url}." - $err");
+
+ readingsSingleUpdate($hash, "Error", $err, 1);
+
+ return login($hash,$apiref,$fret);
+
+ } elsif ($myjson ne "") { # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)
+ ($success) = evaljson($hash,$myjson); # Evaluiere ob Daten im JSON-Format empfangen wurden
+ if (!$success) {
+ Log3($name, 4, "$name - no JSON-Data returned: ".$myjson);
+ delActiveToken($hash) if($type eq "SSCam");
+ return;
+ }
+
+ my $data = decode_json($myjson);
+
+ Log3($name, 5, "$name - JSON decoded: ". Dumper $data);
+
+ $success = $data->{'success'};
+
+ if ($success) { # login war erfolgreich
+ my $sid = $data->{'data'}->{'sid'};
+
+ $hash->{HELPER}{SID} = $sid; # Session ID in hash eintragen
+
+ readingsBeginUpdate ($hash);
+ readingsBulkUpdate ($hash,"Errorcode","none");
+ readingsBulkUpdate ($hash,"Error","none");
+ readingsEndUpdate ($hash, 1);
+
+ Log3($name, 4, "$name - Login of User $username successful - SID: $sid");
+
+ return &$fret($hash);
+
+ } else {
+ my $errorcode = $data->{'error'}->{'code'}; # Errorcode aus JSON ermitteln
+ my $error = expErrorsAuth($hash,$errorcode); # Fehlertext zum Errorcode ermitteln
+
+ readingsBeginUpdate ($hash);
+ readingsBulkUpdate ($hash,"Errorcode",$errorcode);
+ readingsBulkUpdate ($hash,"Error",$error);
+ readingsEndUpdate ($hash, 1);
+
+ Log3($name, 3, "$name - Login of User $username unsuccessful. Code: $errorcode - $error - try again");
+
+ return login($hash,$apiref,$fret);
+ }
+ }
+
+return login($hash,$apiref,$fret);
+}
+
+###################################################################################
+# Funktion logout
+###################################################################################
+sub logout {
+ my $hash = shift // carp "got no hash value" && return;
+ my $apiref = shift // carp "got no API reference" && return;
+ my $name = $hash->{NAME};
+ my $serveraddr = $hash->{SERVERADDR};
+ my $serverport = $hash->{SERVERPORT};
+ my $apiauth = $apiref->{AUTH}{NAME};
+ my $apiauthpath = $apiref->{AUTH}{PATH};
+ my $apiauthver = $apiref->{AUTH}{VER};
+ my $sid = $hash->{HELPER}{SID};
+ my $proto = $hash->{PROTOCOL};
+
+ my $url;
+
+ Log3($name, 4, "$name - --- Start Synology logout ---");
+
+ my $httptimeout = AttrVal($name,"httptimeout",4);
+ Log3($name, 5, "$name - HTTP-Call will be done with httptimeout-Value: $httptimeout s");
+
+ if (AttrVal($name,"session","DSM") eq "DSM") {
+ $url = "$proto://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthver&method=Logout&_sid=$sid";
+
+ } else {
+ $url = "$proto://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthver&method=Logout&session=SurveillanceStation&_sid=$sid";
+ }
+
+ my $param = {
+ url => $url,
+ timeout => $httptimeout,
+ hash => $hash,
+ method => "GET",
+ header => "Accept: application/json",
+ callback => \&logoutReturn
+ };
+
+ HttpUtils_NonblockingGet ($param);
+
+return;
+}
+
+sub logoutReturn {
+ my $param = shift;
+ my $err = shift;
+ my $myjson = shift;
+ my $hash = $param->{hash};
+ my $name = $hash->{NAME};
+ my $sid = $hash->{HELPER}{SID};
+ my $type = $hash->{TYPE};
+
+ my ($success, $username) = getCredentials($hash,0,"credentials");
+
+ if ($err ne "") { # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist
+ Log3($name, 2, "$name - error while requesting ".$param->{url}." - $err");
+ readingsSingleUpdate($hash, "Error", $err, 1);
+
+ } elsif ($myjson ne "") { # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes)
+ Log3($name, 4, "$name - URL-Call: ".$param->{url});
+
+ ($success) = evaljson($hash,$myjson); # Evaluiere ob Daten im JSON-Format empfangen wurden
+
+ if (!$success) {
+ Log3($name, 4, "$name - Data returned: ".$myjson);
+ delActiveToken($hash) if($type eq "SSCam");
+ return;
+ }
+
+ my $data = decode_json($myjson);
+
+ Log3($name, 4, "$name - JSON returned: ". Dumper $data);
+
+ $success = $data->{'success'};
+
+ if ($success) { # die Logout-URL konnte erfolgreich aufgerufen werden
+ Log3($name, 2, "$name - Session of User \"$username\" terminated - session ID \"$sid\" deleted");
+
+ } else {
+ my $errorcode = $data->{'error'}->{'code'}; # Errorcode aus JSON ermitteln
+ my $error = expErrorsAuth($hash,$errorcode); # Fehlertext zum Errorcode ermitteln
+
+ Log3($name, 2, "$name - ERROR - Logout of User $username was not successful, however SID: \"$sid\" has been deleted. Errorcode: $errorcode - $error");
+ }
+ }
+
+ delete $hash->{HELPER}{SID}; # Session-ID aus Helper-hash löschen
+
+ delActiveToken($hash); # ausgeführte Funktion ist erledigt (auch wenn logout nicht erfolgreich), Freigabe Funktionstoken
+
+ CancelDelayedShutdown($name);
+
+return;
+}
+
+#############################################################################################
+# Token setzen
+#############################################################################################
+sub setActiveToken {
+ my $hash = shift // carp "got no hash value" && return;
+ my $name = $hash->{NAME};
+
+ $hash->{HELPER}{ACTIVE} = "on";
+
+ if (AttrVal($name,"debugactivetoken",0)) {
+ Log3($name, 1, "$name - Active-Token set by OPMODE: $hash->{OPMODE}");
+ }
+
+return;
+}
+
+#############################################################################################
+# Token freigeben
+#############################################################################################
+sub delActiveToken {
+ my $hash = shift // carp "got no hash value" && return;
+ my $name = $hash->{NAME};
+
+ $hash->{HELPER}{ACTIVE} = "off";
+
+ if (AttrVal($name,"debugactivetoken",0)) {
+ Log3($name, 1, "$name - Active-Token deleted by OPMODE: $hash->{OPMODE}");
+ }
+
+return;
+}
+
1;
\ No newline at end of file