diff --git a/fhem/CHANGED b/fhem/CHANGED index ceb87a2f5..fe99ca6f3 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: integrated Email shipping of snapshots, save recordings + locally with get saveRecoring command - new: 98_vitoconnect: initial release - feature: 14_CUL_TCM97001: Add Ventus W155, bugfixing - feature: 89_FULLY: Support for HTTPS added diff --git a/fhem/FHEM/49_SSCam.pm b/fhem/FHEM/49_SSCam.pm index 1a9cc9b59..c1856b9b6 100644 --- a/fhem/FHEM/49_SSCam.pm +++ b/fhem/FHEM/49_SSCam.pm @@ -3,7 +3,7 @@ ######################################################################################################################### # 49_SSCam.pm # -# (c) 2015-2018 by Heiko Maaz +# (c) 2015-2019 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. @@ -41,10 +41,15 @@ use Data::Dumper; # Perl Core mo use MIME::Base64; use Time::HiRes; use HttpUtils; +use Blocking; # für EMail-Versand +use Encode; # no if $] >= 5.017011, warnings => 'experimental'; # Versions History intern our %SSCam_vNotesIntern = ( + "8.3.0" => "02.01.2019 CAMLASTRECID replaced by Reading CamLastRecId, \"SYNO.SurveillanceStation.Recording\" added, ". + "new get command \"saveRecording\"", + "8.2.0" => "02.01.2019 store SMTP credentials with \"smtpcredentials\", SMTP Email integrated ", "8.1.0" => "19.12.2018 tooltipps in camera device for control buttons, commandref revised ", "8.0.0" => "13.12.2018 HLS with sscam_hls.js integrated for SSCamSTRM type hls, realize tooltipps in streaming devices, minor fixes", "7.7.1" => "12.12.2018 change autocreateCams: define new device only if ne device with Internal CAMNAME is defined, ". @@ -103,6 +108,9 @@ our %SSCam_vNotesIntern = ( # Versions History extern our %SSCam_vNotesExtern = ( + "8.3.0" => "02.01.2019 new get command \"saveRecording\"", + "8.2.0" => "02.01.2019 SMTP Email delivery of snapshots implemented. You can send snapshots after it is created subsequentely ". + "with the integrated Email client. You have to store SMTP credentials with \"smtpcredentials\" before. ", "8.1.0" => "19.12.2018 Tooltipps added to camera device control buttons.", "8.0.0" => "18.12.2018 HLS is integrated using sscam_hls.js in Streaming device types \"hls\". HLS streaming is now available ". "for all common used browser types. Tooltipps are added to streaming devices and snapgallery.", @@ -349,6 +357,14 @@ sub SSCam_Initialize($) { "loginRetries:1,2,3,4,5,6,7,8,9,10 ". "videofolderMap ". "pollcaminfoall ". + "smtpCc ". + "smtpDebug:1,0 ". + "smtpFrom ". + "smtpHost ". + "smtpPort ". + "smtpTo ". + "smtpNoUseSSL:1,0 ". + "snapEmailTxt ". "snapGalleryBoost:0,1 ". "snapGallerySize:Icon,Full ". "snapGalleryNumber:$SSCAM_snum ". @@ -408,7 +424,7 @@ sub SSCam_Define($@) { $hash->{HELPER}{APIEXTREC} = "SYNO.SurveillanceStation.ExternalRecording"; $hash->{HELPER}{APIEXTEVT} = "SYNO.SurveillanceStation.ExternalEvent"; $hash->{HELPER}{APICAM} = "SYNO.SurveillanceStation.Camera"; # stark geändert ab API v2.8 - $hash->{HELPER}{APISNAPSHOT} = "SYNO.SurveillanceStation.SnapShot"; + $hash->{HELPER}{APISNAPSHOT} = "SYNO.SurveillanceStation.SnapShot"; # This API provides functions on snapshot, including taking, editing and deleting snapshots. $hash->{HELPER}{APIPTZ} = "SYNO.SurveillanceStation.PTZ"; $hash->{HELPER}{APIPRESET} = "SYNO.SurveillanceStation.PTZ.Preset"; $hash->{HELPER}{APICAMEVENT} = "SYNO.SurveillanceStation.Camera.Event"; @@ -417,8 +433,9 @@ sub SSCam_Define($@) { $hash->{HELPER}{APISTM} = "SYNO.SurveillanceStation.Stream"; # Beschreibung ist falsch und entspricht "SYNO.SurveillanceStation.Streaming" auch noch ab v2.8 $hash->{HELPER}{APIHM} = "SYNO.SurveillanceStation.HomeMode"; $hash->{HELPER}{APILOG} = "SYNO.SurveillanceStation.Log"; - $hash->{HELPER}{APIAUDIOSTM} = "SYNO.SurveillanceStation.AudioStream"; # Audiostream mit SID, removed in API v2.8 - $hash->{HELPER}{APIVIDEOSTMS} = "SYNO.SurveillanceStation.VideoStream"; # Videostream mit SID, removed in API v2.8 + $hash->{HELPER}{APIAUDIOSTM} = "SYNO.SurveillanceStation.AudioStream"; # Audiostream mit SID, removed in API v2.8 (noch undokumentiert verfügbar) + $hash->{HELPER}{APIVIDEOSTMS} = "SYNO.SurveillanceStation.VideoStream"; # Videostream mit SID, removed in API v2.8 (noch undokumentiert verfügbar) + $hash->{HELPER}{APIREC} = "SYNO.SurveillanceStation.Recording"; # This API provides method to query recording information. # Startwerte setzen if(SSCam_IsModelCam($hash)) { @@ -438,7 +455,7 @@ sub SSCam_Define($@) { $hash->{HELPER}{TOTALCNT} = 0; # totale Anzahl Snaps readingsBeginUpdate($hash); - readingsBulkUpdate($hash,"PollState","Inactive"); # es ist keine Gerätepolling aktiv + readingsBulkUpdate($hash,"PollState","Inactive"); # es ist keine Gerätepolling aktiv if(SSCam_IsModelCam($hash)) { readingsBulkUpdate($hash,"Availability", "???"); # Verfügbarkeit ist unbekannt readingsBulkUpdate($hash,"state", "off"); # Init für "state" , Problemlösung für setstate, Forum #308 @@ -447,7 +464,8 @@ sub SSCam_Define($@) { } readingsEndUpdate($hash,1); - SSCam_getcredentials($hash,1); # Credentials lesen und in RAM laden ($boot=1) + SSCam_getcredentials($hash,1,"svs"); # Credentials lesen und in RAM laden ($boot=1) + SSCam_getcredentials($hash,1,"smtp"); # initiale Routinen nach Restart ausführen , verzögerter zufälliger Start RemoveInternalTimer($hash, "SSCam_initonboot"); @@ -458,9 +476,10 @@ return undef; ################################################################ sub SSCam_Undef($$) { - my ($hash, $arg) = @_; - SSCam_logout($hash); - RemoveInternalTimer($hash); + my ($hash, $arg) = @_; + SSCam_logout($hash); + RemoveInternalTimer($hash); + return undef; } @@ -674,6 +693,7 @@ sub SSCam_Set($@) { my $hlslfw = SSCam_IsHLSCap($hash)?",live_fw_hls,":","; $setlist = "Unknown argument $opt, choose one of ". "credentials ". + "smtpcredentials ". ((ReadingsVal("$name", "CapPTZPan", "false") ne "false") ? "delPreset:".ReadingsVal("$name","Presets","")." " : ""). "expmode:auto,day,night ". "on ". @@ -714,7 +734,7 @@ sub SSCam_Set($@) { return "Credentials are incomplete, use username password" if (!$prop || !$prop1); return "Password is too long. It is limited up to and including 20 characters." if (length $prop1 > 20); delete $hash->{HELPER}{SID} if($hash->{HELPER}{SID}); - ($success) = SSCam_setcredentials($hash,$prop,$prop1); + ($success) = SSCam_setcredentials($hash,"svs",$prop,$prop1); $hash->{HELPER}{ACTIVE} = "off"; if($success) { @@ -731,6 +751,17 @@ sub SSCam_Set($@) { } + if ($opt eq "smtpcredentials") { + return "Credentials are incomplete, use username password" if (!$prop || !$prop1); + ($success) = SSCam_setcredentials($hash,"smtp",$prop,$prop1); + + if($success) { + return "SMTP-Username and SMTP-Password saved successfully"; + } else { + return "Error while saving SMTP-Username / SMTP-Password - see logfile for details"; + } + } + if ($opt eq "on" && SSCam_IsModelCam($hash)) { 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)) { @@ -745,9 +776,19 @@ sub SSCam_Set($@) { } elsif ($opt eq "snap" && SSCam_IsModelCam($hash)) { if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} - $hash->{HELPER}{SNAPBYSTRMDEV} = 1 if ($prop); # $prop wird mitgegeben durch Snap by SSCamSTRM-Device + $hash->{HELPER}{SNAPBYSTRMDEV} = 1 if ($prop =~ /STRM/); # $prop wird mitgegeben durch Snap by SSCamSTRM-Device + + if (AttrVal($name, "snapEmailTxt", "")) { + # Snap soll nach Erstellung per Email versendet werden + # snapEmailTxt muss sein: subject => , body => + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + if (!$hash->{SMTPCREDENTIALS}) { + return "Due to attribute \"snapEmailTxt\" is set, you want to send snapshots by email but SMTP credentials are not set - make sure you've set credentials with \"set $name smtpcredentials username password\""; + } + } + SSCam_camsnap($hash); - + } elsif ($opt eq "startTracking" && SSCam_IsModelCam($hash)) { if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} if ($hash->{HELPER}{APIPTZMAXVER} < 5) {return "Function \"$opt\" needs a higher version of Surveillance Station";} @@ -1206,6 +1247,7 @@ sub SSCam_Get($@) { ((ReadingsVal("$name", "CapPTZPresetNumber", 0) != 0) ? "listPresets:noArg " : ""). "snapinfo:noArg ". "svsinfo:noArg ". + "saveRecording ". "snapfileinfo:noArg ". "eventlist:noArg ". "stmUrlPath:noArg ". @@ -1258,6 +1300,11 @@ sub SSCam_Get($@) { SSCam_getclhash($hash,1); SSCam_getpresets($hash); + } elsif ($opt eq "saveRecording") { + if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} + $hash->{HELPER}{RECSAVEPATH} = $arg if($arg); + SSCam_getsaverec($hash); + } elsif ($opt eq "svsinfo") { if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} SSCam_getsvsinfo($hash); @@ -1265,9 +1312,23 @@ sub SSCam_Get($@) { } elsif ($opt eq "storedCredentials") { if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} # Credentials abrufen - my ($success, $username, $password) = SSCam_getcredentials($hash,0); + my ($success, $username, $password) = SSCam_getcredentials($hash,0,"svs"); unless ($success) {return "Credentials couldn't be retrieved successfully - see logfile"}; - return "Stored Credentials for $name - Username: $username, Password: $password"; + + my ($smtpsuccess, $smtpuname, $smtpword) = SSCam_getcredentials($hash,0,"smtp"); + my $so; + if($smtpsuccess) { + $so = "SMTP-Username: $smtpuname, SMTP-Password: $smtpword"; + } else { + $so = "SMTP credentials are not set"; + } + return "Stored Credentials to access surveillance station or DSM:\n". + "=========================================================\n". + "Username: $username, Password: $password\n". + "\n". + "Stored Credentials to access SMTP server:\n". + "=========================================\n". + "$so\n"; } elsif ($opt eq "snapGallery" && SSCam_IsModelCam($hash)) { if (!$hash->{CREDENTIALS}) {return "Credentials of $name are not set - make sure you've set it with \"set $name credentials username password\"";} @@ -1673,15 +1734,16 @@ return; ###################################################################################### # Username / Paßwort speichern +# $cre = "svs" -> Credentials für SVS und Cams +# $cre = "smtp" -> Credentials für Mailversand ###################################################################################### -sub SSCam_setcredentials ($@) { - my ($hash, @credentials) = @_; - my $name = $hash->{NAME}; - my $success; - my $credstr; - my $index; - my $retcode; +sub SSCam_setcredentials ($$@) { + my ($hash, $cre, @credentials) = @_; + my $name = $hash->{NAME}; + my ($success, $credstr, $index, $retcode); my (@key,$len,$i); + my $ao = ($cre eq "svs")?"credentials":"SMTPcredentials"; + $credstr = encode_base64(join(':', @credentials)); @@ -1694,14 +1756,14 @@ sub SSCam_setcredentials ($@) { chr((ord($_) + $key[$i]) % 256) } split //, $credstr; # End Scramble-Routine - $index = $hash->{TYPE}."_".$hash->{NAME}."_credentials"; + $index = $hash->{TYPE}."_".$hash->{NAME}."_".$ao; $retcode = setKeyValue($index, $credstr); if ($retcode) { Log3($name, 2, "$name - Error while saving the Credentials - $retcode"); $success = 0; } else { - SSCam_getcredentials($hash,1); # Credentials nach Speicherung lesen und in RAM laden ($boot=1) + SSCam_getcredentials($hash,1,$cre); # Credentials nach Speicherung lesen und in RAM laden ($boot=1), $ao = credentials oder SMTPcredentials $success = 1; } @@ -1710,20 +1772,20 @@ return ($success); ###################################################################################### # Username / Paßwort abrufen +# $cre = "svs" -> Credentials für SVS und Cams +# $cre = "smtp" -> Credentials für Mailversand ###################################################################################### -sub SSCam_getcredentials ($$) { - my ($hash,$boot) = @_; - my $name = $hash->{NAME}; - my $success; - my $username; - my $passwd; - my $index; - my ($retcode, $credstr); +sub SSCam_getcredentials ($$$) { + my ($hash,$boot, $cre) = @_; + my $name = $hash->{NAME}; + my ($success, $username, $passwd, $index, $retcode, $credstr); my (@key,$len,$i); + my $ao = ($cre eq "svs")?"credentials":"SMTPcredentials"; + my $pp = ""; if ($boot) { # mit $boot=1 Credentials von Platte lesen und als scrambled-String in RAM legen - $index = $hash->{TYPE}."_".$hash->{NAME}."_credentials"; + $index = $hash->{TYPE}."_".$hash->{NAME}."_".$ao; ($retcode, $credstr) = getKeyValue($index); if ($retcode) { @@ -1732,31 +1794,52 @@ sub SSCam_getcredentials ($$) { } if ($credstr) { - # beim Boot scrambled Credentials in den RAM laden - $hash->{HELPER}{CREDENTIALS} = $credstr; + if($cre eq "svs") { + # beim Boot scrambled Credentials in den RAM laden + $hash->{HELPER}{CREDENTIALS} = $credstr; - # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung - $hash->{CREDENTIALS} = "Set"; - $success = 1; + # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung + $hash->{CREDENTIALS} = "Set"; + $success = 1; + + } elsif ($cre eq "smtp") { + # beim Boot scrambled Credentials in den RAM laden + $hash->{HELPER}{SMTPCREDENTIALS} = $credstr; + + # "Credentials" wird als Statusbit ausgewertet. Wenn nicht gesetzt -> Warnmeldung und keine weitere Verarbeitung + $hash->{SMTPCREDENTIALS} = "Set"; + $success = 1; + } } } else { # boot = 0 -> Credentials aus RAM lesen, decoden und zurückgeben - $credstr = $hash->{HELPER}{CREDENTIALS}; - - # Beginn Descramble-Routine - @key = qw(1 3 4 5 6 3 2 1 9); - $len = scalar @key; - $i = 0; - $credstr = join "", - map { $i = ($i + 1) % $len; - chr((ord($_) - $key[$i] + 256) % 256) } - split //, $credstr; - # Ende Descramble-Routine - - ($username, $passwd) = split(":",decode_base64($credstr)); - - my $logpw = AttrVal($name, "showPassInLog", "0") == 1 ? $passwd : "********"; - Log3($name, 4, "$name - Credentials read from RAM: $username $logpw"); + if ($cre eq "svs") { + $credstr = $hash->{HELPER}{CREDENTIALS}; + } elsif ($cre eq "smtp") { + $pp = "SMTP"; + $credstr = $hash->{HELPER}{SMTPCREDENTIALS}; + } + + if($credstr) { + # Beginn Descramble-Routine + @key = qw(1 3 4 5 6 3 2 1 9); + $len = scalar @key; + $i = 0; + $credstr = join "", + map { $i = ($i + 1) % $len; + chr((ord($_) - $key[$i] + 256) % 256) } + split //, $credstr; + # Ende Descramble-Routine + + ($username, $passwd) = split(":",decode_base64($credstr)); + + my $logpw = AttrVal($name, "showPassInLog", "0") == 1 ? $passwd : "********"; + + Log3($name, 4, "$name - ".$pp."Credentials read from RAM: $username $logpw"); + + } else { + Log3($name, 2, "$name - ".$pp."Credentials not set in RAM !"); + } $success = (defined($passwd)) ? 1 : 0; } @@ -2058,6 +2141,7 @@ sub SSCam_camsnap($) { # einen Schnappschuß aufnehmen $hash->{OPMODE} = "Snap"; $hash->{HELPER}{LOGINRETRIES} = 0; + $hash->{HELPER}{CANSENDSNAP} = 1; # Versand Schnappschuß darf erfolgen falls gewünscht SSCam_setActiveToken($hash); SSCam_getapisites($hash); @@ -2067,6 +2151,53 @@ sub SSCam_camsnap($) { } } +############################################################################### +# Kamera gemachte Aufnahme lokal speichern +############################################################################### +sub SSCam_getsaverec($) { + my ($hash) = @_; + my $camname = $hash->{CAMNAME}; + my $name = $hash->{NAME}; + my $errorcode; + my $error; + + RemoveInternalTimer($hash, "SSCam_getsaverec"); + return if(IsDisabled($name)); + + if (ReadingsVal("$name", "state", "") =~ /^dis.*/) { + if (ReadingsVal("$name", "state", "") eq "disabled") { + $errorcode = "402"; + } elsif (ReadingsVal("$name", "state", "") eq "disconnected") { + $errorcode = "502"; + } + + # Fehlertext zum Errorcode ermitteln + $error = SSCam_experror($hash,$errorcode); + + # Setreading + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"Errorcode",$errorcode); + readingsBulkUpdate($hash,"Error",$error); + readingsEndUpdate($hash, 1); + + Log3($name, 2, "$name - ERROR - Save Recording of Camera $camname in local file can't be executed - $error"); + + return; + } + + if ($hash->{HELPER}{ACTIVE} eq "off") { + # einen Schnappschuß aufnehmen + $hash->{OPMODE} = "SaveRec"; + $hash->{HELPER}{LOGINRETRIES} = 0; + + SSCam_setActiveToken($hash); + SSCam_getapisites($hash); + + } else { + InternalTimer(gettimeofday()+0.3, "SSCam_getsaverec", $hash, 0); + } +} + ############################################################################### # Start Object Tracking ############################################################################### @@ -3313,6 +3444,7 @@ sub SSCam_getapisites($) { my $apistm = $hash->{HELPER}{APISTM}; my $apihm = $hash->{HELPER}{APIHM}; my $apilog = $hash->{HELPER}{APILOG}; + my $apirec = $hash->{HELPER}{APIREC}; my $proto = $hash->{PROTOCOL}; my $url; my $param; @@ -3333,7 +3465,7 @@ sub SSCam_getapisites($) { Log3($name, 5, "$name - HTTP-Call will be done with httptimeout-Value: $httptimeout s"); # URL zur Abfrage der Eigenschaften der API's - $url = "$proto://$serveraddr:$serverport/webapi/query.cgi?api=$apiinfo&method=Query&version=1&query=$apiauth,$apiextrec,$apicam,$apitakesnap,$apiptz,$apipreset,$apisvsinfo,$apicamevent,$apievent,$apivideostm,$apiextevt,$apistm,$apihm,$apilog,$apiaudiostm,$apivideostms"; + $url = "$proto://$serveraddr:$serverport/webapi/query.cgi?api=$apiinfo&method=Query&version=1&query=$apiauth,$apiextrec,$apicam,$apitakesnap,$apiptz,$apipreset,$apisvsinfo,$apicamevent,$apievent,$apivideostm,$apiextevt,$apistm,$apihm,$apilog,$apiaudiostm,$apivideostms,$apirec"; Log3($name, 4, "$name - Call-Out now: $url"); @@ -3373,6 +3505,7 @@ sub SSCam_getapisites_parse ($) { my $apistm = $hash->{HELPER}{APISTM}; my $apihm = $hash->{HELPER}{APIHM}; my $apilog = $hash->{HELPER}{APILOG}; + my $apirec = $hash->{HELPER}{APIREC}; my ($apicammaxver,$apicampath); if ($err ne "") { @@ -3563,6 +3696,16 @@ sub SSCam_getapisites_parse ($) { Log3($name, 4, "$name - $logstr"); $logstr = defined($apivideostmsmaxver) ? "MaxVersion of $apivideostms selected: $apivideostmsmaxver" : "MaxVersion of $apivideostms undefined - Surveillance Station may be stopped"; Log3($name, 4, "$name - $logstr"); + + # Pfad und Maxversion von "SYNO.SurveillanceStation.Recording" ermitteln + my $apirecpath = $data->{'data'}->{$apirec}->{'path'}; + $apirecpath =~ tr/_//d if (defined($apirecpath)); + my $apirecmaxver = $data->{'data'}->{$apirec}->{'maxVersion'}; + + $logstr = defined($apirecpath) ? "Path of $apirec selected: $apirecpath" : "Path of $apirec undefined - Surveillance Station may be stopped"; + Log3($name, 4, "$name - $logstr"); + $logstr = defined($apirecmaxver) ? "MaxVersion of $apirec selected: $apirecmaxver" : "MaxVersion of $apirec undefined - Surveillance Station may be stopped"; + Log3($name, 4, "$name - $logstr"); # aktuelle oder simulierte SVS-Version für Fallentscheidung setzen no warnings 'uninitialized'; @@ -3685,6 +3828,8 @@ sub SSCam_getapisites_parse ($) { $hash->{HELPER}{APILOGMAXVER} = $apilogmaxver; $hash->{HELPER}{APIVIDEOSTMSPATH} = $apivideostmspath?$apivideostmspath:"undefinded"; $hash->{HELPER}{APIVIDEOSTMSMAXVER} = $apivideostmsmaxver?$apivideostmsmaxver:0; + $hash->{HELPER}{APIRECPATH} = $apirecpath; + $hash->{HELPER}{APIRECMAXVER} = $apirecmaxver; readingsBeginUpdate($hash); @@ -3983,6 +4128,9 @@ sub SSCam_camop ($) { my $apivideostms = $hash->{HELPER}{APIVIDEOSTMS}; my $apivideostmspath = $hash->{HELPER}{APIVIDEOSTMSPATH}; my $apivideostmsmaxver = $hash->{HELPER}{APIVIDEOSTMSMAXVER}; + my $apirec = $hash->{HELPER}{APIREC}; + my $apirecpath = $hash->{HELPER}{APIRECPATH}; + my $apirecmaxver = $hash->{HELPER}{APIRECMAXVER}; my $sid = $hash->{HELPER}{SID}; my $OpMode = $hash->{OPMODE}; my $camid = $hash->{CAMID}; @@ -4016,6 +4164,17 @@ sub SSCam_camop ($) { readingsSingleUpdate($hash,"state", "snap", 1); readingsSingleUpdate($hash, "LastSnapId", "", 0); + } elsif ($OpMode eq "SaveRec") { + # eine Aufnahme soll in lokalem File (.mp4) gespeichert werden + my $recid = ReadingsVal("$name", "CamLastRecId", 0); + if($recid) { + $url = "$proto://$serveraddr:$serverport/webapi/$apirecpath?api=\"$apirec\"&id=$recid&mountId=0&version=\"$apirecmaxver\"&method=\"Download\"&_sid=\"$sid\""; + } else { + Log3($name, 2, "$name - WARNING - Can't save recording in local file due to no recording available."); + SSCam_delActiveToken($hash); + return; + } + } elsif ($OpMode eq "getsnapinfo" || $OpMode eq "getsnapgallery") { # Informationen über den letzten oder mehrere Schnappschüsse ermitteln my $limit = $hash->{HELPER}{SNAPLIMIT}; @@ -4281,13 +4440,14 @@ sub SSCam_camop ($) { } else { # Abspielen der letzten Aufnahme (EventId) - # externe URL in Reading setzen - $exturl .= "/webapi/$apistmpath?api=$apistm&version=$apistmmaxver&method=EventStream&eventId=$hash->{HELPER}{CAMLASTRECID}×tamp=1&_sid=$sid"; - - # interne URL - $url = "$proto://$serveraddr:$serverport/webapi/$apistmpath?api=$apistm&version=$apistmmaxver&method=EventStream&eventId=$hash->{HELPER}{CAMLASTRECID}×tamp=1&_sid=$sid"; - - readingsSingleUpdate($hash,"LiveStreamUrl", $exturl, 1) if(AttrVal($name, "showStmInfoFull", undef)); + my $lrecid = ReadingsVal("$name", "CamLastRecId", 0); + if($lrecid) { + # externe URL in Reading setzen + $exturl .= "/webapi/$apistmpath?api=$apistm&version=$apistmmaxver&method=EventStream&eventId=$lrecid×tamp=1&_sid=$sid"; + # interne URL + $url = "$proto://$serveraddr:$serverport/webapi/$apistmpath?api=$apistm&version=$apistmmaxver&method=EventStream&eventId=$lrecid×tamp=1&_sid=$sid"; + readingsSingleUpdate($hash,"LiveStreamUrl", $exturl, 1) if(AttrVal($name, "showStmInfoFull", 0)); + } } # Liveview-Link in Hash speichern @@ -4400,22 +4560,25 @@ sub SSCam_camop_parse ($) { } elsif ($myjson ne "") { # wenn die Abfrage erfolgreich war ($data enthält die Ergebnisdaten des HTTP Aufrufes) - # Evaluiere ob Daten im JSON-Format empfangen wurden - ($hash,$success,$myjson) = SSCam_evaljson($hash,$myjson); - - unless ($success) { - Log3($name, 4, "$name - Data returned: ".$myjson); + # Evaluiere ob Daten im JSON-Format empfangen wurden + if($OpMode !~ /SaveRec/) { # "SaveRec" liefert MP4-Daten und kein JSON + ($hash,$success,$myjson) = SSCam_evaljson($hash,$myjson); + unless ($success) { + Log3($name, 4, "$name - Data returned: ".$myjson); - SSCam_delActiveToken($hash); - return; + SSCam_delActiveToken($hash); + return; + } + + $data = decode_json($myjson); + + # Logausgabe decodierte JSON Daten + Log3($name, 5, "$name - JSON returned: ". Dumper $data); + + $success = $data->{'success'}; + } else { + $success = 1; } - - $data = decode_json($myjson); - - # Logausgabe decodierte JSON Daten - Log3($name, 5, "$name - JSON returned: ". Dumper $data); - - $success = $data->{'success'}; if ($success) { # Kameraoperation entsprechend "OpMode" war erfolgreich @@ -4483,6 +4646,33 @@ sub SSCam_camop_parse ($) { # Logausgabe Log3($name, 3, "$name - Camera $camname exposure mode was set to \"$hash->{HELPER}{EXPMODE}\""); + } elsif ($OpMode eq "SaveRec") { + + my $recid = ReadingsVal("$name", "CamLastRecId", 0); + my $lrec = ReadingsVal("$name", "CamLastRec", ""); + $lrec = (split("/",$lrec))[1]; + + my $sp = $hash->{HELPER}{RECSAVEPATH}?$hash->{HELPER}{RECSAVEPATH}:$attr{global}{modpath}; + my $file = $sp."/$lrec"; + delete $hash->{HELPER}{RECSAVEPATH}; + + if(open (FH, '>', $file)) { # in-memory IO Handle + binmode FH; + print FH $myjson; + close(FH); + $err = "none"; + Log3($name, 3, "$name - Recording was saved to local file \"$file\""); + } else { + $err = "Can't open file \"$file\": $!"; + Log3($name, 2, "$name - $err"); + } + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"Errorcode","none"); + readingsBulkUpdate($hash,"Error",$err); + readingsEndUpdate($hash, 1); + + } elsif ($OpMode eq "sethomemode") { readingsBeginUpdate($hash); @@ -4695,7 +4885,10 @@ sub SSCam_camop_parse ($) { RemoveInternalTimer("SSCam_getsnapinfo"); InternalTimer(gettimeofday()+0.6, "SSCam_getsnapinfo", "$name:$slim:$ssize", 0); - } elsif ($OpMode eq "getsnapinfo" || $OpMode eq "getsnapgallery" || ($OpMode eq "runliveview" && $hash->{HELPER}{RUNVIEW} =~ /snap/)) { + } elsif ($OpMode eq "getsnapinfo" || + $OpMode eq "getsnapgallery" || + ($OpMode eq "runliveview" && $hash->{HELPER}{RUNVIEW} =~ /snap/) + ) { # 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."; @@ -4727,7 +4920,7 @@ sub SSCam_camop_parse ($) { if (exists($hash->{HELPER}{RUNVIEW}) && $hash->{HELPER}{RUNVIEW} =~ /snap/ && exists($data->{'data'}{'data'}[0]{imageData})) { delete $hash->{HELPER}{RUNVIEW}; $hash->{HELPER}{LINK} = $data->{data}{data}[0]{imageData}; - } + } if($OpMode eq "getsnapgallery") { # es soll eine Schnappschußgallerie bereitgestellt (Attr snapGalleryBoost=1) bzw. gleich angezeigt werden (Attr snapGalleryBoost=0) @@ -4774,7 +4967,7 @@ sub SSCam_camop_parse ($) { } delete($hash->{HELPER}{GETSNAPGALLERY}); # Steuerbit getsnapgallery statt getsnapinfo - } + } if ($hash->{HELPER}{SNAPBYSTRMDEV} || $hash->{HELPER}{LSNAPBYSTRMDEV}) { # Snap durch SSCamSTRM-Device ausgelöst @@ -4789,6 +4982,12 @@ sub SSCam_camop_parse ($) { } Log3($name, $verbose, "$name - Snapinfos of camera $camname retrieved"); + + # Schnappschuß soll als Email versendet werden + if(AttrVal($name, "snapEmailTxt", "") && $hash->{HELPER}{CANSENDSNAP}) { + SSCam_prepareSendEmail ($hash, $OpMode, $data->{data}{data}[0]{imageData}); + delete $hash->{HELPER}{CANSENDSNAP}; + } } elsif ($OpMode eq "runliveview" && $hash->{HELPER}{RUNVIEW} =~ m/^live_.*hls$/) { # HLS Streaming wurde aktiviert @@ -5261,9 +5460,9 @@ sub SSCam_camop_parse ($) { Log3($name, $verbose, "$name - Informations of camera $camname retrieved"); } elsif ($OpMode eq "geteventlist") { - my $eventnum = $data->{'data'}{'total'}; - my $lastrecord = $data->{'data'}{'events'}[0]{name}; - $hash->{HELPER}{CAMLASTRECID} = $data->{'data'}{'events'}[0]{'eventId'}; + my $eventnum = $data->{'data'}{'total'}; + my $lrec = $data->{'data'}{'events'}[0]{name}; + my $lrecid = $data->{'data'}{'events'}[0]{'eventId'}; my ($lastrecstarttime,$lastrecstoptime); @@ -5279,8 +5478,9 @@ sub SSCam_camop_parse ($) { readingsBeginUpdate($hash); readingsBulkUpdate($hash,"CamEventNum",$eventnum); - readingsBulkUpdate($hash,"CamLastRec",$lastrecord); - if ($lastrecstarttime) {readingsBulkUpdate($hash,"CamLastRecTime",$lastrecstarttime." - ". $lastrecstoptime);} + readingsBulkUpdate($hash,"CamLastRec",$lrec) if($lrec); + readingsBulkUpdate($hash,"CamLastRecId",$lrecid) if($lrecid); + readingsBulkUpdate($hash,"CamLastRecTime",$lastrecstarttime." - ". $lastrecstoptime) if($lastrecstarttime); readingsBulkUpdate($hash,"Errorcode","none"); readingsBulkUpdate($hash,"Error","none"); readingsEndUpdate($hash, 1); @@ -5571,7 +5771,7 @@ sub SSCam_login ($$) { Log3($name, 4, "$name - --- Begin Function SSCam_login ---"); # Credentials abrufen - my ($success, $username, $password) = SSCam_getcredentials($hash,0); + my ($success, $username, $password) = SSCam_getcredentials($hash,0,"svs"); unless ($success) { Log3($name, 2, "$name - Credentials couldn't be retrieved successfully - make sure you've set it with \"set $name credentials \""); @@ -5742,7 +5942,7 @@ sub SSCam_logout_return ($) { my $hash = $param->{hash}; my $name = $hash->{NAME}; my $sid = $hash->{HELPER}{SID}; - my ($success, $username) = SSCam_getcredentials($hash,0); + my ($success, $username) = SSCam_getcredentials($hash,0,"svs"); my $OpMode = $hash->{OPMODE}; my $data; my $error; @@ -5831,7 +6031,7 @@ sub SSCam_Autocreate ($$) { CommandAttr(undef,"$camname httptimeout 20"); # Credentials abrufen und setzen - my ($success, $username, $password) = SSCam_getcredentials($hash,0); + my ($success, $username, $password) = SSCam_getcredentials($hash,0,"svs"); if($success) { CommandSet(undef, "$camname credentials $username $password"); } @@ -5857,7 +6057,7 @@ sub SSCam_evaljson($$) { eval {decode_json($myjson)} or do { - if($hash->{HELPER}{RUNVIEW} =~ m/^live_.*hls$/ || $OpMode =~ m/^.*_hls$/) { + if( ($hash->{HELPER}{RUNVIEW} && $hash->{HELPER}{RUNVIEW} =~ m/^live_.*hls$/) || $OpMode =~ m/^.*_hls$/ ) { # HLS aktivate/deaktivate bringt kein JSON wenn bereits aktiviert/deaktiviert Log3($name, 5, "$name - HLS-activation data return: $myjson"); if ($myjson =~ m/{"success":true}/) { @@ -5975,7 +6175,8 @@ sub SSCam_snaplimsize ($) { if(!AttrVal($name,"snapGalleryBoost",0)) { $slim = 1; - $ssize = 0; + $ssize = 0; + $ssize = 2 if(AttrVal($name,"snapEmailTxt","")); # Full Size für EMail-Versand, wird durch "snapGallerySize" überschrieben } else { $hash->{HELPER}{GETSNAPGALLERY} = 1; $slim = AttrVal($name,"snapGalleryNumber",$SSCam_slim); # Anzahl der abzurufenden Snaps @@ -6752,7 +6953,7 @@ sub SSCam_composegallery ($;$$) { $hash->{HELPER}{STRMDEV} = $strmdev; # Name des aufrufenden SSCamSTRM-Devices $hash->{HELPER}{STRMROOM} = $FW_room?$FW_room:""; # Raum aus dem das SSCamSTRM-Device die Funktion aufrief $hash->{HELPER}{STRMDETAIL} = $FW_detail?$FW_detail:""; # Name des SSCamSTRM-Devices (wenn Detailansicht) - my $streamHash = $defs{$strmdev}; # Hash des SSCamSTRM-Devices + my $streamHash = $defs{$strmdev} if($strmdev); # Hash des SSCamSTRM-Devices delete $streamHash->{HELPER}{STREAM}; my $cmddosnap = "cmd=set $name snap STRM"; # Snapshot auslösen mit Kennzeichnung "by STRM-Device" @@ -6873,6 +7074,384 @@ sub SSCam_experror ($$) { return ($error); } +############################################################################################# +# Vorbereitung SMTP EMail-Versand +# $OpMode = aktueller Operation Mode zur Unterscheidung was versendet werden soll +# $data = zu versendende Daten, evtl. anders bereitgestellt (ReadingsVal) +############################################################################################# +sub SSCam_prepareSendEmail ($$;$) { + my ($hash, $OpMode, $data) = @_; + my $name = $hash->{NAME}; + my $calias = AttrVal($name,"alias",$hash->{CAMNAME}); # Alias der Kamera wenn gesetzt oder Originalname aus SVS + my ($ret,$sdat,$fname,$snapid,$lsnaptime) = ('','','','',''); + + my $sp = AttrVal($name, "smtpPort", 25); + my $nousessl = AttrVal($name, "smtpNoUseSSL", 0); + + # Extraktion EMail-Texte + # Format in $hash->{HELPER}{SMTPMSG} muss sein: subject => , body => + my $mt = AttrVal($name, "snapEmailTxt", ""); + $mt =~ s/['"]//g; + + my($subj,$body) = split(",", $mt); + my($subjk,$subjt) = split("=>", $subj); + my($bodyk,$bodyt) = split("=>", $body); + $subjk = SSCam_trim($subjk); + $subjt = SSCam_trim($subjt); + $subjt =~ s/\$NAME/$calias/g; + $bodyk = SSCam_trim($bodyk); + $bodyt = SSCam_trim($bodyt); + $bodyt =~ s/\$NAME/$calias/g; + my %smtpmsg = (); + $smtpmsg{$subjk} = "$subjt"; + $smtpmsg{$bodyk} = "$bodyt"; + + if($OpMode =~ /^getsnap/) { + $fname = ReadingsVal($name,"LastSnapFilename",""); + $snapid = ReadingsVal($name,"LastSnapId",""); + $lsnaptime = ReadingsVal($name,"LastSnapTime",""); + $sdat = $data; + } + + $ret = SSCam_sendEmail($hash, {'subject' => $smtpmsg{subject}, + 'part1txt' => $smtpmsg{body}, + 'part2type' => 'image/jpeg', + 'fname' => $fname, + 'smtpport' => $sp, + 'sdat' => $sdat, + 'lsnaptime' => $lsnaptime, + 'opmode' => $OpMode, + 'smtpnousessl' => $nousessl, + } + ); +return $ret; +} + +############################################################################################# +# SMTP EMail-Versand +############################################################################################# +sub SSCam_sendEmail ($$) { + my ($hash, $extparamref) = @_; + my $name = $hash->{NAME}; + my $timeout = 60; + my $ret; + + Log3($name, 4, "$name - ####################################################"); + Log3($name, 4, "$name - ### start send snapshot by email "); + Log3($name, 4, "$name - ####################################################"); + + my $m1 = "Net::SMTP"; + my $m2 = "MIME::Lite"; + + my ($vm1,$vm2); + eval { require Net::SMTP; # libnet-3.06 has SSL included, so we need to check the version + Net::SMTP->import; + $vm1 = $Net::SMTP::VERSION; + require MIME::Lite; + MIME::Lite->import; + $vm2 = $MIME::Lite::VERSION; + }; + + if(!$vm1 || !$vm2) { + my $nl = !$vm2?$m2." ":""; + $nl .= !$vm1?$m1:""; + $ret = "required module for sending Email couldn't be loaded. You have to install: $nl"; + Log3($name, 1, "$name - $ret"); + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"sendEmailState",$ret); + readingsEndUpdate($hash, 1); + + return $ret; + } + + Log3($name, 4, "$name - sendEmail version of \"$m1\" is \"$vm1\""); + Log3($name, 4, "$name - sendEmail version of \"$m2\" is \"$vm2\""); + + my %SSCam_mailparams = ( + 'smtpFrom' => {'attr'=>'smtpFrom', 'default'=>'', 'required'=>1, 'set'=>1}, + 'smtpTo' => {'attr'=>'smtpTo', 'default'=>'', 'required'=>1, 'set'=>1}, + 'subject' => {'attr'=>'subject', 'default'=>'', 'required'=>1, 'set'=>1}, + 'smtpCc' => {'attr'=>'smtpCc', 'default'=>'', 'required'=>0, 'set'=>1}, + 'part1type' => { 'default'=>'text/plain; charset=UTF-8', 'required'=>1, 'set'=>1}, + 'part1txt' => { 'default'=>'', 'required'=>0, 'set'=>1}, + 'part2type' => { 'default'=>'', 'required'=>0, 'set'=>1}, + 'smtphost' => {'attr'=>'smtpHost', 'default'=>'', 'required'=>1, 'set'=>0}, + 'smtpport' => {'attr'=>'smtpPort', 'default'=>'25', 'required'=>1, 'set'=>0}, + 'smtpnousessl' => {'attr'=>'smtpNoUseSSL','default'=>'0', 'required'=>0, 'set'=>1}, + 'smtpdebug' => {'attr'=>'smtpDebug', 'default'=>'0', 'required'=>0, 'set'=>0}, + 'sdat' => { 'default'=>'', 'required'=>0, 'set'=>1}, # Daten base64 codiert, wenn gesetzt muss 'part2' auf 'image/jpeg' gesetzt werden + 'image' => { 'default'=>'', 'required'=>0, 'set'=>1}, # Daten als File, wenn gesetzt muss 'part2' auf 'image/jpeg' gesetzt werden + 'fname' => { 'default'=>'image.jpg', 'required'=>0, 'set'=>1}, # Filename für "image" oder "sdat" + 'lsnaptime' => { 'default'=>'', 'required'=>0, 'set'=>1}, # Zeitstempel des letzten Schnappschusses + 'opmode' => { 'default'=>'', 'required'=>1, 'set'=>1}, # OpMode muss gesetzt sein + ); + + my %params = (); + foreach my $key (keys %SSCam_mailparams) { + $params{$key} = AttrVal($name, $SSCam_mailparams{$key}->{attr}, $SSCam_mailparams{$key}->{default}) + if(exists $SSCam_mailparams{$key}->{attr}); + if($SSCam_mailparams{$key}->{set}) { + $params{$key} = $extparamref->{$key} if (exists $extparamref->{$key}); + $params{$key} = $SSCam_mailparams{$key}->{default} if (!$extparamref->{$key} && !$SSCam_mailparams{$key}->{attr}); + } + Log3($name, 4, "$name - param $key is now \"".$params{$key}."\" ") if($key !~ /sdat/); + Log3($name, 4, "$name - param $key is set") if($key =~ /sdat/ && $params{$key} ne ''); + } + + $params{name} = $name; + + my @err = (); + foreach my $key (keys(%SSCam_mailparams)) { + push(@err, $key) if ($SSCam_mailparams{$key}->{required} && !$params{$key}); + } + if ($#err >= 0) { + $ret = "Missing at least one required parameter or attribute: ".join(', ',@err); + Log3($name, 2, "$name - $ret"); + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"sendEmailState",$ret); + readingsEndUpdate($hash, 1); + return $ret; + } + + $hash->{HELPER}{RUNNING_PID} = BlockingCall("SSCam_sendEmailblocking", \%params, "SSCam_sendEmaildone", $timeout, "SSCam_sendEmailto", $hash); + $hash->{HELPER}{RUNNING_PID}{loglevel} = 5 if($hash->{HELPER}{RUNNING_PID}); # Forum #77057 + +return; +} + +#################################################################################################### +# nichtblockierendes Send EMail +#################################################################################################### +sub SSCam_sendEmailblocking($) { + my ($paref) = @_; + my $name = $paref->{name}; + my $cc = $paref->{smtpCc}; + my $from = $paref->{smtpFrom}; + my $part1type = $paref->{part1type}; + my $part1txt = $paref->{part1txt}; + my $part2type = $paref->{part2type}; + my $smtphost = $paref->{smtphost}; + my $smtpport = $paref->{smtpport}; + my $smtpnousessl = $paref->{smtpnousessl}; # SSL Verschlüsselung soll genutzt werden + my $subject = $paref->{subject}; + my $to = $paref->{smtpTo}; + my $msgtext = $paref->{msgtext}; + my $smtpdebug = $paref->{smtpdebug}; + my $sdat = $paref->{sdat}; # base64 kodierte Schnappschußdaten + my $image = $paref->{image}; # image, wenn gesetzt muss 'part2' auf 'image/jpeg' gesetzt sein + my $fname = $paref->{fname}; # Filename von "image" + my $lsnaptime = $paref->{lsnaptime}; # Zeit des letzten Schnappschusses wenn gesetzt + my $opmode = $paref->{opmode}; # aktueller Operation Mode + + my $hash = $defs{$name}; + my $sslver = ""; + my ($err,$fh); + + # Credentials abrufen + my ($success, $username, $password) = SSCam_getcredentials($hash,0,"smtp"); + + unless ($success) { + $err = "SMTP credentials couldn't be retrieved successfully - make sure you've set it with \"set $name smtpcredentials \""; + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + $subject = decode_utf8($subject); + my $mailmsg = MIME::Lite->new( + From => $from, + To => $to, + Subject => $subject, + Type => 'multipart/mixed', #'multipart/mixed', # was 'text/plain' + ); + + ### Add the text message part: + ### (Note that "attach" has same arguments as "new"): + $part1txt = decode_utf8($part1txt); + $mailmsg->attach( + Type => $part1type, + Data => $part1txt, + ); + + ### Add image, Das Image liegt bereits als File vor + if($image) { + $mailmsg->attach( + Type => $part2type, + Path => $image, + Filename => $fname, + Disposition => 'attachment', + ); + } + + ### Add image, Das Image liegt base64-codiert vor und wird dekodiert in ein "in-memory IO" gespeichert (snap) + if($sdat) { + my $decoded = MIME::Base64::decode_base64($sdat); + my $mh = ''; + if(open ($fh, '>', \$mh)) { # in-memory IO Handle + binmode $fh; + print $fh $decoded; + close($fh); + open ($fh, '<', \$mh); + Log3($name, 4, "$name - image data were saved to memory handle for smtp prepare"); + } else { + $err = "Can't open memory handle: $!"; + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + $mailmsg->attach( + Type => $part2type, + FH => $fh, + Filename => $fname, + Disposition => 'attachment', + ); + } + + $mailmsg->attr('content-type.charset' => 'UTF-8'); + + # login to SMTP Host + my $smtp = Net::SMTP->new(Host => $smtphost, Port => $smtpport, SSL => 0, Debug => $smtpdebug); + if(!$smtp) { + $err = "SMTP Error: Can't connect to host $smtphost"; + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + if($smtp->can_ssl() && !$smtpnousessl) { + unless( $smtp->starttls ( SSL_verify_mode => 0, + SSL_version => "TLSv1_2:!TLSv1_1:!SSLv3:!SSLv23:!SSLv2", + ) ) { + $err = "SMTP Error while switch to SSL: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + $sslver = $smtp->get_sslversion(); + Log3($name, 3, "$name - SMTP-Host $smtphost connection switched to SSL version: $sslver"); + } else { + Log3($name, 3, "$name - don't use SSL connection to SMTP-Host $smtphost !"); + } + + unless( $smtp->auth($username, $password) ) { + $err = "SMTP Error authentication: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + unless( $smtp->mail($from) ) { + $err = "SMTP Error setting sender: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + my @r = split(",", $to); + unless( $smtp->to(@r) ) { + $err = "SMTP Error setting receiver: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + if ($cc) { + my @c = split(",", $cc); + unless( $smtp->cc(@c) ) { + $err = "SMTP Error setting carbon-copy $cc: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + } + + unless( $smtp->data() ) { + $err = "SMTP Error setting data: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + unless( $smtp->datasend(encode('utf8',$mailmsg->as_string)) ) { + $err = "SMTP Error sending email: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + unless( $smtp->dataend() ) { + $err = "SMTP Error ending transaction: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + unless( $smtp->quit() ) { + $err = "SMTP Error saying good-bye: ".$smtp->message(); + Log3($name, 2, "$name - $err"); + $err = encode_base64($err,""); + return "$name|$err|''"; + } + + my $ret = "Email successfully sent ".( $sslver?"encoded by $sslver":"" ); + Log3($name, 3, "$name - $ret To: $to".(($cc)?", CC: $cc":"") ); + + close($fh) if($fh); + + # Daten müssen als Einzeiler zurückgegeben werden + $ret = encode_base64($ret,""); + +return "$name|''|$ret"; +} + +#################################################################################################### +# Auswertungsroutine nichtblockierendes Send EMail +#################################################################################################### +sub SSCam_sendEmaildone($) { + my ($string) = @_; + my @a = split("\\|",$string); + my $hash = $defs{$a[0]}; + my $err = $a[1]?decode_base64($a[1]):undef; + my $ret = $a[2]?decode_base64($a[2]):undef; + + if ($err) { + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"sendEmailState",$err); + readingsEndUpdate($hash, 1); + delete($hash->{HELPER}{RUNNING_PID}); + return; + } + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash,"sendEmailState",$ret); + readingsEndUpdate($hash, 1); + + delete($hash->{HELPER}{RUNNING_PID}); + +return; +} + +#################################################################################################### +# Abbruchroutine Send EMail +#################################################################################################### +sub SSCam_sendEmailto(@) { + my ($hash,$cause) = @_; + my $name = $hash->{NAME}; + + $cause = $cause?$cause:"Timeout: process terminated"; + Log3 ($name, 1, "$name -> BlockingCall $hash->{HELPER}{RUNNING_PID}{fn} pid:$hash->{HELPER}{RUNNING_PID}{pid} $cause"); + + readingsBeginUpdate($hash); + readingsBulkUpdateIfChanged($hash,"sendEmailState",$cause); + readingsEndUpdate($hash, 1); + + delete($hash->{HELPER}{RUNNING_PID}); + +return; +} + ############################################################################################# # Token setzen ############################################################################################# @@ -6903,6 +7482,15 @@ sub SSCam_delActiveToken ($) { return; } +############################################################################################# +# Leerzeichen am Anfang / Ende eines strings entfernen +############################################################################################# +sub SSCam_trim ($) { + my $str = shift; + $str =~ s/^\s+|\s+$//g; +return ($str); +} + 1; =pod @@ -6948,6 +7536,8 @@ return;
  • Activation / Deactivation of a camera integrated PIR sensor
  • Creation of a readingsGroup device to display an overview of all defined SSCam devices (createReadingsGroup)
  • automatized definition of all in SVS available cameras in FHEM (autocreateCams)
  • +
  • integrated send Email of snapshots
  • +
  • save the last recording of camera locally

  • @@ -6959,10 +7549,11 @@ return; Prerequisites

      - This module uses the Perl-module JSON.
      - On Debian-Linux based systems this module can be installed by:

      + This module uses the Perl-modules JSON and MIME::Lite which are usually have to be installed in addition.
      + On Debian-Linux based systems these modules can be installed by:

      - sudo apt-get install libjson-perl

      + sudo apt-get install libjson-perl
      + sudo apt-get install libmime-lite-perl

      SSCam is completely using the nonblocking functions of HttpUtils respectively HttpUtils_NonblockingGet.
      In DSM respectively in Synology Surveillance Station an User has to be created. The login credentials are needed later when using a set-command to assign the login-data to a device.
      @@ -6974,8 +7565,13 @@ return; Data::Dumper
      MIME::Base64
      Time::HiRes
      - HttpUtils (FHEM-module) -

      + Encode
      + HttpUtils (FHEM-module)
      + BlockingCall (FHEM-module)
      + Net::SMTP (if integrated send Email is used)
      + MIME::Lite (if integrated send Email is used) + +

      The PTZ panel (only PTZ cameras) in SSCam use its own icons. Thereby the system find the icons, in FHEMWEB device the attribute "iconPath" has to be completed by "sscam" @@ -7674,29 +8270,47 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
      • set <name> snap     (valid for CAM)

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

        + A snapshot will be triggered. + The ID and the filename of the last snapshot will be displayed in Reading "LastSnapId" respectively "LastSnapFilename" of the + device.

        - 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 and the filename of the last snapshot will be displayed as value of variable "LastSnapId" respectively "LastSnapFilename" in the device-Readings.

        + Email shipping

        + The snapshot can be sent after creation with Email. For this purpose the module contains its own Email client. + Before you can use this function you have to install the Perl-module MIME::Lite. On debian systems it can be + installed with command:

        + +
          + sudo apt-get install libmime-lite-perl +
        +
        + + The Email shipping is activated by setting the attribute "snapEmailTxt". + There are more attributes must be set or can be used optionally.
        + At first the Credentials for access the Email outgoing server must be set by command "set <name> smtpcredentials <user> <password>". + The connection establishment to the server is initially done unencrypted and switches to an encrypted connection if SSL + encryption is available. In that case the transmission of User/Password takes place encrypted too. + Attributes which are optional are marked:

        + +
          + + + + + + + + + + +
          snapEmailTxt - Activates the Email shipping. Has the form:
          + subject => <subject text>, body => <message text>
          + The placeholder variable $NAME can be used. $NAME is + replaced by the device alias or the name of camera in SVS if alias is not + defined.
          smtpHost - Hostname of outgoing Email server (e.g. securesmtp.t-online.de)
          smtpFrom - Return address (<name>@<domain>)
          smtpTo - Receiving address(es) (<name>@<domain>)
          smtpPort - (optional) Port of outgoing Email server (default: 25)
          smtpCc - (optional) carbon-copy receiving address(es) (<name>@<domain>)
          smtpNoUseSSL - (optional) "1" if no SSL encryption should be used for Email shipping (default: 0)
          smtpDebug - (optional) switch on the debugging of SMTP connection
          +
        +
        + + For further information please see description of the attributes.


      @@ -7812,6 +8426,21 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
    • get <name> listPresets     (valid for PTZ-CAM)

    • Get a popup with a lists of presets saved for the camera. +
    +

    + +
      +
    • get <name> saveRecording [<path>]     (valid for CAM)

    • + + The current recording present in Reading "CamLastRec" is saved lcally as a MP4 file. Optionally you can specify the path + for the file to save (default: modpath in global device).
      + The name of the saved local file is the same as displayed in Reading "CamLastRec".

      + +
        + Beispiel:

        + get <name> saveRecording /opt/fhem/log +
      +


    @@ -8165,6 +8794,62 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
  • simu_SVSversion
    simulates another SVS version. (only a lower version than the installed one is possible !)

  • + +
  • smtpHost <Hostname>
    + The name or IP-address of outgoing email server (e.g. securesmtp.t-online.de). +
  • +
    + + +
  • smtpCc <name>@<domain>[, <name>@<domain>][, <name>@<domain>]...
    + Optional you can enter a carbon-copy receiving address. Several receiving addresses are separated by ",". +
  • +
    + + +
  • smtpDebug
    + Switch the debugging mode for SMTP connection on (if Email shipping is used). +
  • +
    + + +
  • smtpFrom <name>@<domain>
    + Return address if Email shipping is used. +
  • +
    + + +
  • smtpPort <Port>
    + Optional setting of default SMTP port of outgoing email server (default: 25). +
  • +
    + + +
  • smtpTo <name>@<domain>[, <name>@<domain>][, <name>@<domain>]...
    + Receiving address for emal shipping. Several receiving addresses are separated by ",". +
  • +
    + + +
  • smtpNoUseSSL
    + If no Email SSL encryption should be used, set this attribute to "1" (default: 0). +
  • +
    + + +
  • snapEmailTxt subject => <subject text>, body => <message text>
    + Activates the Email shipping of snapshots after its creation.
    + The attribute has to be definied in the form as described. You can use the placeholder variable $NAME. + The variable $NAME is replaced by the device alias or the name of the camera in SVS if the device alias isn't available. +

    + +
      + Example:
      + snapEmailTxt subject => Motion alarm $NAME, body => A motion was recognized on $NAME. +
    +
    +
  • +
  • snapGalleryBoost
    If set, the last snapshots (default 3) will be retrieved by Polling, will be stored in the FHEM-servers main memory @@ -8254,7 +8939,8 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
  • CamExposureMode
  • - current exposure mode (Day, Night, Auto, Schedule, Unknown)
  • CamForceEnableMulticast
  • - Is the camera forced to enable multicast.
  • CamIP
  • - IP-Address of Camera -
  • CamLastRec
  • - Path / name of the last recording +
  • CamLastRec
  • - Path / name of last recording +
  • CamLastRecId
  • - the ID of last recording
  • CamLastRecTime
  • - date / starttime / endtime of the last recording
  • CamLiveFps
  • - Frames per second of Live-Stream
  • CamLiveMode
  • - Source of Live-View (DS, Camera) @@ -8363,6 +9049,8 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
  • Aktivierung / Deaktivierung eines kamerainternen PIR-Sensors
  • Erzeugung einer readingsGroup zur Anzeige aller definierten SSCam-Devices (createReadingsGroup)
  • Automatisiertes Anlegen aller in der SVS vorhandenen Kameras in FHEM (autocreateCams)
  • +
  • integrierter Email-Versand von Schnappschüssen
  • +
  • lokales Abspeichern der letzten Kamera-Aufnahme

  • @@ -8380,10 +9068,11 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay> Vorbereitung

      - Dieses Modul nutzt das Perl-Modul JSON.
      - Auf Debian-Linux basierenden Systemen kann es installiert werden mit:

      + Dieses Modul nutzt die Perl-Module JSON und MIME::Lite die üblicherweise nachinstalliert werden müssen.
      + Auf Debian-Linux basierenden Systemen können sie installiert werden mit:

      - sudo apt-get install libjson-perl

      + sudo apt-get install libjson-perl
      + sudo apt-get install libmime-lite-perl

      Das Modul verwendet für HTTP-Calls die nichtblockierenden Funktionen von HttpUtils bzw. HttpUtils_NonblockingGet.
      Im DSM bzw. der Synology Surveillance Station muß ein Nutzer angelegt sein. Die Zugangsdaten werden später über ein Set-Kommando dem @@ -8396,9 +9085,14 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay> Data::Dumper
      MIME::Base64
      Time::HiRes
      - HttpUtils (FHEM-Modul) + Encode
      + HttpUtils (FHEM-Modul)
      + BlockingCall (FHEM-Modul)
      + Net::SMTP (wenn Email-Versand verwendet)
      + MIME::Lite (wenn Email-Versand verwendet)

      + Das PTZ-Paneel (nur PTZ Kameras) in SSCam benutzt einen eigenen Satz Icons. Damit das System sie findet, ist im FHEMWEB Device das Attribut "iconPath" um "sscam" zu ergänzen (z.B. "attr WEB iconPath default:fhemSVG:openautomation:sscam"). @@ -9098,31 +9792,60 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>


    +
      +
    • set <name> smtpcredentials <user> <password>     (gilt für CAM)

    • + + Setzt die Credentials für den Zugang zum Postausgangsserver wenn Email-Versand genutzt wird. + +
    +

    +
    • set <name> snap     (gilt für CAM)

    • - Ein Schnappschuß kann ausgelöst werden mit: -
       
      -     set <name> snap 
      -  
      + Ein Schnappschuß wird ausgelöst. + Es wird die ID und der Filename des letzten Snapshots als Wert der Variable "LastSnapId" bzw. "LastSnapFilename" in den + Readings der Kamera gespeichert.

      - Nachfolgend einige Beispiele für die Auslösung von Schnappschüssen.

      + Email-Versand

      + Der Schnappschuß kann nach der Erstellung per Email versendet werden. Dazu enthält das Modul einen eigenen + Email-Client. Zur Verwendung dieser Funktion muss das Perl-Modul MIME::Lite installiert sein. Auf Debian-System kann + es mit

      + +
        + sudo apt-get install libmime-lite-perl +
      +
      + + installiert werden.

      - 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
      -  
      + Der Email-Versand wird durch das Setzen des Attributs "snapEmailTxt" eingeschaltet. + Weitere Attribute müssen gesetzt oder können optional verwendet werden.
      + Die Credentials für den Zugang zum Email-Server müssen mit dem Befehl "set <name> smtpcredentials <user> <password>" + gesetzt werden. Der Verbindungsaufbau zum Postausgangsserver erfolgt initial unverschüsselt und wechselt zu einer verschlüsselten + Verbindung wenn SSL zur Verfügung steht. In diesem Fall erfolgt auch die Übermittlung von User/Password verschlüsselt. + Optionale Attribute sind gekennzeichnet:

      - 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 und der Filename des letzten Snapshots als Wert der Variable "LastSnapId" bzw. "LastSnapFilename" in den Readings der Kamera ausgegeben.

      +
        + + + + + + + + + + +
        snapEmailTxt - Aktiviert den Email-Versand. Die Eingabe hat die Form:
        + subject => <Betreff-Text>, body => <Mitteilung-Text>
        + Es kann der Platzhalter $NAME verwendet werden. $NAME wird + durch den Device-Alias bzw. den Namen der Kamera in der SVS + ersetzt falls der Device-Alias nicht gesetzt ist.
        smtpHost - Hostname oder IP-Adresse des Postausgangsservers (z.B. securesmtp.t-online.de)
        smtpFrom - Absenderadresse (<name>@<domain>)
        smtpTo - Empfängeradresse(n) (<name>@<domain>)
        smtpPort - (optional) Port des Postausgangsservers (default: 25)
        smtpCc - (optional) Carbon-Copy Empfängeradresse(n) (<name>@<domain>)
        smtpNoUseSSL - (optional) "1" wenn kein SSL beim Email-Versand verwendet werden soll (default: 0)
        smtpDebug - (optional) zum Debugging der SMTP-Verbindung setzen
        +
      +
      + + Zur näheren Erläuterung siehe Beschreibung der Attribute.


    @@ -9248,6 +9971,21 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>

    +
      +
    • get <name> saveRecording [<Pfad>]     (gilt für CAM)

    • + + Die aktuell im Reading "CamLastRec" angegebene Aufnahme wird lokal als MP4-File gespeichert. Optional kann der Pfad zur + Speicherung des Files im Befehl angegeben werden (default: modpath im global Device).
      + Das File erhält lokal den gleichen Namen wie im Reading "CamLastRec" angegeben.

      + +
        + Beispiel:

        + get <name> saveRecording /opt/fhem/log +
      + +
    +

    +
    • get <name> scanVirgin     (gilt für CAM/SVS)

    • @@ -9613,7 +10351,63 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
    • simu_SVSversion
      Simuliert eine andere SVS-Version. (es ist nur eine niedrigere als die installierte SVS Version möglich !)

    • - + + +
    • smtpHost <Hostname>
      + Gibt den Hostnamen oder die IP-Adresse des Postausgangsservers für den Emailversand an (z.B. securesmtp.t-online.de). +
    • +
      + + +
    • smtpCc <name>@<domain>[, <name>@<domain>][, <name>@<domain>]...
      + Optionale zusätzliche Empfängeradresse(n) für den Email-Versand. Mehrere Adressen müssen durch "," getrennt werden. +
    • +
      + + +
    • smtpDebug
      + Schaltet den Debugging-Modus der Verbindung zum Email-Server ein (wenn Email Versand verwendet wird). +
    • +
      + + +
    • smtpFrom <name>@<domain>
      + Absenderadresse bei Verwendung des Emailversands. +
    • +
      + + +
    • smtpPort <Port>
      + Optionale Angabe Standard-SMTP-Port des Postausgangsservers (default: 25). +
    • +
      + + +
    • smtpTo <name>@<domain>[, <name>@<domain>][, <name>@<domain>]...
      + Empfängeradresse(n) für den Email-Versand. Mehrere Adressen müssen durch "," getrennt werden. +
    • +
      + + +
    • smtpNoUseSSL
      + Soll keine Email SSL-Verschlüsselung genutzt werden, ist dieses Attribut auf "1" zu setzen (default: 0). +
    • +
      + + +
    • snapEmailTxt subject => <Betreff-Text>, body => <Mitteilung-Text>
      + Aktiviert den Emailversand von Schnappschüssen nach deren Erstellung.
      + Das Attribut muß in der angegebenen Form definiert werden. Es kann der Platzhalter $NAME verwendet werden. + $NAME wird durch den Device-Alias bzw. den Namen der Kamera in der SVS ersetzt falls der Device-Alias nicht vorhanden + ist.

      + +
        + Beispiel:
        + snapEmailTxt subject => Bewegungsalarm $NAME, body => Eine Bewegung wurde an der $NAME registriert. +
      +
      +
    • +
    • snapGalleryBoost
      Wenn gesetzt, werden die letzten Schnappschüsse (default 3) über Polling im Speicher gehalten und mit "set/get snapGallery" @@ -9703,6 +10497,7 @@ attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
    • CamForceEnableMulticast
    • - sagt aus ob die Kamera verpflichet ist Multicast einzuschalten.
    • CamIP
    • - IP-Adresse der Kamera
    • CamLastRec
    • - Pfad / Name der letzten Aufnahme +
    • CamLastRecId
    • - die ID der letzten Aufnahme
    • CamLastRecTime
    • - Datum / Startzeit - Stopzeit der letzten Aufnahme
    • CamLiveFps
    • - Frames pro Sekunde des Live-Streams
    • CamLiveMode
    • - Quelle für Live-Ansicht (DS, Camera)