########################################################################################################################
# $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 <http://www.gnu.org/licenses/>.
#
#########################################################################################################################

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 <name> 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 <name> SSCamSTRM <arg>";
  }

  $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 = "<html>";
      $out .= $htmlCode;
      $out .= "</html>";
      
      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 <html><a href=\"/fhem?detail=$name\">$name</a></html>} };
  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 = "<a href=\"/fhem?detail=$name\">$alias</a> $txt ";
      $dlink    = "$alias $txt " if(AttrVal($name, "noLink", 0));                                         # keine Links im Stream-Dev generieren
      $ret     .= "<span align=\"center\">$dlink </span>"   if(!AttrVal($name,"hideDisplayName",0));
  }
  
  if(IsDisabled($name)) {
      if(AttrVal($name,"hideDisplayName",0)) {
          $ret .= "Stream-device <a href=\"/fhem?detail=$name\">$name</a> is disabled";
      } else {
          $ret .= "<html>Stream-device is disabled</html>";
      } 
      
  } 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 {<Modul>->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 <html><a href=\"/fhem?detail=$name\">$name</a></html>} };
  use strict "refs"; 
  
  my $ret = "<html>";
  if(IsDisabled($name)) {  
      if(AttrVal($name,"hideDisplayName",0)) {
          $ret .= "Stream-device <a href=\"/fhem?detail=$name\">$name</a> is disabled";
      } else {
          $ret .= "Stream-device is disabled";
      }

  } else {
      $ret .= $html;     
  }
  
  $ret .= "</html>";
  
  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: <Reading>:<Wert>)
#   $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,^<td[^>]*>(.*)</td>$,$1,x;
  } else {
      $Adopts = FW_pH "cmd.$name=set $name $cmdAdopt", $cmdAdopt, 0, "", 1, 1;
  }
       
  ## Tabellenerstellung
  $ret .= "<style>.defsize { font-size:16px; } </style>";
  $ret .= '<table class="rc_body defsize">';
  
  $ret .= "<tr>";
  $ret .= "<td>Streaming Device: </td><td>$Adopts</td>";  
  $ret .= "</tr>"; 

  $ret .= "</table>"; 

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

<a name="SSCamSTRM"></a>
<h3>SSCamSTRM</h3>
<br>
  <ul>
  The module SSCamSTRM is a special device module synchronized to the SSCam module. It is used for definition of
  Streaming-Devices. <br>
  Dependend of the Streaming-Device state, different buttons are provided to start actions: <br><br>
  
    <ul>   
      <table>  
      <colgroup> <col width=25%> <col width=75%> </colgroup>
        <tr><td> Switch off      </td><td>- stops a running playback </td></tr>
        <tr><td> Refresh         </td><td>- refresh a view (no page reload) </td></tr>
        <tr><td> Restart         </td><td>- restart a running content (e.g. a HLS-Stream) </td></tr>
        <tr><td> MJPEG           </td><td>- starts a MJPEG Livestream </td></tr>
        <tr><td> HLS             </td><td>- starts HLS (HTTP Live Stream) </td></tr>
        <tr><td> Last Record     </td><td>- playback the last recording as iFrame </td></tr>
        <tr><td> Last Rec H.264  </td><td>- playback the last recording if available as H.264 </td></tr>
        <tr><td> Last Rec MJPEG  </td><td>- playback the last recording if available as MJPEG </td></tr>
        <tr><td> Last SNAP       </td><td>- show the last snapshot </td></tr>
        <tr><td> Start Recording </td><td>- starts an endless recording </td></tr>
        <tr><td> Stop Recording  </td><td>- stopps the recording </td></tr>
        <tr><td> Take Snapshot   </td><td>- take a snapshot </td></tr>
      </table>
     </ul>     
     <br>
   
  <b>Integration into FHEM TabletUI: </b> <br><br>
  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: <br>
   <a href="https://wiki.fhem.de/wiki/FTUI_Widget_f%C3%BCr_SSCam_Streaming_Devices_(SSCamSTRM)">FTUI Widget für SSCam Streaming Devices (SSCamSTRM)</a>.
  <br><br>
  </ul>

<ul>
  <a name="SSCamSTRMdefine"></a>
  <b>Define</b>
  <br><br>
  
  <ul>
    A SSCam Streaming-device is defined by the SSCam command: <br><br>
    
    <ul>
      set &lt;name&gt; createStreamDev  &lt;Device Typ&gt; <br><br>
    </ul>
    
    Please refer to SSCam <a href="#SSCamcreateStreamDev">"createStreamDev"</a> command.  
    <br><br>
  </ul>

  <a name="SSCamSTRMset"></a>
  <b>Set</b> 
  <ul>
  
  <ul>
  <a name="adopt"></a>
  <li><b>adopt &lt;Streaming device&gt; </b>   &nbsp;&nbsp;&nbsp;&nbsp;(only valid if MODEL = master)<br>
  
  A Streaming Device of type <b>master</b> adopts the content of another defined Streaming Device.
  </li>
  </ul>
  <br>
  
  <ul>
  <a name="adoptForTimer"></a>
  <li><b>adoptForTimer &lt;Streaming Device&gt; </b>   &nbsp;&nbsp;&nbsp;&nbsp;(only valid if MODEL = master)<br>
  
  A Streaming Device of type <b>master</b> adopts the content of another defined Streaming Device
  for a certain time. <br>
  The time is set with the command <b>set &lt;name&gt; adoptTime</b>. <br>
  (default: 10 seconds)
  </li>
  </ul>
  <br>
  
  <ul>
  <a name="adoptTime"></a>
  <li><b>adoptTime &lt;seconds&gt; </b>   &nbsp;&nbsp;&nbsp;&nbsp;(only valid if MODEL = master)<br>
  
  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. <br>
  If no argument or "0" is given, the time specification is deleted and the default (10 seconds) is used.
  </li>
  </ul>
  <br>
  
  <ul>
  <a name="snap"></a>
  <li><b> snap [&lt;number&gt;] [&lt;time difference&gt;] </b>  &nbsp;&nbsp;&nbsp;&nbsp;(only valid if MODEL = lastsnap)<br>
  
  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. <br>
  </li>
  </ul>
  <br>
  
  <ul>
  <li><b>popupStream</b>   &nbsp;&nbsp;&nbsp;&nbsp;(only valid if MODEL != master)<br>
  
  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). <br>
  Optionally you can append "OK" or &lt;seconds&gt; directly to override the adjustment by attribute "popupStreamTo".
  </li>
  </ul>
  <br>
  
  </ul>
  <br>
  
  <a name="SSCamSTRMget"></a>
  <b>Get</b> 
  <ul>
    <br>
    <ul>
      <li><b> get &lt;name&gt; html </b> </li>  
      The stream object (camera live view, snapshots or replay) is fetched as HTML-code and depicted. 
    </ul>
    <br>
    
    <br>
  </ul>
  
  <a name="SSCamSTRMattr"></a>
  <b>Attributes</b>
  <br><br>
  
  <ul>
  <ul>
  
    <a name="adoptSubset"></a>
    <li><b>adoptSubset</b> &nbsp;&nbsp;&nbsp;&nbsp;(only valid for MODEL "master") <br>
      In a Streaming <b>master</b> Device a subset of all defined Streaming Devices is selected and used for 
      the <b>adopt</b> command is provided. <br>
      For control in the FTUI, the selection is also stored in the Reading of the same name.
    </li>
    <br>

    <a name="autoRefresh"></a>
    <li><b>autoRefresh</b><br>
      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.
    </li>
    <br>
    
    <a name="autoRefreshFW"></a>
    <li><b>autoRefreshFW</b><br>
      If "autoRefresh" is activated, you can specify a particular FHEMWEB-Device whose active browser pages are refreshed 
      periodically.
    </li>
    <br>
    
    <a name="disable"></a>
    <li><b>disable</b><br>
      Deactivates the device.
    </li>
    <br>
    
    <a name="forcePageRefresh"></a>
    <li><b>forcePageRefresh</b><br>
      The attribute is evaluated by SSCam. <br>
      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.       
    </li>
    <br>
    
  <a name="genericStrmHtmlTag"></a>
  <li><b>genericStrmHtmlTag</b> &nbsp;&nbsp;&nbsp;&nbsp;(only valid for MODEL "generic") <br>
  This attribute contains HTML-Tags for video-specification in a Streaming-Device of type "generic". 
  <br><br> 
  
    <ul>
      <b>Examples:</b>
      <pre>
attr &lt;name&gt; genericStrmHtmlTag &lt;video $HTMLATTR controls autoplay&gt;
                                 &lt;source src='http://192.168.2.10:32000/$NAME.m3u8' type='application/x-mpegURL'&gt;
                               &lt;/video&gt; 
                               
attr &lt;name&gt; genericStrmHtmlTag &lt;img $HTMLATTR 
                                 src="http://192.168.2.10:32774"
                                 onClick="FW_okDialog('&lt;img src=http://192.168.2.10:32774 $PWS&gt')"
                               &gt  
      </pre>
      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.
    </ul>
    <br><br>
    </li>
    
    <a name="hideAudio"></a>
    <li><b>hideAudio</b><br>
      Hide the control block for audio playback in the footer.    
    </li>
    <br>
    
    <a name="hideButtons"></a>
    <li><b>hideButtons</b><br>
      Hide the buttons in the footer. It has no impact for streaming devices of type "switched".    
    </li>
    <br>
    
    <a name="hideDisplayName"></a>
    <li><b>hideDisplayName</b><br>
      Hide the device/alias name (link to detail view).     
    </li>
    <br>
    
    <a name="hideDisplayNameFTUI"></a>
    <li><b>hideDisplayNameFTUI</b><br>
      Hide the device/alias name (link to detail view) in FHEM TabletUI.     
    </li>
    <br>
    
    <a name="htmlattr"></a>
    <li><b>htmlattr</b><br>
      Additional HTML tags to manipulate the streaming device. 
      <br><br>
      <ul>
        <b>Example: </b><br>
        attr &lt;name&gt; htmlattr width="580" height="460" <br>
      </ul>
    </li>
    <br>
    
    <a name="htmlattrFTUI"></a>
    <li><b>htmlattrFTUI</b><br>
      Additional HTML tags to manipulate the streaming device in TabletUI. 
      <br><br>
      <ul>
        <b>Example: </b><br>
        attr &lt;name&gt; htmlattr width="580" height="460" <br>
      </ul>
    </li>
    <br>
    
    <a name="noLink"></a>
    <li><b>noLink</b><br>
      The device name or alias doesn't contain a link to the detail device view. 
    </li>
    <br>
    
    <a name="popupStreamFW"></a>
    <li><b>popupStreamFW</b><br>
      You can specify a particular FHEMWEB device whose active browser pages should open a popup window by the 
      "set &lt;name&gt; popupStream" command (default: all active FHEMWEB devices).
    </li>
    <br>
    
    <a name="popupStreamTo"></a>
    <li><b>popupStreamTo [OK | &lt;seconds&gt;]</b><br>
      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)..
      <br><br>
      <ul>
        <b>Example: </b><br>
        attr &lt;name&gt; popupStreamTo 10  <br>
      </ul>
      <br>
    </li>
    
    <a name="popupWindowSize"></a>
    <li><b>popupWindowSize</b><br>
      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".
      <br><br>
      <ul>
        <b>Example: </b><br>
        attr &lt;name&gt; popupWindowSize width="600" height="425"  <br>
      </ul>
    </li>
    <br>
    
    <a name="ptzButtonSize"></a>
    <li><b>ptzButtonSize</b><br>
      Specifies the PTZ-panel button size (in %).
    </li>
    <br>
    
    <a name="ptzButtonSizeFTUI"></a>
    <li><b>ptzButtonSizeFTUI</b><br>
      Specifies the PTZ-panel button size used in a Tablet UI (in %).
    </li>
  
  </ul>
  </ul>
  
</ul>

=end html
=begin html_DE

<a name="SSCamSTRM"></a>
<h3>SSCamSTRM</h3>
<ul>
  <br>
  Das Modul SSCamSTRM ist ein mit SSCam abgestimmtes Gerätemodul zur Definition von Streaming-Devices. <br>
  Abhängig vom Zustand des Streaming-Devices werden zum Start von Aktionen unterschiedliche Drucktasten angeboten: <br><br>
    <ul>   
      <table>  
      <colgroup> <col width=25%> <col width=75%> </colgroup>
        <tr><td> Switch off      </td><td>- stoppt eine laufende Wiedergabe </td></tr>
        <tr><td> Refresh         </td><td>- auffrischen einer Ansicht (kein Browser Seiten-Reload) </td></tr>
        <tr><td> Restart         </td><td>- neu starten eines laufenden Contents (z.B. eines HLS-Streams) </td></tr>
        <tr><td> MJPEG           </td><td>- Startet MJPEG Livestream </td></tr>
        <tr><td> HLS             </td><td>- Startet HLS (HTTP Live Stream) </td></tr>
        <tr><td> Last Record     </td><td>- spielt die letzte Aufnahme als iFrame </td></tr>
        <tr><td> Last Rec H.264  </td><td>- spielt die letzte Aufnahme wenn als H.264 vorliegend </td></tr>
        <tr><td> Last Rec MJPEG  </td><td>- spielt die letzte Aufnahme wenn als MJPEG vorliegend </td></tr>
        <tr><td> Last SNAP       </td><td>- zeigt den letzten Snapshot </td></tr>
        <tr><td> Start Recording </td><td>- startet eine Endlosaufnahme </td></tr>
        <tr><td> Stop Recording  </td><td>- stoppt eine Aufnahme </td></tr>
        <tr><td> Take Snapshot   </td><td>- löst einen Schnappschuß aus </td></tr>
      </table>
     </ul>     
     <br>
   
    <b>Integration in FHEM TabletUI: </b> <br><br>
    Zur Integration von SSCam Streaming Devices (Typ SSCamSTRM) wird ein Widget bereitgestellt. 
    Für weitere Information dazu bitte den Artikel im Wiki durchlesen: <br>
    <a href="https://wiki.fhem.de/wiki/FTUI_Widget_f%C3%BCr_SSCam_Streaming_Devices_(SSCamSTRM)">FTUI Widget für SSCam Streaming Devices (SSCamSTRM)</a>.
    <br><br><br>
</ul>

<ul>
  <a name="SSCamSTRMdefine"></a>
  <b>Define</b>
  <br><br>
  
  <ul>
    Ein SSCam Streaming-Device wird durch den SSCam Befehl <br><br>
    
    <ul>
      set &lt;name&gt; createStreamDev  &lt;Device Typ&gt; <br><br>
    </ul>
    
    erstellt. Siehe auch die Beschreibung zum SSCam <a href="#SSCamcreateStreamDev">"createStreamDev"</a> Befehl.  
    <br><br>
  </ul>

  <a name="SSCamSTRMset"></a>
  <b>Set</b> 
  <ul>
  
  <ul>
  <a name="adopt"></a>
  <li><b>adopt &lt;Streaming Device&gt; </b>   &nbsp;&nbsp;&nbsp;&nbsp;(nur wenn MODEL = master)<br>
  
  Ein Streaming Device vom Type <b>master</b> übernimmt (adoptiert) den Content eines anderen definierten Streaming Devices.
  </li>
  </ul>
  <br>
  
  <ul>
  <a name="adoptForTimer"></a>
  <li><b>adoptForTimer &lt;Streaming Device&gt; </b>   &nbsp;&nbsp;&nbsp;&nbsp;(nur bei MODEL = master)<br>
  
  Ein Streaming Device vom Type <b>master</b> übernimmt (adoptiert) den Content eines anderen definierten Streaming Devices
  für eine bestimmte Zeit. <br>
  Die Zeit wird mit dem Kommando <b>set &lt;name&gt; adoptTime</b> eingestellt. <br>
  (default: 10 Sekunden)
  </li>
  </ul>
  <br>
  
  <ul>
  <a name="adoptTime"></a>
  <li><b>adoptTime &lt;Sekunden&gt; </b>   &nbsp;&nbsp;&nbsp;&nbsp;(nur bei MODEL = master)<br>
  
  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. <br>
  Wird kein Argument oder "0" angegeben, wird die Zeitvorgabe gelöscht und der Standard (10 Sekunden) verwendet.
  </li>
  </ul>
  <br>
  
  <ul>
  <a name="snap"></a>
  <li><b> snap [&lt;number&gt;] [&lt;time difference&gt;] </b>  &nbsp;&nbsp;&nbsp;&nbsp;(nur bei MODEL = lastsnap)<br>
  
  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. <br>
  </li>
  </ul>
  <br>
  
  <ul>
  <li><b>popupStream [OK | &lt;Sekunden&gt;]</b>   &nbsp;&nbsp;&nbsp;&nbsp;(nur bei MODEL != master)<br>
  
  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). <br>
  Durch die optionalen Angabe von "OK" oder &lt;Sekunden&gt; kann die Einstellung des Attributes "popupStreamTo" übersteuert 
  werden.
  </li>
  </ul>
  <br>
  
  </ul>
  <br>
  
  <a name="SSCamSTRMget"></a>
  <b>Get</b> 
  <ul>
    <br>
    <ul>
      <li><b> get &lt;name&gt; html </b> </li>  
      Das eingebundene Streamobjekt (Kamera Live View, Schnappschüsse oder Wiedergabe einer Aufnahme) wird als HTML-code 
      abgerufen und dargestellt. 
    </ul>
    <br>
    
    <br>
  </ul>

  <a name="SSCamSTRMattr"></a>
  <b>Attribute</b>
  <br><br>
  
  <ul>
  <ul>
  
    <a name="adoptSubset"></a>
    <li><b>adoptSubset</b> &nbsp;&nbsp;&nbsp;&nbsp;(nur für MODEL "master") <br>
      In einem Streaming <b>master</b> Device wird eine Teilmenge aller definierten Streaming Devices ausgewählt und für 
      das  <b>adopt</b> Kommando bereitgestellt. <br>
      Für die Steuerung im FTUI wird die Auswahl ebenfalls im gleichnamigen Reading gespeichert.
    </li>
    <br>
  
    <a name="autoRefresh"></a>
    <li><b>autoRefresh</b><br>
      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.
    </li>
    <br>
    
    <a name="autoRefreshFW"></a>
    <li><b>autoRefreshFW</b><br>
      Ist "autoRefresh" aktiviert, kann mit diesem Attribut das FHEMWEB-Device bestimmt werden dessen aktive Browserseiten
      regelmäßig neu geladen werden sollen.
    </li>
    <br>
  
    <a name="disable"></a>
    <li><b>disable</b><br>
      Aktiviert/deaktiviert das Device.
    </li>
    <br>
    
    <a name="forcePageRefresh"></a>
    <li><b>forcePageRefresh</b><br>
      Das Attribut wird durch SSCam ausgewertet. <br>
      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.     
    </li>
    <br>
    
  <a name="genericStrmHtmlTag"></a>  
  <li><b>genericStrmHtmlTag</b> &nbsp;&nbsp;&nbsp;&nbsp;(nur für MODEL "generic")<br>
  Das Attribut enthält HTML-Tags zur Video-Spezifikation in einem Streaming-Device von Typ "generic". 
  <br><br> 
  
    <ul>
      <b>Beispiele:</b>
      <pre>
attr &lt;name&gt; genericStrmHtmlTag &lt;video $HTMLATTR controls autoplay&gt;
                                 &lt;source src='http://192.168.2.10:32000/$NAME.m3u8' type='application/x-mpegURL'&gt;
                               &lt;/video&gt;
                               
attr &lt;name&gt; genericStrmHtmlTag &lt;img $HTMLATTR 
                                 src="http://192.168.2.10:32774"
                                 onClick="FW_okDialog('&lt;img src=http://192.168.2.10:32774 $PWS &gt')"
                               &gt                              
      </pre>
      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.    
    </ul>
    <br><br>
    </li> 
    
    <a name="hideAudio"></a>
    <li><b>hideAudio</b><br>
      Verbirgt die Steuerungsbereich für die Audiowiedergabe in der Fußzeile.    
    </li>
    <br>  

    <a name="hideButtons"></a>
    <li><b>hideButtons</b><br>
      Verbirgt die Drucktasten in der Fußzeile. Dieses Attribut hat keinen Einfluß bei Streaming-Devices vom Typ "switched".    
    </li>
    <br>    
    
    <a name="hideDisplayName"></a>
    <li><b>hideDisplayName</b><br>
      Verbirgt den Device/Alias-Namen (Link zur Detailansicht).    
    </li>
    <br>
    
    <a name="hideDisplayNameFTUI"></a>
    <li><b>hideDisplayNameFTUI</b><br>
      Verbirgt den Device/Alias-Namen (Link zur Detailansicht) im TabletUI.    
    </li>
    <br>
    
    <a name="htmlattr"></a>
    <li><b>htmlattr</b><br>
      Zusätzliche HTML Tags zur Darstellung im Streaming Device. 
      <br><br>
      <ul>
        <b>Beispiel: </b><br>
        attr &lt;name&gt; htmlattr width="580" height="460"  <br>
      </ul>
    </li>
    <br>
    
    <a name="htmlattrFTUI"></a>
    <li><b>htmlattrFTUI</b><br>
      Zusätzliche HTML Tags zur Darstellung des Streaming Device im TabletUI. 
      <br><br>
      <ul>
        <b>Beispiel: </b><br>
        attr &lt;name&gt; htmlattr width="580" height="460"  <br>
      </ul>
    </li>
    <br>
    
    <a name="noLink"></a>
    <li><b>noLink</b><br>
      Der Devicename oder Alias enthält keinen Link zur Detailansicht. 
    </li>
    <br>
    
    <a name="popupStreamFW"></a>
    <li><b>popupStreamFW</b><br>
      Es kann mit diesem Attribut das FHEMWEB-Device bestimmt werden, auf dessen Browserseiten sich Popup-Fenster mit 
      "set &lt;name&gt; popupStream" öffnen sollen (default: alle aktiven FHEMWEB-Devices).
    </li>
    <br>
    
    <a name="popupStreamTo"></a>
    <li><b>popupStreamTo [OK | &lt;Sekunden&gt;]</b><br>
      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).
      <br><br>
      <ul>
        <b>Beispiel: </b><br>
        attr &lt;name&gt; popupStreamTo 10  <br>
      </ul>
      <br>
    </li>
    
    <a name="popupWindowSize"></a>
    <li><b>popupWindowSize</b><br>
      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".
      <br><br>
      <ul>
        <b>Beispiel: </b><br>
        attr &lt;name&gt; popupWindowSize width="600" height="425"  <br>
      </ul>
    </li>
    <br>
    
    <a name="ptzButtonSize"></a>
    <li><b>ptzButtonSize</b><br>
      Legt die Größe der Drucktasten des PTZ Paneels fest (in %).
    </li>
    <br>
    
    <a name="ptzButtonSizeFTUI"></a>
    <li><b>ptzButtonSizeFTUI</b><br>
      Legt die Größe der Drucktasten des PTZ Paneels in einem Tablet UI fest (in %).
    </li>

  </ul>
  </ul>
  
</ul>

=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 <heiko.maaz@t-online.de>"
  ],
  "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
