diff --git a/fhem/FHEM/73_DoorBird.pm b/fhem/FHEM/73_DoorBird.pm index c63829332..d868a9c81 100644 --- a/fhem/FHEM/73_DoorBird.pm +++ b/fhem/FHEM/73_DoorBird.pm @@ -59,6 +59,7 @@ use LWP::UserAgent; use constant false => 0; use constant true => 1; use Data::Dumper; +use File::Spec::Functions ':ALL'; ###START###### Initialize module ##############################################################################START#### sub DoorBird_Initialize($) @@ -89,6 +90,7 @@ sub DoorBird_Initialize($) "VideoDurationDoorbell " . "VideoDurationMotion " . "VideoDurationKeypad " . + "HistoryFilePath:1,0 " . "EventReset " . "SessionIdSec:slider,0,10,600 " . "WaitForHistory " . @@ -188,6 +190,7 @@ sub DoorBird_Define($$) $hash->{helper}{EventReset} = AttrVal($name, "EventReset", 5); $hash->{helper}{WaitForHistory} = AttrVal($name, "WaitForHistory", 7); $hash->{helper}{CameraInstalled} = false; + $hash->{helper}{HistoryFilePath} = 0; $hash->{helper}{SessionId} = 0; $hash->{helper}{UdpMessageId} = 0; $hash->{helper}{UdpMotionId} = 0; @@ -265,7 +268,7 @@ sub DoorBird_InitializeDevice($) DoorBird_OpenSocketConn($hash); ### Initialize Readings - DoorBird_Info_Request($hash, ""); + DoorBird_Info_Request($hash, ""); DoorBird_Image_Request($hash, ""); DoorBird_Live_Video($hash, "off"); @@ -538,6 +541,30 @@ sub DoorBird_Attr(@) $hash->{helper}{VideoDurationKeypad} = "0"; } } + ### Check whether HistoryFilePath attribute has been provided + elsif ($a[2] eq "HistoryFilePath") { + ### Check whether HistoryFilePath is defined + if (defined($a[3])) { + ### Set helper in hash + $hash->{helper}{HistoryFilePath} = $a[3]; + + if ($a[3] == true) { + ### Update the history list for images and videos + DoorBird_History_List($hash); + } + else { + ### Delete all reading entries to files + fhem("deleteReading " . $name . " HistoryFilePath.*"); + } + } + else { + ### Set helper in hash + $hash->{helper}{HistoryFilePath} = "0"; + + ### Delete all reading entries to files + fhem("deleteReading " . $name . " HistoryFilePath.*"); + } + } ### Check whether VideoFileFormat attribute has been provided elsif ($a[2] eq "VideoFileFormat") { ### Check whether VideoFileFormat is defined @@ -907,6 +934,10 @@ sub DoorBird_Set($@) ### Call Subroutine and hand back return value return DoorBird_Transmit_Audio($hash, $option) } + ### TEST + elsif ($command eq "Test") { + DoorBird_History_List($hash); + } ### ADD OR CHANGE FAVORITE ### DELETE FAVORITE ### ADD OR UPDATE SCHEDULE ENTRY @@ -2550,6 +2581,9 @@ sub DoorBird_Image_Request($$) { ### Log Entry for debugging purposes Log3 $name, 5, $name. " : DoorBird_Image_Request - write file : Successfully written " . $ImageFileName; + ### Update the history list for images and videos + DoorBird_History_List($hash); + ### Close file or write error message in log close $fh or do { ### Log Entry @@ -2771,6 +2805,9 @@ sub DoorBird_LastEvent_Image($$$) { ### Write Last Image into reading readingsSingleUpdate($hash, $ReadingImage, $ImageFileName, 1); + + ### Update the history list for images and videos + DoorBird_History_List($hash); } ### Log Entry for debugging purposes Log3 $name, 5, $name. " : DoorBird_LastEvent_Image - ImageData - event : " . length($ImageData); @@ -3519,6 +3556,9 @@ sub DoorBird_History_Request_Parse($) { ### Log Entry for debugging purposes Log3 $name, 5, $name. " : DoorBird_History_Request - write file : Successfully written " . $ImageFileName; + ### Update the history list for images and videos + DoorBird_History_List($hash); + ### Close file or write error message in log close $fh or do { ### Log Entry @@ -3555,6 +3595,284 @@ sub DoorBird_History_Request_Parse($) { } ####END####### Define Subfunction for HISTORY IMAGE REQUEST ####################################################END##### +###START###### Define Subfunction for history list update as readings #########################################START#### +sub DoorBird_History_List($) { + my ($hash) = @_; + + ### Obtain values from hash + my $name = $hash->{NAME}; + + ### If the HistoryFile List shall be created + if ($hash->{helper}{HistoryFilePath} == true) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List ___________________________________________________________"; + Log3 $name, 5, $name. " : DoorBird_History_List - The HistoryList has been activated. Processing..."; + + ### Delete all older reading entries to files + #fhem("deleteReading " . $name . " HistoryFilePath.*", 1); + + foreach my $DeleteReading (keys %{$hash->{READINGS}}) { + if ($DeleteReading =~ m/HistoryFilePath/ ){ + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - Delete Reading : " . $DeleteReading; + + ### Delete Reading + readingsDelete($hash, $DeleteReading); + } + } + + ### Get current working directory + my $cwd = getcwd(); + + ### If the ImageFileDir has been defined and images are taken + if ((defined($hash->{helper}{ImageFileDir})) && (($hash->{helper}{ImageFileDir} =~ m/\/fhem\/www/)) || (($hash->{helper}{ImageFileDir} =~ m/\\fhem\\www/))){ + my $ImageFileName; + my @ImageFileList; + + ### If the path is given as UNIX file system format + if ($cwd =~ /\//) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - file system format : UNIX"; + + ### Find out whether it is an absolute path or an relative one (leading "/") + if ($hash->{helper}{ImageFileDir} =~ /^\//) { + $ImageFileName = $hash->{helper}{ImageFileDir}; + } + else { + $ImageFileName = $cwd . "/" . $hash->{helper}{ImageFileDir}; + } + } + + ### If the path is given as Windows file system format + if ($hash->{helper}{ImageFileDir} =~ /\\/) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - file system format : WINDOWS"; + + ### Find out whether it is an absolute path or an relative one (containing ":\") + if ($hash->{helper}{ImageFileDir} != /^.:\//) { + $ImageFileName = $cwd . $hash->{helper}{ImageFileDir}; + } + else { + $ImageFileName = $hash->{helper}{ImageFileDir}; + } + } + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - ImageFileName : " . $ImageFileName; + + ### Get list of directory items + if (opendir(FileSearch,$ImageFileName)) { + @ImageFileList = readdir(FileSearch); + close FileSearch; + + ### Define Types to be searched for + my @FileTypes = ("motionsensor", "doorbell", "keypad", "snapshot", "manual"); + + readingsBeginUpdate($hash); + + foreach my $SearchType (@FileTypes) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - SearchType : " . $SearchType; + + ### Extract all Filenames which matches the SearchType + my @ImageFileListSearch = grep {/$SearchType/} @ImageFileList; + + ### Extract all Filenames which matches the file extension + my @ImageFileListExt = grep {/jpg/} @ImageFileListSearch; + + # Sort list + @ImageFileListExt=sort(@ImageFileListExt); + + ### Get the last n elements (hash->{helper}{MaxHistory}) + @ImageFileListExt = ($hash->{helper}{MaxHistory} >= @ImageFileListExt) ? @ImageFileListExt : @ImageFileListExt[-$hash->{helper}{MaxHistory}..-1]; + + # Sort list + @ImageFileListExt=sort({$b cmp $a} @ImageFileListExt); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - ImageFileListSearch : \n" . Dumper(@ImageFileListExt); + + ### Update Readings + my $index = 0; + foreach my $FileName (@ImageFileListExt){ + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - FileName : " . $FileName; + + ### Extract timestamp from Filename + my $TimeStamp = substr($FileName,0,4) . "-" . substr($FileName, 4,2) . "-" . substr($FileName, 6,2) . " "; + $TimeStamp .= substr($FileName,9,2) . ":" . substr($FileName,11,2) . ":" . substr($FileName,13,2); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - TimeStamp : " . $TimeStamp; + + ### Complete relative path from /opt/fhem/www/tablet to $hash->{helper}{ImageFileDir} + my $FilePath = abs2rel(rel2abs($ImageFileName), rel2abs("/opt/fhem/www/tablet")); + my $ReadingsValue = $FilePath . "/" . $FileName; + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - FilePath : " . $FilePath; + + ### Create ReadingsName for Imagepath and write reading + my $ReadingsName = "HistoryFilePath_" . $SearchType . "_Image_" . sprintf("%02d", $index); + readingsBulkUpdate($hash, $ReadingsName, $ReadingsValue); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsName-Image : " . $ReadingsName; + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsValue-Image : " . $ReadingsValue; + + + ### Create ReadingsName for Timestamp and write reading + $ReadingsName = "HistoryFilePath_" . $SearchType . "_Image_" . sprintf("%02d", $index) . "_Timestamp"; + readingsBulkUpdate($hash, $ReadingsName, $TimeStamp); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsName-Timestamp : " . $ReadingsName; + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsValue-Timestamp : " . $TimeStamp; + + ### Increase Index + $index++; + } + } + } + else { + ### Log Entry for warning + Log3 $name, 2, $name. " : DoorBird_History_List - The Attribute \"ImageFileDir\" leads to an directory which produced an error! - Does it actually exist?"; + } + } + else { + ### Log Entry for warning + Log3 $name, 4, $name. " : DoorBird_History_List - The ImageFileDir has not been provided or has not been created below the web-folder e.g. \"/opt/fhem/www/doorbird-images/\""; + } + ### If the VideoFileDir has been defined and Videos are taken + if ((defined($hash->{helper}{VideoFileDir})) && (($hash->{helper}{VideoFileDir} =~ m/\/fhem\/www/) || (($hash->{helper}{ImageFileDir} =~ m/\\fhem\\www/)))){ + my $VideoFileName; + my @VideoFileList; + + ### If the path is given as UNIX file system format + if ($cwd =~ /\//) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - file system format : UNIX"; + + ### Find out whether it is an absolute path or an relative one (leading "/") + if ($hash->{helper}{VideoFileDir} =~ /^\//) { + $VideoFileName = $hash->{helper}{VideoFileDir}; + } + else { + $VideoFileName = $cwd . "/" . $hash->{helper}{VideoFileDir}; + } + } + + ### If the path is given as Windows file system format + if ($hash->{helper}{VideoFileDir} =~ /\\/) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - file system format : WINDOWS"; + + ### Find out whether it is an absolute path or an relative one (containing ":\") + if ($hash->{helper}{VideoFileDir} != /^.:\//) { + $VideoFileName = $cwd . $hash->{helper}{VideoFileDir}; + } + else { + $VideoFileName = $hash->{helper}{VideoFileDir}; + } + } + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - VideoFileName : " . $VideoFileName; + + ### Get list of directory items + if (opendir(FileSearch,$VideoFileName)) { + @VideoFileList = readdir(FileSearch); + close FileSearch; + + ### Define Types to be searched for + my @FileTypes = ("motionsensor", "doorbell", "keypad", "snapshot", "manual"); + + readingsBeginUpdate($hash); + + foreach my $SearchType (@FileTypes) { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - SearchType : " . $SearchType; + + ### Extract all Filenames which matches the SearchType + my @VideoFileListSearch = grep {/$SearchType/} @VideoFileList; + + ### Extract all Filenames which matches the file extension + my @VideoFileListExt = grep {/$hash->{helper}{VideoFileFormat}/} @VideoFileListSearch; + + # Sort list + @VideoFileListExt=sort(@VideoFileListExt); + + ### Get the last n elements (hash->{helper}{MaxHistory}) + @VideoFileListExt = ($hash->{helper}{MaxHistory} >= @VideoFileListExt) ? @VideoFileListExt : @VideoFileListExt[-$hash->{helper}{MaxHistory}..-1]; + + # Sort list + @VideoFileListExt=sort({$b cmp $a} @VideoFileListExt); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - VideoFileListSearch : \n" . Dumper(@VideoFileListExt); + + ### Update Readings + my $index = 0; + foreach my $FileName (@VideoFileListExt){ + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - FileName : " . $FileName; + + ### Extract timestamp from Filename + my $TimeStamp = substr($FileName,0,4) . "-" . substr($FileName, 4,2) . "-" . substr($FileName, 6,2) . " "; + $TimeStamp .= substr($FileName,9,2) . ":" . substr($FileName,11,2) . ":" . substr($FileName,13,2); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - TimeStamp : " . $TimeStamp; + + ### Complete relative path from /opt/fhem/www/tablet to $hash->{helper}{VideoFileDir} + my $FilePath = abs2rel(rel2abs($VideoFileName), rel2abs("/opt/fhem/www/tablet")); + my $ReadingsValue = $FilePath . "/" . $FileName; + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - FilePath : " . $FilePath; + + ### Create ReadingsName for Videopath and write reading + my $ReadingsName = "HistoryFilePath_" . $SearchType . "_Video_" . sprintf("%02d", $index); + readingsBulkUpdate($hash, $ReadingsName, $ReadingsValue); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsName-Video : " . $ReadingsName; + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsValue-Video : " . $ReadingsValue; + + + ### Create ReadingsName for Timestamp and write reading + $ReadingsName = "HistoryFilePath_" . $SearchType . "_Video_" . sprintf("%02d", $index) . "_Timestamp"; + readingsBulkUpdate($hash, $ReadingsName, $TimeStamp); + + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsName-Timestamp : " . $ReadingsName; + Log3 $name, 5, $name. " : DoorBird_History_List - ReadingsValue-Timestamp : " . $TimeStamp; + + ### Increase Index + $index++; + } + } + } + else { + ### Log Entry for warning + Log3 $name, 2, $name. " : DoorBird_History_List - The Attribute \"VideoFileDir\" leads to an directory which produced an error! - Does it actually exist?"; + } + } + else { + ### Log Entry for warning + Log3 $name, 4, $name. " : DoorBird_History_List - The VideoFileDir has not been provided or has not been created below the web-folder e.g. \"/opt/fhem/www/doorbird-videos/\""; + } + + ### Initiate Readings Update + readingsEndUpdate($hash, 1); + } + else { + ### Log Entry for debugging purposes + Log3 $name, 5, $name. " : DoorBird_History_List - The HistoryList has been disabled. Not Links to pictures are provided."; + } +} +####END####### Define Subfunction for history list update as readings ##########################################END##### + ###START###### Define Subfunction for VIDEO REQUEST ###########################################################START#### sub DoorBird_Video_Request($$$$) { my ($hash, $duration, $event, $timestamp) = @_; @@ -3709,6 +4027,9 @@ sub DoorBird_Video_Request($$$$) { ### Log Entry for debugging purposes Log3 $name, 2, $name. " : DoorBird_Video_Request - Video-Request ha not been implemented for Windows file system. Contact fhem forum and WIKI."; } + + ### Update the history list for images and videos after the video has been taken + InternalTimer(gettimeofday()+$duration+3,"DoorBird_History_List", $hash, 0); } ### If attribute to video directory has NOT been set else { @@ -4163,6 +4484,22 @@ sub DoorBird_BlockGet($$$$) { } ####END####### Blocking Get ####################################################################################END##### +###START###### Calculate relative path ########################################################################START#### +sub DoorBird_Rel_Path($$) { + my ($start,$target) = @_; + $start =~ s:/[^/]+:/:; # remove trailing filename + my @spath = grep {$_ ne ''} split(/\//,$start); + my @tpath = grep {$_ ne ''} split(/\//,$target); + + # strip common start of the path + while ( @spath && @tpath && $spath[0] eq $tpath[0]) { + shift @spath; + shift @tpath; + } + + return ("../"x(@spath)).join('/', @tpath); +} +####END####### Calculate relative path #########################################################################END##### ###START###### Processing Change Log ##########################################################################START#### # Changelog parser for DoorBird changelog as of 2020-02-22 (or earlier) containing multiple product lines. @@ -4436,7 +4773,13 @@ sub DoorBird_findNewestFWVersion($$$) OpsModeList : A space separated list of names for operational modes (e.g. "Normal Party Fire") on which the DoorBird reacts automatically on events.
The default value is "" = disabled
- + + + + HistoryFilePath : Creates relative datapaths to the last pictures, and videos in order to indicate them directly (e.g. fhem ftui widget "image")
+ The default value is "0" = disabled
+ + @@ -4614,7 +4957,13 @@ sub DoorBird_findNewestFWVersion($$$) OpsModeList : Eine durch Leerzeichen getrennte Liste von Namen für Operationszustände (e.g. "Normal Party Feuer" auf diese der DoorBird automatisch bei Events reagiert.
Der Default Wert ist "" = deaktiviert
- + + + + HistoryFilePath : Erstellt Dateipfade zu den letzten Bildern und Videos um sie in den User Interfaces direkt anzuzeigen (e.g. fhem ftui Widget "Image")
+ Der Default Wert ist "0" = disabled
+ +