########################################################################################################## # $Id$ ########################################################################################################## # 49_SSCam.pm # # written by Heiko Maaz # e-mail: Heiko dot Maaz at t-online dot de # # This Module can be used to operate Cameras defined in Synology Surveillance Station 7.0 or higher. # It's based on Synology Surveillance Station API Guide 2.0 # # This file 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 .# # ########################################################################################################## # Versions History: # # 1.7 18.01.2016 Attribute "httptimeout" added # 1.6 16.01.2016 Change the define-string related to rectime. # Note: See all changes to rectime usage in commandref or here: # http://forum.fhem.de/index.php/topic,45671.msg391664.html#msg391664 # 1.5.1 11.01.2016 Vars "USERNAME" and "RECTIME" removed from internals, # Var (Internals) "SERVERNAME" changed to "SERVERADDR", # minor change of Log messages, # Note: use rereadcfg in order to activate the changes # 1.5 04.01.2016 Function "Get" for creating Camera-Readings integrated, # Attributs pollcaminfoall, pollnologging added, # Function for Polling Cam-Infos added. # 1.4 23.12.2015 function "enable" and "disable" for SS-Cams added, # changed timout of Http-calls to a higher value # 1.3 19.12.2015 function "snap" for taking snapshots added, # fixed a bug that functions may impact each other # 1.2 14.12.2015 improve usage of verbose-modes # 1.1 13.12.2015 use of InternalTimer instead of fhem(sleep) # 1.0 12.12.2015 changed completly to HttpUtils_NonblockingGet for calling websites nonblocking, # LWP is not needed anymore # # # Definition: define SSCam # # Example: define CamCP1 SSCAM 192.168.2.20 5000 apiuser apipw Carport # package main; use JSON qw( decode_json ); # From CPAN,, Debian libjson-perl use Data::Dumper; # Perl Core module use strict; use warnings; use HttpUtils; sub SSCam_Initialize($) { # die Namen der Funktionen, die das Modul implementiert und die fhem.pl aufrufen soll my ($hash) = @_; $hash->{DefFn} = "SSCam_Define"; $hash->{UndefFn} = "SSCam_Undef"; $hash->{SetFn} = "SSCam_Set"; $hash->{GetFn} = "SSCam_Get"; $hash->{AttrFn} = "SSCam_Attr"; $hash->{AttrList} = "httptimeout ". "pollcaminfoall ". "pollnologging:1,0 ". "rectime ". "webCmd ". $readingFnAttributes; return undef; } sub SSCam_Define { # Die Define-Funktion eines Moduls wird von Fhem aufgerufen wenn der Define-Befehl für ein Gerät ausgeführt wird # Welche und wie viele Parameter akzeptiert werden ist Sache dieser Funktion. Die Werte werden nach dem übergebenen Hash in ein Array aufgeteilt # define CamCP1 SSCAM 192.168.2.20 5000 apiuser Support4me Carport # ($hash) [1] [2] [3] [4] [5] [6] # my ($hash, $def) = @_; my $name = $hash->{NAME}; my @a = split("[ \t][ \t]*", $def); if(int(@a) < 7) { return "You need to specify more parameters.\n". "Format: define SSCAM "; } my $serveraddr = $a[2]; my $serverport = $a[3]; my $username = $a[4]; my $password = $a[5]; my $camname = $a[6]; $hash->{SERVERADDR} = $serveraddr; $hash->{SERVERPORT} = $serverport; $hash->{HELPER}{USERNAME} = $username; $hash->{HELPER}{PASSWORD} = $password; $hash->{CAMNAME} = $camname; # benötigte API's in $hash einfügen $hash->{HELPER}{APIINFO} = "SYNO.API.Info"; # Info-Seite für alle API's, einzige statische Seite ! $hash->{HELPER}{APIAUTH} = "SYNO.API.Auth"; $hash->{HELPER}{APIEXTREC} = "SYNO.SurveillanceStation.ExternalRecording"; $hash->{HELPER}{APICAM} = "SYNO.SurveillanceStation.Camera"; $hash->{HELPER}{APISNAPSHOT} = "SYNO.SurveillanceStation.SnapShot"; $hash->{HELPER}{APIPTZ} = "SYNO.SurveillanceStation.PTZ"; # Anfangswerte setzen $attr{$name}{webCmd} = "on:off:snap:enable:disable"; # initiale Webkommandos setzen $hash->{HELPER}{ACTIVE} = "off"; # Funktionstoken "off", Funktionen können sofort starten $hash->{HELPER}{OLDVALPOLLNOLOGGING} = "0"; # Loggingfunktion für Polling ist an $hash->{STATE} = "initialized"; # Anfangsstatus der Geräte $hash->{HELPER}{RECTIME_DEF} = "15"; # Standard für rectime setzen, überschreibbar durch Attribut "rectime" bzw. beim "set .. on-for-time" readingsSingleUpdate($hash,"Record","Stop",0); # Recordings laufen nicht readingsSingleUpdate($hash,"Availability", "", 0); # Verfügbarkeit ist unbekannt readingsSingleUpdate($hash,"PollState","Inactive",0); # es ist keine Gerätepolling aktiv RemoveInternalTimer($hash); # alle evtl. noch laufenden Timer löschen # Subroutine Watchdog-Timer starten (sollen Cam-Infos abgerufen werden ?), verzögerter zufälliger Start 0-60s InternalTimer(gettimeofday()+int(rand(60)), "watchdogpollcaminfo", $hash, 0); return undef; } sub SSCam_Undef { my ($hash, $arg) = @_; RemoveInternalTimer($hash); return undef; } sub SSCam_Attr { my ($cmd,$name,$aName,$aVal) = @_; # $cmd can be "del" or "set" # $name is device name # aName and aVal are Attribute name and value if ($cmd eq "set") { if ($aName eq "pollcaminfoall") { unless ($aVal =~ /^\d+$/) { return " The Value for $aName is not valid. Use only figures 0-9 without decimal places !";} } if ($aName eq "rectime") { unless ($aVal =~ /^\d+$/) { return " The Value for $aName is not valid. Use only figures 0-9 without decimal places !";} } if ($aName eq "httptimeout") { unless ($aVal =~ /^[0-9]+$/) { return " The Value for $aName is not valid. Use only figures 1-9 !";} } } return undef; } sub SSCam_Set { my ($hash, @a) = @_; return "\"set X\" needs at least an argument" if ( @a < 2 ); my $name = $a[0]; my $opt = $a[1]; my $prop = $a[2]; my %SSCam_sets = ( "on" => "on", "off" => "off", "snap" => "snap:", "enable" => "enable", "disable" => "disable", # presets => "presets", ); # my $list .= "on off snap enable disable on-for-timer"; # return SetExtensions($hash, $list, $name, $opt) if( $opt eq "?" ); # return SetExtensions($hash, $list, $name, $opt) if( !grep( $_ =~ /^\Q$opt\E($|:)/, split( ' ', $list ) ) ); my $camname = $hash->{CAMNAME}; my $logstr; my @cList; # ist die angegebene Option verfügbar ? if(!defined($SSCam_sets{$opt})) { @cList = keys(%SSCam_sets); return "Unknown argument $opt, choose one of " . join(" ", @cList); } else { if ($opt eq "on") { if (defined($prop)) { unless ($prop =~ /^\d+$/) { return " The Value for \"$opt\" is not valid. Use only figures 0-9 without decimal places !";} $hash->{HELPER}{RECTIME_TEMP} = $prop; } &camstartrec($hash); } elsif ($opt eq "off") { &camstoprec($hash); } elsif ($opt eq "snap") { &camsnap($hash); } elsif ($opt eq "enable") { &camenable($hash); } elsif ($opt eq "disable") { &camdisable($hash); } } return undef; } sub SSCam_Get { my ($hash, @a) = @_; return "\"get X\" needs at least an argument" if ( @a < 2 ); my $name = shift @a; my $opt = shift @a; my %SSCam_gets = ( caminfoall => "caminfoall", ); my @cList; # ist die angegebene Option verfügbar ? if(!defined($SSCam_gets{$opt})) { @cList = keys %SSCam_gets; return "Unknown argument $opt, choose one of " . join(" ", @cList); } else { # hier die Verarbeitung starten if ($opt eq "caminfoall") { &getcaminfoall($hash); } } return undef; } sub watchdogpollcaminfo ($) { # Überwacht die Wert von Attribut "pollcaminfoall" und Reading "PollState" # wenn Attribut "pollcaminfoall" > 10 und "PollState"=Inactive -> start Polling my ($hash) = @_; my $name = $hash->{NAME}; my $camname = $hash->{CAMNAME}; my $logstr; my $watchdogtimer = 90; if (defined(AttrVal($name, "pollcaminfoall", undef)) and AttrVal($name, "pollcaminfoall", undef) > 10 and ReadingsVal("$name", "PollState", "Active") eq "Inactive") { # Polling ist jetzt aktiv readingsSingleUpdate($hash,"PollState","Active",0); $logstr = "Polling Camera $camname is currently activated - Pollinginterval: ".AttrVal($name, "pollcaminfoall", undef)."s"; &printlog($hash,$logstr,"2"); # in $hash eintragen für späteren Vergleich (Changes von pollcaminfoall) $hash->{HELPER}{OLDVALPOLL} = AttrVal($name, "pollcaminfoall", undef); # Pollingroutine aufrufen &getcaminfoall($hash); } if (defined($hash->{HELPER}{OLDVALPOLL}) and defined(AttrVal($name, "pollcaminfoall", undef)) and AttrVal($name, "pollcaminfoall", undef) > 10) { if ($hash->{HELPER}{OLDVALPOLL} != AttrVal($name, "pollcaminfoall", undef)) { $logstr = "Polling Camera $camname was changed to new Pollinginterval: ".AttrVal($name, "pollcaminfoall", undef)."s"; &printlog($hash,$logstr,"2"); $hash->{HELPER}{OLDVALPOLL} = AttrVal($name, "pollcaminfoall", undef); &getcaminfoall($hash); } } if (defined(AttrVal($name, "pollnologging", undef))) { if ($hash->{HELPER}{OLDVALPOLLNOLOGGING} ne AttrVal($name, "pollnologging", undef)) { if (AttrVal($name, "pollnologging", undef) == "1") { $logstr = "Log of Polling Camera $camname is currently deactivated"; &printlog($hash,$logstr,"2"); # in $hash eintragen für späteren Vergleich (Changes von pollnologging) $hash->{HELPER}{OLDVALPOLLNOLOGGING} = AttrVal($name, "pollnologging", undef); } else { $logstr = "Log of Polling Camera $camname is currently activated"; &printlog($hash,$logstr,"2"); # in $hash eintragen für späteren Vergleich (Changes von pollnologging) $hash->{HELPER}{OLDVALPOLLNOLOGGING} = AttrVal($name, "pollnologging", undef); } } } else { # alter Wert von "pollnologging" war 1 -> Logging war deaktiviert if ($hash->{HELPER}{OLDVALPOLLNOLOGGING} == "1") { $logstr = "Log of Polling Camera $camname is currently activated"; &printlog($hash,$logstr,"2"); # in $hash eintragen für späteren Vergleich (Changes von pollnologging) $hash->{HELPER}{OLDVALPOLLNOLOGGING} = "0"; } } InternalTimer(gettimeofday()+$watchdogtimer, "watchdogpollcaminfo", $hash, 0); return undef; } ############################################################################################################################# ######### OpMode-Startroutinen ############# ######### $hash->{HELPER}{ACTIVE} = Funktionstoken ############# ######### $hash->{HELPER}{ACTIVE} = "on" -> eine Routine läuft, Start anderer Routine erst wenn "off". ############# ######### $hash->{HELPER}{ACTIVE} = "off" -> keine andere Routine läuft, sofortiger Start möglich ############# ############################################################################################################################# ############################################################################### ### Kamera Aufnahme starten sub camstartrec ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; my $errorcode; my $error; if (ReadingsVal("$name", "Availability", "enabled") eq "disabled") { # wenn Kamera disabled ist .... $errorcode = "402"; # Fehlertext zum Errorcode ermitteln $error = &experror($hash,$errorcode); # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode",$errorcode); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); $logstr = "ERROR - Start Recording of Camera $camname can't be executed - $error" ; &printlog($hash,$logstr,"1"); return; } if ($hash->{HELPER}{ACTIVE} ne "on" and ReadingsVal("$name", "Record", "Start") ne "Start") { # Aufnahme starten $logstr = "Recording of Camera $camname will be started now"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Start"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.1, "camstartrec", $hash, 0); } } ############################################################################### ### Kamera Aufnahme stoppen sub camstoprec ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; my $errorcode; my $error; if (ReadingsVal("$name", "Availability", "enabled") eq "disabled") { # wenn Kamera disabled ist .... $errorcode = "402"; # Fehlertext zum Errorcode ermitteln $error = &experror($hash,$errorcode); # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode",$errorcode); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); $logstr = "ERROR - Stop Recording of Camera $camname can't be executed - $error" ; &printlog($hash,$logstr,"1"); return; } if ($hash->{HELPER}{ACTIVE} ne "on" and ReadingsVal("$name", "Record", "Stop") ne "Stop") { # Aufnahme stoppen $logstr = "Recording of Camera $camname will be stopped now"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Stop"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.2, "camstoprec", $hash, 0); } } ############################################################################### ### Kamera Schappschuß aufnehmen sub camsnap ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; my $errorcode; my $error; if (ReadingsVal("$name", "Availability", "enabled") eq "disabled") { # wenn Kamera disabled ist .... $errorcode = "402"; # Fehlertext zum Errorcode ermitteln $error = &experror($hash,$errorcode); # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode",$errorcode); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); $logstr = "ERROR - Snapshot of Camera $camname can't be executed - $error" ; &printlog($hash,$logstr,"1"); return; } if ($hash->{HELPER}{ACTIVE} ne "on") { # einen Schnappschuß aufnehmen $logstr = "Take Snapshot of Camera $camname"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Snap"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.3, "camsnap", $hash, 0); } } ############################################################################### ### Kamera aktivieren sub camenable ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if (ReadingsVal("$name", "Availability", "disabled") eq "enabled") {return;} # Kamera ist bereits enabled if ($hash->{HELPER}{ACTIVE} ne "on") { # eine Kamera aktivieren $logstr = "Enable Camera $camname"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Enable"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.4, "camenable", $hash, 0); } } ############################################################################### ### Kamera deaktivieren sub camdisable ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if (ReadingsVal("$name", "Availability", "enabled") eq "disabled") {return;} # Kamera ist bereits disabled if ($hash->{HELPER}{ACTIVE} ne "on" and ReadingsVal("$name", "Record", "Start") ne "Start") { # eine Kamera deaktivieren $logstr = "Disable Camera $camname"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Disable"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.4, "camdisable", $hash, 0); } } ############################################################################### ### Kamera alle Informationen abrufen (Get) bzw. Einstieg Polling sub getcaminfoall ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if ($hash->{HELPER}{ACTIVE} ne "on") { &getcaminfo($hash); &getcapabilities($hash); &getptzlistpreset($hash); &getptzlistpatrol($hash); } else { InternalTimer(gettimeofday()+0.5, "getcaminfoall", $hash, 0); } if (defined(AttrVal($name, "pollcaminfoall", undef)) and AttrVal($name, "pollcaminfoall", undef) > 10) { # Pollen wenn pollcaminfo > 10, sonst kein Polling InternalTimer(gettimeofday()+AttrVal($name, "pollcaminfoall", undef), "getcaminfoall", $hash, 0); } else { # Beenden Polling aller Caminfos readingsSingleUpdate($hash,"PollState","Inactive",0); $logstr = "Polling of Camera $camname is deactivated now"; &printlog($hash,$logstr,"2"); } } ########################################################################### ### Kamera allgemeine Informationen abrufen (Get), sub von getcaminfoall sub getcaminfo ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if ($hash->{HELPER}{ACTIVE} ne "on") { # Kamerainfos abrufen $logstr = "Retrieval Camera-Informations of $camname starts now"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Getcaminfo"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.6, "getcaminfo", $hash, 0); } } ########################################################################## ### Capabilities von Kamera abrufen (Get), sub von getcaminfoall sub getcapabilities ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if ($hash->{HELPER}{ACTIVE} ne "on") { # PTZ-ListPresets abrufen $logstr = "Retrieval Capabilities of Camera $camname starts now"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Getcapabilities"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.7, "getcapabilities", $hash, 0); } } ########################################################################## ### PTZ Presets abrufen (Get), sub von getcaminfoall sub getptzlistpreset ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if (defined(ReadingsVal("$name", "DeviceType", undef)) and ReadingsVal("$name", "DeviceType", undef) ne "PTZ") { $logstr = "Retrieval of Presets for $camname can't be executed - $camname is not a PTZ-Camera"; &printlog($hash,$logstr,"4"); return; } if ($hash->{HELPER}{ACTIVE} ne "on") { # PTZ-ListPresets abrufen $logstr = "Retrieval PTZ-ListPresets of $camname starts now"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Getptzlistpreset"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.8, "getptzlistpreset", $hash, 0); } } ########################################################################## ### PTZ Patrols abrufen (Get), sub von getcaminfoall sub getptzlistpatrol ($) { my ($hash) = @_; my $camname = $hash->{CAMNAME}; my $name = $hash->{NAME}; my $logstr; if (defined(ReadingsVal("$name", "DeviceType", undef)) and ReadingsVal("$name", "DeviceType", undef) ne "PTZ") { $logstr = "Retrieval of Patrols for $camname can't be executed - $camname is not a PTZ-Camera"; &printlog($hash,$logstr,"4"); return; } if ($hash->{HELPER}{ACTIVE} ne "on") { # PTZ-ListPatrols abrufen $logstr = "Retrieval PTZ-ListPatrols of $camname starts now"; &printlog($hash,$logstr,"4"); $hash->{OPMODE} = "Getptzlistpatrol"; $hash->{HELPER}{ACTIVE} = "on"; &getapisites_nonbl($hash); } else { InternalTimer(gettimeofday()+0.9, "getptzlistpatrol", $hash, 0); } } ############################################################################################################################# ####### Begin Kameraoperationen mit NonblockingGet (nicht blockierender HTTP-Call) ####### ####### ####### ####### Ablauflogik: ####### ####### ####### ####### ####### ####### OpMode-Startroutine ####### ####### | ####### ####### getapisites_nonbl -> login_nonbl -> getcamid_nonbl -> camop_nonbl -> camret_nonbl -> logout_nonbl ####### ####### | ####### ####### OpMode ####### ####### ####### ############################################################################################################################# sub getapisites_nonbl { my ($hash) = @_; my $serveraddr = $hash->{SERVERADDR}; my $serverport = $hash->{SERVERPORT}; my $name = $hash->{NAME}; my $apiinfo = $hash->{HELPER}{APIINFO}; # Info-Seite für alle API's, einzige statische Seite ! my $apiauth = $hash->{HELPER}{APIAUTH}; # benötigte API-Pfade für Funktionen, my $apiextrec = $hash->{HELPER}{APIEXTREC}; # in der Abfrage-Url an Parameter "&query=" my $apicam = $hash->{HELPER}{APICAM}; # mit Komma getrennt angeben my $apitakesnap = $hash->{HELPER}{APISNAPSHOT}; my $apiptz = $hash->{HELPER}{APIPTZ}; my $logstr; my $url; my $param; my $httptimeout; #### API-Pfade und MaxVersions ermitteln ##### # Logausgabe $logstr = "--- Begin Function getapisites nonblocking ---"; &printlog($hash,$logstr,"4"); $httptimeout = AttrVal($name, "httptimeout",undef) ? AttrVal($name, "httptimeout",undef) : "4"; # Logausgabe $logstr = "HTTP-Call will be done with httptimeout-Value: $httptimeout s"; &printlog($hash,$logstr,"5"); # URL zur Abfrage der Eigenschaften der API's $url = "http://$serveraddr:$serverport/webapi/query.cgi?api=$apiinfo&method=Query&version=1&query=$apiauth,$apiextrec,$apicam,$apitakesnap,$apiptz"; $param = { url => $url, timeout => $httptimeout, hash => $hash, method => "GET", header => "Accept: application/json", callback => \&login_nonbl }; # API-Sites werden abgefragt und mit Routine "login_nonbl" verarbeitet HttpUtils_NonblockingGet ($param); } #################################################################################### #### Rückkehr aus Funktion API-Pfade und MaxVersions ermitteln, #### nach erfolgreicher Verarbeitung wird login ausgeführt und $sid ermittelt sub login_nonbl ($) { my ($param, $err, $myjson) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $serveraddr = $hash->{SERVERADDR}; my $serverport = $hash->{SERVERPORT}; my $username = $hash->{HELPER}{USERNAME}; my $password = $hash->{HELPER}{PASSWORD}; my $apiauth = $hash->{HELPER}{APIAUTH}; my $apiextrec = $hash->{HELPER}{APIEXTREC}; my $apicam = $hash->{HELPER}{APICAM}; my $apitakesnap = $hash->{HELPER}{APISNAPSHOT}; my $apiptz = $hash->{HELPER}{APIPTZ}; my $data; my $logstr; my $url; my $success; my $apiauthpath; my $apiauthmaxver; my $apiextrecpath; my $apiextrecmaxver; my $apicampath; my $apicammaxver; my $apitakesnappath; my $apitakesnapmaxver; my $apiptzpath; my $apiptzmaxver; my $error; my $httptimeout; # Verarbeitung der asynchronen Rückkehrdaten aus sub "getapisites_nonbl" if ($err ne "") # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist { $logstr = "error while requesting ".$param->{url}." - $err"; &printlog($hash,$logstr,"1"); $logstr = "--- End Function getapisites nonblocking with error ---"; &printlog($hash,$logstr,"4"); readingsSingleUpdate($hash, "Error", $err, 1); # Readings erzeugen # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } elsif ($myjson ne "") # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) { $logstr = "URL-Call: ".$param->{url}; &printlog($hash,$logstr,"4"); # An dieser Stelle die Antwort parsen / verarbeiten mit $myjson # Evaluiere ob Daten im JSON-Format empfangen wurden ($hash, $success) = &evaljson($hash,$myjson,$param->{url}); unless ($success) {$logstr = "Data returned: ".$myjson; &printlog($hash,$logstr,"4"); $hash->{HELPER}{ACTIVE} = "off"; return($hash,$success)}; $data = decode_json($myjson); $success = $data->{'success'}; if ($success) { # Logausgabe decodierte JSON Daten $logstr = "JSON returned: ". Dumper $data; &printlog($hash,$logstr,"4"); # Pfad und Maxversion von "SYNO.API.Auth" ermitteln $apiauthpath = $data->{'data'}->{$apiauth}->{'path'}; # Unterstriche im Ergebnis z.B. "_______entry.cgi" eleminieren $apiauthpath =~ tr/_//d if (defined($apiauthpath)); $apiauthmaxver = $data->{'data'}->{$apiauth}->{'maxVersion'}; $logstr = defined($apiauthpath) ? "Path of $apiauth selected: $apiauthpath" : "Path of $apiauth undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); $logstr = defined($apiauthmaxver) ? "MaxVersion of $apiauth selected: $apiauthmaxver" : "MaxVersion of $apiauth undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); # Pfad und Maxversion von "SYNO.SurveillanceStation.ExternalRecording" ermitteln $apiextrecpath = $data->{'data'}->{$apiextrec}->{'path'}; # Unterstriche im Ergebnis z.B. "_______entry.cgi" eleminieren $apiextrecpath =~ tr/_//d if (defined($apiextrecpath)); $apiextrecmaxver = $data->{'data'}->{$apiextrec}->{'maxVersion'}; $logstr = defined($apiextrecpath) ? "Path of $apiextrec selected: $apiextrecpath" : "Path of $apiextrec undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); $logstr = defined($apiextrecmaxver) ? "MaxVersion of $apiextrec selected: $apiextrecmaxver" : "MaxVersion of $apiextrec undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); # Pfad und Maxversion von "SYNO.SurveillanceStation.Camera" ermitteln $apicampath = $data->{'data'}->{$apicam}->{'path'}; # Unterstriche im Ergebnis z.B. "_______entry.cgi" eleminieren $apicampath =~ tr/_//d if (defined($apicampath)); $apicammaxver = $data->{'data'}->{$apicam}->{'maxVersion'}; $logstr = defined($apicampath) ? "Path of $apicam selected: $apicampath" : "Path of $apicam undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); $logstr = defined($apiextrecmaxver) ? "MaxVersion of $apicam: $apicammaxver" : "MaxVersion of $apicam undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); # Pfad und Maxversion von "SYNO.SurveillanceStation.SnapShot" ermitteln $apitakesnappath = $data->{'data'}->{$apitakesnap}->{'path'}; # Unterstriche im Ergebnis z.B. "_______entry.cgi" eleminieren $apitakesnappath =~ tr/_//d if (defined($apitakesnappath)); $apitakesnapmaxver = $data->{'data'}->{$apitakesnap}->{'maxVersion'}; $logstr = defined($apitakesnappath) ? "Path of $apitakesnap selected: $apitakesnappath" : "Path of $apitakesnap undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); $logstr = defined($apitakesnapmaxver) ? "MaxVersion of $apitakesnap: $apitakesnapmaxver" : "MaxVersion of $apitakesnap undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); # Pfad und Maxversion von "SYNO.SurveillanceStation.PTZ" ermitteln $apiptzpath = $data->{'data'}->{$apiptz}->{'path'}; # Unterstriche im Ergebnis z.B. "_______entry.cgi" eleminieren $apiptzpath =~ tr/_//d if (defined($apiptzpath)); $apiptzmaxver = $data->{'data'}->{$apiptz}->{'maxVersion'}; $logstr = defined($apiptzpath) ? "Path of $apiptz selected: $apiptzpath" : "Path of $apiptz undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); $logstr = defined($apiptzmaxver) ? "MaxVersion of $apiptz: $apiptzmaxver" : "MaxVersion of $apiptz undefined - Surveillance Station may be stopped"; &printlog($hash, $logstr,"4"); # ermittelte Werte in $hash einfügen $hash->{HELPER}{APIAUTHPATH} = $apiauthpath; $hash->{HELPER}{APIAUTHMAXVER} = $apiauthmaxver; $hash->{HELPER}{APIEXTRECPATH} = $apiextrecpath; $hash->{HELPER}{APIEXTRECMAXVER} = $apiextrecmaxver; $hash->{HELPER}{APICAMPATH} = $apicampath; $hash->{HELPER}{APICAMMAXVER} = $apicammaxver; $hash->{HELPER}{APITAKESNAPPATH} = $apitakesnappath; $hash->{HELPER}{APITAKESNAPMAXVER} = $apitakesnapmaxver; $hash->{HELPER}{APIPTZPATH} = $apiptzpath; $hash->{HELPER}{APIPTZMAXVER} = $apiptzmaxver; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash,1); # Logausgabe $logstr = "--- End Function getapisites nonblocking ---"; &printlog($hash,$logstr,"4"); } else { # Fehlertext setzen $error = "couldn't call API-Infosite"; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "ERROR - the API-Query couldn't be executed successfully"; &printlog($hash,$logstr,"1"); $logstr = "--- End Function getapisites nonblocking with error ---"; &printlog($hash,$logstr,"4"); # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } } # Login und SID ermitteln # Logausgabe $logstr = "--- Begin Function serverlogin nonblocking ---"; &printlog($hash,$logstr,"4"); $httptimeout = AttrVal($name, "httptimeout",undef) ? AttrVal($name, "httptimeout",undef) : "4"; # Logausgabe $logstr = "HTTP-Call will be done with httptimeout-Value: $httptimeout s"; &printlog($hash,$logstr,"5"); $url = "http://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthmaxver&method=Login&account=$username&passwd=$password&session=SurveillanceStation&format=\"sid\""; $param = { url => $url, timeout => $httptimeout, hash => $hash, method => "GET", header => "Accept: application/json", callback => \&getcamid_nonbl }; # login wird ausgeführt, $sid ermittelt und mit Routine "getcamid_nonbl" verarbeitet HttpUtils_NonblockingGet ($param); } ############################################################################### #### Rückkehr aus Funktion login und $sid ermitteln, #### nach erfolgreicher Verarbeitung wird Kamera-ID ermittelt sub getcamid_nonbl ($) { my ($param, $err, $myjson) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $serveraddr = $hash->{SERVERADDR}; my $serverport = $hash->{SERVERPORT}; my $username = $hash->{HELPER}{USERNAME}; my $apicam = $hash->{HELPER}{APICAM}; my $apicampath = $hash->{HELPER}{APICAMPATH}; my $apicammaxver = $hash->{HELPER}{APICAMMAXVER}; my $url; my $data; my $logstr; my $success; my $sid; my $error; my $errorcode; my $httptimeout; # Verarbeitung der asynchronen Rückkehrdaten aus sub "login_nonbl" if ($err ne "") # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist { $logstr = "error while requesting ".$param->{url}." - $err"; &printlog($hash,$logstr,"1"); # Eintrag fürs Log $logstr = "--- End Function serverlogin nonblocking with error ---"; &printlog($hash,$logstr,"4"); readingsSingleUpdate($hash, "Error", $err, 1); # Readings erzeugen # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } elsif ($myjson ne "") # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) { $logstr = "URL-Call: ".$param->{url}; # Eintrag fürs Log &printlog($hash,$logstr,"4"); # An dieser Stelle die Antwort parsen / verarbeiten mit $myjson # Evaluiere ob Daten im JSON-Format empfangen wurden ($hash, $success) = &evaljson($hash,$myjson,$param->{url}); unless ($success) {$logstr = "Data returned: ".$myjson; &printlog($hash,$logstr,"4"); $hash->{HELPER}{ACTIVE} = "off"; return($hash,$success)}; $data = decode_json($myjson); $success = $data->{'success'}; # Fall login war erfolgreich if ($success) { # Logausgabe decodierte JSON Daten $logstr = "JSON returned: ". Dumper $data; &printlog($hash,$logstr,"4"); $sid = $data->{'data'}->{'sid'}; # Session ID in hash eintragen $hash->{HELPER}{SID} = $sid; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "Login of User $username successful - SID: $sid"; &printlog($hash,$logstr,"4"); $logstr = "--- End Function serverlogin nonblocking ---"; &printlog($hash,$logstr,"4"); } else { # Errorcode aus JSON ermitteln $errorcode = $data->{'error'}->{'code'}; # Fehlertext zum Errorcode ermitteln $error = &experrorauth($hash,$errorcode); # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode",$errorcode); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "ERROR - Login of User $username unsuccessful. Errorcode: $errorcode - $error"; &printlog($hash,$logstr,"1"); $logstr = "--- End Function serverlogin nonblocking with error ---"; &printlog($hash,$logstr,"4"); # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } } # die Kamera-Id wird aus dem Kameranamen (Surveillance Station) ermittelt und mit Routine "camop_nonbl" verarbeitet # Logausgabe $logstr = "--- Begin Function getcamid nonblocking ---"; &printlog($hash,$logstr,"4"); $httptimeout = AttrVal($name, "httptimeout",undef) ? AttrVal($name, "httptimeout",undef) : "4"; # Logausgabe $logstr = "HTTP-Call will be done with httptimeout-Value: $httptimeout s"; &printlog($hash,$logstr,"5"); # einlesen aller Kameras - Auswertung in Rückkehrfunktion "camop_nonbl" # $apicammaxver um 1 verringern - Issue in API ! if (defined $apicammaxver) {$apicammaxver -= 1}; $url = "http://$serveraddr:$serverport/webapi/$apicampath?api=$apicam&version=$apicammaxver&method=List&_sid=\"$sid\""; $param = { url => $url, timeout => $httptimeout, hash => $hash, method => "GET", header => "Accept: application/json", callback => \&camop_nonbl }; HttpUtils_NonblockingGet ($param); } ############################################################################################# #### Rückkehr aus Funktion Kamera-ID ermitteln (getcamid_nonbl), #### nach erfolgreicher Verarbeitung wird Kameraoperation entspr. "OpMode" ausgeführt sub camop_nonbl ($) { my ($param, $err, $myjson) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $serveraddr = $hash->{SERVERADDR}; my $serverport = $hash->{SERVERPORT}; my $camname = $hash->{CAMNAME}; my $username = $hash->{HELPER}{USERNAME}; my $apicam = $hash->{HELPER}{APICAM}; my $apicampath = $hash->{HELPER}{APICAMPATH}; my $apicammaxver = $hash->{HELPER}{APICAMMAXVER}; my $apiextrec = $hash->{HELPER}{APIEXTREC}; my $apiextrecpath = $hash->{HELPER}{APIEXTRECPATH}; my $apiextrecmaxver = $hash->{HELPER}{APIEXTRECMAXVER}; my $apitakesnap = $hash->{HELPER}{APISNAPSHOT}; my $apitakesnappath = $hash->{HELPER}{APITAKESNAPPATH}; my $apitakesnapmaxver = $hash->{HELPER}{APITAKESNAPMAXVER}; my $apiptz = $hash->{HELPER}{APIPTZ}; my $apiptzpath = $hash->{HELPER}{APIPTZPATH}; my $apiptzmaxver = $hash->{HELPER}{APIPTZMAXVER}; my $sid = $hash->{HELPER}{SID}; my $OpMode = $hash->{OPMODE}; my $url; my $camid; my $data; my $logstr; my $success; my $error; my $errorcode; my $camcount; my $i; my %allcams; my $n; my $id; my $httptimeout; # Verarbeitung der asynchronen Rückkehrdaten aus sub "getcamid_nonbl" if ($err ne "") # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist { $logstr = "error while requesting ".$param->{url}." - $err"; &printlog($hash,$logstr,"1"); # Eintrag fürs Log $logstr = "--- End Function getcamid nonblocking with error ---"; &printlog($hash,$logstr,"4"); readingsSingleUpdate($hash, "Error", $err, 1); # Readings erzeugen # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } elsif ($myjson ne "") # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) { $logstr = "URL-Call: ".$param->{url}; &printlog($hash,$logstr,"4"); # Evaluiere ob Daten im JSON-Format empfangen wurden, Achtung: sehr viele Daten mit verbose=5 ($hash, $success) = &evaljson($hash,$myjson,$param->{url}); unless ($success) {$logstr = "Data returned: ".$myjson; &printlog($hash,$logstr,"4"); $hash->{HELPER}{ACTIVE} = "off"; return($hash,$success)}; $data = decode_json($myjson); $success = $data->{'success'}; if ($success) # die Liste aller Kameras konnte ausgelesen werden, Anzahl der definierten Kameras ist in Var "total" { # lesbare Ausgabe der decodierten JSON-Daten $logstr = "JSON returned: ". Dumper $data; # Achtung: SEHR viele Daten ! &printlog($hash,$logstr,"5"); $camcount = $data->{'data'}->{'total'}; $i = 0; # Namen aller installierten Kameras mit Id's in Assoziatives Array einlesen %allcams = (); while ($i < $camcount) { $n = $data->{'data'}->{'cameras'}->[$i]->{'name'}; $id = $data->{'data'}->{'cameras'}->[$i]->{'id'}; $allcams{"$n"} = "$id"; $i += 1; } # Ist der gesuchte Kameraname im Hash enhalten (in SS eingerichtet ?) if (exists($allcams{$camname})) { $camid = $allcams{$camname}; # in hash eintragen $hash->{CAMID} = $camid; # Logausgabe $logstr = "Detection Camid successful - $camname ID: $camid"; &printlog($hash,$logstr,"4"); $logstr = "--- End Function getcamid nonblocking ---"; &printlog($hash,$logstr,"4"); } else { # Kameraname nicht gefunden, id = "" # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","Camera(ID) not found in Surveillance Station"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "ERROR - Cameraname $camname wasn't found in Surveillance Station. Check Userrights ($username), Cameraname and Spelling."; &printlog($hash,$logstr,"1"); $logstr = "--- End Function getcamid nonblocking with error ---"; &printlog($hash,$logstr,"4"); # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } } else { # Errorcode aus JSON ermitteln $errorcode = $data->{'error'}->{'code'}; # Fehlertext zum Errorcode ermitteln $error = &experror($hash,$errorcode); # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode",$errorcode); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "ERROR - ID of Camera $camname couldn't be selected. Errorcode: $errorcode - $error"; &printlog($hash,$logstr,"1"); $logstr = "--- End Function getcamid nonblocking with error ---"; &printlog($hash,$logstr,"4"); # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } # Logausgabe $logstr = "--- Begin Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); $httptimeout = AttrVal($name, "httptimeout",undef) ? AttrVal($name, "httptimeout",undef) : "4"; # Logausgabe $logstr = "HTTP-Call will be done with httptimeout-Value: $httptimeout s"; &printlog($hash,$logstr,"5"); if ($OpMode eq "Start") { # die Aufnahme wird gestartet, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apiextrecpath?api=$apiextrec&method=Record&version=$apiextrecmaxver&cameraId=$camid&action=start&_sid=\"$sid\""; } elsif ($OpMode eq "Stop") { # die Aufnahme wird gestoppt, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apiextrecpath?api=$apiextrec&method=Record&version=$apiextrecmaxver&cameraId=$camid&action=stop&_sid=\"$sid\""; } elsif ($OpMode eq "Snap") { # ein Schnappschuß wird ausgelöst und in SS gespeichert, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apitakesnappath?api=\"$apitakesnap\"&dsId=0&method=\"TakeSnapshot\"&version=\"$apitakesnapmaxver\"&camId=$camid&blSave=true&_sid=\"$sid\""; $hash->{STATE} = "snap"; readingsSingleUpdate($hash, "LastSnapId", "", 1); } elsif ($OpMode eq "Enable") { # eine Kamera wird aktiviert, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apicampath?api=$apicam&version=$apicammaxver&method=Enable&cameraIds=$camid&_sid=\"$sid\""; } elsif ($OpMode eq "Disable") { # eine Kamera wird aktiviert, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apicampath?api=$apicam&version=$apicammaxver&method=Disable&cameraIds=$camid&_sid=\"$sid\""; } elsif ($OpMode eq "Getcaminfo") { # Infos einer Kamera werden abgerufen, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apicampath?api=\"$apicam\"&version=\"$apicammaxver\"&method=\"GetInfo\"&cameraIds=\"$camid\"&deviceOutCap=true&streamInfo=true&ptz=true&basic=true&camAppInfo=true&optimize=true&fisheye=true&eventDetection=true&_sid=\"$sid\""; } elsif ($OpMode eq "Getptzlistpreset") { # PTZ-ListPresets werden abgerufen, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apiptzpath?api=$apiptz&version=$apiptzmaxver&method=ListPreset&cameraId=$camid&_sid=\"$sid\""; } elsif ($OpMode eq "Getcapabilities") { # Capabilities einer Cam werden abgerufen, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apicampath?api=$apicam&version=$apicammaxver&method=GetCapabilityByCamId&cameraId=$camid&_sid=\"$sid\""; } elsif ($OpMode eq "Getptzlistpatrol") { # PTZ-ListPatrol werden abgerufen, Rückkehr wird mit "camret_nonbl" verarbeitet $url = "http://$serveraddr:$serverport/webapi/$apiptzpath?api=$apiptz&version=$apiptzmaxver&method=ListPatrol&cameraId=$camid&_sid=\"$sid\""; } $param = { url => $url, timeout => $httptimeout, hash => $hash, method => "GET", header => "Accept: application/json", callback => \&camret_nonbl }; HttpUtils_NonblockingGet ($param); } } ################################################################################### #### Rückkehr aus Funktion camop_nonbl, #### Check ob Kameraoperation erfolgreich wie in "OpMOde" definiert #### danach Verarbeitung Nutzdaten und weiter zum Logout sub camret_nonbl ($) { my ($param, $err, $myjson) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; my $serveraddr = $hash->{SERVERADDR}; my $serverport = $hash->{SERVERPORT}; my $camname = $hash->{CAMNAME}; my $apiauth = $hash->{HELPER}{APIAUTH}; my $apiauthpath = $hash->{HELPER}{APIAUTHPATH}; my $apiauthmaxver = $hash->{HELPER}{APIAUTHMAXVER}; my $sid = $hash->{HELPER}{SID}; my $OpMode = $hash->{OPMODE}; my $rectime; my $url; my $data; my $logstr; my $success; my ($error,$errorcode); my $snapid; my $camLiveMode; my $update_time; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst); my $deviceType; my $camStatus; my ($presetcnt,$cnt,%allpresets,$presid,$presname,@preskeys,$presetlist); my $verbose; my $httptimeout; # Die Aufnahmezeit setzen # wird "set on-for-timer [rectime]" verwendet -> dann [rectime] nutzen, # sonst Attribut "rectime" wenn es gesetzt ist, falls nicht -> "RECTIME_DEF" if (defined($hash->{HELPER}{RECTIME_TEMP})) { $rectime = delete $hash->{HELPER}{RECTIME_TEMP}; } else { $rectime = AttrVal($name, "rectime",undef) ? AttrVal($name, "rectime",undef) : $hash->{HELPER}{RECTIME_DEF}; } # Verarbeitung der asynchronen Rückkehrdaten aus sub "camop_nonbl" if ($err ne "") # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist { $logstr = "error while requesting ".$param->{url}." - $err"; &printlog($hash,$logstr,"1"); # Eintrag fürs Log $logstr = "--- End Function cam: $OpMode nonblocking with error ---"; &printlog($hash,$logstr,"4"); readingsSingleUpdate($hash, "Error", $err, 1); # Readings erzeugen # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } elsif ($myjson ne "") # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) { $logstr = "URL-Call: ".$param->{url}; &printlog($hash,$logstr,"4"); # An dieser Stelle die Antwort parsen / verarbeiten mit $myjson # Evaluiere ob Daten im JSON-Format empfangen wurden ($hash, $success) = &evaljson($hash,$myjson,$param->{url}); unless ($success) {$logstr = "Data returned: ".$myjson; &printlog($hash,$logstr,"4"); $hash->{HELPER}{ACTIVE} = "off"; return($hash,$success)}; $data = decode_json($myjson); $success = $data->{'success'}; if ($success) { # Kameraoperation entsprechend "OpMode" war erfolgreich # Logausgabe decodierte JSON Daten $logstr = "JSON returned: ". Dumper $data; &printlog($hash,$logstr,"4"); if ($OpMode eq "Start") { # bedingt Browseraktualisierung und Status der "Lampen" $hash->{STATE} = "on"; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Record","Start"); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = $rectime != "0" ? "Camera $camname Recording with Recordtime $rectime"."s started" : "Camera $camname endless Recording started - stop it manually or by stop-command !"; &printlog($hash,$logstr,"2"); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); # Logausgabe $logstr = "Time for Recording is set to: $rectime"; &printlog($hash,$logstr,"4"); if ($rectime != "0") { # Stop der Aufnahme nach Ablauf $rectime, wenn rectime = 0 -> endlose Aufnahme InternalTimer(gettimeofday()+$rectime, "camstoprec", $hash, 0); } } elsif ($OpMode eq "Stop") { # bedingt Browseraktualisierung und Status der "Lampen" $hash->{STATE} = "off"; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Record","Stop"); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # RemoveInternalTimer($hash); # Logausgabe $logstr = "Camera $camname Recording stopped"; &printlog($hash,$logstr,"2"); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Snap") { # ein Schnapschuß wurde aufgenommen # falls Aufnahme noch läuft -> STATE = on setzen if (ReadingsVal("$name", "Record", "Stop") eq "Start") { $hash->{STATE} = "on"; } else { $hash->{STATE} = "off"; } $snapid = $data->{data}{'id'}; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsBulkUpdate($hash,"LastSnapId",$snapid); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "Snapshot of Camera $camname has been done successfully"; &printlog($hash,$logstr,"2"); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Enable") { # Kamera wurde aktiviert, sonst kann nichts laufen -> "off" $hash->{STATE} = "off"; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Availability","enabled"); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "Camera $camname has been enabled successfully"; &printlog($hash,$logstr,"2"); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Disable") { # Kamera wurde deaktiviert $hash->{STATE} = "disabled"; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Availability","disabled"); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "Camera $camname has been disabled successfully"; &printlog($hash,$logstr,"2"); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Getcaminfo") { # Parse Caminfos $camLiveMode = $data->{'data'}->{'cameras'}->[0]->{'camLiveMode'}; if ($camLiveMode eq "0") {$camLiveMode = "Liveview from DS";}elsif ($camLiveMode eq "1") {$camLiveMode = "Liveview from Camera";} $update_time = $data->{'data'}->{'cameras'}->[0]->{'update_time'}; ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($update_time); $update_time = sprintf "%02d.%02d.%04d / %02d:%02d:%02d" , $mday , $mon+=1 ,$year+=1900 , $hour , $min , $sec ; $deviceType = $data->{'data'}->{'cameras'}->[0]->{'deviceType'}; if ($deviceType eq "1") { $deviceType = "Camera"; } elsif ($deviceType eq "2") { $deviceType = "Video_Server"; } elsif ($deviceType eq "4") { $deviceType = "PTZ"; } elsif ($deviceType eq "8") { $deviceType = "Fisheye"; } $camStatus = $data->{'data'}->{'cameras'}->[0]->{'camStatus'}; if ($camStatus eq "1") { $camStatus = "enabled"; # falls Aufnahme noch läuft -> STATE = on setzen if (ReadingsVal("$name", "Record", "Stop") eq "Start") { $hash->{STATE} = "on"; } else { $hash->{STATE} = "off"; } } elsif ($camStatus eq "3") { $camStatus = "disconnected"; } elsif ($camStatus eq "7") { $camStatus = "disabled"; $hash->{STATE} = "disable"; } else { $camStatus = "other"; } # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"CamLiveMode",$camLiveMode); readingsBulkUpdate($hash,"CamRecShare",$data->{'data'}->{'cameras'}->[0]->{'camRecShare'}); readingsBulkUpdate($hash,"CamRecVolume",$data->{'data'}->{'cameras'}->[0]->{'camRecVolume'}); readingsBulkUpdate($hash,"CamIP",$data->{'data'}->{'cameras'}->[0]->{'host'}); readingsBulkUpdate($hash,"CamPort",$data->{'data'}->{'cameras'}->[0]->{'port'}); readingsBulkUpdate($hash,"Availability",$camStatus); readingsBulkUpdate($hash,"DeviceType",$deviceType); readingsBulkUpdate($hash,"LastUpdateTime",$update_time); readingsBulkUpdate($hash,"UsedSpaceMB",$data->{'data'}->{'cameras'}->[0]->{'volume_space'}); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "Camera-Informations of $camname retrieved"; # wenn "pollnologging" = 1 -> logging nur bei Verbose=4, sonst 2 if (defined(AttrVal($name, "pollnologging", undef)) and AttrVal($name, "pollnologging", undef) eq "1") { $verbose = 4; } else { $verbose = 2; } &printlog($hash,$logstr,$verbose); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Getcapabilities") { # Parse Infos my $ptzfocus = $data->{'data'}{'ptzFocus'}; if ($ptzfocus eq "0") { $ptzfocus = "false"; } elsif ($ptzfocus eq "1") { $ptzfocus = "support step operation"; } elsif ($ptzfocus eq "2") { $ptzfocus = "support continuous operation"; } my $ptztilt = $data->{'data'}{'ptzTilt'}; if ($ptztilt eq "0") { $ptztilt = "false"; } elsif ($ptztilt eq "1") { $ptztilt = "support step operation"; } elsif ($ptztilt eq "2") { $ptztilt = "support continuous operation"; } my $ptzzoom = $data->{'data'}{'ptzZoom'}; if ($ptzzoom eq "0") { $ptzzoom = "false"; } elsif ($ptzzoom eq "1") { $ptzzoom = "support step operation"; } elsif ($ptzzoom eq "2") { $ptzzoom = "support continuous operation"; } my $ptzpan = $data->{'data'}{'ptzPan'}; if ($ptzpan eq "0") { $ptzpan = "false"; } elsif ($ptzpan eq "1") { $ptzpan = "support step operation"; } elsif ($ptzpan eq "2") { $ptzpan = "support continuous operation"; } my $ptziris = $data->{'data'}{'ptzIris'}; if ($ptziris eq "0") { $ptziris = "false"; } elsif ($ptziris eq "1") { $ptziris = "support step operation"; } elsif ($ptziris eq "2") { $ptziris = "support continuous operation"; } # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"CapPTZAutoFocus",$data->{'data'}{'ptzAutoFocus'}); readingsBulkUpdate($hash,"CapAudioOut",$data->{'data'}{'audioOut'}); readingsBulkUpdate($hash,"CapChangeSpeed",$data->{'data'}{'ptzSpeed'}); readingsBulkUpdate($hash,"CapPTZHome",$data->{'data'}{'ptzHome'}); readingsBulkUpdate($hash,"CapPTZAbs",$data->{'data'}{'ptzAbs'}); readingsBulkUpdate($hash,"CapPTZDirections",$data->{'data'}{'ptzDirection'}); readingsBulkUpdate($hash,"CapPTZFocus",$ptzfocus); readingsBulkUpdate($hash,"CapPTZIris",$ptziris); readingsBulkUpdate($hash,"CapPTZPan",$ptzpan); readingsBulkUpdate($hash,"CapPTZTilt",$ptztilt); readingsBulkUpdate($hash,"CapPTZZoom",$ptzzoom); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "Capabilities of Camera $camname retrieved"; # wenn "pollnologging" = 1 -> logging nur bei Verbose=4, sonst 2 if (defined(AttrVal($name, "pollnologging", undef)) and AttrVal($name, "pollnologging", undef) eq "1") { $verbose = 4; } else { $verbose = 2; } &printlog($hash,$logstr,$verbose); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Getptzlistpreset") { # Parse PTZ-ListPresets $presetcnt = $data->{'data'}->{'total'}; $cnt = 0; # alle Presets der Kamera mit Id's in Assoziatives Array einlesen %allpresets = (); while ($cnt < $presetcnt) { $presid = $data->{'data'}->{'presets'}->[$cnt]->{'id'}; $presname = $data->{'data'}->{'presets'}->[$cnt]->{'name'}; $allpresets{$presname} = "$presid"; $cnt += 1; } # Presethash in $hash einfügen $hash->{HELPER}{ALLPRESETS} = \%allpresets; @preskeys = sort(keys(%allpresets)); $presetlist = join(",",@preskeys); # print "ID von Home ist : ". %allpresets->{"home"}; # print "aus Hash: ".$hash->{HELPER}{ALLPRESETS}{home}; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Presets",$presetlist); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "PTZ Presets of $camname retrieved"; # wenn "pollnologging" = 1 -> logging nur bei Verbose=4, sonst 2 if (defined(AttrVal($name, "pollnologging", undef)) and AttrVal($name, "pollnologging", undef) eq "1") { $verbose = 4; } else { $verbose = 2; } &printlog($hash,$logstr,$verbose); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } elsif ($OpMode eq "Getptzlistpatrol") { # Parse PTZ-ListPatrols my $patrolcnt = $data->{'data'}->{'total'}; $cnt = 0; # alle Patrols der Kamera mit Id's in Assoziatives Array einlesen my %allpatrols = (); while ($cnt < $patrolcnt) { my $patrolid = $data->{'data'}->{'patrols'}->[$cnt]->{'id'}; my $patrolname = $data->{'data'}->{'patrols'}->[$cnt]->{'name'}; $allpatrols{$patrolname} = $patrolid; $cnt += 1; } # Presethash in $hash einfügen $hash->{HELPER}{ALLPATROLS} = \%allpatrols; my @patrolkeys = sort(keys(%allpatrols)); my $patrollist = join(",",@patrolkeys); # print "ID von Tour1 ist : ". %allpatrols->{Tour1}; # print "aus Hash: ".$hash->{HELPER}{ALLPRESETS}{Tour1}; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Patrols",$patrollist); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "PTZ Patrols of $camname retrieved"; # wenn "pollnologging" = 1 -> logging nur bei Verbose=4, sonst 2 if (defined(AttrVal($name, "pollnologging", undef)) and AttrVal($name, "pollnologging", undef) eq "1") { $verbose = 4; } else { $verbose = 2; } &printlog($hash,$logstr,$verbose); $logstr = "--- End Function cam: $OpMode nonblocking ---"; &printlog($hash,$logstr,"4"); } } else { # die URL konnte nicht erfolgreich aufgerufen werden # Errorcode aus JSON ermitteln $errorcode = $data->{'error'}->{'code'}; # Fehlertext zum Errorcode ermitteln $error = &experror($hash,$errorcode); # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode",$errorcode); readingsBulkUpdate($hash,"Error",$error); readingsEndUpdate($hash, 1); # Logausgabe $logstr = "ERROR - Operation $OpMode of Camera $camname was not successful. Errorcode: $errorcode - $error"; &printlog($hash,$logstr,"1"); $logstr = "--- End Function cam: $OpMode nonblocking with error ---"; &printlog($hash,$logstr,"4"); # ausgeführte Funktion ist abgebrochen, Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } # logout wird ausgeführt, Rückkehr wird mit "logout_nonbl" verarbeitet # Logausgabe $logstr = "--- Begin Function logout nonblocking ---"; &printlog($hash,$logstr,"4"); $httptimeout = AttrVal($name, "httptimeout",undef) ? AttrVal($name, "httptimeout",undef) : "4"; # Logausgabe $logstr = "HTTP-Call will be done with httptimeout-Value: $httptimeout s"; &printlog($hash,$logstr,"5"); $url = "http://$serveraddr:$serverport/webapi/$apiauthpath?api=$apiauth&version=$apiauthmaxver&method=Logout&_sid=$sid"; $param = { url => $url, timeout => $httptimeout, hash => $hash, method => "GET", header => "Accept: application/json", callback => \&logout_nonbl }; HttpUtils_NonblockingGet ($param); } } ################################################################################### #### Rückkehr aus Funktion camret_nonbl, #### check Funktion logout sub logout_nonbl ($) { my ($param, $err, $myjson) = @_; my $hash = $param->{hash}; my $username = $hash->{HELPER}{USERNAME}; my $sid = $hash->{HELPER}{SID}; my $data; my $logstr; my $success; my $error; my $errorcode; if($err ne "") # wenn ein Fehler bei der HTTP Abfrage aufgetreten ist { $logstr = "error while requesting ".$param->{url}." - $err"; &printlog($hash,$logstr,"1"); # Eintrag fürs Log $logstr = "--- End Function logout nonblocking with error ---"; &printlog($hash,$logstr,"4"); readingsSingleUpdate($hash, "Error", $err, 1); # Readings erzeugen } elsif($myjson ne "") # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) { $logstr = "URL-Call: ".$param->{url}; &printlog($hash,$logstr,"4"); # An dieser Stelle die Antwort parsen / verarbeiten mit $myjson # Evaluiere ob Daten im JSON-Format empfangen wurden ($hash, $success) = &evaljson($hash,$myjson,$param->{url}); unless ($success) {$logstr = "Data returned: ".$myjson; &printlog($hash,$logstr,"4"); $hash->{HELPER}{ACTIVE} = "off"; return($hash,$success)}; $data = decode_json($myjson); $success = $data->{'success'}; if ($success) { # die Logout-URL konnte erfolgreich aufgerufen werden # Logausgabe decodierte JSON Daten $logstr = "JSON returned: ". Dumper $data; &printlog($hash,$logstr,"4"); # Session-ID aus Helper-hash löschen delete $hash->{HELPER}{SID}; # Logausgabe $logstr = "Session of User $username has ended - SID: $sid has been deleted"; &printlog($hash,$logstr,"4"); $logstr = "--- End Function logout nonblocking ---"; &printlog($hash,$logstr,"4"); } else { # Errorcode aus JSON ermitteln $errorcode = $data->{'error'}->{'code'}; # Fehlertext zum Errorcode ermitteln $error = &experrorauth($hash,$errorcode); # Logausgabe $logstr = "ERROR - Logout of User $username was not successful. Errorcode: $errorcode - $error"; &printlog($hash,$logstr,"1"); $logstr = "--- End Function logout nonblocking with error ---"; &printlog($hash,$logstr,"4"); } } # ausgeführte Funktion ist erledigt (auch wenn logout nicht erfolgreich), Freigabe Funktionstoken $hash->{HELPER}{ACTIVE} = "off"; return; } ############################################################################################################################# ######### Ende Kameraoperationen mit NonblockingGet (nicht blockierender HTTP-Call) ############# ############################################################################################################################# ############################################################################################################################# ######### Hilfsroutinen ############# ############################################################################################################################# ############################################################################### ### Test ob JSON-String empfangen wurde sub evaljson { my ($hash,$myjson,$url)= @_; my $success = 1; my $e; my $logstr; eval {decode_json($myjson);1;} or do { $success = 0; $e = $@; # Setreading readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","malformed JSON string received"); readingsEndUpdate($hash, 1); }; return($hash,$success); } ############################################################################## ### Auflösung Errorcodes bei Login / Logout sub experrorauth { # Übernahmewerte sind $hash, $errorcode my ($hash,@errorcode) = @_; my $device = $hash->{NAME}; my $errorcode = shift @errorcode; my %errorlist; my $error; # Aufbau der Errorcode-Liste (siehe Surveillance_Station_Web_API_v2.0.pdf) %errorlist = ( 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", 403 => "One time password not specified", 404 => "One time password authenticate failed", ); unless (exists ($errorlist {$errorcode})) {$error = "Message for Errorode $errorcode not found. Please turn to Synology Web API-Guide."; return ($error);} # Fehlertext aus Hash-Tabelle oben ermitteln $error = $errorlist {$errorcode}; return ($error); } ############################################################################## ### Auflösung Errorcodes SS API sub experror { # Übernahmewerte sind $hash, $errorcode my ($hash,@errorcode) = @_; my $device = $hash->{NAME}; my $errorcode = shift @errorcode; my %errorlist; my $error; # Aufbau der Errorcode-Liste (siehe Surveillance_Station_Web_API_v2.0.pdf) %errorlist = ( 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", 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", ); unless (exists ($errorlist {$errorcode})) {$error = "Message for Errorode $errorcode not found. Please turn to Synology Web API-Guide."; return ($error);} # Fehlertext aus Hash-Tabelle oben ermitteln $error = $errorlist {$errorcode}; return ($error); } ############################################################################ ### Logausgabe sub printlog { # Übernahmewerte ist $hash, $logstr, $verb (Verbose-Level) my ($hash,$logstr,$verb)= @_; my $name = $hash->{NAME}; Log3 ($name, $verb, "$name - $logstr"); return; } 1; =pod =begin html

SSCam

    Using this Module you are able to operate with cameras which are defined in Synology Surveillance Station (SVS).
    At present the following functions are available:

      • Start a Rocording
      • Stop a Recording (using command or automatically after the <RecordTime> period
      • Trigger a Snapshot
      • Deaktivate a Camera in Synology Surveillance Station
      • Activate a Camera in Synology Surveillance Station
      • Retrieval of Camera Properties (Polling)

    The recordings and snapshots will be stored in Synology Surveillance Station (SVS) and are managed like the other (normal) recordings / snapshots defined by Surveillance Station rules.
    For example the recordings are stored for a defined time in Surveillance Station and will be deleted after that period.

    If you like to discuss or help to improve this module please use FHEM-Forum with link:
    49_SSCam: Fragen, Hinweise, Neuigkeiten und mehr rund um dieses Modul.

    Prerequisites

    This module uses the CPAN-module JSON. Please consider to install this package (Debian: libjson-perl).
    You don't need to install LWP anymore, because of SSCam is completely using the nonblocking functions of HttpUtils respectively HttpUtils_NonblockingGet now.
    You also need to add an user in Synology DSM as member of Administrators group.

    Define

      define <name> SSCam <ServerAddr> <Port> <Username> <Password> <Cameraname>

      Defines a new camera device for SSCam. At first the devices have to be set up and operable in Synology Surveillance Station 7.0 and above.

      The Modul SSCam ist based on functions of Synology Surveillance Station API.
      Please refer the Web API Guide.

      At present only HTTP-protocol is supported to call Synology DS.

      The parameters are in detail:

      name: the name of the new device to use in FHEM
      ServerAddr: IP-address of Synology Surveillance Station Host. Note: avoid using hostnames because of DNS-Calls are not unblocking in FHEM
      Port: the Port Synology surveillance Station Host, normally 5000 (HTTP only)
      Username: Username defined in the Diskstation. Has to be a member of Admin-group
      Password: the Password for the User
      Cameraname: Cameraname as defined in Synology Surveillance Station, Spaces are not allowed in Cameraname !


      Examples:
            define CamCP SSCAM 192.168.2.20 5000 apiuser apipass Carport     
          
      When a new Camera is defined, as a start the recordingtime of 15 seconds will be assigned to the device.
      Using the attribute "rectime" you can adapt the recordingtime for every camera individually.
      The value of "0" for rectime will lead to an endless recording which has to be stopped by a "set <name> off" command.
      Due to a Log-Entry with a hint to that circumstance will be written.

      If the attribute "rectime" would be deleted again, the default-value for recording-time (15s) become active.

      With command "set <name> on [rectime]" a temporary recordingtime is determinded which would overwrite the dafault-value of recordingtime
      and the attribute "rectime" (if it is set) uniquely.

      In that case the command "set <name> on 0" leads also to an endless recording.

      If you have specified a pre-recording time in SVS it will be considered too.

      HTTP-Timeout Settings

      All functions of the SSCam-Module are using HTTP-Calls to the SVS Web API.
      The Default-Value of the HTTP-Timeout amounts 4 seconds. You can set the Attribute "httptimeout" > 0 to adjust the value as needed in your technical environment.



    Set
      Currently there are the following options for "Set <name> ..." :

      "on [rectime]": starts a recording. The recording will be stopped automatically after a period of [rectime]
      if [rectime] = 0 an endless recording will be started
      "off" : stopps a running recording manually or using other events (e.g. with at, notify)
      "snap": triggers a snapshot of the relevant camera and store it into Synology Surveillance Station
      "disable": deactivates a camera in Synology Surveillance Station
      "enable": activates a camera in Synology Surveillance Station


      Examples for simple Start/Stop a Recording:

      set <name> on [rectime] starts a recording of camera <name>, stops automatically after [rectime] (default 15s or defined by attribute)
      set <name> off stops the recording of camera <name>

      A snapshot can be triggered with:
       
           set <name> snap 
        
      Subsequent some Examples for taking snapshots:

      If a serial of snapshots should be released, it can be done using the following notify command. For the example a serial of snapshots are to be triggerd if the recording of a camera starts.
      When the recording of camera "CamHE1" starts (Attribut event-on-change-reading -> "Record" has to be set), then 3 snapshots at intervals of 2 seconds are triggered.
           define he1_snap_3 notify CamHE1:Record.*on define h3 at +*{3}00:00:02 set CamHE1 snap 
        
      Release of 2 Snapshots of camera "CamHE1" at intervals of 6 seconds after the motion sensor "MelderHE1" has sent an event,
      can be done e.g. with following notify-command:
           define he1_snap_2 notify MelderHE1:on.* define h2 at +*{2}00:00:06 set CamHE1 snap 
        
      The ID of the last snapshot will be displayed as value of variable "LastSnapId" in the device-Readings.

      For
      deactivating / activating
      a list of cameras or all cameras using a Regex-expression, subsequent two examples using "at":
           define a13 at 21:46 set CamCP1,CamFL,CamHE1,CamTER disable (enable)
           define a14 at 21:46 set Cam.* disable (enable)
        
      A bit more convenient is it to use a dummy-device for enable/disable all available cameras in Surveillance Station.
      At first the Dummy will be created.
           define allcams dummy
           attr allcams eventMap on:enable off:disable
           attr allcams room Cams
           attr allcams webCmd enable:disable
        
      With combination of two created notifies, respectively one for "enable" and one for "diasble", you are able to switch all cameras into "enable" or "disable" state at the same time if you set the dummy to "enable" or "disable".
           define all_cams_disable notify allcams:.*off set CamCP1,CamFL,CamHE1,CamTER disable
           attr all_cams_disable room Cams
      
           define all_cams_enable notify allcams:on set CamCP1,CamFL,CamHE1,CamTER enable
           attr all_cams_enable room Cams
        

    Get
      With SSCam the properties of defined Cameras could be retrieved. It could be done by using the command:
            get <name> caminfoall
        
      Dependend from the type of Camera (e.g. Fix- or PTZ-Camera) the available properties will be retrieved and provided as Readings.
      For example the Reading "Availability" will be set to "disconnected" if the Camera would be disconnected from Synology Surveillance Station and can be used for further
      processing like crearing events.

      Polling of Camera-Properties:

      Retrieval of Camera-Properties can be done automatically if the attribute "pollcaminfoall" will be set to a value > 10.
      As default that attribute "pollcaminfoall" isn't be set and the automatic polling isn't be active.
      The value of that attribute determines the interval of property-retrieval in seconds. If that attribute isn't be set or < 10 the automatic polling won't be started
      respectively stopped when the value was set to > 10 before.

      The attribute "pollcaminfoall" is monitored by a watchdog-timer. Changes of th attributevalue will be checked every 90 seconds and transact correspondig.
      Changes of the pollingstate and pollinginterval will be reported in FHEM-Logfile. The reporting can be switched off by setting the attribute "pollnologging=1".
      Thereby the needless growing of the logfile can be avoided. But if verbose is set to 4 or above even though the attribute "pollnologging" is set as well, the polling
      will be actived due to analysis purposes.

      If FHEM will be restarted, the first data retrieval will be done within 60 seconds after start.

      The state of automatic polling will be displayed by reading "PollState":
          PollState = Active     -    automatic polling will be executed with interval correspondig value of attribute 
          PollState = Inactive   -    automatic polling won't be executed
        
      The meaning of reading values is described under Readings .

      Notes:

      If polling is used, the interval should be adjusted only as short as needed due to the detected camera values are predominantly static.
      A feasible guide value for attribute "pollcaminfoall" could be between 600 - 1800 (s).
      Per polling call and camera approximately 10 - 20 Http-calls will are stepped against Surveillance Station.
      Because of that if HTTP-Timeout (pls. refer Attribut "httptimeout") is set to 4 seconds, the theoretical processing time couldn't be higher than 80 seconds.
      Considering a safety margin, in that example you shouldn't set the polling interval lower than 160 seconds.

      If several Cameras are defined in SSCam, attribute "pollcaminfoall" of every Cameras shouldn't be set exactly to the same value to avoid processing bottlenecks
      and thereby caused potential source of errors during request Synology Surveillance Station.
      A marginal difference between the polling intervals of the defined cameras, e.g. 1 second, can already be faced as sufficient value.

    Readings

      Using the polling mechanism or retrieval by "get"-call readings are provieded, The meaning of the readings are listed in subsequent table:
      The transfered Readings can be deversified dependend on the type of camera.

      • Availability
      • - Availability of Camera (disabled, enabled, disconnected, other)
      • CamIP
      • - IP-Address of Camera
      • CamLiveMode
      • - Source of Live-View (DS, Camera)
      • CamPort
      • - IP-Port of Camera
      • CamRecShare
      • - shared folder on disk station for recordings
      • CamRecVolume
      • - Volume on disk station for recordings
      • CapAudioOut
      • - Capability to Audio Out over Surveillance Station (false/true)
      • CapChangeSpeed
      • - Capability to various motion speed
      • CapPTZAbs
      • - Capability to perform absolute PTZ action
      • CapPTZAutoFocus
      • - Capability to perform auto focus action
      • CapPTZDirections
      • - the PTZ directions that camera support
      • CapPTZFocus
      • - mode of support for focus action
      • CapPTZHome
      • - Capability to perform home action
      • CapPTZIris
      • - mode of support for iris action
      • CapPTZPan
      • - Capability to perform pan action
      • CapPTZTilt
      • - mode of support for tilt action
      • CapPTZZoom
      • - Capability to perform zoom action
      • DeviceType
      • - device type (Camera, Video_Server, PTZ, Fisheye)
      • Error
      • - message text of last error
      • Errorcode
      • - error code of last error
      • LastUpdateTime
      • - date / time of last update of Camera in Synology Surrveillance Station
      • Patrols
      • - in Synology Surveillance Station predefined patrols (at PTZ-Cameras)
      • PollState
      • - shows the state of automatic polling
      • Presets
      • - in Synology Surveillance Station predefined Presets (at PTZ-Cameras)
      • Record
      • - if recording is running = Start, if no recording is running = Stop
      • UsedSpaceMB
      • - used disk space of recordings by Camera


    Attributes

      • httptimeout - Timeout-Value of HTTP-Calls to Synology Surveillance Station, Default: 4 seconds (if httptimeout = "0" or not set)
      • pollcaminfoall - Interval of automatic polling the Camera properties (if < 10: no polling, if > 10: polling with interval)
      • pollnologging - "0" resp. not set = Logging device polling active (default), "1" = Logging device polling inactive
      • rectime - the determined recordtime when a recording starts. If rectime = 0 an endless recording will be started. If it isn't defined, the default recordtime of 15s is activated
      • verbose

        • Different Verbose-Level are supported.
          Those are in detail:
          0 Start/Stop-Event will be logged
          1 Error messages will be logged
          3 sended commands will be logged
          4 sended and received informations will be logged
          5 all outputs will be logged for error-analyses. Caution: a lot of data could be written into logfile !


        further Attributes:

      • readingFnAttributes


=end html =begin html_DE

SSCam

    Mit diesem Modul können Operationen von in der Synology Surveillance Station (SVS) definierten Kameras ausgeführt werden.
    Zur Zeit werden folgende Funktionen unterstützt:

      • Start einer Aufnahme
      • Stop einer Aufnahme (per Befehl bzw. automatisch nach Ablauf der Aufnahmedauer)
      • Aufnehmen eines Schnappschusses und Ablage in der Synology Surveillance Station
      • Deaktivieren einer Kamera in Synology Surveillance Station
      • Aktivieren einer Kamera in Synology Surveillance Station
      • Abfrage von Kameraeigenschaften (Polling)

    Die Aufnahmen stehen in der Synology Surveillance Station (SVS) zur Verfügung und unterliegen, wie jede andere Aufnahme, den in der Synology Surveillance Station eingestellten Regeln.
    So werden zum Beispiel die Aufnahmen entsprechend ihrer Archivierungsfrist gespeichert und dann gelöscht.

    Wenn sie über dieses Modul diskutieren oder zur Verbesserung des Moduls beitragen möchten, ist im FHEM-Forum ein Sammelplatz unter:
    49_SSCam: Fragen, Hinweise, Neuigkeiten und mehr rund um dieses Modul.

    Vorbereitung

    Dieses Modul nutzt das CPAN Module JSON. Bitte darauf achten dieses Paket zu installieren. (Debian: libjson-perl).
    Das CPAN-Modul LWP wird für SSCam nicht mehr benötigt. Das Modul verwendet für HTTP-Calls die nichtblockierenden Funktionen von HttpUtils bzw. HttpUtils_NonblockingGet.
    Im DSM muß ebenfalls ein Nutzer als Mitglied der Administratorgruppe angelegt sein. Die Daten werden bei der Definition des Gerätes benötigt.

    Definition

      define <name> SSCam <ServerAddr> <Port> <Username> <Password> <Kameraname in SVS>

      Definiert eine neue Kamera für SSCam. Zunächst muß diese Kamera in der Synology Surveillance Station 7.0 oder höher eingebunden sein und entsprechend funktionieren.

      Das Modul SSCam basiert auf Funktionen der Synology Surveillance Station API.
      Weitere Informationen unter: Web API Guide.

      Momentan wird nur das HTTP-Protokoll unterstützt um die Web-Services der Synology DS aufzurufen.

      Die Parameter beschreiben im Einzelnen:

      name: der Name des neuen Gerätes in FHEM
      ServerAddr: die IP-Addresse des Synology Surveillance Station Host. Hinweis: Es sollte kein Servername verwendet werden weil DNS-Aufrufe in FHEM blockierend sind.
      Port: der Port des Synology Surveillance Station Host. Normalerweise ist das 5000 (nur HTTP)
      Username: Name des in der Diskstation definierten Nutzers. Er muß ein Mitglied der Admin-Gruppe sein
      Password: das Passwort des Nutzers
      Cameraname: Kameraname wie er in der Synology Surveillance Station angegeben ist. Leerzeichen im Namen sind nicht erlaubt !


      Beispiel:
            define CamCP SSCAM 192.168.2.20 5000 apiuser apipass Carport     
           
      Wird eine neue Kamera definiert, wird diesem Device zunächst eine Standardaufnahmedauer von 15 zugewiesen.
      Über das Attribut "rectime" kann die Aufnahmedauer für jede Kamera individuell angepasst werden. Der Wert "0" für "rectime" führt zu einer Endlosaufnahme, die durch "set <name> off" wieder gestoppt werden muß.
      Ein Logeintrag mit einem entsprechenden Hinweis auf diesen Umstand wird geschrieben.

      Wird das Attribut "rectime" gelöscht, greift wieder der Default-Wert (15s) für die Aufnahmedauer.

      Mit dem Befehl "set <name> on [rectime]" wird die Aufnahmedauer temporär festgelegt und überschreibt einmalig sowohl den Defaultwert als auch den Wert des gesetzten Attributs "rectime".
      Auch in diesem Fall führt "set <name> on 0" zu einer Daueraufnahme.

      Eine eventuell in der SVS eingestellte Dauer der Voraufzeichnung wird weiterhin berücksichtigt.

      HTTP-Timeout setzen

      Alle Funktionen dieses Moduls verwenden HTTP-Aufrufe gegenüber der SVS Web API.
      Der Standardwert für den HTTP-Timeout beträgt 4 Sekunden. Durch Setzen des Attributes "httptimeout" > 0 kann dieser Wert bei Bedarf entsprechend den technischen Gegebenheiten angepasst werden.



    Set
      Es gibt zur Zeit folgende Optionen für "Set <name> ...":

      "on [rectime]": startet eine Aufnahme. Die Aufnahme wird automatisch nach Ablauf der Zeit [rectime] gestoppt.
      Mit rectime = 0 wird eine Daueraufnahme gestartet die durch "set <name> off" wieder gestoppt werden muß.
      "off" : stoppt eine laufende Aufnahme manuell oder durch die Nutzung anderer Events (z.B. über at, notify)
      "snap": löst einen Schnappschuß der entsprechenden Kamera aus und speichert ihn in der Synology Surveillance Station
      "disable": deaktiviert eine Kamera in der Synology Surveillance Station
      "enable": aktiviert eine Kamera in der Synology Surveillance Station


      Beispiele für einfachen Start/Stop einer Aufnahme:

      set <name> on [rectime] startet die Aufnahme der Kamera <name>, automatischer Stop der Aufnahme nach Ablauf der Zeit [rectime] (default 15s oder wie im Attribut "rectime" angegeben)
      set <name> off stoppt die Aufnahme der Kamera <name>

      Ein Schnappschuß kann ausgelöst werden mit:
       
           set <name> snap 
        
      Nachfolgend einige Beispiele für die Auslösung von Schnappschüssen.

      Soll eine Reihe von Schnappschüssen ausgelöst werden wenn eine Aufnahme startet, kann das z.B. durch folgendes notify geschehen.
      Sobald der Start der Kamera CamHE1 ausgelöst wird (Attribut event-on-change-reading -> "Record" setzen), werden abhängig davon 3 Snapshots im Abstand von 2 Sekunden getriggert.
           define he1_snap_3 notify CamHE1:Record.*Start define h3 at +*{3}00:00:02 set CamHE1 snap
        
      Triggern von 2 Schnappschüssen der Kamera "CamHE1" im Abstand von 6 Sekunden nachdem der Bewegungsmelder "MelderHE1" einen Event gesendet hat,
      kann z.B. mit folgendem notify geschehen:
           define he1_snap_2 notify MelderHE1:on.* define h2 at +*{2}00:00:06 set CamHE1 snap 
        
      Es wird die ID des letzten Snapshots als Wert der Variable "LastSnapId" in den Readings der Kamera ausgegeben.

      Um eine Liste von Kameras oder alle Kameras (mit Regex) zum Beispiel um 21:46 zu deaktivieren / zu aktivieren zwei Beispiele mit at:
           define a13 at 21:46 set CamCP1,CamFL,CamHE1,CamTER disable (enable)
           define a14 at 21:46 set Cam.* disable (enable)
        
      Etwas komfortabler gelingt das Schalten aller Kameras über einen Dummy. Zunächst wird der Dummy angelegt:
           define allcams dummy
           attr allcams eventMap on:enable off:disable
           attr allcams room Cams
           attr allcams webCmd enable:disable
        
      Durch Verknüpfung mit zwei angelegten notify, jeweils ein notify für "enable" und "disable", kann man durch Schalten des Dummys auf "enable" bzw. "disable" alle Kameras auf einmal aktivieren bzw. deaktivieren.
           define all_cams_disable notify allcams:.*off set CamCP1,CamFL,CamHE1,CamTER disable
           attr all_cams_disable room Cams
      
           define all_cams_enable notify allcams:on set CamCP1,CamFL,CamHE1,CamTER enable
           attr all_cams_enable room Cams
        

    Get
      Mit SSCam können die Eigenschaften der Kameras aus der Surveillance Station abgefragt werden. Dazu steht der Befehl zur Verfügung:
            get <name> caminfoall
        
      Abhängig von der Art der Kamera (z.B. Fix- oder PTZ-Kamera) werden die verfügbaren Eigenschaften ermittelt und als Readings zur Verfügung gestellt.
      So wird zum Beispiel das Reading "Availability" auf "disconnected" gesetzt falls die Kamera von der Surveillance Station getrennt wird und kann für weitere
      Verarbeitungen genutzt werden.

      Polling der Kameraeigenschaften:

      Die Abfrage der Kameraeigenschaften erfolgt automatisch, wenn das Attribut "pollcaminfoall" (siehe Attribute) mit einem Wert > 10 gesetzt wird.
      Per Default ist das Attribut "pollcaminfoall" nicht gesetzt und das automatische Polling nicht aktiv.
      Der Wert dieses Attributes legt das Intervall der Abfrage in Sekunden fest. Ist das Attribut nicht gesetzt oder < 10 wird kein automatisches Polling
      gestartet bzw. gestoppt wenn vorher der Wert > 10 gesetzt war.

      Das Attribut "pollcaminfoall" wird durch einen Watchdog-Timer überwacht. Änderungen des Attributwertes werden alle 90 Sekunden ausgewertet und entsprechend umgesetzt.
      Eine Änderung des Pollingstatus / Pollingintervalls wird im FHEM-Logfile protokolliert. Diese Protokollierung kann durch Setzen des Attributes "pollnologging=1" abgeschaltet werden.
      Dadurch kann ein unnötiges Anwachsen des Logs vermieden werden. Ab verbose=4 wird allerdings trotz gesetzten "pollnologging"-Attribut ein Log des Pollings
      zu Analysezwecken aktiviert.

      Wird FHEM neu gestartet, wird bei aktivierten Polling der ersten Datenabruf innerhalb 60s nach dem Start ausgeführt.

      Der Status des automatischen Pollings wird durch das Reading "PollState" signalisiert:
          PollState = Active     -    automatisches Polling wird mit Intervall entsprechend  ausgeführt
          PollState = Inactive   -    automatisches Polling wird nicht ausgeführt
        
      Die Bedeutung der Readingwerte ist unter Readings beschrieben.

      Hinweise:

      Wird Polling eingesetzt, sollte das Intervall nur so kurz wie benötigt eingestellt werden da die ermittelten Werte überwiegend statisch sind.
      Das eingestellte Intervall sollte nicht kleiner sein als die Summe aller HTTP-Verarbeitungszeiten. Pro Pollingaufruf und Kamera werden ca. 10 - 20 Http-Calls gegen die Surveillance Station abgesetzt.

      Bei einem eingestellten HTTP-Timeout (siehe Attribut) "httptimeout") von 4 Sekunden kann die theoretische Verarbeitungszeit Zeit nicht höher als 80 Sekunden betragen.
      In dem Beispiel sollte man das Pollingintervall mit einem Sicherheitszuschlag auf nicht weniger 160 Sekunden setzen.
      Ein praktikabler Richtwert könnte zwischen 600 - 1800 (s) liegen.
      Sind mehrere Kameras in SSCam definiert, sollte "pollcaminfoall" nicht bei allen Kameras auf exakt den gleichen Wert gesetzt werden um Verarbeitungsengpässe
      und dadurch versursachte potentielle Fehlerquellen bei der Abfrage der Synology Surveillance Station zu vermeiden.
      Ein geringfügiger Unterschied zwischen den Pollingintervallen der definierten Kameras von z.B. 1s kann bereits als ausreichend angesehen werden.

    Readings

      Über den Pollingmechanismus bzw. durch Abfrage mit "Get" werden Readings bereitgestellt, deren Bedeutung in der nachfolgenden Tabelle dargestellt sind.
      Die übermittelten Readings können in Abhängigkeit des Kameratyps variieren.

      • Availability
      • - Verfügbarkeit der Kamera (disabled, enabled, disconnected, other)
      • CamIP
      • - IP-Adresse der Kamera
      • CamLiveMode
      • - Quelle für Live-Ansicht (DS, Camera)
      • CamPort
      • - IP-Port der Kamera
      • CamRecShare
      • - gemeinsamer Ordner auf der DS für Aufnahmen
      • CamRecVolume
      • - Volume auf der DS für Aufnahmen
      • CapAudioOut
      • - Fähigkeit der Kamera zur Audioausgabe über Surveillance Station (false/true)
      • CapChangeSpeed
      • - Fähigkeit der Kamera verschiedene Bewegungsgeschwindigkeiten auszuführen
      • CapPTZAbs
      • - Fähigkeit der Kamera für absolute PTZ-Aktionen
      • CapPTZAutoFocus
      • - Fähigkeit der Kamera für Autofokus Aktionen
      • CapPTZDirections
      • - die verfügbaren PTZ-Richtungen der Kamera
      • CapPTZFocus
      • - Art der Kameraunterstützung für Fokussierung
      • CapPTZHome
      • - Unterstützung der Kamera für Home-Position
      • CapPTZIris
      • - Unterstützung der Kamera für Iris-Aktion
      • CapPTZPan
      • - Unterstützung der Kamera für Pan-Aktion
      • CapPTZTilt
      • - Unterstützung der Kamera für Tilt-Aktion
      • CapPTZZoom
      • - Unterstützung der Kamera für Zoom-Aktion
      • DeviceType
      • - Kameratyp (Camera, Video_Server, PTZ, Fisheye)
      • Error
      • - Meldungstext des letzten Fehlers
      • Errorcode
      • - Fehlercode des letzten Fehlers
      • LastUpdateTime
      • - Datum / Zeit der letzten Aktualisierung der Kamera in der Surrveillance Station
      • Patrols
      • - in Surveillance Station voreingestellte Überwachungstouren (bei PTZ-Kameras)
      • PollState
      • - zeigt den Status des automatischen Pollings an
      • Presets
      • - in Surveillance Station voreingestellte Positionen (bei PTZ-Kameras)
      • Record
      • - Aufnahme läuft = Start, keine Aufnahme = Stop
      • UsedSpaceMB
      • - durch Aufnahmen der Kamera belegter Plattenplatz auf dem Volume


    Attribute

      • httptimeout - Timeout-Wert für HTTP-Aufrufe zur Synology Surveillance Station, Default: 4 Sekunden (wenn httptimeout = "0" oder nicht gesetzt)
      • pollcaminfoall - Intervall der automatischen Eigenschaftsabfrage (Polling) einer Kamera (kleiner 10: kein Polling, größer 10: Polling mit Intervall)
      • pollnologging - "0" bzw. nicht gesetzt = Logging Gerätepolling aktiv (default), "1" = Logging Gerätepolling inaktiv
      • rectime - festgelegte Aufnahmezeit wenn eine Aufnahme gestartet wird. Mit rectime = 0 wird eine Endlosaufnahme gestartet. Ist "rectime" nicht gesetzt, wird der Defaultwert von 15s verwendet.
      • verbose

        • Es werden verschiedene Verbose-Level unterstützt. Dies sind im Einzelnen:
          0 - Start/Stop-Ereignisse werden geloggt
          1 - Fehlermeldungen werden geloggt
          3 - gesendete Kommandos werden geloggt
          4 - gesendete und empfangene Daten werden geloggt
          5 - alle Ausgaben zur Fehleranalyse werden geloggt. ACHTUNG: möglicherweise werden sehr viele Daten in das Logfile geschrieben!


        weitere Attribute:

      • readingFnAttributes


=end html_DE =cut