http://wiki.fhem.de/w/api.php?action=feedcontributions&user=Mumpitzstuff&feedformat=atomFHEMWiki - Benutzerbeiträge [de]2024-03-28T20:30:06ZBenutzerbeiträgeMediaWiki 1.39.3http://wiki.fhem.de/w/index.php?title=TV_Programm&diff=33182TV Programm2020-05-03T22:52:42Z<p>Mumpitzstuff: timezone problems fixed</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
# internal variable<br />
my $timepiece = 0;<br />
<br />
eval "use Time::Piece";<br />
$timepiece = 1 if (!$@);<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $t = shift;<br />
<br />
if (1 == $timepiece)<br />
{<br />
return Time::Piece->strptime($t, '%Y%m%d%H%M%S %z')->epoch;<br />
}<br />
else<br />
{<br />
substr($t, 8, 0) = 'T';<br />
<br />
return str2time($t);<br />
}<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() + $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time() + $timeAdjust;<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = xmltv2epoch($_->{'stop'}{'value'});<br />
<br />
# filter old stuff<br />
if ($stop > $old)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = xmltv2epoch($_->{'start'}{'value'});<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = xmltv2epoch($_->{'start'}{'value'});<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#my $output = qx(wget http://192.168.178.100:8083/fhem/www/images/guide.xml -O /opt/fhem/tv/rytecDE_Basic 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("\"perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge\"")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,next_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_000_')}@next_KabelEinsClassic_000_title>,<|>,next_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_001_')}@next_KabelEinsClassic_001_title>,<|>,next_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_002_')}@next_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,next_13thStreet_000_btime,<{rgUnfold($DEVICE,'next_13thStreet_000_')}@next_13thStreet_000_title>,<|>,next_13thStreet_001_btime,<{rgUnfold($DEVICE,'next_13thStreet_001_')}@next_13thStreet_001_title>,<|>,next_13thStreet_002_btime,<{rgUnfold($DEVICE,'next_13thStreet_002_')}@next_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,next_Silverline_000_btime,<{rgUnfold($DEVICE,'next_Silverline_000_')}@next_Silverline_000_title>,<|>,next_Silverline_001_btime,<{rgUnfold($DEVICE,'next_Silverline_001_')}@next_Silverline_001_title>,<|>,next_Silverline_002_btime,<{rgUnfold($DEVICE,'next_Silverline_002_')}@next_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,next_TNTFilm_000_btime,<{rgUnfold($DEVICE,'next_TNTFilm_000_')}@next_TNTFilm_000_title>,<|>,next_TNTFilm_001_btime,<{rgUnfold($DEVICE,'next_TNTFilm_001_')}@next_TNTFilm_001_title>,<|>,next_TNTFilm_002_btime,<{rgUnfold($DEVICE,'next_TNTFilm_002_')}@next_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,next_AXN_000_btime,<{rgUnfold($DEVICE,'next_AXN_000_')}@next_AXN_000_title>,<|>,next_AXN_001_btime,<{rgUnfold($DEVICE,'next_AXN_001_')}@next_AXN_001_title>,<|>,next_AXN_002_btime,<{rgUnfold($DEVICE,'next_AXN_002_')}@next_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,next_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_000_')}@next_SonyEntertainmentTV_000_title>,<|>,next_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_001_')}@next_SonyEntertainmentTV_001_title>,<|>,next_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_002_')}@next_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,next_Kinowelt_000_btime,<{rgUnfold($DEVICE,'next_Kinowelt_000_')}@next_Kinowelt_000_title>,<|>,next_Kinowelt_001_btime,<{rgUnfold($DEVICE,'next_Kinowelt_001_')}@next_Kinowelt_001_title>,<|>,next_Kinowelt_002_btime,<{rgUnfold($DEVICE,'next_Kinowelt_002_')}@next_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,next_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_000_')}@next_ProSiebenMaxx_000_title>,<|>,next_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_001_')}@next_ProSiebenMaxx_001_title>,<|>,next_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_002_')}@next_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,next_Sixx_000_btime,<{rgUnfold($DEVICE,'next_Sixx_000_')}@next_Sixx_000_title>,<|>,next_Sixx_001_btime,<{rgUnfold($DEVICE,'next_Sixx_001_')}@next_Sixx_001_title>,<|>,next_Sixx_002_btime,<{rgUnfold($DEVICE,'next_Sixx_002_')}@next_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,next_TNTSerie_000_btime,<{rgUnfold($DEVICE,'next_TNTSerie_000_')}@next_TNTSerie_000_title>,<|>,next_TNTSerie_001_btime,<{rgUnfold($DEVICE,'next_TNTSerie_001_')}@next_TNTSerie_001_title>,<|>,next_TNTSerie_002_btime,<{rgUnfold($DEVICE,'next_TNTSerie_002_')}@next_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,next_SciFi_000_btime,<{rgUnfold($DEVICE,'next_SciFi_000_')}@next_SciFi_000_title>,<|>,next_SciFi_001_btime,<{rgUnfold($DEVICE,'next_SciFi_001_')}@next_SciFi_001_title>,<|>,next_SciFi_002_btime,<{rgUnfold($DEVICE,'next_SciFi_002_')}@next_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,next_ntv_000_btime,<{rgUnfold($DEVICE,'next_ntv_000_')}@next_ntv_000_title>,<|>,next_ntv_001_btime,<{rgUnfold($DEVICE,'next_ntv_001_')}@next_ntv_001_title>,<|>,next_ntv_002_btime,<{rgUnfold($DEVICE,'next_ntv_002_')}@next_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,next_N24Doku_000_btime,<{rgUnfold($DEVICE,'next_N24Doku_000_')}@next_N24Doku_000_title>,<|>,next_N24Doku_001_btime,<{rgUnfold($DEVICE,'next_N24Doku_001_')}@next_N24Doku_001_title>,<|>,next_N24Doku_002_btime,<{rgUnfold($DEVICE,'next_N24Doku_002_')}@next_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,next_History_000_btime,<{rgUnfold($DEVICE,'next_History_000_')}@next_History_000_title>,<|>,next_History_001_btime,<{rgUnfold($DEVICE,'next_History_001_')}@next_History_001_title>,<|>,next_History_002_btime,<{rgUnfold($DEVICE,'next_History_002_')}@next_History_002_title>\<br />
dmy_TV:<%tv/planet>,next_PLANET_000_btime,<{rgUnfold($DEVICE,'next_PLANET_000_')}@next_PLANET_000_title>,<|>,next_PLANET_001_btime,<{rgUnfold($DEVICE,'next_PLANET_001_')}@next_PLANET_001_title>,<|>,next_PLANET_002_btime,<{rgUnfold($DEVICE,'next_PLANET_002_')}@next_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,next_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_000_')}@next_KabelEinsDoku_000_title>,<|>,next_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_001_')}@next_KabelEinsDoku_001_title>,<|>,next_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_002_')}@next_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,next_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_000_')}@next_AnimalPlanet_000_title>,<|>,next_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_001_')}@next_AnimalPlanet_001_title>,<|>,next_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_002_')}@next_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,next_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_000_')}@next_NatGeoHD_000_title>,<|>,next_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_001_')}@next_NatGeoHD_001_title>,<|>,next_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_002_')}@next_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,next_TLC_000_btime,<{rgUnfold($DEVICE,'next_TLC_000_')}@next_TLC_000_title>,<|>,next_TLC_001_btime,<{rgUnfold($DEVICE,'next_TLC_001_')}@next_TLC_001_title>,<|>,next_TLC_002_btime,<{rgUnfold($DEVICE,'next_TLC_002_')}@next_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,next_AandE_000_btime,<{rgUnfold($DEVICE,'next_AandE_000_')}@next_AandE_000_title>,<|>,next_AandE_001_btime,<{rgUnfold($DEVICE,'next_AandE_001_')}@next_AandE_001_title>,<|>,next_AandE_002_btime,<{rgUnfold($DEVICE,'next_AandE_002_')}@next_AandE_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,prime_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_000_')}@prime_KabelEinsClassic_000_title>,<|>,prime_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_001_')}@prime_KabelEinsClassic_001_title>,<|>,prime_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_002_')}@prime_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,prime_13thStreet_000_btime,<{rgUnfold($DEVICE,'prime_13thStreet_000_')}@prime_13thStreet_000_title>,<|>,prime_13thStreet_001_btime,<{rgUnfold($DEVICE,'prime_13thStreet_001_')}@prime_13thStreet_001_title>,<|>,prime_13thStreet_002_btime,<{rgUnfold($DEVICE,'prime_13thStreet_002_')}@prime_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,prime_Silverline_000_btime,<{rgUnfold($DEVICE,'prime_Silverline_000_')}@prime_Silverline_000_title>,<|>,prime_Silverline_001_btime,<{rgUnfold($DEVICE,'prime_Silverline_001_')}@prime_Silverline_001_title>,<|>,prime_Silverline_002_btime,<{rgUnfold($DEVICE,'prime_Silverline_002_')}@prime_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,prime_TNTFilm_000_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_000_')}@prime_TNTFilm_000_title>,<|>,prime_TNTFilm_001_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_001_')}@prime_TNTFilm_001_title>,<|>,prime_TNTFilm_002_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_002_')}@prime_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,prime_AXN_000_btime,<{rgUnfold($DEVICE,'prime_AXN_000_')}@prime_AXN_000_title>,<|>,prime_AXN_001_btime,<{rgUnfold($DEVICE,'prime_AXN_001_')}@prime_AXN_001_title>,<|>,prime_AXN_002_btime,<{rgUnfold($DEVICE,'prime_AXN_002_')}@prime_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,prime_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_000_')}@prime_SonyEntertainmentTV_000_title>,<|>,prime_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_001_')}@prime_SonyEntertainmentTV_001_title>,<|>,prime_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_002_')}@prime_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,prime_Kinowelt_000_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_000_')}@prime_Kinowelt_000_title>,<|>,prime_Kinowelt_001_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_001_')}@prime_Kinowelt_001_title>,<|>,prime_Kinowelt_002_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_002_')}@prime_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,prime_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_000_')}@prime_ProSiebenMaxx_000_title>,<|>,prime_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_001_')}@prime_ProSiebenMaxx_001_title>,<|>,prime_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_002_')}@prime_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,prime_Sixx_000_btime,<{rgUnfold($DEVICE,'prime_Sixx_000_')}@prime_Sixx_000_title>,<|>,prime_Sixx_001_btime,<{rgUnfold($DEVICE,'prime_Sixx_001_')}@prime_Sixx_001_title>,<|>,prime_Sixx_002_btime,<{rgUnfold($DEVICE,'prime_Sixx_002_')}@prime_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,prime_TNTSerie_000_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_000_')}@prime_TNTSerie_000_title>,<|>,prime_TNTSerie_001_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_001_')}@prime_TNTSerie_001_title>,<|>,prime_TNTSerie_002_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_002_')}@prime_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,prime_SciFi_000_btime,<{rgUnfold($DEVICE,'prime_SciFi_000_')}@prime_SciFi_000_title>,<|>,prime_SciFi_001_btime,<{rgUnfold($DEVICE,'prime_SciFi_001_')}@prime_SciFi_001_title>,<|>,prime_SciFi_002_btime,<{rgUnfold($DEVICE,'prime_SciFi_002_')}@prime_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,prime_ntv_000_btime,<{rgUnfold($DEVICE,'prime_ntv_000_')}@prime_ntv_000_title>,<|>,prime_ntv_001_btime,<{rgUnfold($DEVICE,'prime_ntv_001_')}@prime_ntv_001_title>,<|>,prime_ntv_002_btime,<{rgUnfold($DEVICE,'prime_ntv_002_')}@prime_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,prime_N24Doku_000_btime,<{rgUnfold($DEVICE,'prime_N24Doku_000_')}@prime_N24Doku_000_title>,<|>,prime_N24Doku_001_btime,<{rgUnfold($DEVICE,'prime_N24Doku_001_')}@prime_N24Doku_001_title>,<|>,prime_N24Doku_002_btime,<{rgUnfold($DEVICE,'prime_N24Doku_002_')}@prime_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,prime_History_000_btime,<{rgUnfold($DEVICE,'prime_History_000_')}@prime_History_000_title>,<|>,prime_History_001_btime,<{rgUnfold($DEVICE,'prime_History_001_')}@prime_History_001_title>,<|>,prime_History_002_btime,<{rgUnfold($DEVICE,'prime_History_002_')}@prime_History_002_title>\<br />
dmy_TV:<%tv/planet>,prime_PLANET_000_btime,<{rgUnfold($DEVICE,'prime_PLANET_000_')}@prime_PLANET_000_title>,<|>,prime_PLANET_001_btime,<{rgUnfold($DEVICE,'prime_PLANET_001_')}@prime_PLANET_001_title>,<|>,prime_PLANET_002_btime,<{rgUnfold($DEVICE,'prime_PLANET_002_')}@prime_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,prime_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_000_')}@prime_KabelEinsDoku_000_title>,<|>,prime_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_001_')}@prime_KabelEinsDoku_001_title>,<|>,prime_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_002_')}@prime_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,prime_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_000_')}@prime_AnimalPlanet_000_title>,<|>,prime_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_001_')}@prime_AnimalPlanet_001_title>,<|>,prime_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_002_')}@prime_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,prime_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_000_')}@prime_NatGeoHD_000_title>,<|>,prime_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_001_')}@prime_NatGeoHD_001_title>,<|>,prime_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_002_')}@prime_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,prime_TLC_000_btime,<{rgUnfold($DEVICE,'prime_TLC_000_')}@prime_TLC_000_title>,<|>,prime_TLC_001_btime,<{rgUnfold($DEVICE,'prime_TLC_001_')}@prime_TLC_001_title>,<|>,prime_TLC_002_btime,<{rgUnfold($DEVICE,'prime_TLC_002_')}@prime_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,prime_AandE_000_btime,<{rgUnfold($DEVICE,'prime_AandE_000_')}@prime_AandE_000_title>,<|>,prime_AandE_001_btime,<{rgUnfold($DEVICE,'prime_AandE_001_')}@prime_AandE_001_title>,<|>,prime_AandE_002_btime,<{rgUnfold($DEVICE,'prime_AandE_002_')}@prime_AandE_002_title><br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32804TV Programm2020-02-17T21:00:36Z<p>Mumpitzstuff: timing bug fixed</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
#if (defined($7))<br />
#{<br />
# return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
#}<br />
#else<br />
#{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
#}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("\"perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge\"")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,next_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_000_')}@next_KabelEinsClassic_000_title>,<|>,next_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_001_')}@next_KabelEinsClassic_001_title>,<|>,next_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_002_')}@next_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,next_13thStreet_000_btime,<{rgUnfold($DEVICE,'next_13thStreet_000_')}@next_13thStreet_000_title>,<|>,next_13thStreet_001_btime,<{rgUnfold($DEVICE,'next_13thStreet_001_')}@next_13thStreet_001_title>,<|>,next_13thStreet_002_btime,<{rgUnfold($DEVICE,'next_13thStreet_002_')}@next_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,next_Silverline_000_btime,<{rgUnfold($DEVICE,'next_Silverline_000_')}@next_Silverline_000_title>,<|>,next_Silverline_001_btime,<{rgUnfold($DEVICE,'next_Silverline_001_')}@next_Silverline_001_title>,<|>,next_Silverline_002_btime,<{rgUnfold($DEVICE,'next_Silverline_002_')}@next_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,next_TNTFilm_000_btime,<{rgUnfold($DEVICE,'next_TNTFilm_000_')}@next_TNTFilm_000_title>,<|>,next_TNTFilm_001_btime,<{rgUnfold($DEVICE,'next_TNTFilm_001_')}@next_TNTFilm_001_title>,<|>,next_TNTFilm_002_btime,<{rgUnfold($DEVICE,'next_TNTFilm_002_')}@next_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,next_AXN_000_btime,<{rgUnfold($DEVICE,'next_AXN_000_')}@next_AXN_000_title>,<|>,next_AXN_001_btime,<{rgUnfold($DEVICE,'next_AXN_001_')}@next_AXN_001_title>,<|>,next_AXN_002_btime,<{rgUnfold($DEVICE,'next_AXN_002_')}@next_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,next_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_000_')}@next_SonyEntertainmentTV_000_title>,<|>,next_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_001_')}@next_SonyEntertainmentTV_001_title>,<|>,next_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_002_')}@next_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,next_Kinowelt_000_btime,<{rgUnfold($DEVICE,'next_Kinowelt_000_')}@next_Kinowelt_000_title>,<|>,next_Kinowelt_001_btime,<{rgUnfold($DEVICE,'next_Kinowelt_001_')}@next_Kinowelt_001_title>,<|>,next_Kinowelt_002_btime,<{rgUnfold($DEVICE,'next_Kinowelt_002_')}@next_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,next_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_000_')}@next_ProSiebenMaxx_000_title>,<|>,next_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_001_')}@next_ProSiebenMaxx_001_title>,<|>,next_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_002_')}@next_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,next_Sixx_000_btime,<{rgUnfold($DEVICE,'next_Sixx_000_')}@next_Sixx_000_title>,<|>,next_Sixx_001_btime,<{rgUnfold($DEVICE,'next_Sixx_001_')}@next_Sixx_001_title>,<|>,next_Sixx_002_btime,<{rgUnfold($DEVICE,'next_Sixx_002_')}@next_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,next_TNTSerie_000_btime,<{rgUnfold($DEVICE,'next_TNTSerie_000_')}@next_TNTSerie_000_title>,<|>,next_TNTSerie_001_btime,<{rgUnfold($DEVICE,'next_TNTSerie_001_')}@next_TNTSerie_001_title>,<|>,next_TNTSerie_002_btime,<{rgUnfold($DEVICE,'next_TNTSerie_002_')}@next_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,next_SciFi_000_btime,<{rgUnfold($DEVICE,'next_SciFi_000_')}@next_SciFi_000_title>,<|>,next_SciFi_001_btime,<{rgUnfold($DEVICE,'next_SciFi_001_')}@next_SciFi_001_title>,<|>,next_SciFi_002_btime,<{rgUnfold($DEVICE,'next_SciFi_002_')}@next_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,next_ntv_000_btime,<{rgUnfold($DEVICE,'next_ntv_000_')}@next_ntv_000_title>,<|>,next_ntv_001_btime,<{rgUnfold($DEVICE,'next_ntv_001_')}@next_ntv_001_title>,<|>,next_ntv_002_btime,<{rgUnfold($DEVICE,'next_ntv_002_')}@next_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,next_N24Doku_000_btime,<{rgUnfold($DEVICE,'next_N24Doku_000_')}@next_N24Doku_000_title>,<|>,next_N24Doku_001_btime,<{rgUnfold($DEVICE,'next_N24Doku_001_')}@next_N24Doku_001_title>,<|>,next_N24Doku_002_btime,<{rgUnfold($DEVICE,'next_N24Doku_002_')}@next_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,next_History_000_btime,<{rgUnfold($DEVICE,'next_History_000_')}@next_History_000_title>,<|>,next_History_001_btime,<{rgUnfold($DEVICE,'next_History_001_')}@next_History_001_title>,<|>,next_History_002_btime,<{rgUnfold($DEVICE,'next_History_002_')}@next_History_002_title>\<br />
dmy_TV:<%tv/planet>,next_PLANET_000_btime,<{rgUnfold($DEVICE,'next_PLANET_000_')}@next_PLANET_000_title>,<|>,next_PLANET_001_btime,<{rgUnfold($DEVICE,'next_PLANET_001_')}@next_PLANET_001_title>,<|>,next_PLANET_002_btime,<{rgUnfold($DEVICE,'next_PLANET_002_')}@next_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,next_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_000_')}@next_KabelEinsDoku_000_title>,<|>,next_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_001_')}@next_KabelEinsDoku_001_title>,<|>,next_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_002_')}@next_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,next_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_000_')}@next_AnimalPlanet_000_title>,<|>,next_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_001_')}@next_AnimalPlanet_001_title>,<|>,next_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_002_')}@next_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,next_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_000_')}@next_NatGeoHD_000_title>,<|>,next_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_001_')}@next_NatGeoHD_001_title>,<|>,next_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_002_')}@next_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,next_TLC_000_btime,<{rgUnfold($DEVICE,'next_TLC_000_')}@next_TLC_000_title>,<|>,next_TLC_001_btime,<{rgUnfold($DEVICE,'next_TLC_001_')}@next_TLC_001_title>,<|>,next_TLC_002_btime,<{rgUnfold($DEVICE,'next_TLC_002_')}@next_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,next_AandE_000_btime,<{rgUnfold($DEVICE,'next_AandE_000_')}@next_AandE_000_title>,<|>,next_AandE_001_btime,<{rgUnfold($DEVICE,'next_AandE_001_')}@next_AandE_001_title>,<|>,next_AandE_002_btime,<{rgUnfold($DEVICE,'next_AandE_002_')}@next_AandE_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,prime_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_000_')}@prime_KabelEinsClassic_000_title>,<|>,prime_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_001_')}@prime_KabelEinsClassic_001_title>,<|>,prime_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_002_')}@prime_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,prime_13thStreet_000_btime,<{rgUnfold($DEVICE,'prime_13thStreet_000_')}@prime_13thStreet_000_title>,<|>,prime_13thStreet_001_btime,<{rgUnfold($DEVICE,'prime_13thStreet_001_')}@prime_13thStreet_001_title>,<|>,prime_13thStreet_002_btime,<{rgUnfold($DEVICE,'prime_13thStreet_002_')}@prime_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,prime_Silverline_000_btime,<{rgUnfold($DEVICE,'prime_Silverline_000_')}@prime_Silverline_000_title>,<|>,prime_Silverline_001_btime,<{rgUnfold($DEVICE,'prime_Silverline_001_')}@prime_Silverline_001_title>,<|>,prime_Silverline_002_btime,<{rgUnfold($DEVICE,'prime_Silverline_002_')}@prime_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,prime_TNTFilm_000_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_000_')}@prime_TNTFilm_000_title>,<|>,prime_TNTFilm_001_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_001_')}@prime_TNTFilm_001_title>,<|>,prime_TNTFilm_002_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_002_')}@prime_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,prime_AXN_000_btime,<{rgUnfold($DEVICE,'prime_AXN_000_')}@prime_AXN_000_title>,<|>,prime_AXN_001_btime,<{rgUnfold($DEVICE,'prime_AXN_001_')}@prime_AXN_001_title>,<|>,prime_AXN_002_btime,<{rgUnfold($DEVICE,'prime_AXN_002_')}@prime_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,prime_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_000_')}@prime_SonyEntertainmentTV_000_title>,<|>,prime_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_001_')}@prime_SonyEntertainmentTV_001_title>,<|>,prime_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_002_')}@prime_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,prime_Kinowelt_000_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_000_')}@prime_Kinowelt_000_title>,<|>,prime_Kinowelt_001_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_001_')}@prime_Kinowelt_001_title>,<|>,prime_Kinowelt_002_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_002_')}@prime_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,prime_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_000_')}@prime_ProSiebenMaxx_000_title>,<|>,prime_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_001_')}@prime_ProSiebenMaxx_001_title>,<|>,prime_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_002_')}@prime_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,prime_Sixx_000_btime,<{rgUnfold($DEVICE,'prime_Sixx_000_')}@prime_Sixx_000_title>,<|>,prime_Sixx_001_btime,<{rgUnfold($DEVICE,'prime_Sixx_001_')}@prime_Sixx_001_title>,<|>,prime_Sixx_002_btime,<{rgUnfold($DEVICE,'prime_Sixx_002_')}@prime_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,prime_TNTSerie_000_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_000_')}@prime_TNTSerie_000_title>,<|>,prime_TNTSerie_001_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_001_')}@prime_TNTSerie_001_title>,<|>,prime_TNTSerie_002_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_002_')}@prime_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,prime_SciFi_000_btime,<{rgUnfold($DEVICE,'prime_SciFi_000_')}@prime_SciFi_000_title>,<|>,prime_SciFi_001_btime,<{rgUnfold($DEVICE,'prime_SciFi_001_')}@prime_SciFi_001_title>,<|>,prime_SciFi_002_btime,<{rgUnfold($DEVICE,'prime_SciFi_002_')}@prime_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,prime_ntv_000_btime,<{rgUnfold($DEVICE,'prime_ntv_000_')}@prime_ntv_000_title>,<|>,prime_ntv_001_btime,<{rgUnfold($DEVICE,'prime_ntv_001_')}@prime_ntv_001_title>,<|>,prime_ntv_002_btime,<{rgUnfold($DEVICE,'prime_ntv_002_')}@prime_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,prime_N24Doku_000_btime,<{rgUnfold($DEVICE,'prime_N24Doku_000_')}@prime_N24Doku_000_title>,<|>,prime_N24Doku_001_btime,<{rgUnfold($DEVICE,'prime_N24Doku_001_')}@prime_N24Doku_001_title>,<|>,prime_N24Doku_002_btime,<{rgUnfold($DEVICE,'prime_N24Doku_002_')}@prime_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,prime_History_000_btime,<{rgUnfold($DEVICE,'prime_History_000_')}@prime_History_000_title>,<|>,prime_History_001_btime,<{rgUnfold($DEVICE,'prime_History_001_')}@prime_History_001_title>,<|>,prime_History_002_btime,<{rgUnfold($DEVICE,'prime_History_002_')}@prime_History_002_title>\<br />
dmy_TV:<%tv/planet>,prime_PLANET_000_btime,<{rgUnfold($DEVICE,'prime_PLANET_000_')}@prime_PLANET_000_title>,<|>,prime_PLANET_001_btime,<{rgUnfold($DEVICE,'prime_PLANET_001_')}@prime_PLANET_001_title>,<|>,prime_PLANET_002_btime,<{rgUnfold($DEVICE,'prime_PLANET_002_')}@prime_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,prime_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_000_')}@prime_KabelEinsDoku_000_title>,<|>,prime_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_001_')}@prime_KabelEinsDoku_001_title>,<|>,prime_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_002_')}@prime_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,prime_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_000_')}@prime_AnimalPlanet_000_title>,<|>,prime_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_001_')}@prime_AnimalPlanet_001_title>,<|>,prime_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_002_')}@prime_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,prime_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_000_')}@prime_NatGeoHD_000_title>,<|>,prime_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_001_')}@prime_NatGeoHD_001_title>,<|>,prime_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_002_')}@prime_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,prime_TLC_000_btime,<{rgUnfold($DEVICE,'prime_TLC_000_')}@prime_TLC_000_title>,<|>,prime_TLC_001_btime,<{rgUnfold($DEVICE,'prime_TLC_001_')}@prime_TLC_001_title>,<|>,prime_TLC_002_btime,<{rgUnfold($DEVICE,'prime_TLC_002_')}@prime_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,prime_AandE_000_btime,<{rgUnfold($DEVICE,'prime_AandE_000_')}@prime_AandE_000_title>,<|>,prime_AandE_001_btime,<{rgUnfold($DEVICE,'prime_AandE_001_')}@prime_AandE_001_title>,<|>,prime_AandE_002_btime,<{rgUnfold($DEVICE,'prime_AandE_002_')}@prime_AandE_002_title><br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32449TV Programm2020-01-17T00:05:12Z<p>Mumpitzstuff: /* Perl Script einrichten: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("\"perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge\"")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,next_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_000_')}@next_KabelEinsClassic_000_title>,<|>,next_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_001_')}@next_KabelEinsClassic_001_title>,<|>,next_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_002_')}@next_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,next_13thStreet_000_btime,<{rgUnfold($DEVICE,'next_13thStreet_000_')}@next_13thStreet_000_title>,<|>,next_13thStreet_001_btime,<{rgUnfold($DEVICE,'next_13thStreet_001_')}@next_13thStreet_001_title>,<|>,next_13thStreet_002_btime,<{rgUnfold($DEVICE,'next_13thStreet_002_')}@next_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,next_Silverline_000_btime,<{rgUnfold($DEVICE,'next_Silverline_000_')}@next_Silverline_000_title>,<|>,next_Silverline_001_btime,<{rgUnfold($DEVICE,'next_Silverline_001_')}@next_Silverline_001_title>,<|>,next_Silverline_002_btime,<{rgUnfold($DEVICE,'next_Silverline_002_')}@next_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,next_TNTFilm_000_btime,<{rgUnfold($DEVICE,'next_TNTFilm_000_')}@next_TNTFilm_000_title>,<|>,next_TNTFilm_001_btime,<{rgUnfold($DEVICE,'next_TNTFilm_001_')}@next_TNTFilm_001_title>,<|>,next_TNTFilm_002_btime,<{rgUnfold($DEVICE,'next_TNTFilm_002_')}@next_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,next_AXN_000_btime,<{rgUnfold($DEVICE,'next_AXN_000_')}@next_AXN_000_title>,<|>,next_AXN_001_btime,<{rgUnfold($DEVICE,'next_AXN_001_')}@next_AXN_001_title>,<|>,next_AXN_002_btime,<{rgUnfold($DEVICE,'next_AXN_002_')}@next_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,next_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_000_')}@next_SonyEntertainmentTV_000_title>,<|>,next_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_001_')}@next_SonyEntertainmentTV_001_title>,<|>,next_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_002_')}@next_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,next_Kinowelt_000_btime,<{rgUnfold($DEVICE,'next_Kinowelt_000_')}@next_Kinowelt_000_title>,<|>,next_Kinowelt_001_btime,<{rgUnfold($DEVICE,'next_Kinowelt_001_')}@next_Kinowelt_001_title>,<|>,next_Kinowelt_002_btime,<{rgUnfold($DEVICE,'next_Kinowelt_002_')}@next_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,next_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_000_')}@next_ProSiebenMaxx_000_title>,<|>,next_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_001_')}@next_ProSiebenMaxx_001_title>,<|>,next_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_002_')}@next_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,next_Sixx_000_btime,<{rgUnfold($DEVICE,'next_Sixx_000_')}@next_Sixx_000_title>,<|>,next_Sixx_001_btime,<{rgUnfold($DEVICE,'next_Sixx_001_')}@next_Sixx_001_title>,<|>,next_Sixx_002_btime,<{rgUnfold($DEVICE,'next_Sixx_002_')}@next_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,next_TNTSerie_000_btime,<{rgUnfold($DEVICE,'next_TNTSerie_000_')}@next_TNTSerie_000_title>,<|>,next_TNTSerie_001_btime,<{rgUnfold($DEVICE,'next_TNTSerie_001_')}@next_TNTSerie_001_title>,<|>,next_TNTSerie_002_btime,<{rgUnfold($DEVICE,'next_TNTSerie_002_')}@next_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,next_SciFi_000_btime,<{rgUnfold($DEVICE,'next_SciFi_000_')}@next_SciFi_000_title>,<|>,next_SciFi_001_btime,<{rgUnfold($DEVICE,'next_SciFi_001_')}@next_SciFi_001_title>,<|>,next_SciFi_002_btime,<{rgUnfold($DEVICE,'next_SciFi_002_')}@next_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,next_ntv_000_btime,<{rgUnfold($DEVICE,'next_ntv_000_')}@next_ntv_000_title>,<|>,next_ntv_001_btime,<{rgUnfold($DEVICE,'next_ntv_001_')}@next_ntv_001_title>,<|>,next_ntv_002_btime,<{rgUnfold($DEVICE,'next_ntv_002_')}@next_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,next_N24Doku_000_btime,<{rgUnfold($DEVICE,'next_N24Doku_000_')}@next_N24Doku_000_title>,<|>,next_N24Doku_001_btime,<{rgUnfold($DEVICE,'next_N24Doku_001_')}@next_N24Doku_001_title>,<|>,next_N24Doku_002_btime,<{rgUnfold($DEVICE,'next_N24Doku_002_')}@next_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,next_History_000_btime,<{rgUnfold($DEVICE,'next_History_000_')}@next_History_000_title>,<|>,next_History_001_btime,<{rgUnfold($DEVICE,'next_History_001_')}@next_History_001_title>,<|>,next_History_002_btime,<{rgUnfold($DEVICE,'next_History_002_')}@next_History_002_title>\<br />
dmy_TV:<%tv/planet>,next_PLANET_000_btime,<{rgUnfold($DEVICE,'next_PLANET_000_')}@next_PLANET_000_title>,<|>,next_PLANET_001_btime,<{rgUnfold($DEVICE,'next_PLANET_001_')}@next_PLANET_001_title>,<|>,next_PLANET_002_btime,<{rgUnfold($DEVICE,'next_PLANET_002_')}@next_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,next_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_000_')}@next_KabelEinsDoku_000_title>,<|>,next_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_001_')}@next_KabelEinsDoku_001_title>,<|>,next_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_002_')}@next_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,next_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_000_')}@next_AnimalPlanet_000_title>,<|>,next_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_001_')}@next_AnimalPlanet_001_title>,<|>,next_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_002_')}@next_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,next_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_000_')}@next_NatGeoHD_000_title>,<|>,next_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_001_')}@next_NatGeoHD_001_title>,<|>,next_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_002_')}@next_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,next_TLC_000_btime,<{rgUnfold($DEVICE,'next_TLC_000_')}@next_TLC_000_title>,<|>,next_TLC_001_btime,<{rgUnfold($DEVICE,'next_TLC_001_')}@next_TLC_001_title>,<|>,next_TLC_002_btime,<{rgUnfold($DEVICE,'next_TLC_002_')}@next_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,next_AandE_000_btime,<{rgUnfold($DEVICE,'next_AandE_000_')}@next_AandE_000_title>,<|>,next_AandE_001_btime,<{rgUnfold($DEVICE,'next_AandE_001_')}@next_AandE_001_title>,<|>,next_AandE_002_btime,<{rgUnfold($DEVICE,'next_AandE_002_')}@next_AandE_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,prime_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_000_')}@prime_KabelEinsClassic_000_title>,<|>,prime_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_001_')}@prime_KabelEinsClassic_001_title>,<|>,prime_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_002_')}@prime_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,prime_13thStreet_000_btime,<{rgUnfold($DEVICE,'prime_13thStreet_000_')}@prime_13thStreet_000_title>,<|>,prime_13thStreet_001_btime,<{rgUnfold($DEVICE,'prime_13thStreet_001_')}@prime_13thStreet_001_title>,<|>,prime_13thStreet_002_btime,<{rgUnfold($DEVICE,'prime_13thStreet_002_')}@prime_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,prime_Silverline_000_btime,<{rgUnfold($DEVICE,'prime_Silverline_000_')}@prime_Silverline_000_title>,<|>,prime_Silverline_001_btime,<{rgUnfold($DEVICE,'prime_Silverline_001_')}@prime_Silverline_001_title>,<|>,prime_Silverline_002_btime,<{rgUnfold($DEVICE,'prime_Silverline_002_')}@prime_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,prime_TNTFilm_000_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_000_')}@prime_TNTFilm_000_title>,<|>,prime_TNTFilm_001_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_001_')}@prime_TNTFilm_001_title>,<|>,prime_TNTFilm_002_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_002_')}@prime_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,prime_AXN_000_btime,<{rgUnfold($DEVICE,'prime_AXN_000_')}@prime_AXN_000_title>,<|>,prime_AXN_001_btime,<{rgUnfold($DEVICE,'prime_AXN_001_')}@prime_AXN_001_title>,<|>,prime_AXN_002_btime,<{rgUnfold($DEVICE,'prime_AXN_002_')}@prime_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,prime_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_000_')}@prime_SonyEntertainmentTV_000_title>,<|>,prime_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_001_')}@prime_SonyEntertainmentTV_001_title>,<|>,prime_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_002_')}@prime_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,prime_Kinowelt_000_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_000_')}@prime_Kinowelt_000_title>,<|>,prime_Kinowelt_001_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_001_')}@prime_Kinowelt_001_title>,<|>,prime_Kinowelt_002_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_002_')}@prime_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,prime_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_000_')}@prime_ProSiebenMaxx_000_title>,<|>,prime_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_001_')}@prime_ProSiebenMaxx_001_title>,<|>,prime_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_002_')}@prime_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,prime_Sixx_000_btime,<{rgUnfold($DEVICE,'prime_Sixx_000_')}@prime_Sixx_000_title>,<|>,prime_Sixx_001_btime,<{rgUnfold($DEVICE,'prime_Sixx_001_')}@prime_Sixx_001_title>,<|>,prime_Sixx_002_btime,<{rgUnfold($DEVICE,'prime_Sixx_002_')}@prime_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,prime_TNTSerie_000_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_000_')}@prime_TNTSerie_000_title>,<|>,prime_TNTSerie_001_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_001_')}@prime_TNTSerie_001_title>,<|>,prime_TNTSerie_002_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_002_')}@prime_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,prime_SciFi_000_btime,<{rgUnfold($DEVICE,'prime_SciFi_000_')}@prime_SciFi_000_title>,<|>,prime_SciFi_001_btime,<{rgUnfold($DEVICE,'prime_SciFi_001_')}@prime_SciFi_001_title>,<|>,prime_SciFi_002_btime,<{rgUnfold($DEVICE,'prime_SciFi_002_')}@prime_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,prime_ntv_000_btime,<{rgUnfold($DEVICE,'prime_ntv_000_')}@prime_ntv_000_title>,<|>,prime_ntv_001_btime,<{rgUnfold($DEVICE,'prime_ntv_001_')}@prime_ntv_001_title>,<|>,prime_ntv_002_btime,<{rgUnfold($DEVICE,'prime_ntv_002_')}@prime_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,prime_N24Doku_000_btime,<{rgUnfold($DEVICE,'prime_N24Doku_000_')}@prime_N24Doku_000_title>,<|>,prime_N24Doku_001_btime,<{rgUnfold($DEVICE,'prime_N24Doku_001_')}@prime_N24Doku_001_title>,<|>,prime_N24Doku_002_btime,<{rgUnfold($DEVICE,'prime_N24Doku_002_')}@prime_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,prime_History_000_btime,<{rgUnfold($DEVICE,'prime_History_000_')}@prime_History_000_title>,<|>,prime_History_001_btime,<{rgUnfold($DEVICE,'prime_History_001_')}@prime_History_001_title>,<|>,prime_History_002_btime,<{rgUnfold($DEVICE,'prime_History_002_')}@prime_History_002_title>\<br />
dmy_TV:<%tv/planet>,prime_PLANET_000_btime,<{rgUnfold($DEVICE,'prime_PLANET_000_')}@prime_PLANET_000_title>,<|>,prime_PLANET_001_btime,<{rgUnfold($DEVICE,'prime_PLANET_001_')}@prime_PLANET_001_title>,<|>,prime_PLANET_002_btime,<{rgUnfold($DEVICE,'prime_PLANET_002_')}@prime_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,prime_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_000_')}@prime_KabelEinsDoku_000_title>,<|>,prime_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_001_')}@prime_KabelEinsDoku_001_title>,<|>,prime_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_002_')}@prime_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,prime_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_000_')}@prime_AnimalPlanet_000_title>,<|>,prime_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_001_')}@prime_AnimalPlanet_001_title>,<|>,prime_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_002_')}@prime_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,prime_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_000_')}@prime_NatGeoHD_000_title>,<|>,prime_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_001_')}@prime_NatGeoHD_001_title>,<|>,prime_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_002_')}@prime_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,prime_TLC_000_btime,<{rgUnfold($DEVICE,'prime_TLC_000_')}@prime_TLC_000_title>,<|>,prime_TLC_001_btime,<{rgUnfold($DEVICE,'prime_TLC_001_')}@prime_TLC_001_title>,<|>,prime_TLC_002_btime,<{rgUnfold($DEVICE,'prime_TLC_002_')}@prime_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,prime_AandE_000_btime,<{rgUnfold($DEVICE,'prime_AandE_000_')}@prime_AandE_000_title>,<|>,prime_AandE_001_btime,<{rgUnfold($DEVICE,'prime_AandE_001_')}@prime_AandE_001_title>,<|>,prime_AandE_002_btime,<{rgUnfold($DEVICE,'prime_AandE_002_')}@prime_AandE_002_title><br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32448TV Programm2020-01-17T00:04:29Z<p>Mumpitzstuff: /* Perl Script einrichten: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
"perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge"<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("\"perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge\"")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,next_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_000_')}@next_KabelEinsClassic_000_title>,<|>,next_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_001_')}@next_KabelEinsClassic_001_title>,<|>,next_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_002_')}@next_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,next_13thStreet_000_btime,<{rgUnfold($DEVICE,'next_13thStreet_000_')}@next_13thStreet_000_title>,<|>,next_13thStreet_001_btime,<{rgUnfold($DEVICE,'next_13thStreet_001_')}@next_13thStreet_001_title>,<|>,next_13thStreet_002_btime,<{rgUnfold($DEVICE,'next_13thStreet_002_')}@next_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,next_Silverline_000_btime,<{rgUnfold($DEVICE,'next_Silverline_000_')}@next_Silverline_000_title>,<|>,next_Silverline_001_btime,<{rgUnfold($DEVICE,'next_Silverline_001_')}@next_Silverline_001_title>,<|>,next_Silverline_002_btime,<{rgUnfold($DEVICE,'next_Silverline_002_')}@next_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,next_TNTFilm_000_btime,<{rgUnfold($DEVICE,'next_TNTFilm_000_')}@next_TNTFilm_000_title>,<|>,next_TNTFilm_001_btime,<{rgUnfold($DEVICE,'next_TNTFilm_001_')}@next_TNTFilm_001_title>,<|>,next_TNTFilm_002_btime,<{rgUnfold($DEVICE,'next_TNTFilm_002_')}@next_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,next_AXN_000_btime,<{rgUnfold($DEVICE,'next_AXN_000_')}@next_AXN_000_title>,<|>,next_AXN_001_btime,<{rgUnfold($DEVICE,'next_AXN_001_')}@next_AXN_001_title>,<|>,next_AXN_002_btime,<{rgUnfold($DEVICE,'next_AXN_002_')}@next_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,next_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_000_')}@next_SonyEntertainmentTV_000_title>,<|>,next_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_001_')}@next_SonyEntertainmentTV_001_title>,<|>,next_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_002_')}@next_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,next_Kinowelt_000_btime,<{rgUnfold($DEVICE,'next_Kinowelt_000_')}@next_Kinowelt_000_title>,<|>,next_Kinowelt_001_btime,<{rgUnfold($DEVICE,'next_Kinowelt_001_')}@next_Kinowelt_001_title>,<|>,next_Kinowelt_002_btime,<{rgUnfold($DEVICE,'next_Kinowelt_002_')}@next_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,next_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_000_')}@next_ProSiebenMaxx_000_title>,<|>,next_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_001_')}@next_ProSiebenMaxx_001_title>,<|>,next_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_002_')}@next_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,next_Sixx_000_btime,<{rgUnfold($DEVICE,'next_Sixx_000_')}@next_Sixx_000_title>,<|>,next_Sixx_001_btime,<{rgUnfold($DEVICE,'next_Sixx_001_')}@next_Sixx_001_title>,<|>,next_Sixx_002_btime,<{rgUnfold($DEVICE,'next_Sixx_002_')}@next_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,next_TNTSerie_000_btime,<{rgUnfold($DEVICE,'next_TNTSerie_000_')}@next_TNTSerie_000_title>,<|>,next_TNTSerie_001_btime,<{rgUnfold($DEVICE,'next_TNTSerie_001_')}@next_TNTSerie_001_title>,<|>,next_TNTSerie_002_btime,<{rgUnfold($DEVICE,'next_TNTSerie_002_')}@next_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,next_SciFi_000_btime,<{rgUnfold($DEVICE,'next_SciFi_000_')}@next_SciFi_000_title>,<|>,next_SciFi_001_btime,<{rgUnfold($DEVICE,'next_SciFi_001_')}@next_SciFi_001_title>,<|>,next_SciFi_002_btime,<{rgUnfold($DEVICE,'next_SciFi_002_')}@next_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,next_ntv_000_btime,<{rgUnfold($DEVICE,'next_ntv_000_')}@next_ntv_000_title>,<|>,next_ntv_001_btime,<{rgUnfold($DEVICE,'next_ntv_001_')}@next_ntv_001_title>,<|>,next_ntv_002_btime,<{rgUnfold($DEVICE,'next_ntv_002_')}@next_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,next_N24Doku_000_btime,<{rgUnfold($DEVICE,'next_N24Doku_000_')}@next_N24Doku_000_title>,<|>,next_N24Doku_001_btime,<{rgUnfold($DEVICE,'next_N24Doku_001_')}@next_N24Doku_001_title>,<|>,next_N24Doku_002_btime,<{rgUnfold($DEVICE,'next_N24Doku_002_')}@next_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,next_History_000_btime,<{rgUnfold($DEVICE,'next_History_000_')}@next_History_000_title>,<|>,next_History_001_btime,<{rgUnfold($DEVICE,'next_History_001_')}@next_History_001_title>,<|>,next_History_002_btime,<{rgUnfold($DEVICE,'next_History_002_')}@next_History_002_title>\<br />
dmy_TV:<%tv/planet>,next_PLANET_000_btime,<{rgUnfold($DEVICE,'next_PLANET_000_')}@next_PLANET_000_title>,<|>,next_PLANET_001_btime,<{rgUnfold($DEVICE,'next_PLANET_001_')}@next_PLANET_001_title>,<|>,next_PLANET_002_btime,<{rgUnfold($DEVICE,'next_PLANET_002_')}@next_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,next_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_000_')}@next_KabelEinsDoku_000_title>,<|>,next_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_001_')}@next_KabelEinsDoku_001_title>,<|>,next_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_002_')}@next_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,next_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_000_')}@next_AnimalPlanet_000_title>,<|>,next_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_001_')}@next_AnimalPlanet_001_title>,<|>,next_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_002_')}@next_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,next_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_000_')}@next_NatGeoHD_000_title>,<|>,next_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_001_')}@next_NatGeoHD_001_title>,<|>,next_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_002_')}@next_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,next_TLC_000_btime,<{rgUnfold($DEVICE,'next_TLC_000_')}@next_TLC_000_title>,<|>,next_TLC_001_btime,<{rgUnfold($DEVICE,'next_TLC_001_')}@next_TLC_001_title>,<|>,next_TLC_002_btime,<{rgUnfold($DEVICE,'next_TLC_002_')}@next_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,next_AandE_000_btime,<{rgUnfold($DEVICE,'next_AandE_000_')}@next_AandE_000_title>,<|>,next_AandE_001_btime,<{rgUnfold($DEVICE,'next_AandE_001_')}@next_AandE_001_title>,<|>,next_AandE_002_btime,<{rgUnfold($DEVICE,'next_AandE_002_')}@next_AandE_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,prime_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_000_')}@prime_KabelEinsClassic_000_title>,<|>,prime_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_001_')}@prime_KabelEinsClassic_001_title>,<|>,prime_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_002_')}@prime_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,prime_13thStreet_000_btime,<{rgUnfold($DEVICE,'prime_13thStreet_000_')}@prime_13thStreet_000_title>,<|>,prime_13thStreet_001_btime,<{rgUnfold($DEVICE,'prime_13thStreet_001_')}@prime_13thStreet_001_title>,<|>,prime_13thStreet_002_btime,<{rgUnfold($DEVICE,'prime_13thStreet_002_')}@prime_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,prime_Silverline_000_btime,<{rgUnfold($DEVICE,'prime_Silverline_000_')}@prime_Silverline_000_title>,<|>,prime_Silverline_001_btime,<{rgUnfold($DEVICE,'prime_Silverline_001_')}@prime_Silverline_001_title>,<|>,prime_Silverline_002_btime,<{rgUnfold($DEVICE,'prime_Silverline_002_')}@prime_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,prime_TNTFilm_000_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_000_')}@prime_TNTFilm_000_title>,<|>,prime_TNTFilm_001_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_001_')}@prime_TNTFilm_001_title>,<|>,prime_TNTFilm_002_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_002_')}@prime_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,prime_AXN_000_btime,<{rgUnfold($DEVICE,'prime_AXN_000_')}@prime_AXN_000_title>,<|>,prime_AXN_001_btime,<{rgUnfold($DEVICE,'prime_AXN_001_')}@prime_AXN_001_title>,<|>,prime_AXN_002_btime,<{rgUnfold($DEVICE,'prime_AXN_002_')}@prime_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,prime_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_000_')}@prime_SonyEntertainmentTV_000_title>,<|>,prime_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_001_')}@prime_SonyEntertainmentTV_001_title>,<|>,prime_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_002_')}@prime_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,prime_Kinowelt_000_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_000_')}@prime_Kinowelt_000_title>,<|>,prime_Kinowelt_001_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_001_')}@prime_Kinowelt_001_title>,<|>,prime_Kinowelt_002_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_002_')}@prime_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,prime_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_000_')}@prime_ProSiebenMaxx_000_title>,<|>,prime_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_001_')}@prime_ProSiebenMaxx_001_title>,<|>,prime_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_002_')}@prime_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,prime_Sixx_000_btime,<{rgUnfold($DEVICE,'prime_Sixx_000_')}@prime_Sixx_000_title>,<|>,prime_Sixx_001_btime,<{rgUnfold($DEVICE,'prime_Sixx_001_')}@prime_Sixx_001_title>,<|>,prime_Sixx_002_btime,<{rgUnfold($DEVICE,'prime_Sixx_002_')}@prime_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,prime_TNTSerie_000_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_000_')}@prime_TNTSerie_000_title>,<|>,prime_TNTSerie_001_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_001_')}@prime_TNTSerie_001_title>,<|>,prime_TNTSerie_002_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_002_')}@prime_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,prime_SciFi_000_btime,<{rgUnfold($DEVICE,'prime_SciFi_000_')}@prime_SciFi_000_title>,<|>,prime_SciFi_001_btime,<{rgUnfold($DEVICE,'prime_SciFi_001_')}@prime_SciFi_001_title>,<|>,prime_SciFi_002_btime,<{rgUnfold($DEVICE,'prime_SciFi_002_')}@prime_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,prime_ntv_000_btime,<{rgUnfold($DEVICE,'prime_ntv_000_')}@prime_ntv_000_title>,<|>,prime_ntv_001_btime,<{rgUnfold($DEVICE,'prime_ntv_001_')}@prime_ntv_001_title>,<|>,prime_ntv_002_btime,<{rgUnfold($DEVICE,'prime_ntv_002_')}@prime_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,prime_N24Doku_000_btime,<{rgUnfold($DEVICE,'prime_N24Doku_000_')}@prime_N24Doku_000_title>,<|>,prime_N24Doku_001_btime,<{rgUnfold($DEVICE,'prime_N24Doku_001_')}@prime_N24Doku_001_title>,<|>,prime_N24Doku_002_btime,<{rgUnfold($DEVICE,'prime_N24Doku_002_')}@prime_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,prime_History_000_btime,<{rgUnfold($DEVICE,'prime_History_000_')}@prime_History_000_title>,<|>,prime_History_001_btime,<{rgUnfold($DEVICE,'prime_History_001_')}@prime_History_001_title>,<|>,prime_History_002_btime,<{rgUnfold($DEVICE,'prime_History_002_')}@prime_History_002_title>\<br />
dmy_TV:<%tv/planet>,prime_PLANET_000_btime,<{rgUnfold($DEVICE,'prime_PLANET_000_')}@prime_PLANET_000_title>,<|>,prime_PLANET_001_btime,<{rgUnfold($DEVICE,'prime_PLANET_001_')}@prime_PLANET_001_title>,<|>,prime_PLANET_002_btime,<{rgUnfold($DEVICE,'prime_PLANET_002_')}@prime_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,prime_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_000_')}@prime_KabelEinsDoku_000_title>,<|>,prime_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_001_')}@prime_KabelEinsDoku_001_title>,<|>,prime_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_002_')}@prime_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,prime_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_000_')}@prime_AnimalPlanet_000_title>,<|>,prime_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_001_')}@prime_AnimalPlanet_001_title>,<|>,prime_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_002_')}@prime_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,prime_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_000_')}@prime_NatGeoHD_000_title>,<|>,prime_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_001_')}@prime_NatGeoHD_001_title>,<|>,prime_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_002_')}@prime_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,prime_TLC_000_btime,<{rgUnfold($DEVICE,'prime_TLC_000_')}@prime_TLC_000_title>,<|>,prime_TLC_001_btime,<{rgUnfold($DEVICE,'prime_TLC_001_')}@prime_TLC_001_title>,<|>,prime_TLC_002_btime,<{rgUnfold($DEVICE,'prime_TLC_002_')}@prime_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,prime_AandE_000_btime,<{rgUnfold($DEVICE,'prime_AandE_000_')}@prime_AandE_000_title>,<|>,prime_AandE_001_btime,<{rgUnfold($DEVICE,'prime_AandE_001_')}@prime_AandE_001_title>,<|>,prime_AandE_002_btime,<{rgUnfold($DEVICE,'prime_AandE_002_')}@prime_AandE_002_title><br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32447TV Programm2020-01-16T23:59:59Z<p>Mumpitzstuff: /* at Devices anlegen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("\"perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge\"")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,next_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_000_')}@next_KabelEinsClassic_000_title>,<|>,next_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_001_')}@next_KabelEinsClassic_001_title>,<|>,next_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_002_')}@next_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,next_13thStreet_000_btime,<{rgUnfold($DEVICE,'next_13thStreet_000_')}@next_13thStreet_000_title>,<|>,next_13thStreet_001_btime,<{rgUnfold($DEVICE,'next_13thStreet_001_')}@next_13thStreet_001_title>,<|>,next_13thStreet_002_btime,<{rgUnfold($DEVICE,'next_13thStreet_002_')}@next_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,next_Silverline_000_btime,<{rgUnfold($DEVICE,'next_Silverline_000_')}@next_Silverline_000_title>,<|>,next_Silverline_001_btime,<{rgUnfold($DEVICE,'next_Silverline_001_')}@next_Silverline_001_title>,<|>,next_Silverline_002_btime,<{rgUnfold($DEVICE,'next_Silverline_002_')}@next_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,next_TNTFilm_000_btime,<{rgUnfold($DEVICE,'next_TNTFilm_000_')}@next_TNTFilm_000_title>,<|>,next_TNTFilm_001_btime,<{rgUnfold($DEVICE,'next_TNTFilm_001_')}@next_TNTFilm_001_title>,<|>,next_TNTFilm_002_btime,<{rgUnfold($DEVICE,'next_TNTFilm_002_')}@next_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,next_AXN_000_btime,<{rgUnfold($DEVICE,'next_AXN_000_')}@next_AXN_000_title>,<|>,next_AXN_001_btime,<{rgUnfold($DEVICE,'next_AXN_001_')}@next_AXN_001_title>,<|>,next_AXN_002_btime,<{rgUnfold($DEVICE,'next_AXN_002_')}@next_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,next_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_000_')}@next_SonyEntertainmentTV_000_title>,<|>,next_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_001_')}@next_SonyEntertainmentTV_001_title>,<|>,next_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_002_')}@next_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,next_Kinowelt_000_btime,<{rgUnfold($DEVICE,'next_Kinowelt_000_')}@next_Kinowelt_000_title>,<|>,next_Kinowelt_001_btime,<{rgUnfold($DEVICE,'next_Kinowelt_001_')}@next_Kinowelt_001_title>,<|>,next_Kinowelt_002_btime,<{rgUnfold($DEVICE,'next_Kinowelt_002_')}@next_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,next_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_000_')}@next_ProSiebenMaxx_000_title>,<|>,next_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_001_')}@next_ProSiebenMaxx_001_title>,<|>,next_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_002_')}@next_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,next_Sixx_000_btime,<{rgUnfold($DEVICE,'next_Sixx_000_')}@next_Sixx_000_title>,<|>,next_Sixx_001_btime,<{rgUnfold($DEVICE,'next_Sixx_001_')}@next_Sixx_001_title>,<|>,next_Sixx_002_btime,<{rgUnfold($DEVICE,'next_Sixx_002_')}@next_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,next_TNTSerie_000_btime,<{rgUnfold($DEVICE,'next_TNTSerie_000_')}@next_TNTSerie_000_title>,<|>,next_TNTSerie_001_btime,<{rgUnfold($DEVICE,'next_TNTSerie_001_')}@next_TNTSerie_001_title>,<|>,next_TNTSerie_002_btime,<{rgUnfold($DEVICE,'next_TNTSerie_002_')}@next_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,next_SciFi_000_btime,<{rgUnfold($DEVICE,'next_SciFi_000_')}@next_SciFi_000_title>,<|>,next_SciFi_001_btime,<{rgUnfold($DEVICE,'next_SciFi_001_')}@next_SciFi_001_title>,<|>,next_SciFi_002_btime,<{rgUnfold($DEVICE,'next_SciFi_002_')}@next_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,next_ntv_000_btime,<{rgUnfold($DEVICE,'next_ntv_000_')}@next_ntv_000_title>,<|>,next_ntv_001_btime,<{rgUnfold($DEVICE,'next_ntv_001_')}@next_ntv_001_title>,<|>,next_ntv_002_btime,<{rgUnfold($DEVICE,'next_ntv_002_')}@next_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,next_N24Doku_000_btime,<{rgUnfold($DEVICE,'next_N24Doku_000_')}@next_N24Doku_000_title>,<|>,next_N24Doku_001_btime,<{rgUnfold($DEVICE,'next_N24Doku_001_')}@next_N24Doku_001_title>,<|>,next_N24Doku_002_btime,<{rgUnfold($DEVICE,'next_N24Doku_002_')}@next_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,next_History_000_btime,<{rgUnfold($DEVICE,'next_History_000_')}@next_History_000_title>,<|>,next_History_001_btime,<{rgUnfold($DEVICE,'next_History_001_')}@next_History_001_title>,<|>,next_History_002_btime,<{rgUnfold($DEVICE,'next_History_002_')}@next_History_002_title>\<br />
dmy_TV:<%tv/planet>,next_PLANET_000_btime,<{rgUnfold($DEVICE,'next_PLANET_000_')}@next_PLANET_000_title>,<|>,next_PLANET_001_btime,<{rgUnfold($DEVICE,'next_PLANET_001_')}@next_PLANET_001_title>,<|>,next_PLANET_002_btime,<{rgUnfold($DEVICE,'next_PLANET_002_')}@next_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,next_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_000_')}@next_KabelEinsDoku_000_title>,<|>,next_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_001_')}@next_KabelEinsDoku_001_title>,<|>,next_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_002_')}@next_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,next_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_000_')}@next_AnimalPlanet_000_title>,<|>,next_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_001_')}@next_AnimalPlanet_001_title>,<|>,next_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_002_')}@next_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,next_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_000_')}@next_NatGeoHD_000_title>,<|>,next_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_001_')}@next_NatGeoHD_001_title>,<|>,next_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_002_')}@next_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,next_TLC_000_btime,<{rgUnfold($DEVICE,'next_TLC_000_')}@next_TLC_000_title>,<|>,next_TLC_001_btime,<{rgUnfold($DEVICE,'next_TLC_001_')}@next_TLC_001_title>,<|>,next_TLC_002_btime,<{rgUnfold($DEVICE,'next_TLC_002_')}@next_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,next_AandE_000_btime,<{rgUnfold($DEVICE,'next_AandE_000_')}@next_AandE_000_title>,<|>,next_AandE_001_btime,<{rgUnfold($DEVICE,'next_AandE_001_')}@next_AandE_001_title>,<|>,next_AandE_002_btime,<{rgUnfold($DEVICE,'next_AandE_002_')}@next_AandE_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,prime_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_000_')}@prime_KabelEinsClassic_000_title>,<|>,prime_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_001_')}@prime_KabelEinsClassic_001_title>,<|>,prime_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_002_')}@prime_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,prime_13thStreet_000_btime,<{rgUnfold($DEVICE,'prime_13thStreet_000_')}@prime_13thStreet_000_title>,<|>,prime_13thStreet_001_btime,<{rgUnfold($DEVICE,'prime_13thStreet_001_')}@prime_13thStreet_001_title>,<|>,prime_13thStreet_002_btime,<{rgUnfold($DEVICE,'prime_13thStreet_002_')}@prime_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,prime_Silverline_000_btime,<{rgUnfold($DEVICE,'prime_Silverline_000_')}@prime_Silverline_000_title>,<|>,prime_Silverline_001_btime,<{rgUnfold($DEVICE,'prime_Silverline_001_')}@prime_Silverline_001_title>,<|>,prime_Silverline_002_btime,<{rgUnfold($DEVICE,'prime_Silverline_002_')}@prime_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,prime_TNTFilm_000_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_000_')}@prime_TNTFilm_000_title>,<|>,prime_TNTFilm_001_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_001_')}@prime_TNTFilm_001_title>,<|>,prime_TNTFilm_002_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_002_')}@prime_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,prime_AXN_000_btime,<{rgUnfold($DEVICE,'prime_AXN_000_')}@prime_AXN_000_title>,<|>,prime_AXN_001_btime,<{rgUnfold($DEVICE,'prime_AXN_001_')}@prime_AXN_001_title>,<|>,prime_AXN_002_btime,<{rgUnfold($DEVICE,'prime_AXN_002_')}@prime_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,prime_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_000_')}@prime_SonyEntertainmentTV_000_title>,<|>,prime_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_001_')}@prime_SonyEntertainmentTV_001_title>,<|>,prime_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_002_')}@prime_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,prime_Kinowelt_000_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_000_')}@prime_Kinowelt_000_title>,<|>,prime_Kinowelt_001_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_001_')}@prime_Kinowelt_001_title>,<|>,prime_Kinowelt_002_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_002_')}@prime_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,prime_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_000_')}@prime_ProSiebenMaxx_000_title>,<|>,prime_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_001_')}@prime_ProSiebenMaxx_001_title>,<|>,prime_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_002_')}@prime_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,prime_Sixx_000_btime,<{rgUnfold($DEVICE,'prime_Sixx_000_')}@prime_Sixx_000_title>,<|>,prime_Sixx_001_btime,<{rgUnfold($DEVICE,'prime_Sixx_001_')}@prime_Sixx_001_title>,<|>,prime_Sixx_002_btime,<{rgUnfold($DEVICE,'prime_Sixx_002_')}@prime_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,prime_TNTSerie_000_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_000_')}@prime_TNTSerie_000_title>,<|>,prime_TNTSerie_001_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_001_')}@prime_TNTSerie_001_title>,<|>,prime_TNTSerie_002_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_002_')}@prime_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,prime_SciFi_000_btime,<{rgUnfold($DEVICE,'prime_SciFi_000_')}@prime_SciFi_000_title>,<|>,prime_SciFi_001_btime,<{rgUnfold($DEVICE,'prime_SciFi_001_')}@prime_SciFi_001_title>,<|>,prime_SciFi_002_btime,<{rgUnfold($DEVICE,'prime_SciFi_002_')}@prime_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,prime_ntv_000_btime,<{rgUnfold($DEVICE,'prime_ntv_000_')}@prime_ntv_000_title>,<|>,prime_ntv_001_btime,<{rgUnfold($DEVICE,'prime_ntv_001_')}@prime_ntv_001_title>,<|>,prime_ntv_002_btime,<{rgUnfold($DEVICE,'prime_ntv_002_')}@prime_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,prime_N24Doku_000_btime,<{rgUnfold($DEVICE,'prime_N24Doku_000_')}@prime_N24Doku_000_title>,<|>,prime_N24Doku_001_btime,<{rgUnfold($DEVICE,'prime_N24Doku_001_')}@prime_N24Doku_001_title>,<|>,prime_N24Doku_002_btime,<{rgUnfold($DEVICE,'prime_N24Doku_002_')}@prime_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,prime_History_000_btime,<{rgUnfold($DEVICE,'prime_History_000_')}@prime_History_000_title>,<|>,prime_History_001_btime,<{rgUnfold($DEVICE,'prime_History_001_')}@prime_History_001_title>,<|>,prime_History_002_btime,<{rgUnfold($DEVICE,'prime_History_002_')}@prime_History_002_title>\<br />
dmy_TV:<%tv/planet>,prime_PLANET_000_btime,<{rgUnfold($DEVICE,'prime_PLANET_000_')}@prime_PLANET_000_title>,<|>,prime_PLANET_001_btime,<{rgUnfold($DEVICE,'prime_PLANET_001_')}@prime_PLANET_001_title>,<|>,prime_PLANET_002_btime,<{rgUnfold($DEVICE,'prime_PLANET_002_')}@prime_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,prime_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_000_')}@prime_KabelEinsDoku_000_title>,<|>,prime_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_001_')}@prime_KabelEinsDoku_001_title>,<|>,prime_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_002_')}@prime_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,prime_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_000_')}@prime_AnimalPlanet_000_title>,<|>,prime_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_001_')}@prime_AnimalPlanet_001_title>,<|>,prime_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_002_')}@prime_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,prime_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_000_')}@prime_NatGeoHD_000_title>,<|>,prime_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_001_')}@prime_NatGeoHD_001_title>,<|>,prime_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_002_')}@prime_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,prime_TLC_000_btime,<{rgUnfold($DEVICE,'prime_TLC_000_')}@prime_TLC_000_title>,<|>,prime_TLC_001_btime,<{rgUnfold($DEVICE,'prime_TLC_001_')}@prime_TLC_001_title>,<|>,prime_TLC_002_btime,<{rgUnfold($DEVICE,'prime_TLC_002_')}@prime_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,prime_AandE_000_btime,<{rgUnfold($DEVICE,'prime_AandE_000_')}@prime_AandE_000_title>,<|>,prime_AandE_001_btime,<{rgUnfold($DEVICE,'prime_AandE_001_')}@prime_AandE_001_title>,<|>,prime_AandE_002_btime,<{rgUnfold($DEVICE,'prime_AandE_002_')}@prime_AandE_002_title><br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32229TV Programm2019-12-30T14:10:05Z<p>Mumpitzstuff: more channels added</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,next_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_000_')}@next_KabelEinsClassic_000_title>,<|>,next_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_001_')}@next_KabelEinsClassic_001_title>,<|>,next_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsClassic_002_')}@next_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,next_13thStreet_000_btime,<{rgUnfold($DEVICE,'next_13thStreet_000_')}@next_13thStreet_000_title>,<|>,next_13thStreet_001_btime,<{rgUnfold($DEVICE,'next_13thStreet_001_')}@next_13thStreet_001_title>,<|>,next_13thStreet_002_btime,<{rgUnfold($DEVICE,'next_13thStreet_002_')}@next_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,next_Silverline_000_btime,<{rgUnfold($DEVICE,'next_Silverline_000_')}@next_Silverline_000_title>,<|>,next_Silverline_001_btime,<{rgUnfold($DEVICE,'next_Silverline_001_')}@next_Silverline_001_title>,<|>,next_Silverline_002_btime,<{rgUnfold($DEVICE,'next_Silverline_002_')}@next_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,next_TNTFilm_000_btime,<{rgUnfold($DEVICE,'next_TNTFilm_000_')}@next_TNTFilm_000_title>,<|>,next_TNTFilm_001_btime,<{rgUnfold($DEVICE,'next_TNTFilm_001_')}@next_TNTFilm_001_title>,<|>,next_TNTFilm_002_btime,<{rgUnfold($DEVICE,'next_TNTFilm_002_')}@next_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,next_AXN_000_btime,<{rgUnfold($DEVICE,'next_AXN_000_')}@next_AXN_000_title>,<|>,next_AXN_001_btime,<{rgUnfold($DEVICE,'next_AXN_001_')}@next_AXN_001_title>,<|>,next_AXN_002_btime,<{rgUnfold($DEVICE,'next_AXN_002_')}@next_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,next_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_000_')}@next_SonyEntertainmentTV_000_title>,<|>,next_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_001_')}@next_SonyEntertainmentTV_001_title>,<|>,next_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'next_SonyEntertainmentTV_002_')}@next_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,next_Kinowelt_000_btime,<{rgUnfold($DEVICE,'next_Kinowelt_000_')}@next_Kinowelt_000_title>,<|>,next_Kinowelt_001_btime,<{rgUnfold($DEVICE,'next_Kinowelt_001_')}@next_Kinowelt_001_title>,<|>,next_Kinowelt_002_btime,<{rgUnfold($DEVICE,'next_Kinowelt_002_')}@next_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,next_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_000_')}@next_ProSiebenMaxx_000_title>,<|>,next_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_001_')}@next_ProSiebenMaxx_001_title>,<|>,next_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'next_ProSiebenMaxx_002_')}@next_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,next_Sixx_000_btime,<{rgUnfold($DEVICE,'next_Sixx_000_')}@next_Sixx_000_title>,<|>,next_Sixx_001_btime,<{rgUnfold($DEVICE,'next_Sixx_001_')}@next_Sixx_001_title>,<|>,next_Sixx_002_btime,<{rgUnfold($DEVICE,'next_Sixx_002_')}@next_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,next_TNTSerie_000_btime,<{rgUnfold($DEVICE,'next_TNTSerie_000_')}@next_TNTSerie_000_title>,<|>,next_TNTSerie_001_btime,<{rgUnfold($DEVICE,'next_TNTSerie_001_')}@next_TNTSerie_001_title>,<|>,next_TNTSerie_002_btime,<{rgUnfold($DEVICE,'next_TNTSerie_002_')}@next_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,next_SciFi_000_btime,<{rgUnfold($DEVICE,'next_SciFi_000_')}@next_SciFi_000_title>,<|>,next_SciFi_001_btime,<{rgUnfold($DEVICE,'next_SciFi_001_')}@next_SciFi_001_title>,<|>,next_SciFi_002_btime,<{rgUnfold($DEVICE,'next_SciFi_002_')}@next_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,next_ntv_000_btime,<{rgUnfold($DEVICE,'next_ntv_000_')}@next_ntv_000_title>,<|>,next_ntv_001_btime,<{rgUnfold($DEVICE,'next_ntv_001_')}@next_ntv_001_title>,<|>,next_ntv_002_btime,<{rgUnfold($DEVICE,'next_ntv_002_')}@next_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,next_N24Doku_000_btime,<{rgUnfold($DEVICE,'next_N24Doku_000_')}@next_N24Doku_000_title>,<|>,next_N24Doku_001_btime,<{rgUnfold($DEVICE,'next_N24Doku_001_')}@next_N24Doku_001_title>,<|>,next_N24Doku_002_btime,<{rgUnfold($DEVICE,'next_N24Doku_002_')}@next_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,next_History_000_btime,<{rgUnfold($DEVICE,'next_History_000_')}@next_History_000_title>,<|>,next_History_001_btime,<{rgUnfold($DEVICE,'next_History_001_')}@next_History_001_title>,<|>,next_History_002_btime,<{rgUnfold($DEVICE,'next_History_002_')}@next_History_002_title>\<br />
dmy_TV:<%tv/planet>,next_PLANET_000_btime,<{rgUnfold($DEVICE,'next_PLANET_000_')}@next_PLANET_000_title>,<|>,next_PLANET_001_btime,<{rgUnfold($DEVICE,'next_PLANET_001_')}@next_PLANET_001_title>,<|>,next_PLANET_002_btime,<{rgUnfold($DEVICE,'next_PLANET_002_')}@next_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,next_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_000_')}@next_KabelEinsDoku_000_title>,<|>,next_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_001_')}@next_KabelEinsDoku_001_title>,<|>,next_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'next_KabelEinsDoku_002_')}@next_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,next_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_000_')}@next_AnimalPlanet_000_title>,<|>,next_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_001_')}@next_AnimalPlanet_001_title>,<|>,next_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'next_AnimalPlanet_002_')}@next_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,next_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_000_')}@next_NatGeoHD_000_title>,<|>,next_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_001_')}@next_NatGeoHD_001_title>,<|>,next_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'next_NatGeoHD_002_')}@next_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,next_TLC_000_btime,<{rgUnfold($DEVICE,'next_TLC_000_')}@next_TLC_000_title>,<|>,next_TLC_001_btime,<{rgUnfold($DEVICE,'next_TLC_001_')}@next_TLC_001_title>,<|>,next_TLC_002_btime,<{rgUnfold($DEVICE,'next_TLC_002_')}@next_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,next_AandE_000_btime,<{rgUnfold($DEVICE,'next_AandE_000_')}@next_AandE_000_title>,<|>,next_AandE_001_btime,<{rgUnfold($DEVICE,'next_AandE_001_')}@next_AandE_001_title>,<|>,next_AandE_002_btime,<{rgUnfold($DEVICE,'next_AandE_002_')}@next_AandE_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
dmy_TV:<%tv/kabel1classic>,prime_KabelEinsClassic_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_000_')}@prime_KabelEinsClassic_000_title>,<|>,prime_KabelEinsClassic_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_001_')}@prime_KabelEinsClassic_001_title>,<|>,prime_KabelEinsClassic_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsClassic_002_')}@prime_KabelEinsClassic_002_title>\<br />
dmy_TV:<%tv/13thstreet>,prime_13thStreet_000_btime,<{rgUnfold($DEVICE,'prime_13thStreet_000_')}@prime_13thStreet_000_title>,<|>,prime_13thStreet_001_btime,<{rgUnfold($DEVICE,'prime_13thStreet_001_')}@prime_13thStreet_001_title>,<|>,prime_13thStreet_002_btime,<{rgUnfold($DEVICE,'prime_13thStreet_002_')}@prime_13thStreet_002_title>\<br />
dmy_TV:<%tv/silverline>,prime_Silverline_000_btime,<{rgUnfold($DEVICE,'prime_Silverline_000_')}@prime_Silverline_000_title>,<|>,prime_Silverline_001_btime,<{rgUnfold($DEVICE,'prime_Silverline_001_')}@prime_Silverline_001_title>,<|>,prime_Silverline_002_btime,<{rgUnfold($DEVICE,'prime_Silverline_002_')}@prime_Silverline_002_title>\<br />
dmy_TV:<%tv/tntfilm>,prime_TNTFilm_000_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_000_')}@prime_TNTFilm_000_title>,<|>,prime_TNTFilm_001_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_001_')}@prime_TNTFilm_001_title>,<|>,prime_TNTFilm_002_btime,<{rgUnfold($DEVICE,'prime_TNTFilm_002_')}@prime_TNTFilm_002_title>\<br />
dmy_TV:<%tv/axn>,prime_AXN_000_btime,<{rgUnfold($DEVICE,'prime_AXN_000_')}@prime_AXN_000_title>,<|>,prime_AXN_001_btime,<{rgUnfold($DEVICE,'prime_AXN_001_')}@prime_AXN_001_title>,<|>,prime_AXN_002_btime,<{rgUnfold($DEVICE,'prime_AXN_002_')}@prime_AXN_002_title>\<br />
dmy_TV:<%tv/sonytv>,prime_SonyEntertainmentTV_000_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_000_')}@prime_SonyEntertainmentTV_000_title>,<|>,prime_SonyEntertainmentTV_001_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_001_')}@prime_SonyEntertainmentTV_001_title>,<|>,prime_SonyEntertainmentTV_002_btime,<{rgUnfold($DEVICE,'prime_SonyEntertainmentTV_002_')}@prime_SonyEntertainmentTV_002_title>\<br />
dmy_TV:<%tv/kinowelt>,prime_Kinowelt_000_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_000_')}@prime_Kinowelt_000_title>,<|>,prime_Kinowelt_001_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_001_')}@prime_Kinowelt_001_title>,<|>,prime_Kinowelt_002_btime,<{rgUnfold($DEVICE,'prime_Kinowelt_002_')}@prime_Kinowelt_002_title>\<br />
dmy_TV:<%tv/pro7maxx>,prime_ProSiebenMaxx_000_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_000_')}@prime_ProSiebenMaxx_000_title>,<|>,prime_ProSiebenMaxx_001_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_001_')}@prime_ProSiebenMaxx_001_title>,<|>,prime_ProSiebenMaxx_002_btime,<{rgUnfold($DEVICE,'prime_ProSiebenMaxx_002_')}@prime_ProSiebenMaxx_002_title>\<br />
dmy_TV:<%tv/sixx>,prime_Sixx_000_btime,<{rgUnfold($DEVICE,'prime_Sixx_000_')}@prime_Sixx_000_title>,<|>,prime_Sixx_001_btime,<{rgUnfold($DEVICE,'prime_Sixx_001_')}@prime_Sixx_001_title>,<|>,prime_Sixx_002_btime,<{rgUnfold($DEVICE,'prime_Sixx_002_')}@prime_Sixx_002_title>\<br />
dmy_TV:<%tv/tntserie>,prime_TNTSerie_000_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_000_')}@prime_TNTSerie_000_title>,<|>,prime_TNTSerie_001_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_001_')}@prime_TNTSerie_001_title>,<|>,prime_TNTSerie_002_btime,<{rgUnfold($DEVICE,'prime_TNTSerie_002_')}@prime_TNTSerie_002_title>\<br />
dmy_TV:<%tv/syfy>,prime_SciFi_000_btime,<{rgUnfold($DEVICE,'prime_SciFi_000_')}@prime_SciFi_000_title>,<|>,prime_SciFi_001_btime,<{rgUnfold($DEVICE,'prime_SciFi_001_')}@prime_SciFi_001_title>,<|>,prime_SciFi_002_btime,<{rgUnfold($DEVICE,'prime_SciFi_002_')}@prime_SciFi_002_title>\<br />
dmy_TV:<%tv/ntv>,prime_ntv_000_btime,<{rgUnfold($DEVICE,'prime_ntv_000_')}@prime_ntv_000_title>,<|>,prime_ntv_001_btime,<{rgUnfold($DEVICE,'prime_ntv_001_')}@prime_ntv_001_title>,<|>,prime_ntv_002_btime,<{rgUnfold($DEVICE,'prime_ntv_002_')}@prime_ntv_002_title>\<br />
dmy_TV:<%tv/n24>,prime_N24Doku_000_btime,<{rgUnfold($DEVICE,'prime_N24Doku_000_')}@prime_N24Doku_000_title>,<|>,prime_N24Doku_001_btime,<{rgUnfold($DEVICE,'prime_N24Doku_001_')}@prime_N24Doku_001_title>,<|>,prime_N24Doku_002_btime,<{rgUnfold($DEVICE,'prime_N24Doku_002_')}@prime_N24Doku_002_title>\<br />
dmy_TV:<%tv/history>,prime_History_000_btime,<{rgUnfold($DEVICE,'prime_History_000_')}@prime_History_000_title>,<|>,prime_History_001_btime,<{rgUnfold($DEVICE,'prime_History_001_')}@prime_History_001_title>,<|>,prime_History_002_btime,<{rgUnfold($DEVICE,'prime_History_002_')}@prime_History_002_title>\<br />
dmy_TV:<%tv/planet>,prime_PLANET_000_btime,<{rgUnfold($DEVICE,'prime_PLANET_000_')}@prime_PLANET_000_title>,<|>,prime_PLANET_001_btime,<{rgUnfold($DEVICE,'prime_PLANET_001_')}@prime_PLANET_001_title>,<|>,prime_PLANET_002_btime,<{rgUnfold($DEVICE,'prime_PLANET_002_')}@prime_PLANET_002_title>\<br />
dmy_TV:<%tv/kabel1doku>,prime_KabelEinsDoku_000_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_000_')}@prime_KabelEinsDoku_000_title>,<|>,prime_KabelEinsDoku_001_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_001_')}@prime_KabelEinsDoku_001_title>,<|>,prime_KabelEinsDoku_002_btime,<{rgUnfold($DEVICE,'prime_KabelEinsDoku_002_')}@prime_KabelEinsDoku_002_title>\<br />
dmy_TV:<%tv/animalplanet>,prime_AnimalPlanet_000_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_000_')}@prime_AnimalPlanet_000_title>,<|>,prime_AnimalPlanet_001_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_001_')}@prime_AnimalPlanet_001_title>,<|>,prime_AnimalPlanet_002_btime,<{rgUnfold($DEVICE,'prime_AnimalPlanet_002_')}@prime_AnimalPlanet_002_title>\<br />
dmy_TV:<%tv/natgeo>,prime_NatGeoHD_000_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_000_')}@prime_NatGeoHD_000_title>,<|>,prime_NatGeoHD_001_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_001_')}@prime_NatGeoHD_001_title>,<|>,prime_NatGeoHD_002_btime,<{rgUnfold($DEVICE,'prime_NatGeoHD_002_')}@prime_NatGeoHD_002_title>\<br />
dmy_TV:<%tv/tlc>,prime_TLC_000_btime,<{rgUnfold($DEVICE,'prime_TLC_000_')}@prime_TLC_000_title>,<|>,prime_TLC_001_btime,<{rgUnfold($DEVICE,'prime_TLC_001_')}@prime_TLC_001_title>,<|>,prime_TLC_002_btime,<{rgUnfold($DEVICE,'prime_TLC_002_')}@prime_TLC_002_title>\<br />
dmy_TV:<%tv/ae>,prime_AandE_000_btime,<{rgUnfold($DEVICE,'prime_AandE_000_')}@prime_AandE_000_title>,<|>,prime_AandE_001_btime,<{rgUnfold($DEVICE,'prime_AandE_001_')}@prime_AandE_001_title>,<|>,prime_AandE_002_btime,<{rgUnfold($DEVICE,'prime_AandE_002_')}@prime_AandE_002_title><br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32228TV Programm2019-12-30T14:06:54Z<p>Mumpitzstuff: merge feature used | split of next and prime updates</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
3 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten) und eins für das Parsen der Primtime Sendungen (einmal am Tag). Die 3 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:10:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV downloadMerge")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV next"<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV prime"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=32227TV Programm2019-12-30T14:02:05Z<p>Mumpitzstuff: script runtime optimizations and bugfixes | merge feature added</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings;<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.|KabelEinsClassic\.|KabelEinsDoku\.|ntv\.|ProSiebenMaxx\.|Sixx\.|TLC\.|N24Doku\.|SonyEntertainmentTV\.|AandE\.|TNTSerie\.|AnimalPlanet\.|History\.|Kinowelt\.|NatGeoHD\.|PLANET\.|Silverline\.|13thStreet\.|AXN\.|SciFi\.|TNTFilm\.)/;<br />
my $timeAdjust = 0;<br />
<br />
#my $channelFilter = qr/^(?:ARD|ZDF$|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1|13TH STREET|ANIMAL PLANET|Silverline|TNT Film|N24|kabel eins classics|ProSieben MAXX|Syfy|AE|TLC|AXN|sixx|Kinowelt TV|History)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub time2xmltv($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d%02d%02d%02d%02d%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($;$)<br />
{<br />
my ($device, $mode) = @_;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $k = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
my $old = time2xmltv(time() - $timeAdjust);<br />
<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = substr($_->{'stop'}{'value'}, 0, 14);<br />
<br />
# filter old stuff<br />
if (($stop cmp $old) >= 0)<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3 && (!defined($mode) || 'next' eq $mode))<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$i++;<br />
$k++;<br />
}<br />
<br />
if ($n < 3 && (!defined($mode) || 'prime' eq $mode))<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start);<br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
#$stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
#$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
#$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
#$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$n++;<br />
$k++;<br />
}<br />
}<br />
<br />
if ($k >= 10)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$k = 0;<br />
$sendTelnet = '';<br />
}<br />
}<br />
}<br />
}<br />
<br />
if ('' ne $sendTelnet)<br />
{<br />
#system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
}<br />
}<br />
}<br />
<br />
sub tvMerge($$)<br />
{<br />
my ($dstName, $srcName) = @_;<br />
my $fh;<br />
my $dst;<br />
my $src;<br />
my $start = '';<br />
my $channels1 = '';<br />
my $channels2 = '';<br />
my $programms1 = '';<br />
my $programms2 = '';<br />
my $end = '';<br />
<br />
open($fh, '<', $dstName) or die "Can't open file $!";<br />
read($fh, $dst, -s $fh);<br />
close($fh);<br />
<br />
open($fh, '<', $srcName) or die "Can't open file $!";<br />
read($fh, $src, -s $fh);<br />
close($fh);<br />
<br />
if ($dst =~ /^(.*?)<channel/s)<br />
{<br />
$start = $1;<br />
}<br />
<br />
if ($dst =~ /<\/programme>(?!.*<\/programme>)(.*)$/s)<br />
{<br />
$end = $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels1 .= $1;<br />
}<br />
<br />
while ($dst =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms1 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<channel\s.*?<\/channel>)/sg)<br />
{<br />
$channels2 .= $1;<br />
}<br />
<br />
while ($src =~ /(\s*<programme\s.*?<\/programme>)/sg)<br />
{<br />
$programms2 .= $1;<br />
}<br />
<br />
open($fh, '>', $dstName) or die "Can't open file $!";<br />
print $fh $start.$channels1.$channels2.$programms1.$programms2.$end;<br />
close($fh);<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvDownloadMerge()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Common.xz -O /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Common.xz 2>&1);<br />
#print $output;<br />
tvMerge('/opt/fhem/tv/rytecDE_Basic', '/opt/fhem/tv/rytecDE_Common');<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
# mode 'parse': update next and prime<br />
# mode 'next' : update next only<br />
# mode 'prime': update prime only<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('downloadMerge' eq $m)<br />
{<br />
tvDownloadMerge();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
else<br />
{<br />
tvParse($d, $m);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=29942TV Programm2019-03-18T16:16:16Z<p>Mumpitzstuff: /* Perl Script einrichten: */Einzelne Telnet Aufrufe zu einem zusammengefasst.</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=29900TV Programm2019-03-14T10:38:21Z<p>Mumpitzstuff: /* Vorbereitungen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
./install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=29899TV Programm2019-03-14T10:37:43Z<p>Mumpitzstuff: /* WebGrab++ in Crontab einbinden: */Tippfehler korrigiert</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
30 0 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=28132TV Programm2018-10-20T21:24:54Z<p>Mumpitzstuff: /* Perl Script einrichten: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
# other server<br />
# http://www.xmltvepg.nl/rytecDE_Basic.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Basic.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_Common.xz<br />
# http://www.xmltvepg.nl/rytecDE_Common.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_Common.xz<br />
# http://www.vuplus-community.net/rytec/rytecDE_SportMovies.xz<br />
# http://www.xmltvepg.nl/rytecDE_SportMovies.xz<br />
# http://91.121.106.172/~rytecepg/epg_data/rytecDE_SportMovies.xz<br />
my $output = qx(wget http://www.vuplus-community.net/rytec/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=27937TV Programm2018-09-25T21:03:21Z<p>Mumpitzstuff: /* readingsGroups anlegen: */Rereadicons hinzugefügt</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren und die entsprechenden Rechte für diese Dateien vergeben.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
Falls die Icons nicht dargestellt werden sollten, könnte eventuell der folgende Befehl helfen:<br />
<pre><br />
set WEB rereadicons<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=27936TV Programm2018-09-25T21:00:04Z<p>Mumpitzstuff: /* Perl Script einrichten: */Filterung erweitert</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$reading =~ s/de$//;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=27790TV Programm2018-09-04T21:50:35Z<p>Mumpitzstuff: Umstellung auf externes Script und Fix für Memory Leak von XML::Bare</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Den folgenden Code in die Datei tv.pl kopieren und in den Ordner /opt/fhem/tv/tv.pl kopieren: <br />
<br />
<pre><br />
#!/usr/bin/perl<br />
use strict;<br />
use warnings; <br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare 0.53 qw(forcearray);<br />
use Data::Dumper;<br />
<br />
my $channelFilter = qr/^(?:ARD|ZDF|Sat1|RTL|RTL2|Pro7|DMax|Vox|Kabel)/;<br />
my $timeAdjust = 0;<br />
#my $channelFilter = qr/^(?:ARD|ZDF|SAT\.1|RTL$|RTL II|PRO 7|DMAX|VOX|KABEL 1)/;<br />
#my $timeAdjust = 86400;<br />
<br />
<br />
my $redt = qr/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/;<br />
<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ $redt)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub FmtDateTime($)<br />
{<br />
my @t = localtime(shift);<br />
return sprintf("%04d-%02d-%02d %02d:%02d:%02d", $t[5]+1900, $t[4]+1, $t[3], $t[2], $t[1], $t[0]);<br />
}<br />
<br />
sub filterText($)<br />
{<br />
my $text = shift;<br />
<br />
$text =~ s/["`;'\r]//g;<br />
$text =~ s/[\n]/<br>/g;<br />
<br />
return $text;<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
my $device = shift;<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->parse();<br />
my $lastChannel = '';<br />
my $reading = '';<br />
my $i = 0;<br />
my $n = 0;<br />
my $primeTime = substr(FmtDateTime(time() - $timeAdjust), 0, 11).'20:14:00';<br />
my $sendTelnet = '';<br />
<br />
if (!$@)<br />
{<br />
foreach (@{forcearray($xml->{'tv'}{'programme'})})<br />
{<br />
if ($_->{'channel'}{'value'} =~ $channelFilter)<br />
{<br />
my $stop = str2time(xmltv2epoch($_->{'stop'}{'value'}));<br />
<br />
# filter old stuff<br />
if (($stop + $timeAdjust) >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'}{'value'})<br />
{<br />
$lastChannel = $_->{'channel'}{'value'};<br />
$reading = $_->{'channel'}{'value'};<br />
$reading =~ s/[\.\s]//g;<br />
$i = 0;<br />
$n = 0;<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
my $fi = sprintf("%03d", $i);<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'next_'.$reading.'_'.$fi.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$i++;<br />
}<br />
<br />
if ($n < 3)<br />
{<br />
my $start = str2time(xmltv2epoch($_->{'start'}{'value'}));<br />
my $fmtStart = FmtDateTime($start); <br />
my $bdate = substr($fmtStart, 0, 10);<br />
my $btime = substr($fmtStart, 11, 8);<br />
<br />
if ($bdate.' '.$btime gt $primeTime)<br />
{<br />
my $fn = sprintf("%03d", $n);<br />
my $readingName;<br />
my $readingValue;<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_bdate';<br />
$readingValue = substr(FmtDateTime($start), 0, 10);<br />
$sendTelnet = "setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_btime';<br />
$readingValue = substr(FmtDateTime($start), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_edate';<br />
$readingValue = substr(FmtDateTime($stop), 0, 10);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_etime';<br />
$readingValue = substr(FmtDateTime($stop), 11, 8);<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_title';<br />
$readingValue = filterText(@{forcearray($_->{'title'})}[0]->{'value'});<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
#print $readingValue."\n";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_stitle';<br />
if (exists($_->{'sub-title'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'sub-title'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
$readingName = 'prime_'.$reading.'_'.$fn.'_desc';<br />
if (exists($_->{'desc'}{'value'}))<br />
{<br />
$readingValue = filterText($_->{'desc'}{'value'});<br />
}<br />
else<br />
{<br />
$readingValue = 'na';<br />
}<br />
$sendTelnet .= ";setreading $device $readingName $readingValue";<br />
<br />
my $result = `perl /opt/fhem/fhem.pl 7072 "$sendTelnet"`;<br />
<br />
$n++;<br />
}<br />
}<br />
}<br />
}<br />
}<br />
<br />
system('/opt/fhem/fhem.pl 7072 "'.$sendTelnet.'"');<br />
}<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
<br />
my $d = shift || die "Need a device!\n";<br />
my $m = shift || die "Need a mode!\n";<br />
<br />
if ('download' eq $m)<br />
{<br />
tvDownload();<br />
}<br />
elsif ('parse' eq $m)<br />
{<br />
tvParse($d);<br />
}<br />
<br />
exit;<br />
<br />
</pre><br />
<br />
<br />
Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=27789TV Programm2018-09-04T21:44:43Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/<br>/\n/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== Perl Script einrichten: ====<br />
<br />
Das angehängte Perl Script tv.pl muss in den Ordner /opt/fhem/tv/tv.pl kopiert werden. Sowohl das Verzeichnis, als auch das Script selbst muss mit den entsprechenden Rechten versehen werden:<br />
<br />
<pre><br />
sudo chmod 744 /opt/fhem/tv/tv.pl<br />
sudo chown fhem:dialout /opt/fhem/tv/tv.pl<br />
</pre><br />
<br />
Bei Bedarf kann nun innerhalb des Scriptes die Senderliste angepasst werden.<br />
<br />
Jetzt muss der Download einmalig von der Konsole (nicht mit der FHEM Befehlszeile verwechseln!) gestartet werden, da man ansonsten mehrere Tage warten muss, bis alles funktioniert:<br />
<br />
<pre><br />
perl /opt/fhem/tv/tv.pl dmy_TV download<br />
</pre><br />
<br />
Danach muss die entstandene Datei /opt/fhem/tv/rytecDE_Basic mit den richtigen Rechten ausgestattet werden:<br />
<br />
<pre><br />
sudo chown fhem:dialout /opt/fhem/tv/rytecDE_Basic<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
2 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der aktuellen Daten ins Dummy Device (alle 15 Minuten). Die 2 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {fhem("perl /opt/fhem/tv/tv.pl dmy_TV download")}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 "perl /opt/fhem/tv/tv.pl dmy_TV parse"<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind und man nicht erst 15 Minuten warten muss.<br />
<br />
<pre><br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26945TV Programm2018-06-05T20:20:28Z<p>Mumpitzstuff: /* 99_myUtils.pm erweitern: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8(@{forcearray($_->{'title'})}[0]->{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8(@{forcearray($_->{'title'})}[0]->{'content'}));<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26944TV Programm2018-06-05T20:15:46Z<p>Mumpitzstuff: /* WebGrab++ in Crontab einbinden: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene Datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26943TV Programm2018-06-05T20:14:54Z<p>Mumpitzstuff: /* Vorbereitungen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<title lang="de">'title'</title><br />
<sub-title lang="de">{Episode: 'episode' }'subtitle'</sub-title><br />
<desc lang="de">'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26942TV Programm2018-06-05T20:11:30Z<p>Mumpitzstuff: /* readingsGroups anlegen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
</pre><br />
Jetzt die Icons in das Verzeichnis kopieren.<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26941TV Programm2018-06-05T20:10:36Z<p>Mumpitzstuff: /* readingsGroups anlegen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default/tv. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo mkdir /opt/fhem/www/images/default/tv<br />
sudo chown fhem:dialout /opt/fhem/www/images/default/tv<br />
<Icons in das Verzeichnis kopieren><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26940TV Programm2018-06-05T20:08:46Z<p>Mumpitzstuff: /* readingsGroups anlegen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Devices, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26939TV Programm2018-06-05T20:08:15Z<p>Mumpitzstuff: /* readingsGroups anlegen: */</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann. Die readingsGroups müssen, wie zuvor die at Kommandos, als Raw Import importiert werden!:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26506TV Programm2018-04-28T14:43:33Z<p>Mumpitzstuff: Beispielbild hinzugefügt</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Datei:screenshot-2018-04-28-16-28-03.png|200px|thumb|left|Beispiel]]<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=Datei:Screenshot-2018-04-28-16-28-03.png&diff=26505Datei:Screenshot-2018-04-28-16-28-03.png2018-04-28T14:40:20Z<p>Mumpitzstuff: Mumpitzstuff lud eine neue Version von Datei:Screenshot-2018-04-28-16-28-03.png hoch</p>
<hr />
<div></div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=Datei:Screenshot-2018-04-28-16-28-03.png&diff=26504Datei:Screenshot-2018-04-28-16-28-03.png2018-04-28T14:36:00Z<p>Mumpitzstuff: </p>
<hr />
<div></div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26503TV Programm2018-04-28T14:20:08Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das Parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Es gibt Webseiten, die das TV Programm als rss zur Verfügung stellen. Darüber hinaus gibt es Services die online rss zu json konvertieren. Kombiniert man nun beides, dann kann man mit httpmod das Ganze mit wenigen Zeilen einlesen.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
Die Icons der readingsGroups müsst ihr natürlich anpassen! Ladet euch einfach irgendwo die Senderlogos runter und speichert diese unter /opt/fhem/www/images/default. Vergesst bitte nicht die entsprechenden Rechte zu setzen:<br />
<pre><br />
sudo chown fhem:dialout *.png<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
<br />
== Variante 4 (RSS Daten einlesen): ==<br />
<br />
Zuerst muss ein Service gefunden werden, der RSS zu JSON Daten konvertieren kann. Anbieten würde sich hier z.B. www.rss2json.com. Hier kann man sich kostenlos anmelden und einen API Key generieren.<br />
<br />
=== httpmod Device anlegen: ===<br />
<br />
Vergesst bitte nicht im unten stehenden Code euren API Key einzutragen!<br />
<br />
<pre><br />
defmod TV_JETZT_HAUPTSENDER HTTPMOD https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100 300<br />
attr TV_JETZT_HAUPTSENDER userattr get01Name get01URL getEncode readingEncode<br />
attr TV_JETZT_HAUPTSENDER disable 1<br />
attr TV_JETZT_HAUPTSENDER extractAllJSON 1<br />
attr TV_JETZT_HAUPTSENDER get01Name update<br />
attr TV_JETZT_HAUPTSENDER get01URL https://api.rss2json.com/v1/api.json?rss_url=http%3A%2F%2Fwww.texxas.de%2Ftv%2FhauptsenderJetzt.xml&api_key=<dein API Key>&order_by=pubDate&order_dir=asc&count=100<br />
attr TV_JETZT_HAUPTSENDER getEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER readingEncode UTF-8<br />
attr TV_JETZT_HAUPTSENDER timeout 10<br />
</pre><br />
<br />
www.texxas.de bietet weitere rss an, die man ebenfalls einbinden könnte. Wie man daraus dann eine readingsGroup erstellt, muss ich leider schuldig bleiben, ich habe diesen Ansatz nicht weiter verfolgt. Funktionieren würde er aber und das Download Volumen ist bei jeder Abfrage nur einige wenige kb.<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26502TV Programm2018-04-28T14:08:45Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
== Variante 3 (EPG Daten selbst erstellen): ==<br />
<br />
=== Vorbereitungen: ===<br />
<br />
Mono und screen installieren:<br />
<pre><br />
cd ~<br />
sudo apt-get install mono-runtime libmono-system-data4.0-cil libmono-system-web4.0-cil screen<br />
</pre><br />
<br />
WebGrab++ downloaden:<br />
<pre><br />
wget http://www.webgrabplus.com/sites/default/files/download/SW/V2.1.0/WebGrabPlus_V2.1_install.tar.gz<br />
</pre><br />
<br />
WebGrab++ entpacken:<br />
<pre><br />
tar -zxvf WebGrabPlus_V2.0.tar.gz<br />
</pre><br />
<br />
WebGrab++ installieren:<br />
<pre><br />
cd .wg++<br />
install.sh<br />
</pre><br />
<br />
Nun müssen zuerst einmal die Konfigurationsdateien angepasst werden:<br />
<pre><br />
sudo nano WebGrab++.config.xml<br />
</pre><br />
<br />
Diese Settings können z.B. verwendet werden:<br />
<pre><br />
<filename>guide.xml</filename><br />
<mode>n</mode><br />
<postprocess grab="y" run="y">rex</postprocess><br />
<user-agent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:29.0) Gecko/20100101 Firefox/29.0</user-agent><br />
<logging>on</logging><br />
<retry time-out="15">5</retry><br />
<timespan>6</timespan><br />
<update>i</update><br />
</pre><br />
<br />
Dann müssen noch die Channels eingetragen werden. Diese Channels können von der Webseite von WebGrab++ kopiert werden. Hier einmal Beispielhaft eine Zeile:<br />
<pre><br />
<channel update="i" site="tvmovie.de" site_id="ard" xmltv_id="ARD">ARD</channel><br />
</pre><br />
<br />
Jetzt die Datei speichern und schliessen.<br />
<br />
Jetzt muss noch die Datei für die Nachbearbeitung angepasst werden:<br />
<pre><br />
cd rex<br />
sudo nano rex.config.xml<br />
</pre><br />
<br />
Hier können diese Settings verwendet werden:<br />
<pre><br />
<sub-title>{Episode: 'episode' }'subtitle'</sub-title><br />
<desc>'description'{\nProduced in: 'productiondate'. }{\nCategory: 'category(, )'. }{\nActors: 'actor(, )'}{\nDirector: 'director(, )'}{\nPresenter: 'presenter(, )'}</desc><br />
<credits></credits><br />
<episode-num></episode-num><br />
<date></date><br />
<category></category><br />
<review>{Ratings: 'rating(, )'.}</review><br />
<rating></rating><br />
</pre><br />
<br />
Datei wieder abspeichern und schliessen.<br />
<br />
=== EPG Daten grabben und aufbereiten: ===<br />
<br />
Bitte vorsichtig sein, das grabben und aufbereiten der Daten kann extrem lange dauern. Fangt am besten mit einem oder 2 Sendern an, um einen ersten Eindruck zu bekommen.<br />
<br />
<pre><br />
cd ~/.wg++<br />
./run.sh<br />
</pre><br />
<br />
=== WebGrab++ in Crontab einbinden: ===<br />
<br />
Einfach nur das Script aufzurufen hat bei mir nicht funktioniert. Ich habe deshalb screen verwenden müssen.<br />
<br />
<pre><br />
0 30 * * 1,4 /usr/bin/screen -dmS webgrab /home/<user z.B. pi>/.wg++/run.sh<br />
</pre><br />
<br />
Damit wird der Vorgang alle 3-4 Tage gestartet und generiert das Programm der nächsten Woche. Die entstandene datei könnte dann über Variante 2 weiter verarbeitet werden.<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26501TV Programm2018-04-28T13:36:36Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann:<br />
<br />
Aktuelle Sendungen:<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
Primetime Sendungen:<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26500TV Programm2018-04-28T13:35:11Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
Damit sind die Vorbereitungen abgeschlossen!<br />
<br />
=== Dummy Device mit Daten füllen: ===<br />
<br />
Dieser Vorgang muss nur einmalig gemacht werden, damit sofort Ergebnisse sichtbar sind, da die at Devices (welche danach die Arbeit übernehmen) nur sehr selten gestartet werden und man sonst bis zu 3 Tage warten müsste.<br />
<br />
Dazu führt man die at Devices nacheinander einfach aus:<br />
<br />
<pre><br />
set at_TV_DOWNLOAD execNow<br />
set at_TV_PARSE execNow<br />
set at_TV_UPDATE_PRIME execNow<br />
set at_TV_UPDATE execNow<br />
</pre><br />
<br />
=== readingsGroups anlegen: ===<br />
<br />
Zuletzt müssen wir uns noch 2 readingsGroups anlegen, damit das aktuelle TV Programm in FHEM auch dargestellt werden kann:<br />
<br />
<pre><br />
defmod rg_TV readingsGroup <Sender>,<ab>,<Aktuelle Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,next_ARD_000_btime,<{rgUnfold($DEVICE,'next_ARD_000_')}@next_ARD_000_title>,<|>,next_ARD_001_btime,<{rgUnfold($DEVICE,'next_ARD_001_')}@next_ARD_001_title>,<|>,next_ARD_002_btime,<{rgUnfold($DEVICE,'next_ARD_002_')}@next_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,next_ZDF_000_btime,<{rgUnfold($DEVICE,'next_ZDF_000_')}@next_ZDF_000_title>,<|>,next_ZDF_001_btime,<{rgUnfold($DEVICE,'next_ZDF_001_')}@next_ZDF_001_title>,<|>,next_ZDF_002_btime,<{rgUnfold($DEVICE,'next_ZDF_002_')}@next_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,next_Sat1_000_btime,<{rgUnfold($DEVICE,'next_Sat1_000_')}@next_Sat1_000_title>,<|>,next_Sat1_001_btime,<{rgUnfold($DEVICE,'next_Sat1_001_')}@next_Sat1_001_title>,<|>,next_Sat1_002_btime,<{rgUnfold($DEVICE,'next_Sat1_002_')}@next_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,next_RTL_000_btime,<{rgUnfold($DEVICE,'next_RTL_000_')}@next_RTL_000_title>,<|>,next_RTL_001_btime,<{rgUnfold($DEVICE,'next_RTL_001_')}@next_RTL_001_title>,<|>,next_RTL_002_btime,<{rgUnfold($DEVICE,'next_RTL_002_')}@next_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,next_RTL2_000_btime,<{rgUnfold($DEVICE,'next_RTL2_000_')}@next_RTL2_000_title>,<|>,next_RTL2_001_btime,<{rgUnfold($DEVICE,'next_RTL2_001_')}@next_RTL2_001_title>,<|>,next_RTL2_002_btime,<{rgUnfold($DEVICE,'next_RTL2_002_')}@next_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,next_Pro7_000_btime,<{rgUnfold($DEVICE,'next_Pro7_000_')}@next_Pro7_000_title>,<|>,next_Pro7_001_btime,<{rgUnfold($DEVICE,'next_Pro7_001_')}@next_Pro7_001_title>,<|>,next_Pro7_002_btime,<{rgUnfold($DEVICE,'next_Pro7_002_')}@next_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,next_DMax_000_btime,<{rgUnfold($DEVICE,'next_DMax_000_')}@next_DMax_000_title>,<|>,next_DMax_001_btime,<{rgUnfold($DEVICE,'next_DMax_001_')}@next_DMax_001_title>,<|>,next_DMax_002_btime,<{rgUnfold($DEVICE,'next_DMax_002_')}@next_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,next_Vox_000_btime,<{rgUnfold($DEVICE,'next_Vox_000_')}@next_Vox_000_title>,<|>,next_Vox_001_btime,<{rgUnfold($DEVICE,'next_Vox_001_')}@next_Vox_001_title>,<|>,next_Vox_002_btime,<{rgUnfold($DEVICE,'next_Vox_002_')}@next_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,next_Kabel_000_btime,<{rgUnfold($DEVICE,'next_Kabel_000_')}@next_Kabel_000_title>,<|>,next_Kabel_001_btime,<{rgUnfold($DEVICE,'next_Kabel_001_')}@next_Kabel_001_title>,<|>,next_Kabel_002_btime,<{rgUnfold($DEVICE,'next_Kabel_002_')}@next_Kabel_002_title><br />
attr rg_TV alias Aktuelles TV-Programm<br />
attr rg_TV cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV group TV Programm<br />
attr rg_TV nonames 1<br />
attr rg_TV style style="font-size:16px;;"<br />
</pre><br />
<br />
<pre><br />
defmod rg_TV_PRIME readingsGroup <Sender>,<ab>,<Sendung>,<|>,<ab>,<Sendung>,<|>,<ab>,<Sendung>\<br />
dmy_TV:<%tv/ard>,prime_ARD_000_btime,<{rgUnfold($DEVICE,'prime_ARD_000_')}@prime_ARD_000_title>,<|>,prime_ARD_001_btime,<{rgUnfold($DEVICE,'prime_ARD_001_')}@prime_ARD_001_title>,<|>,prime_ARD_002_btime,<{rgUnfold($DEVICE,'prime_ARD_002_')}@prime_ARD_002_title>\<br />
dmy_TV:<%tv/zdf>,prime_ZDF_000_btime,<{rgUnfold($DEVICE,'prime_ZDF_000_')}@prime_ZDF_000_title>,<|>,prime_ZDF_001_btime,<{rgUnfold($DEVICE,'prime_ZDF_001_')}@prime_ZDF_001_title>,<|>,prime_ZDF_002_btime,<{rgUnfold($DEVICE,'prime_ZDF_002_')}@prime_ZDF_002_title>\<br />
dmy_TV:<%tv/sat1>,prime_Sat1_000_btime,<{rgUnfold($DEVICE,'prime_Sat1_000_')}@prime_Sat1_000_title>,<|>,prime_Sat1_001_btime,<{rgUnfold($DEVICE,'prime_Sat1_001_')}@prime_Sat1_001_title>,<|>,prime_Sat1_002_btime,<{rgUnfold($DEVICE,'prime_Sat1_002_')}@prime_Sat1_002_title>\<br />
dmy_TV:<%tv/rtl>,prime_RTL_000_btime,<{rgUnfold($DEVICE,'prime_RTL_000_')}@prime_RTL_000_title>,<|>,prime_RTL_001_btime,<{rgUnfold($DEVICE,'prime_RTL_001_')}@prime_RTL_001_title>,<|>,prime_RTL_002_btime,<{rgUnfold($DEVICE,'prime_RTL_002_')}@prime_RTL_002_title>\<br />
dmy_TV:<%tv/rtl2>,prime_RTL2_000_btime,<{rgUnfold($DEVICE,'prime_RTL2_000_')}@prime_RTL2_000_title>,<|>,prime_RTL2_001_btime,<{rgUnfold($DEVICE,'prime_RTL2_001_')}@prime_RTL2_001_title>,<|>,prime_RTL2_002_btime,<{rgUnfold($DEVICE,'prime_RTL2_002_')}@prime_RTL2_002_title>\<br />
dmy_TV:<%tv/pro7>,prime_Pro7_000_btime,<{rgUnfold($DEVICE,'prime_Pro7_000_')}@prime_Pro7_000_title>,<|>,prime_Pro7_001_btime,<{rgUnfold($DEVICE,'prime_Pro7_001_')}@prime_Pro7_001_title>,<|>,prime_Pro7_002_btime,<{rgUnfold($DEVICE,'prime_Pro7_002_')}@prime_Pro7_002_title>\<br />
dmy_TV:<%tv/dmax>,prime_DMax_000_btime,<{rgUnfold($DEVICE,'prime_DMax_000_')}@prime_DMax_000_title>,<|>,prime_DMax_001_btime,<{rgUnfold($DEVICE,'prime_DMax_001_')}@prime_DMax_001_title>,<|>,prime_DMax_002_btime,<{rgUnfold($DEVICE,'prime_DMax_002_')}@prime_DMax_002_title>\<br />
dmy_TV:<%tv/vox>,prime_Vox_000_btime,<{rgUnfold($DEVICE,'prime_Vox_000_')}@prime_Vox_000_title>,<|>,prime_Vox_001_btime,<{rgUnfold($DEVICE,'prime_Vox_001_')}@prime_Vox_001_title>,<|>,prime_Vox_002_btime,<{rgUnfold($DEVICE,'prime_Vox_002_')}@prime_Vox_002_title>\<br />
dmy_TV:<%tv/kabel1>,prime_Kabel_000_btime,<{rgUnfold($DEVICE,'prime_Kabel_000_')}@prime_Kabel_000_title>,<|>,prime_Kabel_001_btime,<{rgUnfold($DEVICE,'prime_Kabel_001_')}@prime_Kabel_001_title>,<|>,prime_Kabel_002_btime,<{rgUnfold($DEVICE,'prime_Kabel_002_')}@prime_Kabel_002_title>\<br />
<br />
attr rg_TV_PRIME alias TV-Programm Primetime<br />
attr rg_TV_PRIME cellStyle { \<br />
'r:1,c:1' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:2' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:3' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:5' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:6' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:8' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"',\<br />
'r:1,c:9' => 'style="color:yellow;;text-align:center;;font-weight:bold;;"'\<br />
}<br />
attr rg_TV_PRIME group TV Programm<br />
attr rg_TV_PRIME nonames 1<br />
attr rg_TV_PRIME style style="font-size:16px;;"<br />
</pre><br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26499TV Programm2018-04-28T13:22:24Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<pre><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</pre><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<pre><br />
sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils<br />
</pre><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<pre><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</pre><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
Dieser Code kann einfach in die Zwischenablage kopiert und in die Datei 99_myUtils.pm eingefügt werden.<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
==== Dummy Device anlegen: ====<br />
<br />
Dieses Device dient zur Datenhaltung. Hier werden immer die nächsten 3 Sendungen und die 3 Primetime Sendungen des aktuellen Tages als Readings abgelegt. Zusätzlich dazu wird intern eine kleine Datenbank aufgebaut und aktualisiert, damit die große XML Datei nicht ständig neu eingelsen und geparsed werden muss.<br />
<br />
<pre><br />
define dmy_TV dummy<br />
</pre><br />
<br />
==== at Devices anlegen: ====<br />
<br />
4 "at" Devices müssen angelegt werden. Eins für den Download (alle 3 Tage 1x), eins für das Parsen der Daten ins Dummy Device (jeden Tag 1x), eins um die nächsten Sendungen zu filtern (alle 15min) und noch eins um die Primetime Sendungen zu filtern (jeden Tag 1x). Die 4 "at" sind als raw Definitionen kopiert und können auch als solche wieder angelegt werden. Dazu irgend ein Device öffnen, ganz unten auf "Raw definition" klicken und alles entfernen. Den Code von hier einfügen und ausführen und die Devices sind angelegt. Jedes at Device muss separat angelegt werden!<br />
<br />
<pre><br />
defmod at_TV_DOWNLOAD at *00:15:00 {if ((1 == $wday) || (4 == $wday)) {tvDownload()}}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_PARSE at *00:30:00 {tvParse('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE_PRIME at *00:45:00 {tvUpdatePrimetime('dmy_TV')}<br />
</pre><br />
<br />
<pre><br />
defmod at_TV_UPDATE at +*00:15:00 {tvUpdate('dmy_TV')}<br />
</pre><br />
<br />
<br />
<br />
<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26498TV Programm2018-04-28T13:12:19Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<nowiki><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</nowiki><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<nowiki>sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils</nowiki><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<nowiki><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</nowiki><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
<pre><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</pre><br />
<br />
<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26497TV Programm2018-04-28T13:08:49Z<p>Mumpitzstuff: </p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
== Variante 1 (iframe): ==<br />
<nowiki><br />
define wl_TV weblink iframe <Webseite des TV Programmanbieters z.B. http://www.klack.de/fernsehprogramm/was-laeuft-gerade/0/0/all.html><br />
attr wl_TV htmlattr width="1024" height="768"<br />
</nowiki><br />
<br />
Das Attribut legt die Größe des iframes fest und kann beliebig angepasst werden.<br />
<br />
== Variante 2 (Download der EPG Daten): ==<br />
<br />
Dieser Ansatz ist bereits etwas komplizierter, aber immer noch sehr einfach einzubinden.<br />
<br />
=== Vorbereitungen: ===<br />
==== Fehlende Perl Module installieren: ====<br />
<br />
<nowiki>sudo apt-get install libxml-bare-perl libdatetime-perl wget xz-utils</nowiki><br />
<br />
Die ersten beiden Bibliotheken werden benötigt, um die XML Datei zu parsen. wget wird benötigt um die Datei zu downloaden und xz enthält den Unpacker für die runtergeladene Datei.<br />
<br />
==== Pfad für den Download anlegen und mit den entsprechenden Rechten versehen: ====<br />
<br />
<nowiki><br />
sudo mkdir /opt/fhem/tv<br />
sudo chown fhem:dialout /opt/fhem/tv<br />
</nowiki><br />
<br />
In diesem Verzeichnis soll später die mit wget runtergeladene XML Datei landen.<br />
<br />
==== 99_myUtils.pm erweitern: ====<br />
<br />
<nowiki><br />
sub rgUnfold($$)<br />
{<br />
my ($device, $reading) = @_;<br />
my $title = ReadingsVal($device, $reading.'title', 'na');<br />
my $desc = ReadingsVal($device, $reading.'stitle', 'na')."\n\n".<br />
ReadingsVal($device, $reading.'desc', 'na');<br />
<br />
$title =~ s/(.{1,45}|\S{46,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g;<br />
$desc =~ s/(.{1,65}|\S{66,})(?:\s[^\S\r\n]*|\Z)/$1<br>/g; <br />
$desc =~ s/[\r\'\"]/ /g;<br />
$desc =~ s/[\n]|\\n/<br>/g;<br />
return "<a href=\"#!\" onclick=\"FW_okDialog('".$desc."')\">".$title."</a>";<br />
}<br />
<br />
sub xmltv2epoch($)<br />
{<br />
my $dt = shift;<br />
<br />
if ($dt =~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})(?:\s+([+-]\d{4}))?$/)<br />
{<br />
if (defined($7))<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6.' '.$7;<br />
}<br />
else<br />
{<br />
return $1.'-'.$2.'-'.$3.' '.$4.':'.$5.':'.$6;<br />
}<br />
}<br />
<br />
return '2000-01-01 00:00:00';<br />
}<br />
<br />
sub tvParse($)<br />
{<br />
use utf8;<br />
use Date::Parse;<br />
use Encode qw(encode_utf8 decode_utf8);<br />
use XML::Bare qw(forcearray);<br />
<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my $obj = XML::Bare->new(file => '/opt/fhem/tv/rytecDE_Basic');<br />
my $xml = $obj->simple();<br />
my $start;<br />
my $stop;<br />
my $i = 0;<br />
my $fi = '000';<br />
my $lastChannel = '';<br />
my $reading = '';<br />
<br />
if (!$@)<br />
{<br />
# clear all old internals<br />
delete($hash->{helper}); <br />
<br />
readingsBeginUpdate($hash);<br />
<br />
foreach (@{forcearray($xml->{tv}{programme})})<br />
{<br />
# channel filter<br />
if ($_->{'channel'} =~ /^(?:ARD\.|ZDF\.|Sat1\.|RTL\.|RTL2\.|Pro7\.|DMax\.|Vox\.|Kabel\.)/)<br />
{<br />
$stop = str2time(xmltv2epoch($_->{'stop'}));<br />
<br />
# filter old stuff<br />
if ($stop >= time())<br />
{<br />
if ($lastChannel ne $_->{'channel'})<br />
{<br />
$lastChannel = $_->{'channel'};<br />
$reading = $_->{'channel'};<br />
$reading =~ s/\..*$//;<br />
$i = 0;<br />
<br />
$hash->{helper}{$reading.'_lastIndex'} = 0;<br />
}<br />
<br />
# limit number of readings<br />
next if ($i > 75);<br />
<br />
$fi = sprintf("%03d", $i);<br />
$start = str2time(xmltv2epoch($_->{'start'}));<br />
<br />
$hash->{helper}{$reading.'_'.$fi.'_bdate'} = substr(FmtDateTime($start), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_btime'} = substr(FmtDateTime($start), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_edate'} = substr(FmtDateTime($stop), 0, 10);<br />
$hash->{helper}{$reading.'_'.$fi.'_etime'} = substr(FmtDateTime($stop), 11, 8);<br />
$hash->{helper}{$reading.'_'.$fi.'_title'} = encode_utf8($_->{'title'}{'content'});<br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = encode_utf8($_->{'sub-title'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_stitle'} = 'na';<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = encode_utf8($_->{'desc'}{'content'});<br />
}<br />
else<br />
{<br />
$hash->{helper}{$reading.'_'.$fi.'_desc'} = 'na';<br />
}<br />
<br />
if ($i < 3)<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_bdate', substr(FmtDateTime($start), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_btime', substr(FmtDateTime($start), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_edate', substr(FmtDateTime($stop), 0, 10)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_etime', substr(FmtDateTime($stop), 11, 8)); <br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_title', encode_utf8($_->{'title'}{'content'})); <br />
<br />
if (exists($_->{'sub-title'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', encode_utf8($_->{'sub-title'}{'content'})); <br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_stitle', 'na');<br />
} <br />
<br />
if (exists($_->{'desc'}{'content'}))<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', encode_utf8($_->{'desc'}{'content'}));<br />
}<br />
else<br />
{<br />
readingsBulkUpdate($hash, 'next_'.$reading.'_'.$fi.'_desc', 'na');<br />
}<br />
}<br />
<br />
$i++;<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'parsed'); <br />
readingsEndUpdate($hash, 0);<br />
}<br />
<br />
return undef;<br />
}<br />
<br />
sub tvDownload()<br />
{<br />
my $output = qx(wget http://rytecepg.ipservers.eu/epg_data/rytecDE_Basic.xz -O /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
$output = qx(xz -df /opt/fhem/tv/rytecDE_Basic.xz 2>&1);<br />
#print $output;<br />
}<br />
<br />
sub tvUpdate($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{ <br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
my $isNew = 1;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
<br />
while (($i < 3) && (defined($edate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $etime = (exists($hash->{helper}{$channel.'_'.$index.'_etime'}) ? $hash->{helper}{$channel.'_'.$index.'_etime'} : undef);<br />
<br />
if ($edate.' '.$etime gt TimeNow())<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$edate = undef;<br />
last;<br />
}<br />
<br />
if (1 == $isNew)<br />
{<br />
$hash->{helper}{$channel.'_lastIndex'} = $lastIndex;<br />
$isNew = 0;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_bdate', $hash->{helper}{$channel.'_'.$index.'_bdate'});<br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_btime', $hash->{helper}{$channel.'_'.$index.'_btime'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_edate', $edate); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_etime', $etime); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'next_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'});<br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$edate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_edate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
else<br />
{<br />
tvParse($device);<br />
}<br />
}<br />
<br />
sub tvUpdatePrimetime($)<br />
{<br />
my $device = shift;<br />
my $hash = $defs{$device};<br />
my @channels = ( 'ARD', 'ZDF', 'Sat1', 'RTL', 'RTL2', 'Pro7', 'DMax', 'Vox', 'Kabel' );<br />
<br />
if (exists($hash->{helper}))<br />
{<br />
readingsBeginUpdate($hash);<br />
<br />
foreach my $channel (@channels)<br />
{<br />
my $lastIndex = (exists($hash->{helper}{$channel.'_lastIndex'}) ? $hash->{helper}{$channel.'_lastIndex'} : undef);<br />
my $newLastIndex = $lastIndex;<br />
<br />
if (defined($lastIndex))<br />
{<br />
my $i = 0;<br />
my $bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
<br />
while (($i < 3) && (defined($bdate)))<br />
{<br />
my $index = sprintf("%03d", $lastIndex);<br />
my $btime = (exists($hash->{helper}{$channel.'_'.$index.'_btime'}) ? $hash->{helper}{$channel.'_'.$index.'_btime'} : undef);<br />
my $timeNow = substr(TimeNow(), 0, 11).'20:14:00';<br />
<br />
if ($bdate.' '.$btime gt $timeNow)<br />
{<br />
my $nindex = sprintf("%03d", $i);<br />
<br />
if ($lastIndex == $newLastIndex)<br />
{<br />
$bdate = undef;<br />
last;<br />
}<br />
<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_bdate', $bdate);<br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_btime', $btime); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_edate', $hash->{helper}{$channel.'_'.$index.'_edate'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_etime', $hash->{helper}{$channel.'_'.$index.'_etime'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_title', $hash->{helper}{$channel.'_'.$index.'_title'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_stitle', $hash->{helper}{$channel.'_'.$index.'_stitle'}); <br />
readingsBulkUpdate($hash, 'prime_'.$channel.'_'.$nindex.'_desc', $hash->{helper}{$channel.'_'.$index.'_desc'}); <br />
<br />
$i++;<br />
}<br />
<br />
$lastIndex++;<br />
$bdate = (exists($hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'}) ? $hash->{helper}{$channel.'_'.sprintf("%03d", $lastIndex).'_bdate'} : undef);<br />
}<br />
}<br />
}<br />
<br />
readingsBulkUpdate($hash, 'state', 'updated'); <br />
readingsEndUpdate($hash, 1);<br />
}<br />
}<br />
</nowiki><br />
<br />
<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=TV_Programm&diff=26496TV Programm2018-04-28T12:48:10Z<p>Mumpitzstuff: Die Seite wurde neu angelegt: „Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schni…“</p>
<hr />
<div>Sich das aktuelle Fernsehprogramm in FHEM anzeigen zu lassen, ist leider gar nicht so einfach. Die einzelnen Anbieter stellen leider keine entsprechenden Schnittstellen zur Verfügung, um auf einfache Art und Weise das aktuelle Fernsehprogramm für beliebige Sender einlesen und darstellen zu können. Mit ein paar Tricks funktioniert es aber trotzdem.<br />
<br />
Hierfür gibt es gleich mehrere Ansätze:<br />
* Im einfachsten Fall bindet man sich ein iframe in die FHEM Weboberfläche ein. Einige Anbieter bieten sogar einen personalisierten Zugriff, so das man sich eine Übersicht nur mit den gewünschten Sendern zusammen stellen kann.<br />
* Es besteht die Möglichkeit EPG Daten in einem speziellen xmltv Format in die FHEM Installation zu laden (enthält das Programm für 6-7 Tage), diese XML Datei zu parsen und dann in FHEM z.B. über eine readingsGroup darzustellen.<br />
* Für Linux und auch Windows sind Tools verfügbar, mit denen man die Seiten von verschiedenen TV Programmanbietern grabben und daraus eine XML Datei im xmltv Format erstellen kann (enthaält das Programm von 1-14 Tagen je nach Anbieter). Einer der bekanntesten Vertreter ist WebGrab++. Diese XML Datei kann dann wieder in FHEM eingelesen und angezeigt werden z.B. über eine readingsGroup. Das parsen der Daten ist mit einem sehr hohen Traffic verbunden, so das hier die vorher erwähnten Methoden vorzuziehen sind.<br />
* Über httpmod könnte man eine Webseite eines TV Programmanbieters zyklisch einlesen und die für FHEM notwendigen Daten extrahieren. Von dieser Methode ist dringend abzuraten, da diese einen enormen Traffic sowohl für einen selbst, als auch für den Anbieter bedeutet. Aus diesem Grund soll diese Methode hier auch nicht dargestellt werden.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
[[Kategorie:Code Snippets]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&diff=26464DevelopmentFHEMWEB-API2018-04-19T19:42:31Z<p>Mumpitzstuff: /* FW_okDialog */</p>
<hr />
<div>{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.<br />
<hr /><br />
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.<br />
}}<br />
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.<br />
<br />
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.<br />
<br />
== FHEMWEB-Server (pgm2) Perl-Funktionen ==<br />
<br />
=== Globale Variablen ===<br />
<br />
Globale Variablen:<br />
{| class="wikitable"<br />
|-<br />
! Variablenname!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_ME</code>'''<br />
<br />
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.<br />
<br />
Bsp: <code>/fhem</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_dir</code>'''<br />
<br />
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.<br />
<br />
Bsp: <code>./www</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_CSRF</code>''' <br />
<br />
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.<br />
<br />
Bsp: <code>&fwcsrf=csrf_165435377333711</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_detail</code>''' <br />
<br />
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. <br />
<br />
Bsp: <code>Licht_Wohnzimmer</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_room</code>''' <br />
<br />
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.<br />
<br />
Bsp: <code>Wohnzimmer</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_wname</code>''' <br />
<br />
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.<br />
<br />
Bsp: <code>WEB</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_cname</code>''' <br />
<br />
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).<br />
<br />
Bsp: <code>WEB_192.168.179.65_54406</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_userAgent</code>''' <br />
<br />
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.<br />
<br />
Bsp: <code>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36</code><br />
|}<br />
<br />
=== FW_makeImage ===<br />
<br />
:<syntaxhighlight lang="perl">$html = FW_makeImage($icon, $text, $class);</syntaxhighlight><br />
<br />
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$icon</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, mit optionaler Farbangabe <code>'<iconname>[@color>]'</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$text</code>''' <br />
<br />
''optional''<br />
|| Eine Zeichenkette, die einen alternativen Text angibt.<br><br />
|-<br />
| style="vertical-align:top" | '''<code>$class</code>''' <br />
<br />
''optional''<br />
|| Eine Zeichenkette, die eine CSS-Klasse angibt.<br><br />
|}<br />
<br />
Rückgabewerte:<br />
{| class="wikitable"<br />
|-<br />
! Rückgabewert!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$html</code>'''<br />
|| Wenn '''<code>$icon</code>''' mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.<br />
Mit nachgestellter Farbangabe <code>'<iconname@color>'</code>, wird das Icon entsprechend eingefärbt.<br />
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="perl">$html = FW_makeImage('fts_shutter_10@red');</syntaxhighlight><br />
<br />
=== FW_directNotify ===<br />
<br />
:<syntaxhighlight lang="perl">FW_directNotify($filter, $fhemweb_instance, $javascript, "");</syntaxhighlight><br />
<br />
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$filter</code>'''<br />
<br />
''optional''<br />
|| Eine Zeichenkette, die mit "FILTER=" beginnt <code>"FILTER=<filter expression>"</code>.<br />
|-<br />
| style="vertical-align:top" | '''<code>$fhemweb_instance</code>''' <br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz <code>"#FHEMWEB:<FHEMWEB devicename>"</code> angibt.<br><br />
|-<br />
| style="vertical-align:top" | '''<code>$javascript</code>''' <br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die ein Javascript angibt.<br />
|-<br />
| style="vertical-align:top" | '''<code>""</code>''' <br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, leer oder (noch zu präzisieren).<br />
<br />
|}<br />
<br />
Beispiele:<br />
* <syntaxhighlight lang="perl">FW_directNotify("FILTER=room=XXX", "#FHEMWEB:WEB", "FW_okDialog('Hello world!')", "");</syntaxhighlight><br />
* <syntaxhighlight lang="perl">FW_directNotify("FILTER=room=XXX", "#FHEMWEB:WEB", "location.reload('true')", "");</syntaxhighlight><br />
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}<br />
<br />
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==<br />
<br />
=== Globale Variablen ===<br />
<br />
Globale Variablen:<br />
{| class="wikitable"<br />
|-<br />
! Variablenname!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>FW_root</code>'''<br />
<br />
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut <code>root</code> zur Verfügung.<br />
|-<br />
| style="vertical-align:top" | '''<code>FW_version</code>''' <br />
<br />
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.<br />
<br />
|}<br />
<br />
=== FW_cmd ===<br />
<br />
:<syntaxhighlight lang="javascript">FW_cmd(arg, callback);</syntaxhighlight><br />
<br />
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>arg</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.<br />
|-<br />
| style="vertical-align:top" | '''<code>callback</code>''' <br />
<br />
''optional''<br />
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">FW_cmd(FW_root+'?cmd={FW_makeImage("fts_shutter_10")}&XHR=1', function(data){FW_okDialog(data)});</syntaxhighlight> Das Beispiel holt sich vom Server den HTML-Code des Icon "fts_shutter_10" und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet <syntaxhighlight lang="perl" inline>{FW_makeImage("fts_shutter_10")}</syntaxhighlight>. An die Callback-Funktion <syntaxhighlight lang="javascript" inline>function(data){FW_okDialog(data)}</syntaxhighlight> wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable <code>data</code> zur Verfügung.<br />
<br />
=== FW_errmsg ===<br />
<br />
:<syntaxhighlight lang="javascript">FW_errmsg(text, timeout);</syntaxhighlight><br />
<br />
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>text</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.<br />
|-<br />
| style="vertical-align:top" | '''<code>timeout</code>''' <br />
<br />
''mandatory''<br />
|| Anzeigedauer der Zeichenkette in Millisekunden.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">FW_errmsg('Hello world!',10000);</syntaxhighlight><br />
<br />
=== FW_okDialog ===<br />
<br />
:<syntaxhighlight lang="javascript">FW_okDialog(txt, parent, removeFn);</syntaxhighlight><br />
<br />
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>text</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.<br />
|-<br />
| style="vertical-align:top" | '''<code>parent</code>''' <br />
<br />
''optional''<br />
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.<br />
<br />
|-<br />
| style="vertical-align:top" | '''<code>returnFn</code>''' <br />
<br />
''optional''<br />
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">FW_okDialog('Hello world!',10000);</syntaxhighlight><br />
* <syntaxhighlight lang="javascript"><a href="#!" onclick="FW_okDialog('Hello world!')">Hello</a></syntaxhighlight><br />
<br />
Soll das Popup-Fenster aus einem Hyperlink heraus aufgerufen werden, dann ist darauf zu achten, dass ein ungültiger Anchor Tag verwendet wird. In diesem Beispiel "!". Dadurch wird ein unkontrolliertes Scrollen der Webseite effektiv verhindert und der Dialog erscheint genau dort wo man ihn haben möchte.<br />
<br />
=== loadScript ===<br />
<br />
:<syntaxhighlight lang="javascript">loadScript(scriptname, callback, force);</syntaxhighlight><br />
<br />
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>scriptname</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die den Sriptnamen enthält.<br />
<br />
|-<br />
| style="vertical-align:top" | '''<code>callback</code>''' <br />
<br />
''optional''<br />
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.<br />
<br />
|-<br />
| style="vertical-align:top" | '''<code>force</code>''' <br />
<br />
''optional''<br />
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">loadScript("pgm2/jquery.knob.min.js", function(){<Javascript-Code>});</syntaxhighlight><br />
<br />
=== log ===<br />
<br />
:<syntaxhighlight lang="javascript">log(text);</syntaxhighlight><br />
<br />
log zeigt einen Logeintrag in der Konsole des Browsers an.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>text</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die angezeigt wird.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">log('Hello world!');</syntaxhighlight><br />
<br />
== Links ==<br />
<br />
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]<br />
* [[DevelopmentModuleAPI]]<br />
<br />
[[Kategorie:Development]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=DevelopmentFHEMWEB-API&diff=26463DevelopmentFHEMWEB-API2018-04-19T19:38:51Z<p>Mumpitzstuff: /* FW_okDialog */</p>
<hr />
<div>{{Randnotiz|RNTyp=r|RNText=Bitte beachten: Diese Auflistung von Funktionen wird nicht aktiv durch alle Entwickler gepflegt. Es kann daher keine Garantie auf Vollständigkeit und Korrektheit gegeben werden, entscheidend ist immer der aktuelle Programm-Code.<br />
<hr /><br />
Please note: This list of functions is not official maintained by all developers. So there is no guarantee for completeness and correctness, at least the program code itself is relevant.<br />
}}<br />
Dieses Seite soll die verfügbaren Funktionen zugänglicher machen, um das Beisteuern von Funktionalität zu erleichtern.<br />
<br />
Natürlich hat diese Seite keinen Anspruch auf Vollständigkeit und jeder ist aufgefordert mitzuarbeiten, sobald er beim Stöbern über eine Funktion stolpert, die auch andere interessieren könnte.<br />
<br />
== FHEMWEB-Server (pgm2) Perl-Funktionen ==<br />
<br />
=== Globale Variablen ===<br />
<br />
Globale Variablen:<br />
{| class="wikitable"<br />
|-<br />
! Variablenname!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_ME</code>'''<br />
<br />
|| URL-Pfad unter dem der FHEMWEB-Server via HTTP erreichbar ist.<br />
<br />
Bsp: <code>/fhem</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_dir</code>'''<br />
<br />
|| Das Basisverzeichnis auf dem lokalen Filesystem des FHEMWEB-Servers.<br />
<br />
Bsp: <code>./www</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_CSRF</code>''' <br />
<br />
|| Enthält das aktuell gültige CSRF-Token als HTTP URL-Parameter für die aktuelle Verbindung oder bleibt leer.<br />
<br />
Bsp: <code>&fwcsrf=csrf_165435377333711</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_detail</code>''' <br />
<br />
|| Enthält den Definitionsnamen der aktuell erzeugten Detailansicht. <br />
<br />
Bsp: <code>Licht_Wohnzimmer</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_room</code>''' <br />
<br />
|| Enthält den Raumnamen der aktuell erzeugten Raumansicht.<br />
<br />
Bsp: <code>Wohnzimmer</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_wname</code>''' <br />
<br />
|| Enthält den Definitionsnamen der aktuellen FHEMWEB-Instanz.<br />
<br />
Bsp: <code>WEB</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_cname</code>''' <br />
<br />
|| Enthält den Definitionsnamen der aktuellen Verbindung (Connection-Name).<br />
<br />
Bsp: <code>WEB_192.168.179.65_54406</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$FW_userAgent</code>''' <br />
<br />
|| Enthält den User-Agent, welcher für den aktuellen Aufruf übermittelt wurde.<br />
<br />
Bsp: <code>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36</code><br />
|}<br />
<br />
=== FW_makeImage ===<br />
<br />
:<syntaxhighlight lang="perl">$html = FW_makeImage($icon, $text, $class);</syntaxhighlight><br />
<br />
FW_makeImage gibt HTML-Code zurück, der ein Icon aus dem dem FHEMWEB-Iconpool beschreibt.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$icon</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, mit optionaler Farbangabe <code>'<iconname>[@color>]'</code><br />
|-<br />
| style="vertical-align:top" | '''<code>$text</code>''' <br />
<br />
''optional''<br />
|| Eine Zeichenkette, die einen alternativen Text angibt.<br><br />
|-<br />
| style="vertical-align:top" | '''<code>$class</code>''' <br />
<br />
''optional''<br />
|| Eine Zeichenkette, die eine CSS-Klasse angibt.<br><br />
|}<br />
<br />
Rückgabewerte:<br />
{| class="wikitable"<br />
|-<br />
! Rückgabewert!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$html</code>'''<br />
|| Wenn '''<code>$icon</code>''' mit einem Iconnamen aus dem FHEMWEB-Iconpool übereinstimmt, wird der HTML-Code zum Einbinden des Icons zurückgegeben.<br />
Mit nachgestellter Farbangabe <code>'<iconname@color>'</code>, wird das Icon entsprechend eingefärbt.<br />
Entspricht die Zeichenkette keinem Iconnamen, wird die Zeichenkette zurückgegeben.<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="perl">$html = FW_makeImage('fts_shutter_10@red');</syntaxhighlight><br />
<br />
=== FW_directNotify ===<br />
<br />
:<syntaxhighlight lang="perl">FW_directNotify($filter, $fhemweb_instance, $javascript, "");</syntaxhighlight><br />
<br />
FW_directNotify führt Javascript auf einer longpoll FHEMWEB-Instanz aus, ohne Events zu erzeugen.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>$filter</code>'''<br />
<br />
''optional''<br />
|| Eine Zeichenkette, die mit "FILTER=" beginnt <code>"FILTER=<filter expression>"</code>.<br />
|-<br />
| style="vertical-align:top" | '''<code>$fhemweb_instance</code>''' <br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die eine longpoll FHEMWEB-Instanz <code>"#FHEMWEB:<FHEMWEB devicename>"</code> angibt.<br><br />
|-<br />
| style="vertical-align:top" | '''<code>$javascript</code>''' <br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die ein Javascript angibt.<br />
|-<br />
| style="vertical-align:top" | '''<code>""</code>''' <br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, leer oder (noch zu präzisieren).<br />
<br />
|}<br />
<br />
Beispiele:<br />
* <syntaxhighlight lang="perl">FW_directNotify("FILTER=room=XXX", "#FHEMWEB:WEB", "FW_okDialog('Hello world!')", "");</syntaxhighlight><br />
* <syntaxhighlight lang="perl">FW_directNotify("FILTER=room=XXX", "#FHEMWEB:WEB", "location.reload('true')", "");</syntaxhighlight><br />
* siehe auch {{Link2Forum|Topic=48736|Message=404497|LinkText=Forenthread}}<br />
<br />
== FHEMWEB-Client (pgm2) Javascript-Funktionen ==<br />
<br />
=== Globale Variablen ===<br />
<br />
Globale Variablen:<br />
{| class="wikitable"<br />
|-<br />
! Variablenname!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>FW_root</code>'''<br />
<br />
|| FHEM-Ursprungsverzeichnis, steht auch im head-Element als Attribut <code>root</code> zur Verfügung.<br />
|-<br />
| style="vertical-align:top" | '''<code>FW_version</code>''' <br />
<br />
|| Ein assoziatives Array, das die Versionszeichenketten der geladenen Javascript-Dateien enthält.<br />
<br />
|}<br />
<br />
=== FW_cmd ===<br />
<br />
:<syntaxhighlight lang="javascript">FW_cmd(arg, callback);</syntaxhighlight><br />
<br />
FW_cmd führt einen Befehl auf dem FHEMWEB-Server aus.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>arg</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die einem Befehl an den Server entspricht.<br />
|-<br />
| style="vertical-align:top" | '''<code>callback</code>''' <br />
<br />
''optional''<br />
|| Eine Javascript-Funktion, die ausgeführt wird, wenn der auf dem FHEM-Server ausgeführte Befehl einen Rückgabewert enthält, der weiter verarbeitet werden soll.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">FW_cmd(FW_root+'?cmd={FW_makeImage("fts_shutter_10")}&XHR=1', function(data){FW_okDialog(data)});</syntaxhighlight> Das Beispiel holt sich vom Server den HTML-Code des Icon "fts_shutter_10" und zeigt das Icon in einem Popup-Fenster an. Der vom Server auszuführende Befehl lautet <syntaxhighlight lang="perl" inline>{FW_makeImage("fts_shutter_10")}</syntaxhighlight>. An die Callback-Funktion <syntaxhighlight lang="javascript" inline>function(data){FW_okDialog(data)}</syntaxhighlight> wird der Rückgabewert vom Server als Parameter übergeben und steht als Variable <code>data</code> zur Verfügung.<br />
<br />
=== FW_errmsg ===<br />
<br />
:<syntaxhighlight lang="javascript">FW_errmsg(text, timeout);</syntaxhighlight><br />
<br />
FW_errmsg zeigt eine Meldung im FHEMWEB-Client (Browser) an.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>text</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die angezeigt wird. HTML-Code wird interpretiert.<br />
|-<br />
| style="vertical-align:top" | '''<code>timeout</code>''' <br />
<br />
''mandatory''<br />
|| Anzeigedauer der Zeichenkette in Millisekunden.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">FW_errmsg('Hello world!',10000);</syntaxhighlight><br />
<br />
=== FW_okDialog ===<br />
<br />
:<syntaxhighlight lang="javascript">FW_okDialog(txt, parent, removeFn);</syntaxhighlight><br />
<br />
FW_okDialog öffnet im FHEMWEB-Client (Browser) ein modales Popup-Fenster.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>text</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die im Popup-Fenster angezeigt wird. HTML-Code wird interpretiert.<br />
|-<br />
| style="vertical-align:top" | '''<code>parent</code>''' <br />
<br />
''optional''<br />
|| Ein Objekt zur Verankerung des Popup-Fensters, default ist das body-Element. Eine FHEM-Widget-Schnittstelle wird interpretiert.<br />
<br />
|-<br />
| style="vertical-align:top" | '''<code>returnFn</code>''' <br />
<br />
''optional''<br />
|| Eine Javascript-Funktion, die beim Schliessen des Popup-Fensters ausgeführt wird.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">FW_okDialog('Hello world!',10000);</syntaxhighlight><br />
* <syntaxhighlight lang="javascript"><a href="#!" onclick="FW_okDialog('Hello world!')>Hello</a></syntaxhighlight><br />
<br />
=== loadScript ===<br />
<br />
:<syntaxhighlight lang="javascript">loadScript(scriptname, callback, force);</syntaxhighlight><br />
<br />
loadScript läd im FHEMWEB-Client (Browser) ein Javascript nach.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>scriptname</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die den Sriptnamen enthält.<br />
<br />
|-<br />
| style="vertical-align:top" | '''<code>callback</code>''' <br />
<br />
''optional''<br />
|| Eine Javascript-Funktion, die ausgeführt wird, wenn das Script geladen wurde.<br />
<br />
|-<br />
| style="vertical-align:top" | '''<code>force</code>''' <br />
<br />
''optional''<br />
|| Ein Flag, wenn gesetzt, wird das Laden erzwungen.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">loadScript("pgm2/jquery.knob.min.js", function(){<Javascript-Code>});</syntaxhighlight><br />
<br />
=== log ===<br />
<br />
:<syntaxhighlight lang="javascript">log(text);</syntaxhighlight><br />
<br />
log zeigt einen Logeintrag in der Konsole des Browsers an.<br />
<br />
Parameter:<br />
{| class="wikitable"<br />
|-<br />
! Parameter!! Bedeutung<br />
|-<br />
| style="vertical-align:top" | '''<code>text</code>'''<br />
<br />
''mandatory''<br />
|| Eine Zeichenkette, die angezeigt wird.<br />
<br />
|}<br />
<br />
Beispiel:<br />
* <syntaxhighlight lang="javascript">log('Hello world!');</syntaxhighlight><br />
<br />
== Links ==<br />
<br />
* [[DevelopmentFHEMWEB|Development FHEMWEB (englischsprachig)]]<br />
* [[DevelopmentModuleAPI]]<br />
<br />
[[Kategorie:Development]]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=Text2Speech&diff=21758Text2Speech2017-06-27T19:02:32Z<p>Mumpitzstuff: sudo hinzugefügt</p>
<hr />
<div>{{Infobox Modul<br />
|ModPurpose=Sprachausgabe über FHEM<br />
|ModType=h<br />
|ModForumArea=Unterstützende Dienste<br />
|ModTechName=98_Text2Speech.pm<br />
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}} / [[Benutzer_Diskussion:Tobias.faust|Wiki]])<br />
}}<br />
<br />
Das Modul [[Text2Speech]] ermöglicht es, eine Sprachausgabe über FHEM zu realisieren. Das Modul ist Client-Server-fähig.<br />
<br />
==Definition Lokale Architektur==<br />
Voraussetzung ist ein installierter Mplayer:<br />
:<code>sudo apt-get install mplayer</code><br />
<br />
Danach den Mplayer für alle aufrufbar machen<br />
====a) per SUDOERS====<br />
<pre style="width:400px;"><br />
EDITOR=nano<br />
visudo<br />
</pre><br />
Dort an letzter Stelle eintragen:<br />
:<code> ALL ALL = NOPASSWD: /usr/bin/mplayer</code><br />
====b) oder(!) per GruppenRechte====<br />
:<code> sudo usermod -aG audio fhem </code><br />
<br />
Hat man Mplayer per Gruppenrechte verfügbar gemacht, muss später folgendes Attribut gesetzt sein. Damit wird verhindert, dass Mplayer mit dem defaultmäßig eingestellten "sudo" aufgerufen wird, welches für a) per sudoers gebraucht wird.<br />
:<code> attr MyTTS TTS_MplayerCall /usr/bin/mplayer</code><br />
<br />
Empfohlen wird die Nutzung von MP3Wrap. Dadurch wird bei wiederholten Sprachbausteinen eine deutlich flüssigere Sprachausgabe erreicht:<br />
:<code> sudo apt-get install mp3wrap </code><br />
<br />
per ''aplay -l'' erhält man eine Liste der möglichen Audio Ausgabedevices. Von dem gewünschten Ausgabedevice ermittelt man nun die Kartennummer und das Subdevice. Mit diesen Daten kann man nun das Modul Text2Speech definieren:<br />
<pre style="width:400px;"><br />
define MyTTS Text2Speech hw=0.0;<br />
attr MyTTS room Text2Speech;<br />
attr MyTTS TTS_UseMP3Wrap 1;<br />
attr MyTTS verbose 4;<br />
</pre><br />
<br />
Ein erster Test kann wie folgt durchgeführt werden (als Einzeiler, ohne Zeilenumbruch einzugeben):<br />
:<code> set MyTTS tts Das ist ein Test. Jetzt prüfen wir, ob wir etwas aus dem angeschlossenen Lautsprecher hören. Beim ersten Mal haben wir allerdings noch Sprechpausen. Spätestens beim zweiten Durchlauf ist der Textfluss flüssiger.</code><br />
Hört man etwas aus dem Lautsprecher, so führen wir das Kommando ein zweites Mal aus. Jetzt sollte es sich besser anhören. Bleiben die Lautsprecher stumm, so ist das FHEM-Log zu kontrollieren. Gegebenenfalls ist das [[verbose]]-Level auf 5 zu stellen.<br />
<br />
War der Test erfolgreich, kann das verbose-Attribut wieder gelöscht werden.<br />
<br />
==== Abhängigkeiten der lokalen Sprachengines ====<br />
Für beide lokale Engines ist <code>lame</code> für die Umwandlung von .wav in .mp3 notwendig:<br />
:<code>sudo apt-get install lame</code><br />
<br />
======Espeak======<br />
:<code>sudo apt-get install espeak</code><br />
<br />
======SVOX-pico======<br />
:<code>sudo apt-get install libttspico-utils</code><br />
Für ARM/Raspbian sind die <code>libttspico-utils</code> leider nicht verfügbar, deswegen müsste man diese selbst kompilieren oder das vorkompilierte Paket aus [http://www.robotnet.de/2014/03/20/sprich-freund-und-tritt-ein-sprachausgabe-fur-den-rasberry-pi/ dieser Anleitung] verwenden, folgend in aller Kürze:<br />
<pre style="width:400px;"><br />
sudo apt-get install libpopt-dev<br />
cd /tmp<br />
wget http://www.dr-bischoff.de/raspi/pico2wave.deb<br />
sudo dpkg --install pico2wave.deb<br />
</pre><br />
<br />
==Definition Client-Server Architektur==<br />
Es gibt Installationen, bei denen der FHEM-Server z.B. im Keller steht, die Sprachausgabe soll aber räumlich entfernt stattfinden, z.B. im Wohnzimmer. Hierzu ist am Zielort eine FHEM-Instanz anzulegen, z.B. über einen [[Raspberry Pi]], IP: 192.168.178.10. <br />
Auf dem FHEM-Server ist Text2Speech als Remote zu definieren:<br />
define MyTTS Text2Speech 192.168.178.10:7072 <Passwort>;<br />
attr Text2Speech room Text2Speech;<br />
<br />
Am Zielort ist Text2Speech auf dem Raspi wie folgt zu definieren:<br />
define MyTTS Text2Speech hw=0.0;<br />
attr MyTTS room Text2Speech;<br />
attr MyTTS TTS_UseMP3Wrap 1;<br />
attr MyTTS verbose 4;<br />
Der Passwort Zugang ist entsprechend der commandref zu definieren, siehe dort allowed. <br />
Bitte die Installation von Mplayer und Mp3Wrap nicht vergessen!<br />
Danach der Test wie oben angegeben.<br />
<br />
==Sprachausgabe per Bluetooth-Lautsprecher unter Raspbian Jessie==<br />
Für die Sprachausgabe über Bluetooth bieten sich verschiedene Lautsprecher an. Wichtig ist jedoch, dass diese entweder gar nicht in einen Standby Modus wechseln oder aber über Bluetooth aus dem Standby aufgeweckt werden können. Folgende Lautsprecher sollen laut Internetrecherche funktionieren:<br />
<br />
* Creative D80/D100/D200<br />
* Sony Soundbar HT-XT3<br />
* Panasonic SC-ALL6EG-W / SC-NE1<br />
* Bose Soundtouch 10<br />
* Bose Soundlink Mini II<br />
* B&O Beoplay S3<br />
* JBL Charge 2+/3<br />
* LG Art 51<br />
* UE Roll<br />
* Energy PowerBar Elite<br />
* Teufel Cinebar 11<br />
<br />
Die folgenden Schritte müssen nacheinander durchgeführt werden, um auf einem aktuellen Jessie System Audio Inhalte über Bluetooth zu streamen:<br />
<br />
'''1.''' USB-Bluetooth-Stick verbinden<br />
und per ''lsusb'' prüfen, ob der Stick erkannt wurde.<br />
<br />
'''2.''' System aktualisieren und notwendige Pakete installieren<br />
sudo apt-get update<br />
sudo apt-get upgrade<br />
sudo apt-get install alsa-utils bluez bluez-tools pulseaudio-module-bluetooth python-gobject python-gobject-2<br />
sudo reboot<br />
<br />
'''3.''' Benutzer pi zur PulseAudio Gruppe hinzufügen<br />
sudo usermod -a -G lp pi<br />
sudo usermod -a -G pulse-access,audio root<br />
sudo adduser pi pulse-access<br />
<br />
'''4.''' Datei daemon.conf von PulseAudio anpassen<br />
sudo nano /etc/pulse/daemon.conf<br />
Nach folgender Zeile suchen<br />
; resample-method = speex-float-1<br />
und wie folgt erweitern<br />
; resample-method = speex-float-1<br />
resample-method = trivial<br />
und<br />
; system-instance = no<br />
system-instance = yes<br />
speichern und schliessen<br />
<br />
'''5.''' In PulseAudio müssen jetzt zusätzliche Module geladen werden<br />
sudo nano /etc/pulse/system.pa<br />
am Ende der Datei folgendes hinzufügen<br />
.ifexists module-bluetooth-policy.so<br />
load-module module-bluetooth-policy<br />
.endif<br />
.ifexists module-bluetooth-discover.so<br />
load-module module-bluetooth-discover<br />
.endif<br />
speichern und schliessen<br />
<br />
'''6.''' Jetzt müssen wir dafür sorgen, dass PulseAudio automatisch geladen wird<br />
sudo nano /etc/systemd/system/pulseaudio.service<br />
und folgenden Inhalt einfügen<br />
[Unit]<br />
Description=PulseAudio Sound System<br />
Before=sound.target<br />
[Service]<br />
BusName=org.pulseaudio.Server<br />
ExecStart=/usr/bin/pulseaudio<br />
Restart=always<br />
[Install]<br />
WantedBy=multi-user.target<br />
speichern und schliessen<br />
<br />
'''7.''' Danach müssen wir PulseAudio neu starten<br />
sudo systemctl daemon-reload<br />
sudo systemctl enable pulseaudio.service<br />
sudo systemctl start pulseaudio.service<br />
<br />
'''8.''' Prüfen ob PulseAudio läuft<br />
systemctl status pulseaudio<br />
<br />
'''9.''' Jetzt den Audio Output einstellen: X entweder mit 0 (auto), 1 (3,5mm Klinke) oder 2 (HDMI) ersetzen.<br />
sudo amixer cset numid=3 X<br />
<br />
'''10.''' Die Lautstärke kann auf 100% gesetzt werden mit<br />
sudo amixer set Master 100%<br />
sudo pacmd set-sink-volume 0 65535<br />
<br />
'''11.''' Soundausgabe z.B. über Kopfhörer prüfen<br />
paplay /usr/share/sounds/alsa/Front_Left.wav<br />
<br />
'''12.''' Jetzt können wir mit der Einrichtung von Bluetooth beginnen<br />
sudo nano /etc/bluetooth/audio.conf<br />
und folgendes einfügen<br />
[General]<br />
Enable=Source,Sink,Headset,Gateway,Control,Media,Socket<br />
HFP=true<br />
Class=0x20041C<br />
speichern und schliessen<br />
<br />
sudo nano /etc/bluetooth/main.conf<br />
unter [General] folgendes einfügen<br />
Name = RaspStream<br />
Class = 0x20041C<br />
speichern und schliessen<br />
<br />
sudo hciconfig -a<br />
eingeben und die MAC Adresse des Bluetooth Dongles aufschreiben<br />
<br />
Jetzt müssen die Bluetooth Settings angepasst werden (XX:XX:XX:XX:XX:XX steht für die MAC Adresse des Bluetooth Dongles)<br />
sudo nano /var/lib/bluetooth/XX:XX:XX:XX:XX:XX/settings<br />
einngeben und folgendes einfügen <br />
[General]<br />
Discoverable=true<br />
Alias=RaspStream<br />
Class=0x20041C<br />
speichern und schliessen<br />
<br />
Jetzt muss der Raspberry wieder neu gestartet werden<br />
sudo reboot<br />
<br />
'''13.''' Jetzt können Bluetooth Dongle und Bluetooth Lautsprecher gepaired werden<br />
bluetoothctl<br />
und folgende Befehle nacheinander eingeben<br />
agent on<br />
default-agent<br />
discoverable on<br />
scan on<br />
<br />
der Bluetooth Lautsprecher sollte jetzt irgendwann aufgelistet werden und kann dann gepaired werden mit<br />
pair XX:XX:XX:XX:XX:XX<br />
trust XX:XX:XX:XX:XX:XX<br />
Bluetooth console verlassen mit<br />
quit<br />
<br />
Damit sollte die Einrichtung abgeschlossen sein und wir können überprüfen, ob wir etwas über Bluetooth ausgeben können<br />
mplayer -ao pulse http://stream01.iloveradio.de/iloveradio1.mp3<br />
<br />
'''14.''' Das Modul Text2Speech sollte dann mit 'default' angelegt werden<br />
define TTS Text2Speech default<br />
<br />
==Sprachausgabe per Bluetooth-Lautsprecher==<br />
{{Randnotiz|RNTyp=Info|RNText=Dieser Abschnitt repräsentiert nicht den aktuellen Stand. Bezüglich Verbindung, Neustart alsa und dem Testbeispiel am Ende muss er überarbeitet werden}}<br />
Es ist ebenfalls möglich die Sprachausgabe nicht über einen direkt per Kabel angeschlossenen Lautsprecher auszugeben, sondern per Bluetooth an einen oder mehrere Bluetooth-Lautsprecher. Der Test2Speech-Author verwendet einen CreativeD80.<br />
Dabei ist auf dem Client mit der lokal eingerichteten Text2Speech-Instanz folgendes vorzunehmen:<br />
<br />
'''1.''' USB-Bluetooth-Stick verbinden<br />
per ''lsusb'' prüfen, ob der Stick erkannt wurde.<br />
<br />
'''2.''' Bluetooth Bibliotheken installieren<br />
sudo apt-get install bluez bluez-alsa mplayer<br />
<br />
'''3.''' Bluetooth für eine AudioVerbindung konfigurieren<br />
Dazu in ''/etc/bluetooth/audio.conf'' in der ''General section'' folgendes einfügen:<br />
Enable=Source,Sink,Headset,Gateway,Control,Socket,Media<br />
<br />
'''4.''' den Rechner neu starten<br />
sudo reboot<br />
<br />
'''5.''' Den Bluetooth-Lautsprecher lokalisieren<br />
Den Lautsprecher in den Pairing-Modus versetzen. Erst Dadurch wird er im Scanning sichtbar<br />
hcitool scan<br />
Folgendes Ergebnis sollte kommen:<br />
Scanning ...<br />
11:22:33:44:55:66 device 1<br />
12:34:56:78:90:12 device 2<br />
<br />
'''6.''' eine RFComm Connection vorbereiten<br />
Dazu ''/etc/bluetooth/rfcomm.conf'' editieren und folgendes eintragen (Mac-Adresse anpassen!)<br />
rfcomm0 {<br />
# Automatically bind the device at startup<br />
bind no;<br />
# Bluetooth address of the device<br />
device 11:22:33:44:55:66;<br />
# RFCOMM channel for the connection<br />
channel 3;<br />
# Description of the connection<br />
comment "This is Device 1's serial port.";<br />
}<br />
<br />
'''7.''' rebooten<br />
sudo reboot<br />
<br />
'''8.''' Pairing-Key vergeben<br />
Die meisten Bluetoothgeräte verlangen einen Schlüssel während des Pairings. Der Creative-D80 erwartet den Schlüssel 0000<br />
bluetooth-agent 0000 &<br />
<br />
'''9.''' Das eigentliche Pairing<br />
Den Lautsprecher in den Pairing-Modus versetzen. Danach:<br />
sudo rfcomm connect rfcomm0<br />
Jetzt sollte keine Fehlermeldung mehr kommen....<br />
<br />
'''10.''' Alsa konfigurieren<br />
Der Bluetooth-Lautsprecher muss nun noch als Alsa-Audiodevice bekannt gemacht werden. Dazu muss ''/etc/asound.conf'' erstellt werden. MAC anpassen!<br />
pcm.bluetooth {<br />
type bluetooth<br />
device 11:22:33:44:55:66<br />
profile "auto"<br />
}<br />
<br />
Danach Alsa restarten:<br />
{{Randnotiz|RNTyp=Fehl|RNText=Aktuell bei wheezy /etc/init.d/alsa-utils restart}}<br />
/etc/init.d/alsasound restart<br />
<br />
'''11.''' Testen der Verbindung<br />
{{Randnotiz|RNTyp=Fehl|RNText=Dieses Beispiel geht allein wegen der ständigen Änderung bei Google nicht. Auch stimmt das Ausgabe Gerät nicht. Man könnte so testen :<code>mplayer -ao alsa:device=bluetooth http://1live-diggi.akacast.akamaistream.net/7/965/119435/v1/gnl.akacast.akamaistream.net/1live-diggi</code>}}<br />
:<code>mplayer -ao alsa:device=hw=0.0 -nolirc -noconsolecontrols -http-header-fields "User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22m" "http://translate.google.com/translate_tts?tl=de&q=Das ist ein Test.";</code><br />
<br />
== Links ==<br />
* Vorstellung und Diskussion des Moduls im {{Link2Forum|Topic=18481|LinkText=FHEM Forum}}<br />
* [[Wetter_vorlesen_lassen|Code Snippets: Wetter vorlesen lassen]]<br />
* [https://aubreykloppers.wordpress.com/2015/06/25/bluetooth-speaker-raspberry-pi-the-ultimate-guide/ weitere Guideline für Bluetooth per Rasperry Pi]<br />
* [https://www.alefo.de/bluetooth-lautsprecher-und-adapter-f63/liste-kompatibler-bt-lautsprecher-fuer-amazon-dot-t907.html Liste von Bluetooth Lautsprechern]<br />
* [http://www.forum-raspberrypi.de/Thread-tutorial-bluetooth-audio-streaming-mit-a2dp-raspbian-jessie Bluetooth einrichten mit Raspbian Jessie]</div>Mumpitzstuffhttp://wiki.fhem.de/w/index.php?title=Text2Speech&diff=21743Text2Speech2017-06-22T22:27:34Z<p>Mumpitzstuff: Bluetooth Einrichtung für Raspbian Jessie</p>
<hr />
<div>{{Infobox Modul<br />
|ModPurpose=Sprachausgabe über FHEM<br />
|ModType=h<br />
|ModForumArea=Unterstützende Dienste<br />
|ModTechName=98_Text2Speech.pm<br />
|ModOwner=tobiasfaust ({{Link2FU|118|Forum}} / [[Benutzer_Diskussion:Tobias.faust|Wiki]])<br />
}}<br />
<br />
Das Modul [[Text2Speech]] ermöglicht es, eine Sprachausgabe über FHEM zu realisieren. Das Modul ist Client-Server-fähig.<br />
<br />
==Definition Lokale Architektur==<br />
Voraussetzung ist ein installierter Mplayer:<br />
:<code>sudo apt-get install mplayer</code><br />
<br />
Danach den Mplayer für alle aufrufbar machen<br />
====a) per SUDOERS====<br />
<pre style="width:400px;"><br />
EDITOR=nano<br />
visudo<br />
</pre><br />
Dort an letzter Stelle eintragen:<br />
:<code> ALL ALL = NOPASSWD: /usr/bin/mplayer</code><br />
====b) oder(!) per GruppenRechte====<br />
:<code> sudo usermod -aG audio fhem </code><br />
<br />
Hat man Mplayer per Gruppenrechte verfügbar gemacht, muss später folgendes Attribut gesetzt sein. Damit wird verhindert, dass Mplayer mit dem defaultmäßig eingestellten "sudo" aufgerufen wird, welches für a) per sudoers gebraucht wird.<br />
:<code> attr MyTTS TTS_MplayerCall /usr/bin/mplayer</code><br />
<br />
Empfohlen wird die Nutzung von MP3Wrap. Dadurch wird bei wiederholten Sprachbausteinen eine deutlich flüssigere Sprachausgabe erreicht:<br />
:<code> sudo apt-get install mp3wrap </code><br />
<br />
per ''aplay -l'' erhält man eine Liste der möglichen Audio Ausgabedevices. Von dem gewünschten Ausgabedevice ermittelt man nun die Kartennummer und das Subdevice. Mit diesen Daten kann man nun das Modul Text2Speech definieren:<br />
<pre style="width:400px;"><br />
define MyTTS Text2Speech hw=0.0;<br />
attr MyTTS room Text2Speech;<br />
attr MyTTS TTS_UseMP3Wrap 1;<br />
attr MyTTS verbose 4;<br />
</pre><br />
<br />
Ein erster Test kann wie folgt durchgeführt werden (als Einzeiler, ohne Zeilenumbruch einzugeben):<br />
:<code> set MyTTS tts Das ist ein Test. Jetzt prüfen wir, ob wir etwas aus dem angeschlossenen Lautsprecher hören. Beim ersten Mal haben wir allerdings noch Sprechpausen. Spätestens beim zweiten Durchlauf ist der Textfluss flüssiger.</code><br />
Hört man etwas aus dem Lautsprecher, so führen wir das Kommando ein zweites Mal aus. Jetzt sollte es sich besser anhören. Bleiben die Lautsprecher stumm, so ist das FHEM-Log zu kontrollieren. Gegebenenfalls ist das [[verbose]]-Level auf 5 zu stellen.<br />
<br />
War der Test erfolgreich, kann das verbose-Attribut wieder gelöscht werden.<br />
<br />
==== Abhängigkeiten der lokalen Sprachengines ====<br />
Für beide lokale Engines ist <code>lame</code> für die Umwandlung von .wav in .mp3 notwendig:<br />
:<code>sudo apt-get install lame</code><br />
<br />
======Espeak======<br />
:<code>sudo apt-get install espeak</code><br />
<br />
======SVOX-pico======<br />
:<code>sudo apt-get install libttspico-utils</code><br />
Für ARM/Raspbian sind die <code>libttspico-utils</code> leider nicht verfügbar, deswegen müsste man diese selbst kompilieren oder das vorkompilierte Paket aus [http://www.robotnet.de/2014/03/20/sprich-freund-und-tritt-ein-sprachausgabe-fur-den-rasberry-pi/ dieser Anleitung] verwenden, folgend in aller Kürze:<br />
<pre style="width:400px;"><br />
sudo apt-get install libpopt-dev<br />
cd /tmp<br />
wget http://www.dr-bischoff.de/raspi/pico2wave.deb<br />
sudo dpkg --install pico2wave.deb<br />
</pre><br />
<br />
==Definition Client-Server Architektur==<br />
Es gibt Installationen, bei denen der FHEM-Server z.B. im Keller steht, die Sprachausgabe soll aber räumlich entfernt stattfinden, z.B. im Wohnzimmer. Hierzu ist am Zielort eine FHEM-Instanz anzulegen, z.B. über einen [[Raspberry Pi]], IP: 192.168.178.10. <br />
Auf dem FHEM-Server ist Text2Speech als Remote zu definieren:<br />
define MyTTS Text2Speech 192.168.178.10:7072 <Passwort>;<br />
attr Text2Speech room Text2Speech;<br />
<br />
Am Zielort ist Text2Speech auf dem Raspi wie folgt zu definieren:<br />
define MyTTS Text2Speech hw=0.0;<br />
attr MyTTS room Text2Speech;<br />
attr MyTTS TTS_UseMP3Wrap 1;<br />
attr MyTTS verbose 4;<br />
Der Passwort Zugang ist entsprechend der commandref zu definieren, siehe dort allowed. <br />
Bitte die Installation von Mplayer und Mp3Wrap nicht vergessen!<br />
Danach der Test wie oben angegeben.<br />
<br />
==Sprachausgabe per Bluetooth-Lautsprecher unter Raspbian Jessie==<br />
Für die Sprachausgabe über Bluetooth bieten sich verschiedene Lautsprecher an. Wichtig ist jedoch, das diese entweder gar nicht in einen Standby Modus wechseln, oder aber über Bluetooth aus dem Standby aufgeweckt werden können. Folgende Lautsprecher sollen laut Internetrecherche funktionieren (falls ihr weitere Lautsprecher findet, meldet diese bitte im Forum):<br />
<br />
- Creative D80/D100/D200<br />
- Sony Soundbar HT-XT3<br />
- Panasonic SC-ALL6EG-W / SC-NE1<br />
- Bose Soundtouch 10<br />
- Bose Soundlink Mini II<br />
- B&O Beoplay S3<br />
- JBL Charge 2+/3<br />
- LG Art 51<br />
- UE Roll<br />
- Energy PowerBar Elite<br />
- Teufel Cinebar 11<br />
<br />
Die folgenden Schritte müssen nacheinander durchgeführt werden, um auf einem aktuellen Jessie System Audio Inhalte über Bluetooth zu streamen:<br />
<br />
'''1.''' USB-Bluetooth-Stick verbinden<br />
per ''lsusb'' prüfen, ob der Stick erkannt wurde.<br />
<br />
'''2.''' System aktualisieren und notwendige Pakete installieren<br />
sudo apt-get update<br />
sudo apt-get upgrade<br />
sudo apt-get install alsa-utils bluez bluez-tools pulseaudio-module-bluetooth python-gobject python-gobject-2<br />
sudo reboot<br />
<br />
'''3.''' Benutzer pi zur PulseAudio Gruppe hinzufügen<br />
sudo usermod -a -G lp pi<br />
sudo usermod -a -G pulse-access,audio root<br />
sudo adduser pi pulse-access<br />
<br />
'''4.''' Datei daemon.conf von PulseAudio anpassen<br />
sudo nano /etc/pulse/daemon.conf<br />
Nach folgender Zeile suchen<br />
; resample-method = speex-float-1<br />
und wie folgt erweitern<br />
; resample-method = speex-float-1<br />
resample-method = trivial<br />
und<br />
; system-instance = no<br />
system-instance = yes<br />
speichern und schliessen<br />
<br />
'''5.''' In PulseAudio müssen jetzt zusätzliche Module geladen werden<br />
sudo nano /etc/pulse/system.pa<br />
am Ende der Datei folgendes hinzufügen<br />
.ifexists module-bluetooth-policy.so<br />
load-module module-bluetooth-policy<br />
.endif<br />
.ifexists module-bluetooth-discover.so<br />
load-module module-bluetooth-discover<br />
.endif<br />
speichern und schliessen<br />
<br />
'''6.''' Jetzt müssen wir dafür sorgen, dass PulseAudio automatisch geladen wird<br />
sudo nano /etc/systemd/system/pulseaudio.service<br />
und folgenden Inhalt einfügen<br />
[Unit]<br />
Description=PulseAudio Sound System<br />
Before=sound.target<br />
[Service]<br />
BusName=org.pulseaudio.Server<br />
ExecStart=/usr/bin/pulseaudio<br />
Restart=always<br />
[Install]<br />
WantedBy=multi-user.target<br />
speichern und schliessen<br />
<br />
'''7.''' Danach müssen wir PulseAudio neu starten<br />
sudo systemctl daemon-reload<br />
sudo systemctl enable pulseaudio.service<br />
sudo systemctl start pulseaudio.service<br />
<br />
'''8.''' Prüfen ob PulseAudio läuft<br />
systemctl status pulseaudio<br />
<br />
'''9.''' Jetzt den Audio Output einstellen: X entweder mit 0 (auto), 1 (3,5mm Klinke) oder 2 (HDMI) ersetzen.<br />
amixer cset numid=3 X<br />
<br />
'''10.''' Die Lautstärke kann auf 100% gesetzt werden mit<br />
amixer set Master 100%<br />
pacmd set-sink-volume 0 65535<br />
<br />
'''11.''' Soundausgabe z.B. über Kopfhörer prüfen<br />
paplay /usr/share/sounds/alsa/Front_Left.wav<br />
<br />
'''12.''' Jetzt können wir mit der Einrichtung von Bluetooth beginnen<br />
sudo nano /etc/bluetooth/audio.conf<br />
und folgendes einfügen<br />
[General]<br />
Enable=Source,Sink,Headset,Gateway,Control,Media,Socket<br />
HFP=true<br />
Class=0x20041C<br />
speichern und schliessen<br />
<br />
sudo nano /etc/bluetooth/main.conf<br />
unter [General] folgendes einfügen<br />
Name = RaspStream<br />
Class = 0x20041C<br />
speichern und schliessen<br />
<br />
sudo hciconfig -a<br />
eingeben und die MAC Adresse des Bluetooth Dongles aufschreiben<br />
<br />
Jetzt müssen die Bluetooth Settings angepasst werden (XX:XX:XX:XX:XX:XX steht für die MAC Adresse des Bluetooth Dongles)<br />
sudo nano /var/lib/bluetooth/XX:XX:XX:XX:XX:XX/settings<br />
einngeben und folgendes einfügen <br />
[General]<br />
Discoverable=true<br />
Alias=RaspStream<br />
Class=0x20041C<br />
speichern und schliessen<br />
<br />
Jetzt muss der Raspberry wieder neu gestartet werden<br />
sudo reboot<br />
<br />
'''13.''' Jetzt können Bluetooth Dongle und Bluetooth Lautsprecher gepaired werden<br />
bluetoothctl<br />
und folgende Befehle nacheinander eingeben<br />
agent on<br />
default-agent<br />
discoverable on<br />
scan on<br />
<br />
der Bluetooth Lautsprecher sollte jetzt irgendwann aufgelistet werden und kann dann gepaired werden mit<br />
pair XX:XX:XX:XX:XX:XX<br />
trust XX:XX:XX:XX:XX:XX<br />
Bluetooth console verlassen mit<br />
quit<br />
<br />
Damit sollte die Einrichtung abgeschlossen sein und wir können überprüfen, ob wir etwas über Bluetooth ausgeben können<br />
mplayer -ao pulse http://stream01.iloveradio.de/iloveradio1.mp3<br />
<br />
'''14.''' Das Modul Text2Speech sollte dann mit 'default' angelegt werden<br />
define TTS Text2Speech default<br />
<br />
==Sprachausgabe per Bluetooth-Lautsprecher (alt)==<br />
{{Randnotiz|RNTyp=Info|RNText=Dieser Abschnitt repränsentiert keinen aktuellen Stand. Bezüglich Verbindung, Neustart alsa und dem Testbeispiel am Ende muss er überarbeitet werden}}<br />
Es ist ebenfalls möglich die Sprachausgabe nicht über einen direkt per Kabel angeschlossenen Lautsprecher auszugeben, sondern per Bluetooth an einen oder mehrere Bluetooth-Lautsprecher. Der Test2Speech-Author verwendet einen CreativeD80.<br />
Dabei ist auf dem Client mit der lokal eingerichteten Text2Speech-Instanz folgendes vorzunehmen:<br />
<br />
'''1.''' USB-Bluetooth-Stick verbinden<br />
per ''lsusb'' prüfen, ob der Stick erkannt wurde.<br />
<br />
'''2.''' Bluetooth Bibliotheken installieren<br />
sudo apt-get install bluez bluez-alsa mplayer<br />
<br />
'''3.''' Bluetooth für eine AudioVerbindung konfigurieren<br />
Dazu in ''/etc/bluetooth/audio.conf'' in der ''General section'' folgendes einfügen:<br />
Enable=Source,Sink,Headset,Gateway,Control,Socket,Media<br />
<br />
'''4.''' den Rechner neu starten<br />
sudo reboot<br />
<br />
'''5.''' Den Bluetooth-Lautsprecher lokalisieren<br />
Den Lautsprecher in den Pairing-Modus versetzen. Erst Dadurch wird er im Scanning sichtbar<br />
hcitool scan<br />
Folgendes Ergebnis sollte kommen:<br />
Scanning ...<br />
11:22:33:44:55:66 device 1<br />
12:34:56:78:90:12 device 2<br />
<br />
'''6.''' eine RFComm Connection vorbereiten<br />
Dazu ''/etc/bluetooth/rfcomm.conf'' editieren und folgendes eintragen (Mac-Adresse anpassen!)<br />
rfcomm0 {<br />
# Automatically bind the device at startup<br />
bind no;<br />
# Bluetooth address of the device<br />
device 11:22:33:44:55:66;<br />
# RFCOMM channel for the connection<br />
channel 3;<br />
# Description of the connection<br />
comment "This is Device 1's serial port.";<br />
}<br />
<br />
'''7.''' rebooten<br />
sudo reboot<br />
<br />
'''8.''' Pairing-Key vergeben<br />
Die meisten Bluetoothgeräte verlangen einen Schlüssel während des Pairings. Der Creative-D80 erwartet den Schlüssel 0000<br />
bluetooth-agent 0000 &<br />
<br />
'''9.''' Das eigentliche Pairing<br />
Den Lautsprecher in den Pairing-Modus versetzen. Danach:<br />
sudo rfcomm connect rfcomm0<br />
Jetzt sollte keine Fehlermeldung mehr kommen....<br />
<br />
'''10.''' Alsa konfigurieren<br />
Der Bluetooth-Lautsprecher muss nun noch als Alsa-Audiodevice bekannt gemacht werden. Dazu muss ''/etc/asound.conf'' erstellt werden. MAC anpassen!<br />
pcm.bluetooth {<br />
type bluetooth<br />
device 11:22:33:44:55:66<br />
profile "auto"<br />
}<br />
<br />
Danach Alsa restarten:<br />
{{Randnotiz|RNTyp=Fehl|RNText=Aktuell bei wheezy /etc/init.d/alsa-utils restart}}<br />
/etc/init.d/alsasound restart<br />
<br />
'''11.''' Testen der Verbindung<br />
{{Randnotiz|RNTyp=Fehl|RNText=Dieses Beispiel geht allein wegen der ständigen Änderung bei Google nicht. Auch stimmt das Ausgabe Gerät nicht. Man könnte so testen :<code>mplayer -ao alsa:device=bluetooth http://1live-diggi.akacast.akamaistream.net/7/965/119435/v1/gnl.akacast.akamaistream.net/1live-diggi</code>}}<br />
:<code>mplayer -ao alsa:device=hw=0.0 -nolirc -noconsolecontrols -http-header-fields "User-Agent:Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/25.0.1364.172 Safari/537.22m" "http://translate.google.com/translate_tts?tl=de&q=Das ist ein Test.";</code><br />
<br />
== Links ==<br />
* Vorstellung und Diskussion des Moduls im {{Link2Forum|Topic=18481|LinkText=FHEM Forum}}<br />
* [[Wetter_vorlesen_lassen|Code Snippets: Wetter vorlesen lassen]]<br />
* [https://aubreykloppers.wordpress.com/2015/06/25/bluetooth-speaker-raspberry-pi-the-ultimate-guide/ weitere Guideline für Bluetooth per Rasperry Pi]<br />
* [https://www.alefo.de/bluetooth-lautsprecher-und-adapter-f63/liste-kompatibler-bt-lautsprecher-fuer-amazon-dot-t907.html Liste von Bluetooth Lautsprechern]<br />
* [http://www.forum-raspberrypi.de/Thread-tutorial-bluetooth-audio-streaming-mit-a2dp-raspbian-jessie Bluetooth einrichten mit Raspbian Jessie]</div>Mumpitzstuff