Fernsehprogramm DOIF (aktuell und primetime | non blocking | minimaler traffic)

Begonnen von mumpitzstuff, 12 Juni 2020, 21:47:48

Vorheriges Thema - Nächstes Thema

Tommy82

Zitat von: mumpitzstuff am 12 Juni 2020, 21:47:48
Ich habe mich mal hingesetzt und ein DOIF generiert, das die aktuellen Fernsehdaten einer Woche als xml von einem Server lädt (das DOIF macht das 1x alle 3-4 Tage) und daraus eine Programmzeitschrift generiert. Sowohl der Download als auch die Generierung geschehen non blocking. Es werden 2 Dinge generiert. Zum Einen die nächsten 3 Sendungen ab jetzt und zum Anderen die 3 Sendungen ab 20:15 Uhr. Die einzelnen Sendungen sind anklickbar und zeigen dann eine detaillierte Beschreibung zu der Sendung.

Nachdem das DOIF erstellt wurde, dauert es 5min bis die aktuellen Sendungen sichtbar werden. Die Prime Sendungen sind nach weiteren 5 Minuten sichtbar. Wenn die Prime Sendungen zwischen 20:15 Uhr und 0:00 aktualisiert werden, dann laufen sie quasi mit, da keine veralteten Sendungen angezeigt werden. Erst nach 0:30 werden dann die wirklichen Prime Sendungen des nächsten Tages (20:15 Uhr Sendungen) angezeigt.

Es gibt genau eine Vorbedingung, damit das DOIF funktioniert (alle Dinge ab 2. sind optional).

1.) XML::Bare muss mind. in der Version 0.53 installiert sein. Das kann man z.B. hiermit erledigen:

sudo apt-get install libxml-bare-perl

2.) Will man sich die Senderlogos anzeigen lassen, dann muss das Verzeichnis "/opt/fhem/www/images/default/tv" erstellt und die Rechte so gesetzt sein, das FHEM darauf zugreifen kann. Danach muss man die Senderlogos als PNGs in dieses Verzeichnis kopieren und ebenfalls die entsprechenden Rechte setzen. Die Dateien müssen die richtigen Namen besitzen! Diese kann man in der ersten Spalte der Darstellung sehen. Steht hier zum Beispiel "tv/ard" dann muss die Datei "/opt/fhem/www/images/default/tv/ard.png" heissen. Eine Quelle dafür ist z.B. (meine Logos kann ich leider aus rechtlichen Gründen hier nicht zur Verfügung stellen, aber höflich per PM fragen funktioniert vielleicht...):

https://github.com/3PO/Senderlogos/archive/master.zip

https://github.com/picons/picons/archive/master.zip

https://de.wikipedia.org/wiki/Liste_deutschsprachiger_Fernsehsender

SVG Logos gibt es z.B. hier (habe ich noch nie verwendet, sollte aber auch gehen):

https://commons.wikimedia.org/wiki/Category:SVG_logos_of_television_channels_and_networks?uselang=de

Ansonsten gibt es diverse Programmezeitschriften online, bei denen man sich die Bilder der Sender runterladen kann.

3.) Wenn man Sender hinzufügen möchte, dann kann man sich die original xml Dateien runterladen und die entsprechenden Sender raus suchen.

http://rytecepg.dyndns.tv/epg_data/

Danach muss man folgende Dinge anpassen (wenn man einen Sender entfernen möchte, muss man die entsprechenden Dinge ebenfalls anpassen):
a) Die Variable $_channelFilter.
b) Im Attribut uiTable muss man 2 Zeilen hinzufügen: "TPL_TV($SELF,next,<sender name>,<logo name>)" und weiter unten "TPL_TV($SELF,prime,<sender name>,<logo name>)"
c) Danach muss man noch die Formatierung ebenfalls im Attribut uiTable anpassen. Diese beginnen mit $TR und $TD.

Die xml Daten stellen jede Menge weiterer Sender zur Verfügung.

Unten habe ich auch mal ein Testscript dl.pl angehangen, womit man seinen channelfilter erst einmal überprüfen kann. Einfach den channelfilter im Script anpassen und ausführen und man sieht das Ergebnis erst einmal in einer Testdatei und kann, wenn alles funktioniert, das Regex ins DOIF übertragen. Es ist wichtig, das man immer den Text aus der id nimmt!

<channel id="ARD.de">

4.) Es stehen 2 Templates zur Verfügung. TPL_TV wird normalerweise verwendet und stellt das Programmlogo und 3 Sendezeiten dar. Darüber hinaus kann auch TPL_TVSET verwendet werden. Hier kann durch ein Klick auf das Programmlogo ein set Befehl an ein beliebiges FHEM Device geschickt werden, um z.B. den Fernsehsender aus FHEM heraus zu wechseln.

Beispiel:
TPL_TV($SELF,next,ARD,ard)
kann ersetzt werden durch:
TPL_TVSET($SELF,next,ARD,ard,irblasterdevice,send IR_SAT_DasErste)

5.) Wenn man andere Sender oder mehr als die vorhandenen haben möchte, kann man z.B. hier sehen was es sonst noch alles gibt:

http://rytecepg.dyndns.tv/epg_data/

Im DOIF werden nur die beiden Dateien rytecDE_Basic.xz und rytecDE_Common.xz geladen und gemerged. Das bdedeutet, das man aktuell nur Sender aus diesen beiden Dateien anzeigen lassen kann. Es gibt aber die Möglichkeit weitere Dateien zu mergen und dann ebenfalls anzeigen zu lassen wie z.B. rytecDE_SportMovies.xz.

Dazu muss man einfach die Funktion "sub tvDownloadMerge()" suchen und diese ersetzen:

  sub tvDownloadMerge()
  {
    my $output = '';
   
    # other server
    # http://www.xmltvepg.nl/rytecDE_Basic.xz
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz
    # http://www.vuplus-community.net/rytec/rytecDE_Common.xz
    # http://www.xmltvepg.nl/rytecDE_Common.xz
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz
    # http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz
    # http://www.xmltvepg.nl/rytecDE_SportMovies.xz
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/rytecDE_Basic.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecDE_Basic.xz 2>&1);
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/rytecDE_Common.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecDE_Common.xz 2>&1);
   
    tvMerge($_dataFile, $_path.'rytecDE_Common');

    # download and merge other files here if needed
   
    return $output;
  }


durch

  sub tvDownloadMerge()
  {
    my $output = '';
   
    # other server
    # http://www.xmltvepg.nl/rytecDE_Basic.xz
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz
    # http://www.vuplus-community.net/rytec/rytecDE_Common.xz
    # http://www.xmltvepg.nl/rytecDE_Common.xz
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz
    # http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz
    # http://www.xmltvepg.nl/rytecDE_SportMovies.xz
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/rytecDE_Basic.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecDE_Basic.xz 2>&1);
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/rytecDE_Common.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecDE_Common.xz 2>&1);
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/rytecDE_SportMovies.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecDE_SportMovies.xz 2>&1);
   
    tvMerge($_dataFile, $_path.'rytecDE_Common');
    tvMerge($_dataFile, $_path.'rytecDE_SportMovies');

    # download and merge other files here if needed
   
    return $output;
  }


ersetzen. Das Spiel lässt sich beliebig fortsetzen.


defmod doif_TEST DOIF subs\
{\
  use utf8;;\
  use Date::Parse;;\
  # sudo apt-get install libxml-bare-perl\
  use XML::Bare 0.53 qw(forcearray);;\
  use Blocking;;\
  #use Encode qw(encode_utf8 decode_utf8);;\
  \
  ### CONFIG AREA ###\
  $_channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL2?\.|Pro(?:7|SiebenMaxx)\.|DMax\.|Vox\.|Kabel(?:EinsClassic|EinsDoku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.)/;;\
  # telnet port must not be password protected (open)\
  # this is used as fallback if telnet port cannot created automatically\
  $_telnetPort = 7072;;\
  # can be used to adjust the program times (mostly not needed!)\
  $_timeAdjust = 0;;\
  $_path = '/opt/fhem/';;\
  $_dataFile = $_path.'rytecDE_Basic';;\
  # enable/disable unused channel filtering on filemerge (enabled = small file = faster)\
  $_filterChannels = 1;;\
  # enable/disable updates based on starttimes (enabled = update channels only if needed = faster)\
  $_updateBasedOnStarttimes = 1;;\
  # enable/disable use of Time::Piece (timepiece is faster but not instaled on some systems)\
  $_timepiece = 0;;\
  \
  # internal variables\
  $_startTimes = ();;\
  \
  \
  sub filterText($)\
  {\
    my $text = shift;;\
\
    $text =~ s/[\x{0022}\x{0060}\x{003b}\x{0027}\"\`;;\'\r]//g;;\
    #$text =~ s/[\"\`;;\'\r]//g;;\
    $text =~ s/[\n]/<br>/g;;\
\
    return $text;;\
  }\
  \
  sub xmltv2epoch($)\
  {\
    my $t = shift;;\
\
    if ($_timepiece)\
    {\
      use Time::Piece;;\
      \
      # fast version\
      return Time::Piece->strptime($t, '%Y%m%d%H%M%S %z')->epoch;;\
    }\
    else\
    {\
      # slow but compatible version\
      substr($t, 8, 0) = 'T';;\
\
      return str2time($t);;\
    }\
  }\
\
  sub FmtDateTime($)\
  {\
    my @t = localtime(shift);;\
    return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);;\
  }\
  \
  sub createTelnet($)\
  {\
    my $device = shift;;\
    my $telnet = undef;;\
    \
    foreach my $d (sort keys %::defs) \
    { \
      next if ($d !~ /telnetForTvUpdateFn_\d+/);;\
      my $h = $::defs{$d};;\
      next if (!$h->{TYPE} || $h->{TYPE} ne 'telnet' || $h->{SNAME});;\
      next if (::AttrVal($d, 'allowfrom', '127.0.0.1') ne '127.0.0.1');;\
      next if ($h->{DEF} !~ /^\d+( global)?$/);;\
      next if ($h->{DEF} =~ /IPV6/);;\
\
      $telnet = $d;;\
      last;;\
    }    \
    \
    if (!defined($telnet))\
    {\
      $telnet = 'telnetForTvUpdateFn_'.time();;\
      my $ret = ::CommandDefine(undef, "-temporary $telnet telnet 0");;\
    \
      if (defined($ret))\
      {\
        ::Log3 $device, 1, $device.': Cannot create telnet port ('.$ret.')';;\
        return undef;;\
      }\
    \
      $::attr{$telnet}{room} = 'hidden';;\
      $::attr{$telnet}{allowfrom} = '127.0.0.1';;\
    }\
    \
    return $::defs{$telnet}{PORT};;\
  }\
\
  sub tvParse($$$)\
  {\
    my ($device, $mode, $port) = @_;;\
    my $obj;;\
    my $xml;;\
    my $lastChannel = '';;\
    my $reading = '';;\
    my $i = 999;;\
    my $n = 999;;\
    my $k = 0;;\
    my $primeTime = substr(FmtDateTime(time() + $_timeAdjust), 0, 11).'20:14:00';;\
    my $sendTelnet = '';;\
    \
    $obj = XML::Bare->new(file => $_dataFile);;\
    $xml = $obj->parse();;\
\
    if (!$@)\
    {\
      my $old = time() + $_timeAdjust;;\
\
      foreach (@{forcearray($xml->{'tv'}{'programme'})})\
      {\
        if ((0 != $_filterChannels) ||\
            ($_->{'channel'}{'value'} =~ $_channelFilter))\
        {\
          my $stop = xmltv2epoch($_->{'stop'}{'value'});;\
\
          # filter old stuff\
          if ($stop > $old)\
          {\
            if ($lastChannel ne $_->{'channel'}{'value'})\
            {\
              $lastChannel = $_->{'channel'}{'value'};;\
              $reading = $_->{'channel'}{'value'};;\
              $reading =~ s/[\.\s]//g;;\
              $reading =~ s/de$//;;\
              $n = 0;;\
               \
              if ((0 == $_updateBasedOnStarttimes) || !exists($_startTimes{$reading}) || ($_startTimes{$reading} <= $old))\
              {\
                $i = 0;;\
\
                if (0 != $_updateBasedOnStarttimes)\
                {\
                  $_startTimes{$reading} = $stop;;\
                }\
              }\
              else\
              {\
                ::Log3 $device, 4, $device.': '.$reading.' is blocked because actual program is not finished (start: '.$_startTimes{$reading}.', old: '.$old.').';;\
              }\
            }\
\
            if ($i < 3 && 'next' eq $mode)\
            {\
              my $fi = sprintf("%03d", $i);;\
              my $start = xmltv2epoch($_->{'start'}{'value'});;\
              my $readingName;;\
              my $readingValue;;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_bdate';;\
              $readingValue = substr(FmtDateTime($start), 0, 10);;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_btime';;\
              $readingValue = substr(FmtDateTime($start), 11, 8);;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_title';;\
              $readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});;\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_stitle';;\
              if (exists($_->{'sub-title'}{'value'}))\
              {\
                $readingValue = filterText($_->{'sub-title'}{'value'});;\
              }\
              else\
              {\
                $readingValue = 'na';;\
              }\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $readingName = 'next_'.$reading.'_'.$fi.'_desc';;\
              if (exists($_->{'desc'}{'value'}))\
              {\
                $readingValue = filterText($_->{'desc'}{'value'});;\
              }\
              else\
              {\
                $readingValue = 'na';;\
              }\
              $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
              $k++;;\
              $i++;;\
            }\
\
            if ($n < 3 && 'prime' eq $mode)\
            {\
              my $start = xmltv2epoch($_->{'start'}{'value'});;\
              my $fmtStart = FmtDateTime($start);;\
              my $bdate = substr($fmtStart, 0, 10);;\
              my $btime = substr($fmtStart, 11, 8);;\
\
              if ($bdate.' '.$btime gt $primeTime)\
              {\
                my $fn = sprintf("%03d", $n);;\
                my $readingName;;\
                my $readingValue;;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_bdate';;\
                $readingValue = substr(FmtDateTime($start), 0, 10);;\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_btime';;\
                $readingValue = substr(FmtDateTime($start), 11, 8);;\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_title';;\
                $readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});;\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_stitle';;\
                if (exists($_->{'sub-title'}{'value'}))\
                {\
                  $readingValue = filterText($_->{'sub-title'}{'value'});;\
                }\
                else\
                {\
                  $readingValue = 'na';;\
                }\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $readingName = 'prime_'.$reading.'_'.$fn.'_desc';;\
                if (exists($_->{'desc'}{'value'}))\
                {\
                  $readingValue = filterText($_->{'desc'}{'value'});;\
                }\
                else\
                {\
                  $readingValue = 'na';;\
                }\
                $sendTelnet .= ";;setreading $device $readingName $readingValue";;\
\
                $k++;;\
                $n++;;\
              }\
            }\
\
            if ($k >= 10)\
            {\
              #::Log3 $device, 5, $device.': '.encode_utf8($sendTelnet);;\
              \
              `perl /opt/fhem/fhem.pl $port "$sendTelnet"`;;\
              \
              $k = 0;;\
              $sendTelnet = '';;\
            }\
          }\
        }\
      }\
\
      if ('' ne $sendTelnet)\
      {\
        #::Log3 $device, 5, $device.': '.encode_utf8($sendTelnet);;\
        \
        `perl /opt/fhem/fhem.pl $port "$sendTelnet"`;;\
      }\
    }\
    \
    return %{$_startTimes};;\
  }\
  \
  sub tvMerge($;;$)\
  {\
    my ($dstName, $srcName) = @_;;\
    my $fh;;\
    my $dst;;\
    my $src;;\
    my $start = '';;\
    my $channels1 = '';;\
    my $channels1_flt = '';;\
    my $channels2 = '';;\
    my $channels2_flt = '';;\
    my $programms1 = '';;\
    my $programms1_flt = '';;\
    my $programms2 = '';;\
    my $programms2_flt = '';;\
    my $end = '';;\
    my $pos;;\
\
    open($fh, '<', $dstName) or die "Can't open file $!";;\
    read($fh, $dst, -s $fh);;\
    close($fh);;\
\
    if (defined($srcName))\
    {\
      open($fh, '<', $srcName) or die "Can't open file $!";;\
      read($fh, $src, -s $fh);;\
      close($fh);;\
    }\
\
    if (-1 != ($pos = index($dst, '<channel ')))\
    {\
      $start = substr($dst, 0, $pos);;\
    }\
\
    if (-1 != ($pos = rindex($dst, '</programme>')))\
    {\
      $end = substr($dst, $pos + 12);;\
    }\
\
    while ($dst =~ /(\s*<channel\s.*?id="(.*?)".*?<\/channel>)/sg)\
    {\
      if (0 != $_filterChannels)\
      {\
        $_ = $1;;\
\
        if ($2 =~ $_channelFilter)\
        {\
          $channels1_flt .= $_;;\
        }\
      }\
      else\
      {\
        $channels1 .= $1;;\
      }\
    }\
\
    while ($dst =~ /(\s*<programme\s.*?channel="(.*?)".*?<\/programme>)/sg)\
    {\
      if (0 != $_filterChannels)\
      {\
        $_ = $1;;\
\
        if ($2 =~ $_channelFilter)\
        {\
          $programms1_flt .= $_;;\
        }\
      }\
      else\
      {\
        $programms1 .= $1;;\
      }\
    }\
\
    if (defined($srcName))\
    {\
      while ($src =~ /(\s*<channel\s.*?id="(.*?)".*?<\/channel>)/sg)\
      {\
        if (0 != $_filterChannels)\
        {\
          $_ = $1;;\
\
          if ($2 =~ $_channelFilter)\
          {\
            $channels2_flt .= $_;;\
          }\
        }\
        else\
        {\
          $channels2 .= $1;;\
        }\
      }\
\
      while ($src =~ /(\s*<programme\s.*?channel="(.*?)".*?<\/programme>)/sg)\
      {\
        if (0 != $_filterChannels)\
        {\
          $_ = $1;;\
\
          if ($2 =~ $_channelFilter)\
          {\
            $programms2_flt .= $_;;\
          }\
        }\
        else\
        {\
          $programms2 .= $1;;\
        }\
      }\
    }\
\
    if (0 != $_filterChannels)\
    {\
      open($fh, '>', $dstName) or die "Can't open file $!";;\
\
      if (defined($srcName))\
      {\
        print $fh $start.$channels1_flt.$channels2_flt.$programms1_flt.$programms2_flt.$end;;\
      }\
      else\
      {\
        print $fh $start.$channels1_flt.$programms1_flt.$end;;\
      }\
\
      close($fh);;\
    }\
    else\
    {\
      open($fh, '>', $dstName) or die "Can't open file $!";;\
\
      if (defined($srcName))\
      {\
        print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;;\
      }\
      else\
      {\
        print $fh $start.$channels1.$programms1.$end;;\
      }\
\
      close($fh);;\
    }\
  }\
\
  sub tvDownload()\
  {\
    my $output = '';; \
    \
    # other server\
    # http://www.xmltvepg.nl/rytecDE_Basic.xz\
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz\
    # http://www.vuplus-community.net/rytec/rytecDE_Common.xz\
    # http://www.xmltvepg.nl/rytecDE_Common.xz\
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz\
    # http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz\
    # http://www.xmltvepg.nl/rytecDE_SportMovies.xz\
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz\
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df /opt/fhem/rytecDE_Basic.xz 2>&1);;\
\
    if (0 != $_filterChannels)\
    {\
      tvMerge($_dataFile);;\
    }\
    \
    return $output;;\
  }\
\
  sub tvDownloadMerge()\
  {\
    my $output = '';; \
    \
    # other server\
    # http://www.xmltvepg.nl/rytecDE_Basic.xz\
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz\
    # http://www.vuplus-community.net/rytec/rytecDE_Common.xz\
    # http://www.xmltvepg.nl/rytecDE_Common.xz\
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz\
    # http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz\
    # http://www.xmltvepg.nl/rytecDE_SportMovies.xz\
    # http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz\
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(xz -df /opt/fhem/rytecDE_Basic.xz 2>&1);;\
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/rytecDE_Common.xz 2>&1);;\
    $output .= qx(xz -df /opt/fhem/rytecDE_Common.xz 2>&1);;\
    \
    tvMerge($_dataFile, $_path.'rytecDE_Common');;\
\
    # download and merge other files here if needed\
    \
    return $output;;\
  }\
    \
  sub startDownload($)\
  {\
    my $name = shift;;\
    \
    # prevent download spamming\
    if (-e $_dataFile)\
    {\
      my $ftime = ((time() - (stat($_dataFile))[9]) / 60.0 / 60.0 / 24.0);;\
      \
      if ($ftime < 1.0)\
      {\
        ::Log3 $name, 1, $name.': Download of TV data skipped because file is not older than 1 day ('.($ftime).').';;\
        return;;\
      }\
    }\
    \
    if (defined($_blockingcalls{PID_DOWNLOAD}))\
    {\
      ::Log3 $name, 3, $name.': Blocking call already running (download).';;\
\
      ::BlockingKill($_blockingcalls{PID_DOWNLOAD});;\
    }\
\
    $_blockingcalls{PID_DOWNLOAD} = ::BlockingCall('DOIF::doDownload', $name, 'DOIF::endDownload', 300, 'DOIF::abortDownload', $name);;\
  }\
  \
  sub DOIF::doDownload($)\
  {\
    my $name = shift;;\
    my $output = '';;\
    \
    $output = tvDownloadMerge();;\
    \
    return $name.'|'.$output;;\
  }\
  \
  sub DOIF::endDownload($)\
  {\
    my ($name, $output) = split("\\|", shift);;\
    \
    ::Log3 $name, 5, $name.': '.$output;;\
    ::Log3 $name, 4, $name.': Blocking call finished to download tv data.';;\
    \
    delete($_blockingcalls{PID_DOWNLOAD});;\
  }\
  \
  sub DOIF::abortDownload($)\
  {\
    my $name = shift;;\
    \
    delete($_blockingcalls{PID_DOWNLOAD});;\
    \
    ::Log3 $name, 1, $name.': Blocking call aborted (download).';;\
  }\
  \
  sub startParse($$)\
  {\
    my ($name, $mode) = @_;;\
    my $port;;\
    \
    if (defined($_blockingcalls{PID_PARSE}))\
    {\
      ::Log3 $name, 3, $name.': Blocking call already running (parse).';;\
\
      ::BlockingKill($_blockingcalls{PID_PARSE});;\
    }\
    \
    $port = createTelnet($name);;\
    $port = $_telnetPort if (!defined($port));;\
    \
    $_blockingcalls{PID_PARSE} = ::BlockingCall('DOIF::doParse', $name.'|'.$mode.'|'.$port, 'DOIF::endParse', 300, 'DOIF::abortParse', $name);;\
  }\
  \
  sub DOIF::doParse($)\
  {\
    my ($name, $mode, $port) = split("\\|", shift);;\
    my $ret = $name;;\
    my %startTimes = tvParse($name, $mode, $port);;\
    \
    foreach (keys(%startTimes))\
    {\
      $ret .= '|'.$_.'|'.$startTimes{$_};;\
    }\
    \
    return $ret;;\
  }\
  \
  sub DOIF::endParse($)\
  {\
    my ($name, @startTimes) = split("\\|", shift);;\
        \
    for (my $i = 0;; $i < (scalar(@startTimes) / 2);; $i += 2)\
    {\
      $_startTimes{$startTimes[$i]} = $startTimes[$i + 1];;\
    }\
    \
    ::Log3 $name, 4, $name.': Blocking call finished to parse tv data.';;\
    \
    delete($_blockingcalls{PID_PARSE});;\
  }\
  \
  sub DOIF::abortParse($)\
  {\
    my $name = shift;;\
    \
    delete($_blockingcalls{PID_PARSE});;\
    \
    ::Log3 $name, 1, $name.': Blocking call aborted (parse).';;\
  }\
}\
init\
{\
  startDownload("$SELF");;\
  set_Exec('init_next', 300, 'startParse("$SELF", "next")');;\
  set_Exec('init_prime', 600, 'startParse("$SELF", "prime")');;\
}\
{\
  if ([00:05|Mo Do])\
  {\
    startDownload("$SELF");;\
  }\
  \
  if ([+00:15])\
  {\
    startParse("$SELF", 'next');;\
  }\
  \
  if ([00:30])\
  {\
    startParse("$SELF", 'prime');;\
  }\
}
attr doif_TEST alias Aktuelles TV-Programm
attr doif_TEST room TEST
attr doif_TEST uiTable {\
  package ui_Table;;\
\
  $SHOWNOSTATE = 1;;\
  $ATTRIBUTESFIRST = 1;;\
  \
  $TR{0,31} = "style='color:yellow;;text-align:center;;font-weight:bold;;font-size:18px'";;\
  $TD{0..29,31..60}{2,4} = "style='font-size:16px;;border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
  $TD{0..29,31..60}{0} = "align='center' style='border-right-style:solid;;border-color:#CCCCCC;;border-right-width:1px;;'";;\
  $TD{0..60}{1,3,5,6} = "style='font-size:16px;;'";;\
  $TD{30}{0..6} = "style='border-top-style:solid;;border-bottom-style:solid;;border-color:#CCCCCC;;border-top-width:1px;;border-bottom-width:1px;;'";;\
\
  sub showIcon\
  {\
    my ($icon, $device, $state) = @_;;\
    \
    if (defined($device) && defined($state))\
    {\
      return "<a href=\"$::FW_ME?cmd=set $device $state$::FW_CSRF\">".ICON("tv/$icon")."</a>";;\
    }\
    else\
    {\
      return ICON("tv/$icon");;\
    }\
  }\
  \
  sub unfold\
  {\
    my ($title, $desc) = @_;;\
    \
    $title = 'na' if (!defined($title));;\
    $desc = 'na'."\n\n".'na' if (!defined($desc));;\
    \
    $title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;;\
    $desc =~ s/<br>/\n/g;;\
    $desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;; \
    $desc =~ s/[\r\'\"]/ /g;;\
    $desc =~ s/[\n]|\\n/<br>/g;;\
\
    return "<a href=\"#!\" onclick=\"FW_okDialog(';".$desc."';)\">".$title."</a>";;\
  }\
}\
\
## parameter: device name, mode (next or prime), channel name (see xml data file), icon name (filename of channel logo)\
DEF TPL_TV(showIcon("$4",undef,undef)|substr([$1:$2_$3_000_btime],0,5)|unfold([$1:$2_$3_000_title],[$1:$2_$3_000_stitle]."\n\n".[$1:$2_$3_000_desc])|substr([$1:$2_$3_001_btime],0,5)|unfold([$1:$2_$3_001_title],[$1:$2_$3_001_stitle]."\n\n".[$1:$2_$3_001_desc])|substr([$1:$2_$3_002_btime],0,5)|unfold([$1:$2_$3_002_title],[$1:$2_$3_002_stitle]."\n\n".[$1:$2_$3_002_desc]))\
\
## parameter: device name, mode (next or prime), channel name (see xml data file), icon name (filename of channel logo), device name for set command, command\
DEF TPL_TVSET(showIcon("$4","$5","$6")|substr([$1:$2_$3_000_btime],0,5)|unfold([$1:$2_$3_000_title],[$1:$2_$3_000_stitle]."\n\n".[$1:$2_$3_000_desc])|substr([$1:$2_$3_001_btime],0,5)|unfold([$1:$2_$3_001_title],[$1:$2_$3_001_stitle]."\n\n".[$1:$2_$3_001_desc])|substr([$1:$2_$3_002_btime],0,5)|unfold([$1:$2_$3_002_title],[$1:$2_$3_002_stitle]."\n\n".[$1:$2_$3_002_desc]))\
\
"Sender"|"ab"|"Aktuelle Sendung"|"ab"|"Nächste Sendung"|"ab"|"Sendung"\
TPL_TV($SELF,next,ARD,ard)\
TPL_TV($SELF,next,ZDF,zdf)\
TPL_TV($SELF,next,Sat1,sat1)\
TPL_TV($SELF,next,RTL,rtl)\
TPL_TV($SELF,next,RTL2,rtl2)\
TPL_TV($SELF,next,Pro7,pro7)\
TPL_TV($SELF,next,DMax,dmax)\
TPL_TV($SELF,next,Vox,vox)\
TPL_TV($SELF,next,Kabel,kabel1)\
TPL_TV($SELF,next,KabelEinsClassic,kabel1classic)\
TPL_TV($SELF,next,13thStreet,13thstreet)\
TPL_TV($SELF,next,Silverline,silverline)\
TPL_TV($SELF,next,TNTFilm,tntfilm)\
TPL_TV($SELF,next,AXN,axn)\
TPL_TV($SELF,next,SonyEntertainmentTV,sonytv)\
TPL_TV($SELF,next,Kinowelt,kinowelt)\
TPL_TV($SELF,next,ProSiebenMaxx,pro7maxx)\
TPL_TV($SELF,next,Sixx,sixx)\
TPL_TV($SELF,next,TNTSerie,tntserie)\
TPL_TV($SELF,next,SciFi,syfy)\
TPL_TV($SELF,next,ntv,ntv)\
TPL_TV($SELF,next,N24Doku,n24)\
TPL_TV($SELF,next,History,history)\
TPL_TV($SELF,next,PLANET,planet)\
TPL_TV($SELF,next,KabelEinsDoku,kabel1doku)\
TPL_TV($SELF,next,AnimalPlanet,animalplanet)\
TPL_TV($SELF,next,NatGeoHD,natgeo)\
TPL_TV($SELF,next,TLC,tlc)\
TPL_TV($SELF,next,AandE,ae)\
"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"|"&nbsp;;"\
"Sender"|"ab"|"Sendung"|"ab"|"Sendung"|"ab"|"Sendung"\
TPL_TV($SELF,prime,ARD,ard)\
TPL_TV($SELF,prime,ZDF,zdf)\
TPL_TV($SELF,prime,Sat1,sat1)\
TPL_TV($SELF,prime,RTL,rtl)\
TPL_TV($SELF,prime,RTL2,rtl2)\
TPL_TV($SELF,prime,Pro7,pro7)\
TPL_TV($SELF,prime,DMax,dmax)\
TPL_TV($SELF,prime,Vox,vox)\
TPL_TV($SELF,prime,Kabel,kabel1)\
TPL_TV($SELF,prime,KabelEinsClassic,kabel1classic)\
TPL_TV($SELF,prime,13thStreet,13thstreet)\
TPL_TV($SELF,prime,Silverline,silverline)\
TPL_TV($SELF,prime,TNTFilm,tntfilm)\
TPL_TV($SELF,prime,AXN,axn)\
TPL_TV($SELF,prime,SonyEntertainmentTV,sonytv)\
TPL_TV($SELF,prime,Kinowelt,kinowelt)\
TPL_TV($SELF,prime,ProSiebenMaxx,pro7maxx)\
TPL_TV($SELF,prime,Sixx,sixx)\
TPL_TV($SELF,prime,TNTSerie,tntserie)\
TPL_TV($SELF,prime,SciFi,syfy)\
TPL_TV($SELF,prime,ntv,ntv)\
TPL_TV($SELF,prime,N24Doku,n24)\
TPL_TV($SELF,prime,History,history)\
TPL_TV($SELF,prime,PLANET,planet)\
TPL_TV($SELF,prime,KabelEinsDoku,kabel1doku)\
TPL_TV($SELF,prime,AnimalPlanet,animalplanet)\
TPL_TV($SELF,prime,NatGeoHD,natgeo)\
TPL_TV($SELF,prime,TLC,tlc)\
TPL_TV($SELF,prime,AandE,ae)


Hi,
entweder hab ich was überlesen oder stehe mir auf dem schlauch, was genau muss ich machen um das zu nutzen? die XML::Bare hab ich installiert und die Senderlogos ins entsprechende Fhem verzeichniss kopiert. Und jetzt?

Danke
Fhem Cubitruck  Armbian Buster with Linux 5.3.9-sunxi
HM-CC_RT-DN, HM-Sec-RHS,HM-Sec-SD, HM-Sec-SCo,IT1500,1xIT GRR-3500 Fritz!Dect200,Powerline546E,Enigma2 Modul mit 3 Vu+,Wol Modul für WinServer2016 und WinServer 2019,FB6590
Allnetl Wandtablett mit FTUI

mumpitzstuff

Du öffnest einfach ein beliebiges Device und scrollst ganz nach unten und klickst auf raw Definition. Dort löschst du alles was drin steht und kopierst die gesamte Definition (das letzte das mit defmod anfängt aus dem ersten Beitrag) dort rein und klickst dann auf execute commands. Fertig.

gestein

Hallo,

habe mir gerade das tolle DOIF installiert und es funktioniert erst mal. Danke für die tolle Arbeit.

Allerdings scheitere ich daran, einen neuen Sender hinzuzufügen: ORF1 (testweise)
Der wäre in der Datei "rytecAT_Basic".
Also habe ich in der Funktion tvDownloadMerge die folgenden Zeilen hinzugefügt:
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecAT_Basic.xz -O /opt/fhem/rytecAT_Basic.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecAT_Basic.xz 2>&1);

    tvMerge($_dataFile, $_path.'rytecDE_Common');
    tvMerge($_dataFile, $_path.'rytecAT_Basic');


Kann es sein, dass da in der Anleitung ein kleiner Fehler ist? Wieso wird "rytecDE_Common" als "rytecDE_SportMovies.xz" abgespeichert?
Da steht nämlich:
    $output .= qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/rytecDE_SportMovies.xz 2>&1);
    $output .= qx(xz -df /opt/fhem/rytecDE_SportMovies.xz 2>&1);


Die Datei "rytecAT_Basic" ist auch im Verzeichnis "/opt/fhem" zu finden.
Da steht für ORF1 folgendes drinnen:
  <channel id="ORF1.at">
    <display-name lang="en">ORF eins</display-name>
  </channel>


Also habe ich im Attribut "uiTable" in den "TPL_TV"-Blöcken folgenden Eintrag hinzugefügt:
TPL_TV($SELF,next,ORF1,orf1)
und
TPL_TV($SELF,prime,ORF1,orf1)

Es erscheinen nun zwei Zeilen mit dem richtigen Logo, aber es kommen keine Programm-Einträge.
Die Spalten sind immer leer.
Die anderen Spalten sind sehr wohl befüllt.

Was mache ich falsch?

Danke für jede Hilfe.
lg, Gerhard

mumpitzstuff

Das Mergen ist richtig wie es scheint. In der Beschreibung ist tatsächlich ein kleiner Fehler drin (habe ich korrigiert). Ich hatte das nur schnell im Editor angepasst ohne das zu prüfen.

Soweit ich sehe hast du vergessen die Variable $_channelFilter anzupassen. Wenn hier nicht bereits der gewünschte Sender eingefügt wird, dann wird dieser bereits beim mergen raus geschmissen und wird niemals in deinem Programm auftauchen.

In deinem Fall müsste das dann so etwa aussehen:

$_channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL2?\.|Pro(?:7|SiebenMaxx)\.|DMax\.|Vox\.|Kabel(?:EinsClassic|EinsDoku)?\.|ntv\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNT(?:Serie|Film)\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|ORF1\.)/;;\

Danach musst du erst einmal den Download wieder ausführen. Am einfachsten geht das wenn du alle rytec Dateien löschst und dann fhem mit shutdown reload restartest.

gestein

Hallo,

Vielen Dank für Deine Hilfe.
Ich hatte doch glatt vergessen, dass mit dem $_channelFilter zu erwähnen.
Den neuen Kanal ORF1 habe ich dort ebenfalls hinzugegeben.
Zur Sicherheit habe ich also Deine Liste kopiert, alles gespeichert, die 3 Dateien gelöscht und fhem neu gestartet.
Die 3 Spalten beim ORF sind aber immer noch leer.

Also habe ich das Device gelöscht und alles neu angelegt.
Hilft aber auch nix, die 3 Spalten bei ORF 1 und dann auch ORF2 sind leer, obwohl alles andere angezeigt wird.

Kann man irgendwie debug-Ausgaben bekommen?
Verbose bzw. Logging höher zu setzen, bringt leider nix.

Danke!
Lg, Gerhard

gestein

Das eigenartige ist, dass die entsprechenden readings sehr wohl vorhanden sind und auch stimmen.
Es gibt also die next_ORF1at und prime_ORF1at

Wieso heißen die aber ORF1at und nicht so wie beim ARD?
Wenn ich die Bezeichnung ORF1at im uiTable verwende, dann erscheinen die Einträge in den Spalten.

Lg, Gerhard

mumpitzstuff

Hmm ich filtere .de automatisch raus wie es scheint. Das habe ich ganz vergessen. Da ich bisher nur .de Sender verwendet hatte, ist mir das gar nicht mehr aufgefallen.
Im DOIF kannst du aber diese Zeile:
$reading =~ s/de$/;
durch das ersetzen:
$reading =~ s/(de|at)$/;

Dann würde at ebenfalls gefiltert werden.

gestein

Hallo,

ich glaube, ich lasse es mal, so wie es ist (außer Du gibst eine neue offizielle Version heraus ;))
Danke auf alle Fälle.

Nun wollte ich das Ganze natürlich um ORF2, ATV, ServusTV etc. ergänzen.
Die Spalten und die Logos sind auch in der Übersicht, aber halt wieder kein Text.
Da ich keinerlei Fehlermeldungen bekomme, habe ich mich mit dem dl.pl gespielt.

Die Sender erscheinen dort auch und in der Test-Datei "rytecDE_Basic.test" sind auch alle EPG-Einträge.
Trotzdem erscheinen bei den neuen Sendern keine Einträge in der Übersicht, beim ORF1 schon - komisch.

Hast Du vielleicht noch einen Tipp?

Danke, lg, Gerhard

mumpitzstuff

Schau zuerst mal ob die entsprechenden Readings erzeugt wurden. Ist das der Fall? Falls nicht, dann wurde entweder der Download nach deinen Änderungen nicht angestoßen oder dein Regex ist falsch. Falls sie vorhanden sind, wie genau sehen die Readings aus und wie deine TPL_TV Aufrufe im Skript?

gestein

Hallo,

Danke für die Tipps.
Es wurden alle verfügbaren Sender als Readings angelegt.
Nachdem ich einfach nicht mehr weitergekommen bin, habe ich mal testweise Deine Änderung bzgl. de|at eingebaut.
Nach ein paar restarts und den notwendigen Änderungen in dem uiTable sind nun die Einträge da.
Nur für ServusTV noch nicht. Das muss ich mir noch anschauen.
Ganz schön zickig das Teil  ;)
Vor allem weil keinerlei Debug-Info via logging rauskommt.

Ein Thema habe ich aber noch:
In der Übersicht sind die Formatierungen für die Zeilen verschoben.
Auffällig ist das vor allem, weil die Überschrift für Prime ohne jede Formatierung angezeigt wird.
Und mitten im EGP-Block ist dann auf einmal eine Zeile gelb und fett.

Danke im Voraus
Lg, Gerhard

gestein

Hallo nochmal,

nun verzweifle ich an den Logos.
Ich hätte nicht gedacht, dass sich dieses Thema als so zickig und aufwändig herausstellt.

Mein grundsätzliches Problem ist, dass in der Übersicht die Logos zu groß sind.
Kann man die Größe irgendwo einstellen?

1) https://github.com/3PO/Senderlogos/archive/master.zip die sind zu groß
2) https://github.com/picons/picons/archive/master.zip die pngs sind zu groß; aber zuerst das svg von ORF1 getestet, klappt.
    Also alle weiteren runtergeladen und eingespielt -> die klappen nicht mehr :(
3) https://commons.wikimedia.org/wiki/Category:SVG_logos_of_television_channels_and_networks?uselang=de die svgs werden bei mir in fhem nicht angezeigt.

die von Klack.de sind leider auch viel zu groß.

Es ist echt zum Verzweifeln.
Wieso kann fhem die SVGs von z.B. Wikipedia nicht darstellen?

Zur Zeit sieht meine Übersicht so wie im Anhang aus (zumindest ein Teil davon).

lg, Gerhard

mumpitzstuff

Zuerst einmal kriegst du das mit der Formatierung hin, indem du 3c beachtest. Man muss die Stellen mit $TD und $TR entsprechend anpassen. Die Logos musst du vereinheitlichen. Mit anderen Worten du musst dir eine Größe definieren und alle Logos auf diese Größe skalieren.


gestein

Hallo,
vielen Dank für die Hinweise.
Mit 3c hatte ich bis dato gar nichts zu tun.
Aber der Tipp mit $TR und $TD hat mir geholfen.
Durch herumprobieren habe ich nun die Formatierungen richtig.
D.h., jedesmal wenn man einen Sender dazu gibt bzw. wegnimmt, muss man auch das ändern.
Kommt ja nur am Anfang häufig vor, dann eher nicht mehr.

Bei den Logos scheint es also am Einfachsten zu sein, wenn man die PNGs per Gimp kleiner macht.
Schade, dass man die Größe nicht als Parameter übergeben kann und dass das Funktion automatisch macht.
Aber auch das sollte sich händisch machen lassen.

Vielen Dank nochmals für Deine Hilfe!
lg, Gerhard

mumpitzstuff

Bei den Logos könnte man eventuell versuchen eine feste Größe für die Tabellenzelle per css vorzugeben (die $TD und $TR Angaben). Eventuell wird dadurch der Browser die Logos selbstständig skalieren (habe ich nie probiert), was insbesondere bei svg Dateien gut funktionieren müsste.

en-trust

wie beschrieben, habe ich testweise mal die Logos für ARD (das erste.png) und ZDF (zdf.png) nach /opt/fhem/www/images/default/tv kopiert und das doif angepasst.

TPL_TV($SELF,next,ARD,das erste)
TPL_TV($SELF,next,ZDF,zdf)


aber weder ARD noch ZDF werden angezeigt. Rechte auf tv Verzeichnis stehen sogar auf 777 und fhem.