diff --git a/fhem/CHANGED b/fhem/CHANGED index 491f497f2..9f018b1c5 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,7 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 49_SSCam: V2.6.1, new commands snapGallery, createSnapGallery and + corresponding attributes - feature: 34_ESPEasy.pm: plugin neopixelfx/nfx added (Forum #73949) - new: 31_Nello: first release - feature: 01_FHEMWEB.pm: add webCmdLabel (Forum #72581) diff --git a/fhem/FHEM/49_SSCam.pm b/fhem/FHEM/49_SSCam.pm index aa65987b3..b849a5e47 100644 --- a/fhem/FHEM/49_SSCam.pm +++ b/fhem/FHEM/49_SSCam.pm @@ -27,6 +27,15 @@ ######################################################################################################################### # Versions History: # +# 2.6.1 07.08.2017 some changes in composegallery if createSnapGallery used, room Snapshots changed to SnapGalllery +# commandref revised +# 2.6.0 06.08.2017 new command createSnapGallery +# 2.5.4 05.08.2017 analyze $hash->{CL} in SetFn bzw. GetFn, set snapGallery only if snapGalleryBoost=1 is set, +# some snapGallery improvements and fixes +# 2.5.3 02.08.2017 implement snapGallery as set-command +# 2.5.2 01.08.2017 get snapGallery with or without snapGalleryBoost (some more attributes for snapGallery) +# 2.5.1 31.07.2017 sub composegallery (no polling necessary) +# 2.5.0 31.07.2017 logtext revised, new get snapGallery command # 2.4.1 29.07.2017 fix behavior of state when starting lastsnap_fw, fix "uninitialized value in pattern match (m//) # at ./FHEM/49_SSCam.pm line 2895" # 2.4.0 28.07.2017 new set command runView lastsnap_fw, commandref revised, minor fixes @@ -178,7 +187,7 @@ use Time::HiRes; use HttpUtils; # no if $] >= 5.017011, warnings => 'experimental'; -my $SSCamVersion = "2.4.1"; +my $SSCamVersion = "2.6.1"; # Aufbau Errorcode-Hashes (siehe Surveillance Station Web API) my %SSCam_errauthlist = ( @@ -223,6 +232,10 @@ my %SSCam_errlist = ( 600 => "Presetname and PresetID not found in Hash", ); +# Standardvariablen +my $SSCam_slim = 3; # default Anzahl der abzurufenden Schnappschüsse mit snapGallery +my $SSCAM_snum = "1,2,3,4,5,6,7,8,9,10"; # mögliche Anzahl der abzurufenden Schnappschüsse mit snapGallery + sub SSCam_Initialize($) { my ($hash) = @_; $hash->{DefFn} = "SSCam_Define"; @@ -244,6 +257,11 @@ sub SSCam_Initialize($) { "loginRetries:1,2,3,4,5,6,7,8,9,10 ". "videofolderMap ". "pollcaminfoall ". + "snapGalleryBoost:0,1 ". + "snapGallerySize:Icon,Full ". + "snapGalleryNumber:$SSCAM_snum ". + "snapGalleryColumns ". + "snapGalleryHtmlAttr ". "pollnologging:1,0 ". "debugactivetoken:1 ". "rectime ". @@ -335,6 +353,10 @@ sub SSCam_Delete { # gespeicherte Credentials löschen setKeyValue($index, undef); + + # löschen snapGallerie-Device falls vorhanden + my $sgdev = "SSCam.$hash->{NAME}.snapgallery"; + CommandDelete($hash->{CL},"$sgdev"); return undef; } @@ -372,7 +394,72 @@ sub SSCam_Attr { delete($defs{$name}{READINGS}{StmKeyUnicst}) if ($defs{$name}{READINGS}{StmKeyUnicst}); delete($defs{$name}{READINGS}{LiveStreamUrl}) if ($defs{$name}{READINGS}{LiveStreamUrl}); } - } + } + + if ($aName eq "snapGallerySize") { + if($cmd eq "set") { + $do = ($aVal eq "Icon")?1:2; + } + $do = 0 if($cmd eq "del"); + + if ($do == 0) { + delete($hash->{HELPER}{SNAPHASH}) if(AttrVal($name,"snapGalleryBoost",0)); # Snaphash nur löschen wenn Snaps gepollt werden + Log3($name, 4, "$name - Snapshot hash deleted"); + } elsif (AttrVal($name,"snapGalleryBoost",0)) { + # snap-Infos abhängig ermitteln wenn gepollt werden soll + my ($slim,$ssize); + $hash->{HELPER}{GETSNAPGALLERY} = 1; + $slim = AttrVal($name,"snapGalleryNumber",$SSCam_slim); # Anzahl der abzurufenden Snaps + $ssize = $do; + RemoveInternalTimer($hash, "getsnapinfo"); + InternalTimer(gettimeofday()+0.7, "getsnapinfo", "$name:$slim:$ssize", 0); + } + } + + if ($aName eq "snapGalleryBoost") { + if($cmd eq "set") { + $do = ($aVal == 1)?1:0; + } + $do = 0 if($cmd eq "del"); + + if ($do == 0) { + delete($hash->{HELPER}{SNAPHASH}); # Snaphash löschen + Log3($name, 4, "$name - Snapshot hash deleted"); + + } else { + # snapgallery regelmäßig neu einlesen wenn Polling ein + return "When you want activate \"snapGalleryBoost\", you have to set the attribute \"pollcaminfoall\" first because the functionality depends on retrieving snapshots periodical." + if(!AttrVal($name,"pollcaminfoall",0)); + + my ($slim,$ssize); + $hash->{HELPER}{GETSNAPGALLERY} = 1; + $slim = AttrVal($name,"snapGalleryNumber",$SSCam_slim); # Anzahl der abzurufenden Snaps + my $sg = AttrVal($name,"snapGallerySize","Icon"); # Auflösung Image + $ssize = ($sg eq "Icon")?1:2; + RemoveInternalTimer($hash, "getsnapinfo"); + InternalTimer(gettimeofday()+0.7, "getsnapinfo", "$name:$slim:$ssize", 0); + } + } + + if ($aName eq "snapGalleryNumber" && AttrVal($name,"snapGalleryBoost",0)) { + my ($slim,$ssize); + if($cmd eq "set") { + $do = ($aVal != 0)?1:0; + } + $do = 0 if($cmd eq "del"); + + if ($do == 0) { + $slim = 3; + } else { + $slim = $aVal; + } + + $hash->{HELPER}{GETSNAPGALLERY} = 1; + my $sg = AttrVal($name,"snapGallerySize","Icon"); # Auflösung Image + $ssize = ($sg eq "Icon")?1:2; + RemoveInternalTimer($hash, "getsnapinfo"); + InternalTimer(gettimeofday()+0.7, "getsnapinfo", "$name:$slim:$ssize", 0); + } if ($aName eq "simu_SVSversion") { delete $hash->{HELPER}{APIPARSET}; @@ -380,16 +467,20 @@ sub SSCam_Attr { } 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 !";} - } + if ($aName =~ m/httptimeout|snapGalleryColumns|rectime|pollcaminfoall/ ) { + unless ($aVal =~ /^\d+$/) { return " The Value for $aName is not valid. Use only figures 1-9 !";} + } } + + if ($cmd eq "del") { + if ($aName =~ m/pollcaminfoall/ ) { + # Polling nicht ausschalten wenn snapGalleryBoost ein (regelmäßig neu einlesen) + return "Please switch off \"snapGalleryBoost\" first if you want to deactivate \"pollcaminfoall\" because the functionality of \"snapGalleryBoost\" depends on retrieving snapshots periodical." + if(AttrVal($name,"snapGalleryBoost",1)); + } + } + + return undef; } @@ -408,242 +499,258 @@ sub SSCam_Set { my $setlist; my @prop; - $setlist = "Unknown argument $opt, choose one of ". - "credentials ". - "expmode:auto,day,night ". - "on ". - "off ". - "motdetsc:disable,camera,SVS ". - "snap ". - "enable ". - "disable ". - "runView:live_fw,live_link,live_open,lastrec_fw,lastrec_open,lastsnap_fw ". - "stopView:noArg ". - "extevent:1,2,3,4,5,6,7,8,9,10 ". - ((ReadingsVal("$name", "CapPTZPan", "false") ne "false") ? "runPatrol:".ReadingsVal("$name", "Patrols", "")." " : ""). - ((ReadingsVal("$name", "CapPTZPan", "false") ne "false") ? "goPreset:".ReadingsVal("$name", "Presets", "")." " : ""). - ((ReadingsVal("$name", "CapPTZAbs", "false")) ? "goAbsPTZ"." " : ""). - ((ReadingsVal("$name", "CapPTZDirections", "0") > 0) ? "move"." " : ""); + return "module is deactivated" if(IsDisabled($name)); + + $setlist = "Unknown argument $opt, choose one of ". + "credentials ". + "expmode:auto,day,night ". + "on ". + "off ". + "motdetsc:disable,camera,SVS ". + "snap ". + (AttrVal($name, "snapGalleryBoost",0)?(AttrVal($name,"snapGalleryNumber",undef) || AttrVal($name,"snapGalleryBoost",0))?"snapGallery:noArg ":"snapGallery:$SSCAM_snum ":" "). + "createSnapGallery:noArg ". + "enable ". + "disable ". + "runView:live_fw,live_link,live_open,lastrec_fw,lastrec_open,lastsnap_fw ". + "stopView:noArg ". + "extevent:1,2,3,4,5,6,7,8,9,10 ". + ((ReadingsVal("$name", "CapPTZPan", "false") ne "false") ? "runPatrol:".ReadingsVal("$name", "Patrols", "")." " : ""). + ((ReadingsVal("$name", "CapPTZPan", "false") ne "false") ? "goPreset:".ReadingsVal("$name", "Presets", "")." " : ""). + ((ReadingsVal("$name", "CapPTZAbs", "false")) ? "goAbsPTZ"." " : ""). + ((ReadingsVal("$name", "CapPTZDirections", "0") > 0) ? "move"." " : ""); - - if ($opt eq "on") { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + if (!$hash->{CREDENTIALS} && $opt ne "credentials") { + # sind die Credentials gesetzt ? + return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\""; + + } elsif ($opt eq "on") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - 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); + 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") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - camstoprec($hash); - } - elsif ($opt eq "snap") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - camsnap($hash); - } - elsif ($opt eq "enable") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - camenable($hash); - } - elsif ($opt eq "disable") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - camdisable($hash); - } - elsif ($opt eq "motdetsc") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - if (!$prop || $prop !~ /^(disable|camera|SVS)$/) { return " \"$opt\" needs one of those arguments: disable, camera, SVS !";} + } elsif ($opt eq "off") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + camstoprec($hash); + + } elsif ($opt eq "snap") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + camsnap($hash); + + } elsif ($opt eq "snapGallery") { + if(!AttrVal($name, "snapGalleryBoost",0)) { + # Snaphash ist nicht vorhanden und wird neu abgerufen und ausgegeben + if (defined($hash->{CL})) { + # Clienthash auflösen zur Fehlersuche (aufrufende FHEMWEB Instanz) + while (my ($key,$val) = each(%{$hash->{CL}})) { + $val = $val?$val:" "; + Log3($name, 4, "$name - snapGallery Clienthash: $key -> $val"); + } + } else { + Log3($name, 2, "$name - snapGallery Clienthash wasn't delivered !"); + return "Clienthash wasn't delivered. Can't use asynchronous output for snapGallery."; + } + + $hash->{HELPER}{CL} = $hash->{CL}; + $hash->{HELPER}{GETSNAPGALLERY} = 1; + + # snap-Infos für Gallerie abrufen + my ($sg,$slim,$ssize); + $slim = $prop?AttrVal($name,"snapGalleryNumber",$prop):AttrVal($name,"snapGalleryNumber",$SSCam_slim); # Anzahl der abzurufenden Snapshots + $ssize = (AttrVal($name,"snapGallerySize","Icon") eq "Icon")?1:2; # Image Size 1-Icon, 2-Full + + getsnapinfo("$name:$slim:$ssize"); + + } else { + # Snaphash ist vorhanden und wird zur Ausgabe aufbereitet + my $htmlCode = composegallery($name); + return $htmlCode; + } + + } elsif ($opt eq "createSnapGallery") { + my ($ret,$sgdev); + return "When you want use \"$opt\", you have to set the attribute \"snapGalleryBoost\" first because the functionality depends on retrieving snapshots automatically." + if(!AttrVal($name,"snapGalleryBoost",0)); + $sgdev = "SSCam.$name.snapgallery"; + $ret = CommandDefine($hash->{CL},"$sgdev weblink htmlCode {composegallery('$name','$sgdev')}"); + return $ret if($ret); + my $wlname = "SSCam.$name.snapgallery"; + my $room = "SnapGallery"; + CommandAttr($hash->{CL},$wlname." room ".$room); + return "Snapgallery device \"$sgdev\" was created successfully. Please have a look to room $room.
You can now assign it to another room if you want. Don't rename this new device ! "; + + } elsif ($opt eq "enable") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + camenable($hash); + + } elsif ($opt eq "disable") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + camdisable($hash); + + } elsif ($opt eq "motdetsc") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + if (!$prop || $prop !~ /^(disable|camera|SVS)$/) { return " \"$opt\" needs one of those arguments: disable, camera, SVS !";} - $hash->{HELPER}{MOTDETSC} = $prop; + $hash->{HELPER}{MOTDETSC} = $prop; - if ($prop1) { - # check ob Zahl zwischen 1 und 99 - return "invalid value for sensitivity (SVS or camera) - use number between 1 - 99" if ($prop1 !~ /^([1-9]|[1-9][0-9])*$/); - $hash->{HELPER}{MOTDETSC_PROP1} = $prop1; - } - if ($prop2) { - # check ob Zahl zwischen 1 und 99 - return "invalid value for threshold (SVS) / object size (camera) - use number between 1 - 99" if ($prop2 !~ /^([1-9]|[1-9][0-9])*$/); - $hash->{HELPER}{MOTDETSC_PROP2} = $prop2; - } - if ($prop3) { - # check ob Zahl zwischen 1 und 99 - return "invalid value for threshold (SVS) / object size (camera) - use number between 1 - 99" if ($prop3 !~ /^([1-9]|[1-9][0-9])*$/); - $hash->{HELPER}{MOTDETSC_PROP3} = $prop3; - } - cammotdetsc($hash); - } - elsif ($opt eq "credentials") - { - return "module is deactivated" if(IsDisabled($name)); - return "Credentials are incomplete, use username password" if (!$prop || !$prop1); - delete $hash->{HELPER}{SID} if($hash->{HELPER}{SID}); - ($success) = setcredentials($hash,$prop,$prop1); - $hash->{HELPER}{ACTIVE} = "off"; - if($success) { - getsvsinfo($hash); - return "Username and Password saved successfully"; - } else { - return "Error while saving Username / Password - see logfile for details"; - } + if ($prop1) { + # check ob Zahl zwischen 1 und 99 + return "invalid value for sensitivity (SVS or camera) - use number between 1 - 99" if ($prop1 !~ /^([1-9]|[1-9][0-9])*$/); + $hash->{HELPER}{MOTDETSC_PROP1} = $prop1; + } + if ($prop2) { + # check ob Zahl zwischen 1 und 99 + return "invalid value for threshold (SVS) / object size (camera) - use number between 1 - 99" if ($prop2 !~ /^([1-9]|[1-9][0-9])*$/); + $hash->{HELPER}{MOTDETSC_PROP2} = $prop2; + } + if ($prop3) { + # check ob Zahl zwischen 1 und 99 + return "invalid value for threshold (SVS) / object size (camera) - use number between 1 - 99" if ($prop3 !~ /^([1-9]|[1-9][0-9])*$/); + $hash->{HELPER}{MOTDETSC_PROP3} = $prop3; + } + cammotdetsc($hash); + + } elsif ($opt eq "credentials") { + return "Credentials are incomplete, use username password" if (!$prop || !$prop1); + delete $hash->{HELPER}{SID} if($hash->{HELPER}{SID}); + ($success) = setcredentials($hash,$prop,$prop1); + $hash->{HELPER}{ACTIVE} = "off"; + + if($success) { + getsvsinfo($hash); + return "Username and Password saved successfully"; + } else { + return "Error while saving Username / Password - see logfile for details"; + } - } - elsif ($opt eq "expmode") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - unless ($prop) { return " \"$opt\" needs one of those arguments: auto, day, night !";} + } elsif ($opt eq "expmode") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + unless ($prop) { return " \"$opt\" needs one of those arguments: auto, day, night !";} - $hash->{HELPER}{EXPMODE} = $prop; - camexpmode($hash); - } - elsif ($opt eq "goPreset") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - if (!$prop) {return "Function \"goPreset\" needs a \"Presetname\" as an argument";} + $hash->{HELPER}{EXPMODE} = $prop; + camexpmode($hash); + + } elsif ($opt eq "goPreset") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + if (!$prop) {return "Function \"goPreset\" needs a \"Presetname\" as an argument";} - @prop = split(/;/, $prop); - $prop = $prop[0]; - @prop = split(/,/, $prop); - $prop = $prop[0]; - $hash->{HELPER}{GOPRESETNAME} = $prop; - $hash->{HELPER}{PTZACTION} = "gopreset"; - doptzaction($hash); - } - elsif ($opt eq "runPatrol") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - if (!$prop) {return "Function \"runPatrol\" needs a \"Patrolname\" as an argument";} + @prop = split(/;/, $prop); + $prop = $prop[0]; + @prop = split(/,/, $prop); + $prop = $prop[0]; + $hash->{HELPER}{GOPRESETNAME} = $prop; + $hash->{HELPER}{PTZACTION} = "gopreset"; + doptzaction($hash); + + } elsif ($opt eq "runPatrol") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + if (!$prop) {return "Function \"runPatrol\" needs a \"Patrolname\" as an argument";} - @prop = split(/;/, $prop); - $prop = $prop[0]; - @prop = split(/,/, $prop); - $prop = $prop[0]; - $hash->{HELPER}{GOPATROLNAME} = $prop; - $hash->{HELPER}{PTZACTION} = "runpatrol"; - doptzaction($hash); - } - elsif ($opt eq "goAbsPTZ") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + @prop = split(/;/, $prop); + $prop = $prop[0]; + @prop = split(/,/, $prop); + $prop = $prop[0]; + $hash->{HELPER}{GOPATROLNAME} = $prop; + $hash->{HELPER}{PTZACTION} = "runpatrol"; + doptzaction($hash); + + } elsif ($opt eq "goAbsPTZ") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - if ($prop eq "up" || $prop eq "down" || $prop eq "left" || $prop eq "right") { - if ($prop eq "up") {$hash->{HELPER}{GOPTZPOSX} = 320; $hash->{HELPER}{GOPTZPOSY} = 480;} - if ($prop eq "down") {$hash->{HELPER}{GOPTZPOSX} = 320; $hash->{HELPER}{GOPTZPOSY} = 0;} - if ($prop eq "left") {$hash->{HELPER}{GOPTZPOSX} = 0; $hash->{HELPER}{GOPTZPOSY} = 240;} - if ($prop eq "right") {$hash->{HELPER}{GOPTZPOSX} = 640; $hash->{HELPER}{GOPTZPOSY} = 240;} + if ($prop eq "up" || $prop eq "down" || $prop eq "left" || $prop eq "right") { + if ($prop eq "up") {$hash->{HELPER}{GOPTZPOSX} = 320; $hash->{HELPER}{GOPTZPOSY} = 480;} + if ($prop eq "down") {$hash->{HELPER}{GOPTZPOSX} = 320; $hash->{HELPER}{GOPTZPOSY} = 0;} + if ($prop eq "left") {$hash->{HELPER}{GOPTZPOSX} = 0; $hash->{HELPER}{GOPTZPOSY} = 240;} + if ($prop eq "right") {$hash->{HELPER}{GOPTZPOSX} = 640; $hash->{HELPER}{GOPTZPOSY} = 240;} - $hash->{HELPER}{PTZACTION} = "goabsptz"; - doptzaction($hash); - return undef; - } - else - { - if ($prop !~ /\d+/ || $prop1 !~ /\d+/ || abs($prop) > 640 || abs($prop1) > 480) { - return "Function \"goAbsPTZ\" needs two coordinates, posX=0-640 and posY=0-480, as arguments or use up, down, left, right instead"; - } + $hash->{HELPER}{PTZACTION} = "goabsptz"; + doptzaction($hash); + return undef; + + } else { + if ($prop !~ /\d+/ || $prop1 !~ /\d+/ || abs($prop) > 640 || abs($prop1) > 480) { + return "Function \"goAbsPTZ\" needs two coordinates, posX=0-640 and posY=0-480, as arguments or use up, down, left, right instead"; + } - $hash->{HELPER}{GOPTZPOSX} = abs($prop); - $hash->{HELPER}{GOPTZPOSY} = abs($prop1); + $hash->{HELPER}{GOPTZPOSX} = abs($prop); + $hash->{HELPER}{GOPTZPOSY} = abs($prop1); - $hash->{HELPER}{PTZACTION} = "goabsptz"; - doptzaction($hash); + $hash->{HELPER}{PTZACTION} = "goabsptz"; + doptzaction($hash); - return undef; + return undef; - } - return "Function \"goAbsPTZ\" needs two coordinates, posX=0-640 and posY=0-480, as arguments or use up, down, left, right instead"; + } + return "Function \"goAbsPTZ\" needs two coordinates, posX=0-640 and posY=0-480, as arguments or use up, down, left, right instead"; - } - elsif ($opt eq "move") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + } elsif ($opt eq "move") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - if (!defined($prop) || ($prop ne "up" && $prop ne "down" && $prop ne "left" && $prop ne "right" && $prop !~ m/dir_\d/)) {return "Function \"move\" needs an argument like up, down, left, right or dir_X (X = 0 to CapPTZDirections-1)";} + if (!defined($prop) || ($prop ne "up" && $prop ne "down" && $prop ne "left" && $prop ne "right" && $prop !~ m/dir_\d/)) {return "Function \"move\" needs an argument like up, down, left, right or dir_X (X = 0 to CapPTZDirections-1)";} - $hash->{HELPER}{GOMOVEDIR} = $prop; - $hash->{HELPER}{GOMOVETIME} = defined($prop1) ? $prop1 : 1; + $hash->{HELPER}{GOMOVEDIR} = $prop; + $hash->{HELPER}{GOMOVETIME} = defined($prop1) ? $prop1 : 1; - $hash->{HELPER}{PTZACTION} = "movestart"; - doptzaction($hash); - } - elsif ($opt eq "runView") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + $hash->{HELPER}{PTZACTION} = "movestart"; + doptzaction($hash); + + } elsif ($opt eq "runView") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - if ($prop eq "live_open") { - if ($prop1) {$hash->{HELPER}{VIEWOPENROOM} = $prop1;} else {delete $hash->{HELPER}{VIEWOPENROOM};} - $hash->{HELPER}{OPENWINDOW} = 1; - $hash->{HELPER}{WLTYPE} = "link"; - $hash->{HELPER}{ALIAS} = "LiveView"; - $hash->{HELPER}{RUNVIEW} = "live_open"; - } elsif ($prop eq "live_link") { - $hash->{HELPER}{OPENWINDOW} = 0; - $hash->{HELPER}{WLTYPE} = "link"; - $hash->{HELPER}{ALIAS} = "LiveView"; - $hash->{HELPER}{RUNVIEW} = "live_link"; - } elsif ($prop eq "lastrec_open") { - if ($prop1) {$hash->{HELPER}{VIEWOPENROOM} = $prop1;} else {delete $hash->{HELPER}{VIEWOPENROOM};} - $hash->{HELPER}{OPENWINDOW} = 1; - $hash->{HELPER}{WLTYPE} = "link"; - $hash->{HELPER}{ALIAS} = "LastRecording"; - $hash->{HELPER}{RUNVIEW} = "lastrec_open"; - } elsif ($prop eq "lastrec_fw") { - $hash->{HELPER}{OPENWINDOW} = 0; - $hash->{HELPER}{WLTYPE} = "iframe"; - $hash->{HELPER}{ALIAS} = " "; - $hash->{HELPER}{RUNVIEW} = "lastrec"; - } elsif ($prop eq "live_fw") { - $hash->{HELPER}{OPENWINDOW} = 0; - $hash->{HELPER}{WLTYPE} = "image"; - $hash->{HELPER}{ALIAS} = " "; - $hash->{HELPER}{RUNVIEW} = "live_fw"; - } elsif ($prop eq "lastsnap_fw") { - $hash->{HELPER}{OPENWINDOW} = 0; - $hash->{HELPER}{WLTYPE} = "base64img"; - $hash->{HELPER}{ALIAS} = " "; - $hash->{HELPER}{RUNVIEW} = "lastsnap_fw"; - } else { - return "$prop isn't a valid option of runview, use one of live_fw, live_link, live_open, lastrec_fw, lastrec_open, lastsnap_fw"; - } - runliveview($hash); + if ($prop eq "live_open") { + if ($prop1) {$hash->{HELPER}{VIEWOPENROOM} = $prop1;} else {delete $hash->{HELPER}{VIEWOPENROOM};} + $hash->{HELPER}{OPENWINDOW} = 1; + $hash->{HELPER}{WLTYPE} = "link"; + $hash->{HELPER}{ALIAS} = "LiveView"; + $hash->{HELPER}{RUNVIEW} = "live_open"; + } elsif ($prop eq "live_link") { + $hash->{HELPER}{OPENWINDOW} = 0; + $hash->{HELPER}{WLTYPE} = "link"; + $hash->{HELPER}{ALIAS} = "LiveView"; + $hash->{HELPER}{RUNVIEW} = "live_link"; + } elsif ($prop eq "lastrec_open") { + if ($prop1) {$hash->{HELPER}{VIEWOPENROOM} = $prop1;} else {delete $hash->{HELPER}{VIEWOPENROOM};} + $hash->{HELPER}{OPENWINDOW} = 1; + $hash->{HELPER}{WLTYPE} = "link"; + $hash->{HELPER}{ALIAS} = "LastRecording"; + $hash->{HELPER}{RUNVIEW} = "lastrec_open"; + } elsif ($prop eq "lastrec_fw") { + $hash->{HELPER}{OPENWINDOW} = 0; + $hash->{HELPER}{WLTYPE} = "iframe"; + $hash->{HELPER}{ALIAS} = " "; + $hash->{HELPER}{RUNVIEW} = "lastrec"; + } elsif ($prop eq "live_fw") { + $hash->{HELPER}{OPENWINDOW} = 0; + $hash->{HELPER}{WLTYPE} = "image"; + $hash->{HELPER}{ALIAS} = " "; + $hash->{HELPER}{RUNVIEW} = "live_fw"; + } elsif ($prop eq "lastsnap_fw") { + $hash->{HELPER}{OPENWINDOW} = 0; + $hash->{HELPER}{WLTYPE} = "base64img"; + $hash->{HELPER}{ALIAS} = " "; + $hash->{HELPER}{RUNVIEW} = "lastsnap_fw"; + } else { + return "$prop isn't a valid option of runview, use one of live_fw, live_link, live_open, lastrec_fw, lastrec_open, lastsnap_fw"; + } + runliveview($hash); - } - elsif ($opt eq "extevent") - { - return "module is deactivated" if(IsDisabled($name)); - if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + } elsif ($opt eq "extevent") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - $hash->{HELPER}{EVENTID} = $prop; - extevent($hash); - } - elsif ($opt eq "stopView") - { - return "module is deactivated" if(IsDisabled($name)); - stopliveview($hash); - } - else - { - return "$setlist"; - } + $hash->{HELPER}{EVENTID} = $prop; + extevent($hash); + + } elsif ($opt eq "stopView") { + stopliveview($hash); + + } else { + return "$setlist"; + } + return; } @@ -653,9 +760,13 @@ sub SSCam_Get { return "\"get X\" needs at least an argument" if ( @a < 2 ); my $name = shift @a; my $opt = shift @a; - + my $arg = shift @a; + my $ret = ""; + my $getlist = "Unknown argument $opt, choose one of ". "caminfoall:noArg ". + ((AttrVal($name,"snapGalleryNumber",undef) || AttrVal($name,"snapGalleryBoost",0)) + ?"snapGallery:noArg ":"snapGallery:$SSCAM_snum "). "snapinfo:noArg ". "svsinfo:noArg ". "snapfileinfo:noArg ". @@ -677,8 +788,42 @@ sub SSCam_Get { } elsif ($opt eq "svsinfo") { getsvsinfo($hash); + } elsif ($opt eq "snapGallery") { + if(!AttrVal($name, "snapGalleryBoost",0)) { + # Snaphash ist nicht vorhanden und wird neu abgerufen und ausgegeben + if (defined($hash->{CL})) { + # Clienthash auflösen zur Fehlersuche (aufrufende FHEMWEB Instanz) + while (my ($key,$val) = each(%{$hash->{CL}})) { + $val = $val?$val:" "; + Log3($name, 4, "$name - snapGallery Clienthash: $key -> $val"); + } + } else { + Log3($name, 2, "$name - snapGallery Clienthash wasn't delivered !"); + return "Clienthash wasn't delivered. Can't use asynchronous output for snapGallery."; + } + return "Clienthash doesn't contain \"canAsyncOutput\" and therefore can't use asynchronous output for snapGallery. Please set attribute \"snapGalleryBoost=1\" if this error appears constantly." + if(!$hash->{CL}{canAsyncOutput}); + + $hash->{HELPER}{CL} = $hash->{CL}; + $hash->{HELPER}{GETSNAPGALLERY} = 1; + + # snap-Infos für Gallerie abrufen + my ($sg,$slim,$ssize); + $slim = $arg?AttrVal($name,"snapGalleryNumber",$arg):AttrVal($name,"snapGalleryNumber",$SSCam_slim); # Anzahl der abzurufenden Snapshots + $ssize = (AttrVal($name,"snapGallerySize","Icon") eq "Icon")?1:2; # Image Size 1-Icon, 2-Full + + getsnapinfo("$name:$slim:$ssize"); + + } else { + # Snaphash ist vorhanden und wird zur Ausgabe aufbereitet + my $htmlCode = composegallery($name); + return $htmlCode; + } + } elsif ($opt eq "snapinfo") { - getsnapinfo("$name:0:0"); + # Schnappschußgalerie abrufen (snapGalleryBoost) oder nur Info des letzten Snaps + my ($slim,$ssize) = snaplimsize($hash); + getsnapinfo("$name:$slim:$ssize"); } elsif ($opt eq "snapfileinfo") { if (!ReadingsVal("$name", "LastSnapId", undef)) {return "Reading LastSnapId is empty - please take a snapshot before !"} @@ -700,7 +845,7 @@ sub SSCam_Get { } else { return "$getlist"; } -return; +return $ret; # not generate trigger out of command } ###################################################################################### @@ -768,7 +913,11 @@ sub initonboot ($) { getsvsinfo($hash); # Kameraspezifische Infos holen getcaminfo($hash); - getsnapinfo("$name:0:0"); + + # Schnappschußgalerie abrufen (snapGalleryBoost) oder nur Info des letzten Snaps + my ($slim,$ssize) = snaplimsize($hash); + getsnapinfo("$name:$slim:$ssize"); + getcapabilities($hash); # Preset/Patrollisten in Hash einlesen zur PTZ-Steuerung getptzlistpreset($hash); @@ -1241,10 +1390,7 @@ sub runliveview ($) { # Liveview starten $hash->{OPMODE} = "runliveview"; $hash->{HELPER}{ACTIVE} = "on"; - $hash->{HELPER}{LOGINRETRIES} = 0; - $hash->{HELPER}{SNAPLIMIT} = 1; # nur 1 Snap laden, für lastsnap_fw - $hash->{HELPER}{SNAPIMGSIZE} = 2; # full size picture, für lastsnap_fw - $hash->{HELPER}{KEYWORD} = $camname; # nur Snaps von $camname selektieren, für lastsnap_fw + $hash->{HELPER}{LOGINRETRIES} = 0; # erzwingen die Camid zu ermitteln und bei login-Fehler neue SID zu holen delete $hash->{CAMID}; @@ -1541,9 +1687,13 @@ sub getcaminfoall { geteventlist($hash); RemoveInternalTimer($hash, "getcaminfo"); InternalTimer(gettimeofday()+0.4, "getcaminfo", $hash, 0); + + # Schnappschußgalerie abrufen (snapGalleryBoost) oder nur Info des letzten Snaps + my ($slim,$ssize) = snaplimsize($hash); RemoveInternalTimer($hash, "getsnapinfo"); - InternalTimer(gettimeofday()+0.6, "getsnapinfo", "$name:0:0", 0); - RemoveInternalTimer($hash, "getmotionenum"); + InternalTimer(gettimeofday()+0.6, "getsnapinfo", "$name:$slim:$ssize", 0); + + RemoveInternalTimer($hash, "getmotionenum"); InternalTimer(gettimeofday()+0.8, "getmotionenum", $hash, 0); RemoveInternalTimer($hash, "getcapabilities"); InternalTimer(gettimeofday()+1.3, "getcapabilities", $hash, 0); @@ -1582,7 +1732,7 @@ return; } ########################################################################### -# Infos zum letzten Snap abfragen (z.B. weil nicht über SSCam ausgelöst) +# Infos zu Snaps abfragen (z.B. weil nicht über SSCam ausgelöst) ########################################################################### sub getsnapinfo ($) { my ($str) = @_; @@ -1594,9 +1744,10 @@ sub getsnapinfo ($) { if ($hash->{HELPER}{ACTIVE} eq "off") { $hash->{OPMODE} = "getsnapinfo"; + $hash->{OPMODE} = "getsnapgallery" if(exists($hash->{HELPER}{GETSNAPGALLERY})); $hash->{HELPER}{ACTIVE} = "on"; $hash->{HELPER}{LOGINRETRIES} = 0; - $hash->{HELPER}{SNAPLIMIT} = $slim; # alle Snapshots werden abgerufen und ausgewertet + $hash->{HELPER}{SNAPLIMIT} = $slim; # 0-alle Snapshots werden abgerufen und ausgewertet, sonst $slim $hash->{HELPER}{SNAPIMGSIZE} = $ssize; # 0-Do not append image, 1-Icon size, 2-Full size $hash->{HELPER}{KEYWORD} = $camname; @@ -2450,11 +2601,12 @@ sub sscam_camop ($) { readingsSingleUpdate($hash,"state", "snap", 1); readingsSingleUpdate($hash, "LastSnapId", "", 0); - } elsif ($OpMode eq "getsnapinfo") { + } elsif ($OpMode eq "getsnapinfo" || $OpMode eq "getsnapgallery") { # Informationen über den letzten oder mehrere Schnappschüsse ermitteln my $limit = $hash->{HELPER}{SNAPLIMIT}; my $imgsize = $hash->{HELPER}{SNAPIMGSIZE}; my $keyword = $hash->{HELPER}{KEYWORD}; + Log3($name,4, "$name - Call getsnapinfo with params: Image numbers => $limit, Image size => $imgsize, Keyword => $keyword"); $url = "http://$serveraddr:$serverport/webapi/$apitakesnappath?api=\"$apitakesnap\"&method=\"List\"&version=\"$apitakesnapmaxver\"&keyword=\"$keyword\"&imgSize=\"$imgsize\"&limit=\"$limit\"&_sid=\"$sid\""; } elsif ($OpMode eq "getsnapfilename") { @@ -2645,9 +2797,9 @@ sub sscam_camop ($) { } elsif ($OpMode eq "runliveview" && $hash->{HELPER}{RUNVIEW} =~ /snap/) { # den letzten Schnappschuß life anzeigen - my $limit = $hash->{HELPER}{SNAPLIMIT}; - my $imgsize = $hash->{HELPER}{SNAPIMGSIZE}; - my $keyword = $hash->{HELPER}{KEYWORD}; + my $limit = 1; # nur 1 Snap laden, für lastsnap_fw + my $imgsize = 2; # full size picture, für lastsnap_fw + my $keyword = $hash->{CAMNAME}; # nur Snaps von $camname selektieren, für lastsnap_fw $url = "http://$serveraddr:$serverport/webapi/$apitakesnappath?api=\"$apitakesnap\"&method=\"List\"&version=\"$apitakesnapmaxver\"&keyword=\"$keyword\"&imgSize=\"$imgsize\"&limit=\"$limit\"&_sid=\"$sid\""; } @@ -2829,61 +2981,45 @@ sub sscam_camop_parse ($) { } elsif ($OpMode eq "Snap") { # ein Schnapschuß wurde aufgenommen # falls Aufnahme noch läuft -> state = on setzen - if (ReadingsVal("$name", "Record", "Stop") eq "Start") { - readingsSingleUpdate( $hash,"state", "on", 0); - } else { - readingsSingleUpdate($hash,"state", "off", 0); - } + my $st; + (ReadingsVal("$name", "Record", "") eq "Start")?$st="on":$st="off"; + readingsSingleUpdate($hash,"state", $st, 0); $snapid = $data->{data}{'id'}; readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); - readingsBulkUpdate($hash,"LastSnapId",$snapid); readingsEndUpdate($hash, 1); # Logausgabe Log3($name, 3, "$name - Snapshot of Camera $camname has been done successfully"); - # nach Snap Aufnahme Filename des Snaps ermitteln - getsnapfilename($hash); + # Token freigeben vor nächstem Kommando + $hash->{HELPER}{ACTIVE} = "off"; + + # Schnappschußgalerie abrufen (snapGalleryBoost) oder nur Info des letzten Snaps + my ($slim,$ssize) = snaplimsize($hash); + RemoveInternalTimer($hash, "getsnapinfo"); + InternalTimer(gettimeofday()+0.6, "getsnapinfo", "$name:$slim:$ssize", 0); - } elsif ($OpMode eq "getsnapinfo" || $OpMode eq "runliveview") { - # Informationen zu einem oder mehreren Schnapschüssen wurde abgerufen bzw. Lifeanzeige Schappschuß - my $i = 0; - my $sn = 0; - my %allsnaps = (); - while ($data->{'data'}{'data'}[$i]) { - if($data->{'data'}{'data'}[$i]{'camName'} ne $camname) { - $i += 1; - next; - } - $snapid = $data->{'data'}{'data'}[$i]{'id'}; - my $createdTm = $data->{'data'}{'data'}[$i]{'createdTm'}; - my $fileName = $data->{'data'}{'data'}[$i]{'fileName'}; - my $imageData = $data->{'data'}{'data'}[$i]{'imageData'}; # Image data of snapshot in base64 format - - $allsnaps{$sn}{"snapid"} = $snapid; - my @t = split(" ", FmtDateTime($createdTm)); + } elsif ($OpMode eq "getsnapinfo" || $OpMode eq "getsnapgallery" || $OpMode eq "runliveview") { + # Informationen zu einem oder mehreren Schnapschüssen wurde abgerufen bzw. Lifeanzeige Schappschuß + my $lsid = exists($data->{data}{data}[0]{id})?$data->{data}{data}[0]{id}:"n.a."; + my $lfname = exists($data->{data}{data}[0]{fileName})?$data->{data}{data}[0]{fileName}:"n.a."; + + my $lstime; + if(exists($data->{data}{data}[0]{createdTm})) { + $lstime = $data->{data}{data}[0]{createdTm}; + my @t = split(" ", FmtDateTime($lstime)); my @d = split("-", $t[0]); - $createdTm = "$d[2].$d[1].$d[0] / $t[1]"; - $allsnaps{$sn}{createdTm} = $createdTm; - $allsnaps{$sn}{fileName} = $fileName; - $allsnaps{$sn}{imageData} = $imageData; - $sn += 1; - $i += 1; - } + $lstime = "$d[2].$d[1].$d[0] / $t[1]"; + } else { + $lstime = "n.a."; + } - my @as = sort{$a <=>$b}keys(%allsnaps); - foreach my $key (@as) { - Log3($name,5, "$name - Snap '$key': ID => $allsnaps{$key}{snapid}, File => $allsnaps{$key}{fileName}, Created => $allsnaps{$key}{createdTm}"); - } - - my $lsid = exists($allsnaps{0}{snapid})?$allsnaps{0}{snapid}:"n.a."; - my $lfname = exists($allsnaps{0}{fileName})?$allsnaps{0}{fileName}:"n.a."; - my $lstime = exists($allsnaps{0}{createdTm})?$allsnaps{0}{createdTm}:"n.a."; - + Log3($name,4, "$name - Snap [0]: ID => $lsid, File => $lfname, Created => $lstime"); + readingsBeginUpdate($hash); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); @@ -2893,20 +3029,67 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Schnapschuss soll als liveView angezeigt werden (mindestens 1 Bild vorhanden) - if (exists($hash->{HELPER}{RUNVIEW}) && $hash->{HELPER}{RUNVIEW} =~ /snap/ && exists($allsnaps{0}{imageData})) { + Log3($name, 3, "$name - There is no snapshot of camera $camname to display ! Take one snapshot before.") + if(exists($hash->{HELPER}{RUNVIEW}) && $hash->{HELPER}{RUNVIEW} =~ /snap/ && !exists($data->{'data'}{'data'}[0]{imageData})); + + if (exists($hash->{HELPER}{RUNVIEW}) && $hash->{HELPER}{RUNVIEW} =~ /snap/ && exists($data->{'data'}{'data'}[0]{imageData})) { delete $hash->{HELPER}{RUNVIEW}; # Aufnahmestatus in state abbilden my $st; (ReadingsVal("$name", "Record", "") eq "Start")?$st="on":$st="off"; readingsSingleUpdate($hash,"state", $st, 1); - $hash->{HELPER}{LINK} = $allsnaps{0}{imageData}; + $hash->{HELPER}{LINK} = $data->{data}{data}[0]{imageData}; # Longpoll refresh DoTrigger($name,"startview"); } + + if($OpMode eq "getsnapgallery") { + # es soll eine Schnappschußgallerie bereitgestellt (Attr snapGalleryBoost=1) bzw. gleich angezeigt werden (Attr snapGalleryBoost=0) + my $i = 0; + my $sn = 0; + my %allsnaps = (); # Schnappschuss Hash wird leer erstellt + + $hash->{HELPER}{TOTALCNT} = $data->{data}{total}; # total Anzahl Schnappschüsse + + while ($data->{'data'}{'data'}[$i]) { + if($data->{'data'}{'data'}[$i]{'camName'} ne $camname) { + $i += 1; + next; + } + $snapid = $data->{data}{data}[$i]{id}; + my $createdTm = $data->{data}{data}[$i]{createdTm}; + my $fileName = $data->{data}{data}[$i]{fileName}; + my $imageData = $data->{data}{data}[$i]{imageData}; # Image data of snapshot in base64 format + + $allsnaps{$sn}{snapid} = $snapid; + my @t = split(" ", FmtDateTime($createdTm)); + my @d = split("-", $t[0]); + $createdTm = "$d[2].$d[1].$d[0] / $t[1]"; + $allsnaps{$sn}{createdTm} = $createdTm; + $allsnaps{$sn}{fileName} = $fileName; + $allsnaps{$sn}{imageData} = $imageData; + Log3($name,4, "$name - Snap '$sn' added to gallery hash: ID => $allsnaps{$sn}{snapid}, File => $allsnaps{$sn}{fileName}, Created => $allsnaps{$sn}{createdTm}"); + $sn += 1; + $i += 1; + } + + # Hash der Schnapschüsse erstellen + $hash->{HELPER}{SNAPHASH} = \%allsnaps; + + # Direktausgabe Snaphash wenn nicht gepollt wird + if(!AttrVal($name, "snapGalleryBoost",0)) { + my $htmlCode = composegallery($name); + asyncOutput($hash->{HELPER}{CL}, "$htmlCode"); + delete($hash->{HELPER}{SNAPHASH}); # Snaphash löschen wenn nicht gepollt wird + delete($hash->{HELPER}{CL}); + } + + delete($hash->{HELPER}{GETSNAPGALLERY}); # Steuerbit getsnapgallery statt getsnapinfo + } # Logausgabe - Log3($name, $verbose, "$name - Snapinfos of Camera $camname have been retrieved successfully"); + Log3($name, $verbose, "$name - Snapinfos of camera $camname retrieved"); } elsif ($OpMode eq "getsnapfilename") { @@ -3090,7 +3273,7 @@ sub sscam_camop_parse ($) { readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); - Log3($name, $verbose, "$name - Informations related to Surveillance Station retrieved successfully"); + Log3($name, $verbose, "$name - Informations related to Surveillance Station retrieved"); } elsif ($OpMode eq "getStmUrlPath") { # Parse SVS-Infos @@ -3134,7 +3317,7 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Logausgabe - Log3($name, $verbose, "$name - Stream-URLs of $camname retrieved successfully"); + Log3($name, $verbose, "$name - Stream-URLs of camera $camname retrieved"); } elsif ($OpMode eq "Getcaminfo") { # Parse Caminfos @@ -3251,7 +3434,7 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Logausgabe - Log3($name, $verbose, "$name - Informations of camera $camname have been retrieved successfully"); + Log3($name, $verbose, "$name - Informations of camera $camname retrieved"); } elsif ($OpMode eq "geteventlist") { my $eventnum = $data->{'data'}{'total'}; @@ -3280,7 +3463,7 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Logausgabe - Log3($name, $verbose, "$name - Query eventlist of camera $camname have been retrieved successfully"); + Log3($name, $verbose, "$name - Query eventlist of camera $camname retrieved"); } elsif ($OpMode eq "getmotionenum") { @@ -3347,7 +3530,7 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Logausgabe - Log3($name, $verbose, "$name - Enumerate motion detection parameters of $camname have been retrieved successfully"); + Log3($name, $verbose, "$name - Enumerate motion detection parameters of camera $camname retrieved"); } elsif ($OpMode eq "Getcapabilities") { # Parse Infos @@ -3424,7 +3607,7 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Logausgabe - Log3($name, $verbose, "$name - Capabilities of Camera $camname have been retrieved successfully"); + Log3($name, $verbose, "$name - Capabilities of camera $camname retrieved"); } elsif ($OpMode eq "Getptzlistpreset") { # Parse PTZ-ListPresets @@ -3457,7 +3640,7 @@ sub sscam_camop_parse ($) { # Logausgabe - Log3($name, $verbose, "$name - PTZ Presets of $camname retrieved"); + Log3($name, $verbose, "$name - PTZ Presets of camera $camname retrieved"); } elsif ($OpMode eq "Getptzlistpatrol") { # Parse PTZ-ListPatrols @@ -3490,7 +3673,7 @@ sub sscam_camop_parse ($) { readingsEndUpdate($hash, 1); # Logausgabe - Log3($name, $verbose, "$name - PTZ Patrols of $camname retrieved"); + Log3($name, $verbose, "$name - PTZ Patrols of camera $camname retrieved"); } } else { @@ -3516,11 +3699,14 @@ sub sscam_camop_parse ($) { Log3($name, 2, "$name - ERROR - Operation $OpMode of Camera $camname was not successful. Errorcode: $errorcode - $error"); } } - + + # Token freigeben $hash->{HELPER}{ACTIVE} = "off"; + if ($attr{$name}{debugactivetoken}) { Log3($name, 3, "$name - Active-Token deleted by OPMODE: $hash->{OPMODE}"); } + return; } @@ -3803,7 +3989,6 @@ return; ############################################################################### # Test ob JSON-String empfangen wurde - sub evaljson { my ($hash,$myjson,$url)= @_; my $success = 1; @@ -3823,9 +4008,118 @@ sub evaljson { return($hash,$success); } +############################################################################### +# Schnappschußgalerie abrufen (snapGalleryBoost) oder nur Info des letzten Snaps +sub snaplimsize ($) { + my ($hash)= @_; + my $name = $hash->{NAME}; + my ($slim,$ssize); + + if(!AttrVal($name,"snapGalleryBoost",0)) { + $slim = 1; + $ssize = 0; + } else { + $hash->{HELPER}{GETSNAPGALLERY} = 1; + $slim = AttrVal($name,"snapGalleryNumber",$SSCam_slim); # Anzahl der abzurufenden Snaps + my $sg = AttrVal($name,"snapGallerySize","Icon"); # Auflösung Image + $ssize = ($sg eq "Icon")?1:2; + } +return ($slim,$ssize); +} + +############################################################################### +# Schnappschußgalerie zusammenstellen +sub composegallery ($;$$) { + my ($name,$wlname) = @_; + my $hash = $defs{$name}; + my $camname = $hash->{CAMNAME}; + my $allsnaps = $hash->{HELPER}{SNAPHASH}; # = \%allsnaps + my $sgc = AttrVal($name,"snapGalleryColumns",3); # Anzahl der Images in einer Tabellenzeile + my $lss = ReadingsVal($name, "LastSnapTime", " "); # Zeitpunkt neueste Aufnahme + my $lang = AttrVal("global","language","EN"); # Systemsprache + my $limit = $hash->{HELPER}{SNAPLIMIT}; # abgerufene Anzahl Snaps + my $totalcnt = $hash->{HELPER}{TOTALCNT}; # totale Anzahl Snaps + $limit = $totalcnt if ($limit > $totalcnt); # wenn weniger Snaps vorhanden sind als $limit -> Text in Anzeige korrigieren + my $lupt = ((ReadingsTimestamp($name,"LastSnapTime"," ") gt ReadingsTimestamp($name,"LastUpdateTime"," ")) + ? ReadingsTimestamp($name, "LastSnapTime", " ") + : ReadingsTimestamp($name, "LastUpdateTime", " ")); # letzte Aktualisierung + $lupt =~ s/ / \/ /; + + my $ha = AttrVal($name, "snapGalleryHtmlAttr", undef)?AttrVal($name, "snapGalleryHtmlAttr", undef):AttrVal($name, "htmlattr", 'width="500" height="325"'); + + # falls "composegallery" durch ein mit mit "createSnapGallery" angelegtes Device aufgerufen wird + my ($devWlink,$wlhash,$wlha,$wlalias); + if ($wlname) { + $wlalias = $attr{$wlname}{alias}?$attr{$wlname}{alias}:$wlname; # Linktext als Aliasname oder Devicename setzen + $devWlink = "$wlalias"; + $wlhash = $defs{$wlname}; + $wlha = $attr{$wlname}{htmlattr}; + $ha = (defined($wlha))?$wlha:$ha; # htmlattr vom weblink-Device übernehmen falls von wl-Device aufgerufen und gesetzt + } else { + $devWlink = " "; + } + + # wenn Weblink genutzt wird und attr "snapGalleryBoost" nicht gesetzt ist -> Warnung in Gallerie ausgeben + my $sgbnote = " "; + if($wlname && !AttrVal($name,"snapGalleryBoost",0)) { + $sgbnote = "CAUTION - No snapshots can be retrieved. Please set the attribute \"snapGalleryBoost=1\" in device $name" if ($lang eq "EN"); + $sgbnote = "ACHTUNG - Es können keine Schnappschüsse abgerufen werden. Bitte setzen sie das Attribut \"snapGalleryBoost=1\" im Device $name" if ($lang eq "DE"); + } + + my $header; + if ($lang eq "EN") { + $header = "Snapshots ($limit/$totalcnt) of camera $camname - newest Snapshot: $lss
"; + $header .= " (Possibly another snapshots are available. Last recall: $lupt)
" if(AttrVal($name,"snapGalleryBoost",0)); + } else { + $header = "Schnappschüsse ($limit/$totalcnt) von Kamera $camname - neueste Aufnahme: $lss
"; + $header .= " (Eventuell sind neuere Aufnahmen verfügbar. Letzter Abruf: $lupt)
" if(AttrVal($name,"snapGalleryBoost",0)); + } + $header .= $sgbnote; + + my $gattr = (AttrVal($name,"snapGallerySize","Icon") eq "Full")?$ha:" "; + + my @as = sort{$a <=>$b}keys%{$allsnaps}; + + # Ausgabetabelle erstellen + my ($htmlCode,$ct); + $htmlCode = ""; + $htmlCode .= sprintf( "$devWlink
$header
"); + $htmlCode .= ""; + $htmlCode .= ""; + $htmlCode .= ""; + my $cell = 1; + + foreach my $key (@as) { + $ct = $allsnaps->{$key}{createdTm}; + my $html = sprintf( "" ); + + $cell++; + + if ( $cell == $sgc+1 ) { + $htmlCode .= $html; + $htmlCode .= ""; + $htmlCode .= ""; + $cell = 1; + } else { + $htmlCode .= $html; + } + } + + if ( $cell == 2 ) { + $htmlCode .= ""; + } + + $htmlCode .= ""; + $htmlCode .= ""; + $htmlCode .= "
$ct
{$key}{imageData}\" />
"; + $htmlCode .= "
"; + $htmlCode .= ""; + +return $htmlCode; +} + ############################################################################## # Auflösung Errorcodes bei Login / Logout - sub experrorauth { # Übernahmewerte sind $hash, $errorcode my ($hash,@errorcode) = @_; @@ -3888,7 +4182,8 @@ sub experror {
  • trigger of external events 1-10 (action rules in SVS)
  • start and stop of camera livestreams, show the last recording and snapshot embedded
  • fetch of livestream-Url's with key (login not needed in that case)
  • -
  • playback of last recording

  • +
  • playback of last recording and playback the last snapshot
  • +
  • create a gallery of the last 1-10 snapshots (as a Popup or permanent weblink-Device)

  • 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.
    @@ -4005,7 +4300,8 @@ sub experror {
  • set ... goAbsPTZ session: ServeillanceStation - observer with privilege objective control of camera
  • set ... move session: ServeillanceStation - observer with privilege objective control of camera
  • set ... runView session: ServeillanceStation - observer with privilege liveview of camera
  • -
  • set ... extevent session: DSM - user as member of admin-group
  • +
  • set ... snapGallery session: ServeillanceStation - observer
  • +
  • set ... extevent session: DSM - user as member of admin-group
  • set ... stopView -
  • set ... credentials -
  • get ... caminfoall session: ServeillanceStation - observer
  • @@ -4043,13 +4339,15 @@ sub experror { "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 - "credentials <username> <password>": save a set of credentils + "createSnapGallery": creates a snapshot gallery as a permanent (weblink)Device + "credentials <username> <password>": save a set of credentils "expmode [ day | night | auto ]": set the exposure mode to day, night or auto "extevent [ 1-10 ]": triggers the external event 1-10 (see actionrule editor in SVS) "motdetsc [ camera | SVS | disable ]": set motion detection to the desired mode "goPreset <Presetname>": moves a PTZ-camera to a predefinied Preset-position "runPatrol <Patrolname>": starts a predefinied patrol (PTZ-cameras) - "goAbsPTZ [ X Y | up | down | left | right ]": moves a PTZ-camera to a absolute X/Y-coordinate or to direction up/down/left/right + "snapGallery [1-10]": creates an output of the last [n] snapshots + "goAbsPTZ [ X Y | up | down | left | right ]": moves a PTZ-camera to a absolute X/Y-coordinate or to direction up/down/left/right "move [ up | down | left | right | dir_X ]": starts a continuous move of PTZ-camera to direction up/down/left/right or dir_X "runView [image | lastrec | lastrec_open | link | link_open <room> ]": starts a livestream as embedded image or link "stopView": stops a camera livestream @@ -4057,73 +4355,22 @@ sub experror {



    - - + +

    + +

    + + +

    + + +

    + +

    - - -

    - - -

    - - -

    - +

    + +

    + + +

    +

    +
    + + +

    + 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.
    @@ -4809,7 +5194,8 @@ sub experror {
  • set ... motdetsc session: ServeillanceStation - Manager
  • set ... goPreset session: ServeillanceStation - Betrachter mit Privileg Objektivsteuerung der Kamera
  • set ... runPatrol session: ServeillanceStation - Betrachter mit Privileg Objektivsteuerung der Kamera
  • -
  • set ... goAbsPTZ session: ServeillanceStation - Betrachter mit Privileg Objektivsteuerung der Kamera
  • +
  • set ... snapGallery session: ServeillanceStation - Betrachter
  • +
  • set ... goAbsPTZ session: ServeillanceStation - Betrachter mit Privileg Objektivsteuerung der Kamera
  • set ... move session: ServeillanceStation - Betrachter mit Privileg Objektivsteuerung der Kamera
  • set ... runView session: ServeillanceStation - Betrachter mit Privileg Liveansicht für Kamera
  • set ... stopView -
  • @@ -4820,6 +5206,7 @@ sub experror {
  • get ... scanVirgin session: ServeillanceStation - Betrachter
  • get ... svsinfo session: ServeillanceStation - Betrachter
  • get ... snapfileinfo session: ServeillanceStation - Betrachter
  • +
  • get ... snapGallery session: ServeillanceStation - Betrachter
  • get ... snapinfo session: ServeillanceStation - Betrachter
  • get ... stmUrlPath session: ServeillanceStation - Betrachter
  • @@ -4841,7 +5228,7 @@ sub experror { Set

    - - + + -

    +
    -

    - Es ist immer die Reihenfolge der Optionswerte zu beachten. Nicht gewünschte Optionen sind mit "0" zu besetzen sofern danach Optionen folgen - deren Werte verändert werden sollen (siehe Beispiele oben). Der Zahlenwert der Optionen beträgt 1 - 99 (außer Sonderfall "0").

    - - Die jeweils verfügbaren Optionen unterliegen der Funktion der Kamera und der Unterstützung durch die SVS. Es können jeweils nur die Optionen genutzt werden die in - SVS -> Kamera bearbeiten -> Ereigniserkennung zur Verfügung stehen. Weitere Infos sind der Online-Hilfe zur SVS zu entnehmen.

    - - Über den Befehl "get ... caminfoall" wird auch das Reading "CamMotDetSc" aktualisiert welches die gegenwärtige Einstellung der Bewegungserkennung dokumentiert. - Es werden nur die Parameter und Parameterwerte angezeigt, welche die SVS aktiv unterstützt. Die Kamera selbst kann weiterführende Einstellmöglichkeiten besitzen.

    - - Beipiel: -
    -  CamMotDetSc    SVS, sensitivity: 76, threshold: 55
    -  
    -

    - -

    - -

    @@ -5102,6 +5358,89 @@ sub experror {

    + +

    + + +

    + +

    + + +

    + +
    @@ -5191,6 +5609,7 @@ sub experror { get <name> eventlist get <name> scanVirgin get <name> snapfileinfo + get <name> snapGallery get <name> snapinfo get <name> stmUrlPath get <name> svsinfo @@ -5230,6 +5649,32 @@ sub experror {

    +
    + + +

    +