######################################################################################################################## # $Id: 49_SSCamSTRM.pm 25032 2021-09-29 13:23:58Z DS_Starter $ ######################################################################################################################### # 49_SSCamSTRM.pm # # (c) 2018-2021 by Heiko Maaz # forked from 98_weblink.pm by Rudolf König # e-mail: Heiko dot Maaz at t-online dot de # # This Module is used by module 49_SSCam to create Streaming devices. # It can't be used without any SSCam-Device. # # This script is part of fhem. # # Fhem is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # Fhem is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with fhem. If not, see . # ######################################################################################################################### package FHEM::SSCamSTRM; ## no critic 'package'; use strict; use warnings; use GPUtils qw(GP_Import GP_Export); # wird für den Import der FHEM Funktionen aus der fhem.pl benötigt use Time::HiRes qw(gettimeofday); eval "use FHEM::Meta;1" or my $modMetaAbsent = 1; ## no critic 'eval' # Run before module compilation BEGIN { # Import from main:: GP_Import( qw( AnalyzePerlCommand AttrVal CommandSet data defs devspec2array FmtDateTime init_done InternalTimer IsDisabled Log3 modules readingsSingleUpdate readingsBulkUpdate readingsBulkUpdateIfChanged readingsBeginUpdate readingsDelete readingsEndUpdate ReadingsVal RemoveInternalTimer readingFnAttributes sortTopicNum FW_cmd FW_directNotify FW_wname FW_pH FW_widgetFallbackFn FHEM::SSCam::ptzPanel FHEM::SSCam::streamDev FHEM::SSCam::composeGallery FHEM::SSCam::getClHash ) ); # Export to main context with different name # my $pkg = caller(0); # my $main = $pkg; # $main =~ s/^(?:.+::)?([^:]+)$/main::$1\_/gx; # for (@_) { # *{ $main . $_ } = *{ $pkg . '::' . $_ }; # } GP_Export( qw( Initialize ) ); } # Versions History intern my %vNotesIntern = ( "2.15.1" => "15.10.2021 fix warnings 'my variable masks earlier' ", "2.15.0" => "27.09.2021 model lastsnap: add setter snap ", "2.14.5" => "12.08.2020 avoid loose of adoption after restart ", "2.14.4" => "03.08.2020 fix check of ARG in RemoveInternalTimer in _setadoptForTimer sub (sometimes no switch back done) ", "2.14.3" => "01.08.2020 verbose 5 log in _setadoptForTimer sub ", "2.14.2" => "29.07.2020 fix: adoptTime accept not only integer values ", "2.14.1" => "28.07.2020 switching time increases with each adoptForTimer command ", "2.14.0" => "27.07.2020 new commands adoptForTimer and control command adoptTime ", "2.13.1" => "21.07.2020 fix: set of values in attr adoptSubset is empty after restart, changes according level 3 PBP ", "2.13.0" => "14.07.2020 integrate streamDev master ", "2.12.0" => "28.06.2020 upgrade SSCam functions due to SSCam switch to packages ", "2.11.0" => "24.06.2020 switch to packages, changes according to PBP ", "2.10.2" => "08.11.2019 undef \$link in FwFn / streamAsHtml to save memory ", "2.10.1" => "18.10.2019 set parentState initial in Define, Forum: https://forum.fhem.de/index.php/topic,45671.msg985136.html#msg985136 ", "2.10.0" => "21.09.2019 new attribute hideAudio ", "2.9.0" => "19.09.2019 new attribute noLink ", "2.8.0" => "09.09.2019 new attribute hideButtons ", "2.7.0" => "15.07.2019 FTUI support, new attributes htmlattrFTUI, hideDisplayNameFTUI, ptzButtonSize, ptzButtonSizeFTUI ", "2.6.0" => "21.06.2019 GetFn -> get html ", "2.5.0" => "27.03.2019 add Meta.pm support ", "2.4.0" => "24.02.2019 support for \"genericStrmHtmlTag\" in streaming device MODEL generic ", "2.3.0" => "04.02.2019 Rename / Copy added, Streaming device can now be renamed or copied ", "2.2.1" => "19.12.2018 commandref revised ", "2.2.0" => "13.12.2018 load sscam_hls.js, sscam_tooltip.js from pgm2 for HLS Streaming support and tooltips ", "2.1.0" => "11.12.2018 switch \"popupStream\" from get to set ", "2.0.0" => "09.12.2018 get command \"popupStream\" and attribute \"popupStreamFW\" ", "1.5.0" => "02.12.2018 new attribute \"popupWindowSize\" ", "1.4.1" => "31.10.2018 attribute \"autoLoop\" changed to \"autoRefresh\", new attribute \"autoRefreshFW\" ", "1.4.0" => "29.10.2018 readingFnAttributes added ", "1.3.0" => "28.10.2018 direct help for attributes, new attribute \"autoLoop\" ", "1.2.4" => "27.10.2018 fix undefined subroutine &main::SSCam_ptzpanel (https://forum.fhem.de/index.php/topic,45671.msg850505.html#msg850505) ", "1.2.3" => "03.07.2018 behavior changed if device is disabled ", "1.2.2" => "26.06.2018 make changes for generic stream dev ", "1.2.1" => "23.06.2018 no name add-on if MODEL is snapgallery ", "1.2.0" => "20.06.2018 running stream as human readable entry for SSCamSTRM-Device ", "1.1.0" => "16.06.2018 attr hideDisplayName regarding to Forum #88667 ", "1.0.1" => "14.06.2018 commandref revised ", "1.0.0" => "14.06.2018 switch to longpoll refresh ", "0.4.0" => "13.06.2018 new attribute \"noDetaillink\" (deleted in V1.0.0) ", "0.3.0" => "12.06.2018 new attribute \"forcePageRefresh\" ", "0.2.0" => "11.06.2018 check in with SSCam 5.0.0 ", "0.1.0" => "10.06.2018 initial Version " ); my %fupgrade = ( # Funktionsupgrade in SSCamSTRM devices definiert vor SSCam Version 9.4.0 1 => { of => "SSCam_ptzpanel", nf => "FHEM::SSCam::ptzPanel" }, 2 => { of => "SSCam_composegallery", nf => "FHEM::SSCam::composeGallery" }, 3 => { of => "SSCam_StreamDev", nf => "FHEM::SSCam::streamDev" }, ); my %hvattr = ( # Hash zur Validierung von Attributen adoptSubset => { master => 1, nomaster => 0 }, autoRefresh => { master => 1, nomaster => 1 }, autoRefreshFW => { master => 1, nomaster => 1 }, disable => { master => 1, nomaster => 1 }, forcePageRefresh => { master => 1, nomaster => 1 }, genericStrmHtmlTag => { master => 0, nomaster => 1 }, htmlattr => { master => 0, nomaster => 1 }, htmlattrFTUI => { master => 0, nomaster => 1 }, hideAudio => { master => 0, nomaster => 1 }, hideButtons => { master => 0, nomaster => 1 }, hideDisplayName => { master => 1, nomaster => 1 }, hideDisplayNameFTUI => { master => 1, nomaster => 1 }, noLink => { master => 1, nomaster => 1 }, popupWindowSize => { master => 0, nomaster => 1 }, popupStreamFW => { master => 0, nomaster => 1 }, popupStreamTo => { master => 0, nomaster => 1 }, ptzButtonSize => { master => 0, nomaster => 1 }, ptzButtonSizeFTUI => { master => 0, nomaster => 1 }, ); my %hset = ( # Hash für Set-Funktion popupStream => { fn => "_setpopupStream" }, adopt => { fn => "_setadopt" }, adoptForTimer => { fn => "_setadoptForTimer" }, adoptTime => { fn => "_setAdoptTimer" }, reset => { fn => "_setreset" }, snap => { fn => "_setsnap" }, ); my %sdevs = (); # Hash der vorhandenen Streaming Devices my $todef = 5; # Default Popup Zeit für set <> popupStream ################################################################ # Initialize # !! Werte von adoptSubset werden durch Funktion in # sub FwFn überschrieben !! ################################################################ sub Initialize { my $hash = shift; my $fwd = join(",",devspec2array("TYPE=FHEMWEB:FILTER=STATE=Initialized")); $hash->{DefFn} = \&Define; $hash->{SetFn} = \&Set; $hash->{GetFn} = \&Get; $hash->{AttrList} = "adoptSubset:sortable-strict,--reset-- ". "autoRefresh:selectnumbers,120,0.2,1800,0,log10 ". "autoRefreshFW:$fwd ". "disable:1,0 ". "forcePageRefresh:1,0 ". "genericStrmHtmlTag ". "htmlattr ". "htmlattrFTUI ". "hideAudio:1,0 ". "hideButtons:1,0 ". "hideDisplayName:1,0 ". "hideDisplayNameFTUI:1,0 ". "noLink:1,0 ". "popupWindowSize ". "popupStreamFW:$fwd ". "popupStreamTo:OK,1,2,3,4,5,6,7,8,9,10,15,20,25,30,40,50,60 ". "ptzButtonSize:selectnumbers,50,5,100,0,lin ". "ptzButtonSizeFTUI:selectnumbers,50,5,200,0,lin ". $readingFnAttributes; $hash->{RenameFn} = \&Rename; $hash->{CopyFn} = \&Copy; $hash->{FW_summaryFn} = \&FwFn; $hash->{FW_detailFn} = \&FwFn; $hash->{AttrFn} = \&Attr; $hash->{FW_hideDisplayName} = 1; # Forum 88667 # $hash->{FW_addDetailToSummary} = 1; # $hash->{FW_atPageEnd} = 1; # wenn 1 -> kein Longpoll ohne informid in HTML-Tag eval { FHEM::Meta::InitMod( __FILE__, $hash ) }; ## no critic 'eval' # für Meta.pm (https://forum.fhem.de/index.php/topic,97589.0.html) return; } ################################################################ # Define ################################################################ sub Define { my ($hash, $def) = @_; my ($name, $type, $link) = split("[ \t]+", $def, 3); if(!$link) { return "Usage: define SSCamSTRM "; } $link = migrateFunc($hash,$link); explodeLinkData ($hash, $link, 1); $hash->{HELPER}{MODMETAABSENT} = 1 if($modMetaAbsent); # Modul Meta.pm nicht vorhanden # Versionsinformationen setzen setVersionInfo($hash); my @r; push @r, "adoptSubset:--reset--" if(IsModelMaster($hash)); # Init für FTUI Subset wenn benutzt (Attr adoptSubset) push @r, "parentState:initialized"; # Init für "parentState" Forum: https://forum.fhem.de/index.php/topic,45671.msg985136.html#msg985136 push @r, "state:initialized"; # Init für "state" setReadings($hash, \@r, 1); return; } ################################################################ # im DEF hinterlegte Funktionen vor SSCam V9.4.0 migrieren ################################################################ sub migrateFunc { my $hash = shift; my $link = shift; for my $k (keys %fupgrade) { $hash->{DEF} =~ s/$fupgrade{$k}{of}/$fupgrade{$k}{nf}/gx; $link =~ s/$fupgrade{$k}{of}/$fupgrade{$k}{nf}/gx; } return $link; } ############################################################### # SSCamSTRM Copy & Rename # passt die Deviceparameter bei kopierten / umbenennen an ############################################################### sub Rename { my $new_name = shift; my $old_name = shift; my $hash = $defs{$new_name} // return; $hash->{DEF} =~ s/\'$old_name\'/\'$new_name\'/xg; explodeLinkData ($hash, $hash->{DEF}, 1); return; } sub Copy { my $old_name = shift; my $new_name = shift; my $hash = $defs{$new_name} // return; $hash->{DEF} =~ s/\'$old_name\'/\'$new_name\'/xg; explodeLinkData ($hash, $hash->{DEF}, 1); return; } ################################################################ # Set und Subroutinen ################################################################ sub Set { my ($hash, @a) = @_; return "\"set X\" needs at least an argument" if ( @a < 2 ); my $name = $a[0]; my $opt = $a[1]; my $prop = $a[2]; return if(IsDisabled($name) || $hash->{MODEL} =~ /ptzcontrol|snapgallery/x); my $setlist; if(!IsModelMaster($hash)) { $setlist = "Unknown argument $opt, choose one of ". "popupStream " ; $setlist .= "snap " if($hash->{LINKMODEL} eq "lastsnap"); } else { my $as = "--reset--,".allStreamDevs(); my $sd = AttrVal($name, "adoptSubset", $as); $sd =~ s/\s+/#/gx; my $rsd = $as; $rsd =~ s/#/ /g; ## no critic 'regular expression' # Regular expression without "/x" flag nicht anwenden !!! push my @ado, "adoptList:$rsd"; setReadings($hash, \@ado, 0); $setlist = "Unknown argument $opt, choose one of ". "adopt:$sd ". "adoptForTimer:$sd ". "adoptTime " ; } my %params = ( hash => $hash, name => $name, opt => $opt, prop => $prop, aref => \@a, ); no strict "refs"; ## no critic 'NoStrict' if($hset{$opt}) { my $ret = ""; $ret = &{$hset{$opt}{fn}} (\%params) if(defined &{$hset{$opt}{fn}}); return $ret; } use strict "refs"; return "$setlist"; } ################################################################ # Setter snap ################################################################ sub _setsnap { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $aref = $paref->{aref}; my $num = @$aref[2] // 1; my $lag = @$aref[3] // 2; my $camname = $hash->{LINKPARENT}; my $uuid = $hash->{FUUID}; # eindeutige UUID des Streamingdevices CommandSet(undef, "$camname snap $num $lag STRM:$uuid"); return; } ################################################################ # Setter popupStream ################################################################ sub _setpopupStream { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $prop = $paref->{prop}; my $txt = FHEM::SSCam::getClHash($hash); return $txt if($txt); # OK-Dialogbox oder Autoclose my $temp = AttrVal($name, "popupStreamTo", $todef); my $to = $prop // $temp; unless ($to =~ /^\d+$/x || lc($to) eq "ok") { $to = $todef; } $to = ($to =~ /\d+/x) ? (1000 * $to) : $to; my $pd = AttrVal($name, "popupStreamFW", "TYPE=FHEMWEB"); my $htmlCode = $hash->{HELPER}{STREAM}; if ($hash->{HELPER}{STREAMACTIVE}) { my $out = ""; $out .= $htmlCode; $out .= ""; Log3($name, 4, "$name - Stream to display: $htmlCode"); Log3($name, 4, "$name - Stream display to webdevice: $pd"); if($to =~ /\d+/x) { map {FW_directNotify("#FHEMWEB:$_", "FW_errmsg('$out', $to)", "")} devspec2array("$pd"); ## no critic 'void context'; } else { map {FW_directNotify("#FHEMWEB:$_", "FW_okDialog('$out')", "")} devspec2array("$pd"); ## no critic 'void context'; } } return; } ################################################################ # Setter adopt ################################################################ sub _setadopt { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $opt = $paref->{opt}; my $prop = $paref->{prop}; my $aref = $paref->{aref}; shift @$aref; shift @$aref; $prop = join "#", @$aref; if($prop eq "--reset--") { CommandSet(undef, "$name reset"); return; } my $strmd = $sdevs{"$prop"} // ""; my $valid = ($strmd && $defs{$strmd} && $defs{$strmd}{TYPE} eq "SSCamSTRM"); return qq{The command "$opt" needs a valid SSCamSTRM device as argument instead of "$strmd"} if(!$valid); # Übernahme der Readings my @r; delReadings($hash); for my $key (keys %{$defs{$strmd}{READINGS}}) { my $val = ReadingsVal($strmd, $key, ""); next if(!$val); push @r, "$key:$val"; } # Übernahme Link-Parameter my $link = "{$defs{$strmd}{LINKFN}('$defs{$strmd}{LINKPARENT}','$defs{$strmd}{LINKNAME}','$defs{$strmd}{LINKMODEL}')}"; explodeLinkData ($hash, $link, 0); push @r, "clientLink:$link"; push @r, "parentCam:$hash->{LINKPARENT}"; if(@r) { setReadings($hash, \@r, 1); } my $camname = $hash->{LINKPARENT}; $defs{$camname}{HELPER}{INFORM} = $hash->{FUUID}; InternalTimer(gettimeofday()+1.5, "FHEM::SSCam::roomRefresh", "$camname,0,0,0", 0); return; } ############################################################### # setter adopt-for-timer # schaltet für eine bestimmte Zeit auf das ausgewählte # Streaming Device und wieder auf das vorherige zurück ############################################################### sub _setadoptForTimer { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $name = $paref->{name}; my $opt = $paref->{opt}; my $odev = $paref->{odev}; # bisheriges adoptiertes Device (wird erst im InternalTimer gesetzt und verwendet) return if(IsDisabled($name) || $init_done != 1); my $sdev; my $atime = ReadingsVal($name, "adoptTimer", 10); if(!$odev) { # Step 1 -> erster Durchlauf ohne odef $hash->{HELPER}{SWITCHED} = $hash->{LINKNAME} if(!$hash->{HELPER}{SWITCHED}); $paref->{odev} = $hash->{HELPER}{SWITCHED}; # bisheriges adoptiertes Device in %params aufnehmen, InternalTimer mitgeben } else { # Step 2 -> zweiter Durchlauf mit odef gesetzt my @a; delete $hash->{HELPER}{SWITCHED}; $sdev = $odev eq $name ? "--reset--" : $odev; push @a, $name; push @a, $opt; push @a, $sdev; $paref->{aref} = \@a; } no strict "refs"; ## no critic 'NoStrict' &{$hset{adopt}{fn}} ($paref); use strict "refs"; Log3($name, 5, "$name - new => $hash->{LINKNAME}, odev => ".($odev // "")." , sdev => ".($sdev // "")." ,Helper SWITCHED => ".($hash->{HELPER}{SWITCHED} // "").", switch time => $atime"); if($odev) { Log3($name, 4, qq{$name - Switched Stream Device back to "$sdev"}); return; } Log3($name, 4, qq{$name - Switched to Stream Device "$hash->{LINKNAME}" for $atime seconds}); RemoveInternalTimer($hash->{HELPER}{ARG}, "FHEM::SSCamSTRM::_setadoptForTimer"); $hash->{HELPER}{ARG} = $paref; # $paref ist Unikat ! InternalTimer(gettimeofday()+$atime, "FHEM::SSCamSTRM::_setadoptForTimer", $paref, 0); return; } ################################################################ # Setter adoptTimer # setzt die Schaltzeit für setter adoptForTimer ################################################################ sub _setAdoptTimer { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; my $opt = $paref->{opt}; my $prop = $paref->{prop} // 0; my $ret = ""; $ret = qq{The command "$opt" needs an integer as argument.} if($prop !~ /^[0-9]+?$/x); return $ret if($ret); delReadings ($hash, "adoptTimer"); if($prop) { # bei "0" wird das Reading nur gelöscht, nicht wieder gesetzt my @r; push @r, "adoptTimer:$prop"; setReadings($hash, \@r, 0); } return; } ################################################################ # Setter reset ################################################################ sub _setreset { ## no critic "not used" my $paref = shift; my $hash = $paref->{hash}; delReadings ($hash); explodeLinkData ($hash, $hash->{DEF}, 1); my @r; push @r, "parentState:initialized"; push @r, "state:initialized"; push @r, "parentCam:initialized"; setReadings($hash, \@r, 1); my $camname = $hash->{LINKPARENT}; $defs{$camname}{HELPER}{INFORM} = $hash->{FUUID}; InternalTimer(gettimeofday()+1.5, "FHEM::SSCam::roomRefresh", "$camname,0,0,0", 0); return; } ############################################################### # SSCamSTRM Get ############################################################### sub Get { my ($hash, @a) = @_; return "\"get X\" needs at least an argument" if ( @a < 2 ); my $name = shift @a; my $cmd = shift @a; if ($cmd eq "html") { return streamAsHtml($hash); } if ($cmd eq "ftui") { return streamAsHtml($hash,"ftui"); } return; } ################################################################ # Attr # $cmd can be "del" or "set" # $name is device name # aName and aVal are Attribute name and value ################################################################ sub Attr { my ($cmd,$name,$aName,$aVal) = @_; my $hash = $defs{$name}; my $model = $hash->{MODEL}; if(defined $hvattr{$aName}) { if ($model eq "master" && !$hvattr{$aName}{master}) { return qq{The attribute "$aName" is only valid if MODEL is not "$model" !}; } if ($model ne "master" && !$hvattr{$aName}{nomaster}) { return qq{The attribute "$aName" is only valid if MODEL is "master" !}; } } if($aName eq "genericStrmHtmlTag" && $hash->{MODEL} ne "generic") { return qq{This attribute is only valid if MODEL is "generic" !}; } my ($do,$val); if($aName eq "disable") { if($cmd eq "set") { $do = ($aVal) ? 1 : 0; } $do = 0 if($cmd eq "del"); $val = ($do == 1 ? "disabled" : "initialized"); readingsSingleUpdate($hash, "state", $val, 1); } if($aName eq "adoptSubset") { if($cmd eq "set") { readingsSingleUpdate($hash, "adoptSubset", $aVal, 1); } else { readingsSingleUpdate($hash, "adoptSubset", "--reset--", 1); } } if ($cmd eq "set") { if ($aName =~ m/popupStreamTo/x) { unless ($aVal =~ /^\d+$/x || $aVal eq "OK") { return qq{The Value for $aName is not valid. Use only figures 0-9 or "OK" !}; } } } return; } ############################################################################################# # FHEMWEB Summary ############################################################################################# sub FwFn { my ($FW_wname, $name, $room, $pageHash) = @_; # pageHash is set for summaryFn. my $hash = $defs{$name}; RemoveInternalTimer($hash); $hash->{HELPER}{FW} = $FW_wname; my $clink = ReadingsVal($name, "clientLink", ""); sofAdoptSubset ($hash); explodeLinkData ($hash, $clink, 0) if($init_done == 1); # Beispielsyntax: "{$hash->{LINKFN}('$hash->{LINKPARENT}','$hash->{LINKNAME}','$hash->{LINKMODEL}')}"; my $ftui = 0; my $linkfn = $hash->{LINKFN}; my %pars = ( linkparent => $hash->{LINKPARENT}, linkname => $hash->{LINKNAME}, linkmodel => $hash->{LINKMODEL}, omodel => $hash->{MODEL}, oname => $hash->{NAME}, ftui => $ftui ); no strict "refs"; ## no critic 'NoStrict' my $html = eval{ &{$linkfn}(\%pars) } or do { return qq{Error in Streaming function definition of $name} }; use strict "refs"; my $ret = ""; if(IsModelMaster($hash) && $clink) { my $alias = AttrVal($name, "alias", $name); # Linktext als Aliasname oder Devicename setzen my $lang = AttrVal("global", "language", "EN"); my $txt = "is Streaming master of"; $txt = "ist Streaming Master von " if($lang eq "DE"); my $dlink = "$alias $txt "; $dlink = "$alias $txt " if(AttrVal($name, "noLink", 0)); # keine Links im Stream-Dev generieren $ret .= "$dlink " if(!AttrVal($name,"hideDisplayName",0)); } if(IsDisabled($name)) { if(AttrVal($name,"hideDisplayName",0)) { $ret .= "Stream-device $name is disabled"; } else { $ret .= "Stream-device is disabled"; } } else { $ret .= $html; $ret .= sDevsWidget($name) if(IsModelMaster($hash)); } my $al = AttrVal($name, "autoRefresh", 0); # Autorefresh nur des aufrufenden FHEMWEB-Devices if($al) { InternalTimer(gettimeofday()+$al, "FHEM::SSCamSTRM::webRefresh", $hash, 0); Log3($name, 5, "$name - next start of autoRefresh: ".FmtDateTime(gettimeofday()+$al)); } undef $html; return $ret; } ############################################################################################# # Bestandteile des DEF (oder Link) auflösen # $link = aufzulösender String # $def = 1 -> es ist ein Shash->{DEF} Inhalt, 0 -> eine andere Quelle ############################################################################################# sub explodeLinkData { my $hash = shift; my $link = shift; my $def = shift; return if(!$link); my ($fn,$arg) = split("[()]",$link); $arg =~ s/'//xg; $fn =~ s/{//xg; if($def) { ($hash->{PARENT},$hash->{LINKNAME},$hash->{MODEL}) = split(",",$arg); $hash->{LINKMODEL} = $hash->{MODEL}; $hash->{LINKPARENT} = $hash->{PARENT}; } else { ($hash->{LINKPARENT},$hash->{LINKNAME},$hash->{LINKMODEL}) = split(",",$arg); } $hash->{LINKFN} = $fn; return; } ############################################################################################# # Ist das MODEL "master" ? ############################################################################################# sub IsModelMaster { my $hash = shift; my $mm = $hash->{MODEL} eq "master" ? 1 : 0; return $mm; } ############################################################################################# # Seitenrefresh # festgelegt durch SSCamSTRM-Attribut "autoRefresh" und "autoRefreshFW" ############################################################################################# sub webRefresh { my $hash = shift; my $name = $hash->{NAME}; my $rd = AttrVal($name, "autoRefreshFW", $hash->{HELPER}{FW}); { map { FW_directNotify("#FHEMWEB:$_", "location.reload('true')", "") } $rd } ## no critic 'void context'; my $al = AttrVal($name, "autoRefresh", 0); if($al) { InternalTimer(gettimeofday()+$al, "FHEM::SSCamSTRM::webRefresh", $hash, 0); Log3($name, 5, "$name - next start of autoRefresh: ".FmtDateTime(gettimeofday()+$al)); } else { RemoveInternalTimer($hash); } return; } ############################################################################################# # Versionierungen des Moduls setzen # Die Verwendung von Meta.pm und Packages wird berücksichtigt ############################################################################################# sub setVersionInfo { my ($hash) = @_; my $name = $hash->{NAME}; my $v = (sortTopicNum("desc",keys %vNotesIntern))[0]; my $type = $hash->{TYPE}; $hash->{HELPER}{PACKAGE} = __PACKAGE__; $hash->{HELPER}{VERSION} = $v; if($modules{$type}{META}{x_prereqs_src} && !$hash->{HELPER}{MODMETAABSENT}) { # META-Daten sind vorhanden $modules{$type}{META}{version} = "v".$v; # Version aus META.json überschreiben, Anzeige mit {Dumper $modules{SSCamSTRM}{META}} if($modules{$type}{META}{x_version}) { # {x_version} ( nur gesetzt wenn $Id: 49_SSCamSTRM.pm 25032 2021-09-29 13:23:58Z DS_Starter $ im Kopf komplett! vorhanden ) $modules{$type}{META}{x_version} =~ s/1\.1\.1/$v/gx; } else { $modules{$type}{META}{x_version} = $v; } return $@ unless (FHEM::Meta::SetInternals($hash)); # FVERSION wird gesetzt ( nur gesetzt wenn $Id: 49_SSCamSTRM.pm 25032 2021-09-29 13:23:58Z DS_Starter $ im Kopf komplett! vorhanden ) if(__PACKAGE__ eq "FHEM::$type" || __PACKAGE__ eq $type) { # es wird mit Packages gearbeitet -> Perl übliche Modulversion setzen # mit {->VERSION()} im FHEMWEB kann Modulversion abgefragt werden use version 0.77; our $VERSION = FHEM::Meta::Get( $hash, 'version' ); ## no critic 'VERSION' } } else { # herkömmliche Modulstruktur $hash->{VERSION} = $v; } return; } ################################################################ # Grafik als HTML zurück liefern (z.B. für Widget) ################################################################ sub streamAsHtml { my $hash = shift; my $ftui = shift; my $name = $hash->{NAME}; if($ftui && $ftui eq "ftui") { $ftui = 1; } else { $ftui = 0; } my $clink = ReadingsVal($name, "clientLink", ""); explodeLinkData ($hash, $clink, 0); my $linkfn = $hash->{LINKFN}; my %pars = ( linkparent => $hash->{LINKPARENT}, linkname => $hash->{LINKNAME}, linkmodel => $hash->{LINKMODEL}, omodel => $hash->{MODEL}, oname => $hash->{NAME}, ftui => $ftui ); no strict "refs"; ## no critic 'NoStrict' my $html = eval{ &{$linkfn}(\%pars) } or do { return qq{Error in Streaming function definition of $name} }; use strict "refs"; my $ret = ""; if(IsDisabled($name)) { if(AttrVal($name,"hideDisplayName",0)) { $ret .= "Stream-device $name is disabled"; } else { $ret .= "Stream-device is disabled"; } } else { $ret .= $html; } $ret .= ""; undef $html; return $ret; } ################################################################ # Wertevorrat für adoptSubset generieren ################################################################ sub sofAdoptSubset { my $hash = shift; my @na; my $ca = $modules{$hash->{TYPE}}{AttrList}; my $sd = "--reset--,".allStreamDevs(); my @dca = split(" ", $ca); for my $attr (@dca) { push @na, $attr if($attr !~ /adoptSubset:/x); } push @na, "adoptSubset:sortable-strict,$sd "; $hash->{".AttrList"} = join " ", @na; # Device spezifische AttrList, überschreibt Modul AttrList ! return; } ################################################################ # set Readings # $rref = Referenz zum Array der zu setzenen Reading # (Aufbau: :) # $event = 1 wenn Event generiert werden soll ################################################################ sub setReadings { my $hash = shift; my $rref = shift; my $event = shift; my $name = $hash->{NAME}; readingsBeginUpdate($hash); for my $elem (@$rref) { my ($rn,$rval) = split ":", $elem, 2; readingsBulkUpdate($hash, $rn, $rval); } readingsEndUpdate($hash, $event); return; } ################################################################ # delete Readings # $rd = angegebenes Reading löschen unabhängig vom # Inhalt der Blacklist ################################################################ sub delReadings { my $hash = shift; my $rd = shift; my $name = $hash->{NAME}; my $bl = "state|parentState|adoptSubset|adoptTimer"; # Blacklist if($rd) { # angegebenes Reading löschen readingsDelete($hash, $rd); return; } for my $key (keys %{$hash->{READINGS}}) { readingsDelete($hash, $key) if($key !~ /$bl/x); } return; } ################################################################ # liefert String aller Streamingdevices außer MODEL = master # und füllt Hash %sdevs{Alias} = Devicename zu Auflösung # # (es wird Alias (wenn gesetzt) oder Devicename verwendet, # Leerzeichen werden durch "#" ersetzt) ################################################################ sub allStreamDevs { my $sd = ""; undef %sdevs; my @strmdevs = devspec2array("TYPE=SSCamSTRM:FILTER=MODEL!=master"); # Liste Streaming devices außer MODEL = master for my $da (@strmdevs) { next if(!$defs{$da}); my $alias = AttrVal($da, "alias", $da); $alias =~ s/\s+/#/gx; $sdevs{$alias} = "$da"; } for my $a (sort keys %sdevs) { $sd .= "," if($sd); $sd .= $a; } for my $d (@strmdevs) { # Devicenamen zusätzlich als Schlüssel speichern damit set <> adopt ohne Widget funktioniert next if(!$defs{$d}); $sdevs{$d} = "$d"; } return $sd; } ################################################################ # Streaming Devices Drop-Down Widget zur Auswahl # in einem Master Streaming Device ################################################################ sub sDevsWidget { my $name = shift; my $Adopts; my $ret = ""; my $cmdAdopt = "adopt"; my $as = "--reset--,".allStreamDevs(); my $valAdopts = AttrVal($name, "adoptSubset", $as); $valAdopts =~ s/\s+/#/gx; for my $fn (sort keys %{$data{webCmdFn}}) { next if($data{webCmdFn}{$fn} ne "FW_widgetFallbackFn"); no strict "refs"; ## no critic 'NoStrict' $Adopts = &{$data{webCmdFn}{$fn}}($FW_wname,$name,"",$cmdAdopt,$valAdopts); use strict "refs"; last if(defined($Adopts)); } if($Adopts) { $Adopts =~ s,^]*>(.*)$,$1,x; } else { $Adopts = FW_pH "cmd.$name=set $name $cmdAdopt", $cmdAdopt, 0, "", 1, 1; } ## Tabellenerstellung $ret .= ""; $ret .= ''; $ret .= ""; $ret .= ""; $ret .= ""; $ret .= "
Streaming Device: $Adopts
"; return $ret; } 1; =pod =item summary Definition of a streaming device by the SSCam module =item summary_DE Erstellung eines Streaming-Device durch das SSCam-Modul =begin html

SSCamSTRM


    The module SSCamSTRM is a special device module synchronized to the SSCam module. It is used for definition of Streaming-Devices.
    Dependend of the Streaming-Device state, different buttons are provided to start actions:

      Switch off - stops a running playback
      Refresh - refresh a view (no page reload)
      Restart - restart a running content (e.g. a HLS-Stream)
      MJPEG - starts a MJPEG Livestream
      HLS - starts HLS (HTTP Live Stream)
      Last Record - playback the last recording as iFrame
      Last Rec H.264 - playback the last recording if available as H.264
      Last Rec MJPEG - playback the last recording if available as MJPEG
      Last SNAP - show the last snapshot
      Start Recording - starts an endless recording
      Stop Recording - stopps the recording
      Take Snapshot - take a snapshot

    Integration into FHEM TabletUI:

    There is a widget provided for integration of SSCam-Streaming devices into FTUI. For further information please be informed by the (german) FHEM Wiki article:
    FTUI Widget für SSCam Streaming Devices (SSCamSTRM).

    Define

      A SSCam Streaming-device is defined by the SSCam command:

        set <name> createStreamDev <Device Typ>

      Please refer to SSCam "createStreamDev" command.

    Set
      • adopt <Streaming device>     (only valid if MODEL = master)
        A Streaming Device of type master adopts the content of another defined Streaming Device.

      • adoptForTimer <Streaming Device>     (only valid if MODEL = master)
        A Streaming Device of type master adopts the content of another defined Streaming Device for a certain time.
        The time is set with the command set <name> adoptTime.
        (default: 10 seconds)

      • adoptTime <seconds>     (only valid if MODEL = master)
        Setting of the switching time when temporarily taking over the content of another Streaming Device. After the time has expired, playback is switched back to the previously set Streaming Device.
        If no argument or "0" is given, the time specification is deleted and the default (10 seconds) is used.

      • snap [<number>] [<time difference>]     (only valid if MODEL = lastsnap)
        One or multiple snapshots are triggered. The number of snapshots to trigger and the time difference (in seconds) between each snapshot can be optionally specified. Without any specification only one snapshot is triggered.

      • popupStream     (only valid if MODEL != master)
        The current streaming content is depicted in a popup window. By setting attribute "popupWindowSize" the size of display can be adjusted. The attribute "popupStreamTo" determines the type of the popup window. If "OK" is set, an OK-dialog window will be opened. A specified number in seconds closes the popup window after this time automatically (default 5 seconds).
        Optionally you can append "OK" or <seconds> directly to override the adjustment by attribute "popupStreamTo".


    Get

      • get <name> html
      • The stream object (camera live view, snapshots or replay) is fetched as HTML-code and depicted.


    Attributes

      • adoptSubset     (only valid for MODEL "master")
        In a Streaming master Device a subset of all defined Streaming Devices is selected and used for the adopt command is provided.
        For control in the FTUI, the selection is also stored in the Reading of the same name.

      • autoRefresh
        If set, active browser pages of the FHEMWEB-Device which has called the SSCamSTRM-Device, are new reloaded after the specified time (seconds). Browser pages of a particular FHEMWEB-Device to be refreshed can be specified by attribute "autoRefreshFW" instead. This may stabilize the video playback in some cases.

      • autoRefreshFW
        If "autoRefresh" is activated, you can specify a particular FHEMWEB-Device whose active browser pages are refreshed periodically.

      • disable
        Deactivates the device.

      • forcePageRefresh
        The attribute is evaluated by SSCam.
        If set, a reload of all browser pages with active FHEMWEB connections will be enforced when particular camera operations were finished. This may stabilize the video playback in some cases.

      • genericStrmHtmlTag     (only valid for MODEL "generic")
        This attribute contains HTML-Tags for video-specification in a Streaming-Device of type "generic".

          Examples:
          attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
                                           <source src='http://192.168.2.10:32000/$NAME.m3u8' type='application/x-mpegURL'>
                                         </video> 
                                         
          attr <name> genericStrmHtmlTag <img $HTMLATTR 
                                           src="http://192.168.2.10:32774"
                                           onClick="FW_okDialog('<img src=http://192.168.2.10:32774 $PWS>')"
                                         >  
                
          The variables $HTMLATTR, $NAME and $PWS are placeholders and absorb the attribute "htmlattr" (if set), the SSCam-Devicename respectively the value of attribute "popupWindowSize" in streaming-device, which specify the windowsize of a popup window.


      • hideAudio
        Hide the control block for audio playback in the footer.

      • hideButtons
        Hide the buttons in the footer. It has no impact for streaming devices of type "switched".

      • hideDisplayName
        Hide the device/alias name (link to detail view).

      • hideDisplayNameFTUI
        Hide the device/alias name (link to detail view) in FHEM TabletUI.

      • htmlattr
        Additional HTML tags to manipulate the streaming device.

          Example:
          attr <name> htmlattr width="580" height="460"

      • htmlattrFTUI
        Additional HTML tags to manipulate the streaming device in TabletUI.

          Example:
          attr <name> htmlattr width="580" height="460"

      • noLink
        The device name or alias doesn't contain a link to the detail device view.

      • popupStreamFW
        You can specify a particular FHEMWEB device whose active browser pages should open a popup window by the "set <name> popupStream" command (default: all active FHEMWEB devices).

      • popupStreamTo [OK | <seconds>]
        The attribute "popupStreamTo" determines the type of the popup window which is opend by set-function "popupStream". If "OK" is set, an OK-dialog window will be opened. A specified number in seconds closes the popup window after this time automatically (default 5 seconds)..

          Example:
          attr <name> popupStreamTo 10

      • popupWindowSize
        If the content of playback (Videostream or Snapshot gallery) is suitable, by clicking the content a popup window will appear. The size of display can be setup by this attribute. It is also valid for the get-function "popupStream".

          Example:
          attr <name> popupWindowSize width="600" height="425"

      • ptzButtonSize
        Specifies the PTZ-panel button size (in %).

      • ptzButtonSizeFTUI
        Specifies the PTZ-panel button size used in a Tablet UI (in %).
=end html =begin html_DE

SSCamSTRM


    Das Modul SSCamSTRM ist ein mit SSCam abgestimmtes Gerätemodul zur Definition von Streaming-Devices.
    Abhängig vom Zustand des Streaming-Devices werden zum Start von Aktionen unterschiedliche Drucktasten angeboten:

      Switch off - stoppt eine laufende Wiedergabe
      Refresh - auffrischen einer Ansicht (kein Browser Seiten-Reload)
      Restart - neu starten eines laufenden Contents (z.B. eines HLS-Streams)
      MJPEG - Startet MJPEG Livestream
      HLS - Startet HLS (HTTP Live Stream)
      Last Record - spielt die letzte Aufnahme als iFrame
      Last Rec H.264 - spielt die letzte Aufnahme wenn als H.264 vorliegend
      Last Rec MJPEG - spielt die letzte Aufnahme wenn als MJPEG vorliegend
      Last SNAP - zeigt den letzten Snapshot
      Start Recording - startet eine Endlosaufnahme
      Stop Recording - stoppt eine Aufnahme
      Take Snapshot - löst einen Schnappschuß aus

    Integration in FHEM TabletUI:

    Zur Integration von SSCam Streaming Devices (Typ SSCamSTRM) wird ein Widget bereitgestellt. Für weitere Information dazu bitte den Artikel im Wiki durchlesen:
    FTUI Widget für SSCam Streaming Devices (SSCamSTRM).


    Define

      Ein SSCam Streaming-Device wird durch den SSCam Befehl

        set <name> createStreamDev <Device Typ>

      erstellt. Siehe auch die Beschreibung zum SSCam "createStreamDev" Befehl.

    Set
      • adopt <Streaming Device>     (nur wenn MODEL = master)
        Ein Streaming Device vom Type master übernimmt (adoptiert) den Content eines anderen definierten Streaming Devices.

      • adoptForTimer <Streaming Device>     (nur bei MODEL = master)
        Ein Streaming Device vom Type master übernimmt (adoptiert) den Content eines anderen definierten Streaming Devices für eine bestimmte Zeit.
        Die Zeit wird mit dem Kommando set <name> adoptTime eingestellt.
        (default: 10 Sekunden)

      • adoptTime <Sekunden>     (nur bei MODEL = master)
        Einstellung der Schaltzeit bei temporärer Übernahme des Contents eines anderen Streaming Devices. Nach Ablauf der Zeit wird die Wiedergabe auf das vorher eingestellte Streaming Device zurückgeschaltet.
        Wird kein Argument oder "0" angegeben, wird die Zeitvorgabe gelöscht und der Standard (10 Sekunden) verwendet.

      • snap [<number>] [<time difference>]     (nur bei MODEL = lastsnap)
        Es werden ein oder mehrere Schnappschüsse ausgelöst. Die Anzahl der auszulösenden Schnappschüsse und der Zeitabstand (in Sekunden) zwischen jedem Snapshot können optional angegeben werden. Ohne Angabe wird nur ein Snapshot ausgelöst.

      • popupStream [OK | <Sekunden>]     (nur bei MODEL != master)
        Der aktuelle Streaminhalt wird in einem Popup-Fenster dargestellt. Mit dem Attribut "popupWindowSize" kann die Darstellungsgröße eingestellt werden. Das Attribut "popupStreamTo" legt die Art des Popup-Fensters fest. Ist "OK" eingestellt, öffnet sich ein OK-Dialogfenster. Die angegebene Zahl in Sekunden schließt das Fenster nach dieser Zeit automatisch (default 5 Sekunden).
        Durch die optionalen Angabe von "OK" oder <Sekunden> kann die Einstellung des Attributes "popupStreamTo" übersteuert werden.


    Get

      • get <name> html
      • Das eingebundene Streamobjekt (Kamera Live View, Schnappschüsse oder Wiedergabe einer Aufnahme) wird als HTML-code abgerufen und dargestellt.


    Attribute

      • adoptSubset     (nur für MODEL "master")
        In einem Streaming master Device wird eine Teilmenge aller definierten Streaming Devices ausgewählt und für das adopt Kommando bereitgestellt.
        Für die Steuerung im FTUI wird die Auswahl ebenfalls im gleichnamigen Reading gespeichert.

      • autoRefresh
        Wenn gesetzt, werden aktive Browserseiten des FHEMWEB-Devices welches das SSCamSTRM-Device aufgerufen hat, nach der eingestellten Zeit (Sekunden) neu geladen. Sollen statt dessen Browserseiten eines bestimmten FHEMWEB-Devices neu geladen werden, kann dieses Device mit dem Attribut "autoRefreshFW" festgelegt werden. Dies kann in manchen Fällen die Wiedergabe innerhalb einer Anwendung stabilisieren.

      • autoRefreshFW
        Ist "autoRefresh" aktiviert, kann mit diesem Attribut das FHEMWEB-Device bestimmt werden dessen aktive Browserseiten regelmäßig neu geladen werden sollen.

      • disable
        Aktiviert/deaktiviert das Device.

      • forcePageRefresh
        Das Attribut wird durch SSCam ausgewertet.
        Wenn gesetzt, wird ein Reload aller Browserseiten mit aktiven FHEMWEB-Verbindungen nach dem Abschluß bestimmter SSCam-Befehle erzwungen. Dies kann in manchen Fällen die Wiedergabe innerhalb einer Anwendung stabilisieren.

      • genericStrmHtmlTag     (nur für MODEL "generic")
        Das Attribut enthält HTML-Tags zur Video-Spezifikation in einem Streaming-Device von Typ "generic".

          Beispiele:
          attr <name> genericStrmHtmlTag <video $HTMLATTR controls autoplay>
                                           <source src='http://192.168.2.10:32000/$NAME.m3u8' type='application/x-mpegURL'>
                                         </video>
                                         
          attr <name> genericStrmHtmlTag <img $HTMLATTR 
                                           src="http://192.168.2.10:32774"
                                           onClick="FW_okDialog('<img src=http://192.168.2.10:32774 $PWS >')"
                                         >                              
                
          Die Variablen $HTMLATTR, $NAME und $PWS sind Platzhalter und übernehmen ein gesetztes Attribut "htmlattr", den SSCam- Devicenamen bzw. das Attribut "popupWindowSize" im Streaming-Device, welches die Größe eines Popup-Windows festlegt.


      • hideAudio
        Verbirgt die Steuerungsbereich für die Audiowiedergabe in der Fußzeile.

      • hideButtons
        Verbirgt die Drucktasten in der Fußzeile. Dieses Attribut hat keinen Einfluß bei Streaming-Devices vom Typ "switched".

      • hideDisplayName
        Verbirgt den Device/Alias-Namen (Link zur Detailansicht).

      • hideDisplayNameFTUI
        Verbirgt den Device/Alias-Namen (Link zur Detailansicht) im TabletUI.

      • htmlattr
        Zusätzliche HTML Tags zur Darstellung im Streaming Device.

          Beispiel:
          attr <name> htmlattr width="580" height="460"

      • htmlattrFTUI
        Zusätzliche HTML Tags zur Darstellung des Streaming Device im TabletUI.

          Beispiel:
          attr <name> htmlattr width="580" height="460"

      • noLink
        Der Devicename oder Alias enthält keinen Link zur Detailansicht.

      • popupStreamFW
        Es kann mit diesem Attribut das FHEMWEB-Device bestimmt werden, auf dessen Browserseiten sich Popup-Fenster mit "set <name> popupStream" öffnen sollen (default: alle aktiven FHEMWEB-Devices).

      • popupStreamTo [OK | <Sekunden>]
        Das Attribut "popupStreamTo" legt die Art des Popup-Fensters fest welches mit der set-Funktion "popupStream" geöffnet wird. Ist "OK" eingestellt, öffnet sich ein OK-Dialogfenster. Die angegebene Zahl in Sekunden schließt das Fenster nach dieser Zeit automatisch (default 5 Sekunden).

          Beispiel:
          attr <name> popupStreamTo 10

      • popupWindowSize
        Bei geeigneten Wiedergabeinhalten (Videostream oder Schnappschußgalerie) öffnet ein Klick auf den Bildinhalt ein Popup-Fenster mit diesem Inhalt. Die Darstellungsgröße kann mit diesem Attribut eingestellt werden. Das Attribut gilt ebenfalls für die set-Funktion "popupStream".

          Beispiel:
          attr <name> popupWindowSize width="600" height="425"

      • ptzButtonSize
        Legt die Größe der Drucktasten des PTZ Paneels fest (in %).

      • ptzButtonSizeFTUI
        Legt die Größe der Drucktasten des PTZ Paneels in einem Tablet UI fest (in %).
=end html_DE =for :application/json;q=META.json 49_SSCamSTRM.pm { "abstract": "Definition of a streaming device by the SSCam module", "x_lang": { "de": { "abstract": "Erstellung eines Streaming-Device durch das SSCam-Modul" } }, "keywords": [ "camera", "streaming", "PTZ", "Synology Surveillance Station", "MJPEG", "HLS", "RTSP" ], "version": "v1.1.1", "release_status": "stable", "author": [ "Heiko Maaz " ], "x_fhem_maintainer": [ "DS_Starter" ], "x_fhem_maintainer_github": [ "nasseeder1" ], "prereqs": { "runtime": { "requires": { "FHEM": 5.00918799, "perl": 5.014 }, "recommends": { "FHEM::Meta": 0 }, "suggests": { } } }, "resources": { "x_wiki": { "web": "https://wiki.fhem.de/wiki/SSCAM_-_Steuerung_von_Kameras_in_Synology_Surveillance_Station", "title": "SSCAM - Steuerung von Kameras in Synology Surveillance Station" } } } =end :application/json;q=META.json =cut