diff --git a/fhem/FHEM/70_MEDIAPORTAL.pm b/fhem/FHEM/70_MEDIAPORTAL.pm new file mode 100644 index 000000000..d25a3ce8a --- /dev/null +++ b/fhem/FHEM/70_MEDIAPORTAL.pm @@ -0,0 +1,873 @@ +################################################# +# +# 70_MEDIAPORTAL.pm +# Connects to a running MediaPortal instance via the WifiRemote plugin +# +# $Id$ +# +# Changed, adopted and new copyrighted by Reiner Leins (Reinerlein), (c) in February 2016 +# Original Copyright by Andreas Kwasnik (gemx) +# +# Fhem is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# Fhem is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with fhem. If not, see . +# +######################################################################################## +# Changelog +# .02.2016 +# In das offizielle Fhem-Release übernommen +# Allgemein im Code aufgeräumt +# Dokumentation hinzugefügt +# Umlautproblem bei der Titelanzeige behoben +# Mehr Readings befüllt, die sowieso geliefert werden. Dazu gehören z.B. Titelinformationen bei TV, Beschreibungen und die Informationen über den nächsten Titel. +# $readingsFnAttributes hinzugefügt. Damit geht z.B. stateFormat oder event-on-change-reading +# Fehlende Titelanzeige bei initialem Start der Wiedergabe behoben +# WakeUp und Sleep hinzugefügt, damit man schnell den entsprechenden Mediaportal-Rechner hochfahren bzw. in den Hibernate-Modus schalten kann. Dazu wurde ein Attribut "macaddress" eingeführt. +# Mögliche Parameter für Get und Set angegeben, sodass diese in FhemWeb entsprechend angeboten werden. +# Volume umbenannt, damit das Reading die Grundlage für die Lautstärkeauswahl (Slider) ist. +# Es gibt jetzt ein Attribut "generateNowPlayingUpdateEvents", mit dem man die Generierung von (bei der Wiedergabe) sekündlichen Aktualisierungen an-/abschalten kann +# Die Mac-Adresse, die für das Aufwecken benötigt wird, wird nun automatisch ermittelt. +# Die Read-Callbackfunktion wurde überarbeitet, da in einigen Fällen halbe Nachrichten zu einem Freeze geführt hatten. +# Es gibt jetzt einen Setter "reconnect", der eine neue Verbindung zu Mediaportal aufbaut. +# Wenn festgestellt wird, dass eine Verbindung zu Mediaportal nicht mehr lebendig ist, wird ein reconnect ausgeführt. +# +############################################################################## +package main; + +use strict; +use warnings; +use Time::HiRes qw(gettimeofday); +use DevIo; +use JSON; +use HttpUtils; + +use Data::Dumper; + +# Forward-Declarations +sub MEDIAPORTAL_Set($@); +sub MEDIAPORTAL_Log($$$); + +my $MEDIAPORTAL_Interval = 15; + +######################################################################################## +# +# MEDIAPORTAL_Initialize +# +######################################################################################## +sub MEDIAPORTAL_Initialize($) { + my ($hash) = @_; + + require "$attr{global}{modpath}/FHEM/DevIo.pm"; + + $hash->{ReadFn} = 'MEDIAPORTAL_Read'; + $hash->{ReadyFn} = 'MEDIAPORTAL_Ready'; + $hash->{GetFn} = 'MEDIAPORTAL_Get'; + $hash->{SetFn} = 'MEDIAPORTAL_Set'; + $hash->{DefFn} = 'MEDIAPORTAL_Define'; + $hash->{UndefFn} = 'MEDIAPORTAL_Undef'; + #$hash->{AttrFn} = 'MEDIAPORTAL_Attr'; + $hash->{AttrList} = 'authmethod:none,userpassword,passcode,both username password generateNowPlayingUpdateEvents:1,0 macaddress '.$readingFnAttributes; + + $hash->{STATE} = 'Initialized'; +} + +######################################################################################## +# +# MEDIAPORTAL_Define +# +######################################################################################## +sub MEDIAPORTAL_Define($$) { + my ($hash, $def) = @_; + + my @a = split("[ \t][ \t]*", $def); + if(@a != 3) { + my $msg = 'wrong syntax: define MEDIAPORTAL ip[:port]'; + MEDIAPORTAL_Log $hash->{NAME}, 2, $msg; + return $msg; + } + DevIo_CloseDev($hash); + + my $name = $a[0]; + my $dev = $a[2]; + $dev .= ":8017" if ($dev !~ m/:/ && $dev ne "none" && $dev !~ m/\@/); + + $hash->{DeviceName} = $dev; + $hash->{STATE} = 'Disconnected'; + + my $ret = DevIo_OpenDev($hash, 0, 'MEDIAPORTAL_DoInit'); + + return $ret; +} + +######################################################################################## +# +# MEDIAPORTAL_Undef +# +######################################################################################## +sub MEDIAPORTAL_Undef($$) { + my ($hash, $arg) = @_; + + RemoveInternalTimer($hash); + DevIo_CloseDev($hash); + + return undef; +} + +######################################################################################## +# +# MEDIAPORTAL_Get +# +######################################################################################## +sub MEDIAPORTAL_Get($@) { + my ($hash, @a) = @_; + + my $cname = $a[1]; + my $cmd = ''; + + if ($cname eq "status") { + $cmd = "{\"Type\":\"requeststatus\"}\r\n"; + } elsif ($cname eq "nowplaying") { + $cmd = "{\"Type\":\"requestnowplaying\"}\r\n"; + } elsif ($cname eq "notify") { + $cmd = '{"Type":"properties","Properties":["#Play.Current.Title","#TV.View.title"]}'."\r\n"; + } else { + return "Unknown command '$cname', choose one of status:noArg nowplaying:noArg"; + } + + DevIo_SimpleWrite($hash, $cmd, 0); + return undef; +} + +######################################################################################## +# +# MEDIAPORTAL_GetStatus +# +######################################################################################## +sub MEDIAPORTAL_GetStatus($) { + my ($hash) = @_; + + MEDIAPORTAL_Get($hash, ($hash->{NAME}, 'status')); +} + +######################################################################################## +# +# MEDIAPORTAL_GetIntervalStatus +# +######################################################################################## +sub MEDIAPORTAL_GetIntervalStatus($) { + my ($hash) = @_; + + return undef if (ReadingsVal($hash->{NAME}, 'state', 'disconnected') eq 'disconnected'); + + # Prüfen, wann der letzte Status zugestellt wurde... + if (time() - $hash->{helper}{LastStatusTimestamp} > (2 * $MEDIAPORTAL_Interval + 5)) { + MEDIAPORTAL_Log $hash->{NAME}, 3, 'GetIntervalStatus hat festgestellt, dass Mediaportal sich seit '.(time() - $hash->{helper}{LastStatusTimestamp}).'s nicht zurückgemeldet hat. Die Verbindung wird neu aufgebaut!'; + + MEDIAPORTAL_Set($hash, ($hash->{NAME}, 'reconnect')); + InternalTimer(gettimeofday() + $MEDIAPORTAL_Interval, 'MEDIAPORTAL_GetIntervalStatus', $hash, 0); + + return undef; + } + + # Status anfordern... + MEDIAPORTAL_Get($hash, ($hash->{NAME}, 'status')); + InternalTimer(gettimeofday() + $MEDIAPORTAL_Interval, 'MEDIAPORTAL_GetIntervalStatus', $hash, 0); +} + +######################################################################################## +# +# MEDIAPORTAL_GetNowPlaying +# +######################################################################################## +sub MEDIAPORTAL_GetNowPlaying($) { + my ($hash) = @_; + + MEDIAPORTAL_Get($hash, ($hash->{NAME}, 'nowplaying')); +} + +######################################################################################## +# +# MEDIAPORTAL_Set +# +######################################################################################## +sub MEDIAPORTAL_Set($@) { + my ($hash, @a) = @_; + + my $cname = $a[1]; + my $cmd = ''; + my $powermodes = 'logoff suspend hibernate reboot shutdown exit'; + my $mpcommands = 'stop record pause play rewind forward replay skip back info menu up down left right ok volup voldown volmute chup chdown dvdmenu 0 1 2 3 4 5 6 7 8 9 0 clear enter teletext red blue yellow green home basichome nowplaying tvguide tvrecs dvd playlists first last fullscreen subtitles audiotrack screenshot'; + my $playlistcommands = 'play loadlist loadlist_shuffle loadfrompath loadfrompath_shuffle'; + + # Legacy Volume writing... + $cname = 'Volume' if (lc($cname) eq 'volume'); + + if ($cname eq "command") { + if (!MEDIAPORTAL_isInList($a[2], split(/ /, $mpcommands))) { + return "Unknown command '$a[2]'. Supported commands are: $mpcommands"; + } + + $cmd = "{\"Type\":\"command\",\"Command\":\"$a[2]\"}\r\n"; + } elsif ($cname eq "wakeup") { + my $macaddress = AttrVal($hash->{NAME}, 'macaddress', ''); + + if ($macaddress ne '') { + MEDIAPORTAL_Wakeup($macaddress); + return 'WakeUp-Signal sent!'; + } else { + return 'No MacAddress set! No WakeUp-Signal sent!'; + } + } elsif ($cname eq "sleep") { + return MEDIAPORTAL_Set($hash, ($hash->{NAME}, 'powermode', 'hibernate')); + } elsif ($cname eq "key") { + $cmd = "{\"Type\":\"key\",\"Key\":\"$a[2]\"}\r\n"; + } elsif ($cname eq "Volume") { + if (($a[2] ne $a[2]+0) || ($a[2]<0) || ($a[2]>100)) { + return "the volume must be in the range 0..100"; + } + + $cmd = "{\"Type\":\"volume\",\"Volume\":$a[2]}\r\n"; + } elsif ($cname eq "powermode") { + if (!MEDIAPORTAL_isInList($a[2], split(/ /, $powermodes))) { + return "Unknown powermode '$a[2]'. Supported powermodes are: $powermodes"; + } + + $cmd = "{\"Type\":\"powermode\",\"PowerMode\":\"$a[2]\"}\r\n"; + } elsif ($cname eq "playfile") { + $cmd = "{\"Type\":\"playfile\",\"FileType\":\"$a[2]\",\"Filepath\":\"$a[3]\"}\r\n"; + } elsif ($cname eq "playchannel") { + if ($a[2] ne $a[2]+0) { + return "playchannel needs a valid channelid of type int"; + } + + $cmd = "{\"Type\":\"playchannel\",\"ChannelId\":$a[2]}\r\n"; + } elsif ($cname eq "playradiochannel") { + if ($a[2] ne $a[2]+0) { + return "playradiochannel needs a valid channelid of type int"; + } + + $cmd = "{\"Type\":\"playradiochannel\",\"ChannelId\":$a[2]}\r\n"; + } elsif ($cname eq "playlist") { + if (!MEDIAPORTAL_isInList($a[2], split(/ /, $playlistcommands))) { + return "Unknown playlist command '$a[2]'. Supported commands are: $playlistcommands"; + } + + if ($a[2] eq "play") { + if ($a[3] ne $a[3]+0) { + return "playlist play needs a valid index to start of type int"; + } + + $cmd = "{\"Type\":\"playlist\",\"PlaylistAction\":\"play\",\"Index\":$a[3]}\r\n"; + } elsif ($a[2] eq "loadlist") { + $cmd = "{\"Type\":\"playlist\",\"PlaylistAction\":\"load\",\"PlaylistName\":\"$a[3]\"}\r\n"; + } elsif ($a[2] eq "loadlist_shuffle") { + $cmd = "{\"Type\":\"playlist\",\"PlaylistAction\":\"load\",\"PlaylistName\":\"$a[3]\",\"Shuffle\":true}\r\n"; + } elsif ($a[2] eq "loadfrompath") { + $cmd = "{\"Type\":\"playlist\",\"PlaylistAction\":\"load\",\"PlaylistPath\":\"$a[3]\"}\r\n"; + } elsif ($a[2] eq "loadfrompath_shuffle") { + $cmd = "{\"Type\":\"playlist\",\"PlaylistAction\":\"load\",\"PlaylistPath\":\"$a[3]\",\"Shuffle\":true}\r\n"; + } + } elsif ($cname eq "connect") { + $hash->{NEXT_OPEN} = 0; # force NEXT_OPEN used in DevIO + + return undef; + } elsif ($cname eq "reconnect") { + DevIo_CloseDev($hash); + select(undef, undef, undef, 0.2); + DevIo_OpenDev($hash, 0, 'MEDIAPORTAL_DoInit'); + + return undef; + } else { + return "Unknown command '$cname', choose one of wakeup:noArg sleep:noArg connect:noArg reconnect:noArg command:".join(',', split(/ /, $mpcommands))." key Volume:slider,0,1,100 powermode:".join(',', split(/ /, $powermodes))." playfile playchannel playradiochannel playlist"; + } + + DevIo_SimpleWrite($hash, $cmd, 0); + return undef; +} + +######################################################################################## +# +# MEDIAPORTAL_Read +# Receives an event and creates several readings for event triggering +# +######################################################################################## +sub MEDIAPORTAL_Read($) { + my ($hash) = @_; + + my $buf = DevIo_SimpleRead($hash); + if(!defined($buf)) { + MEDIAPORTAL_Log $hash->{NAME}, 3, 'DevIo_SimpleRead hat keine Daten geliefert, obwohl Read aufgerufen wurde! Setze Buffer zurück. Aktueller Buffer: '.$hash->{helper}{buffer}; + $hash->{helper}{buffer} = ''; + return undef; + } + + MEDIAPORTAL_Log $hash->{NAME}, 5, "RAW MSG: $buf"; + + # Zum Buffer hinzufügen + $hash->{helper}{buffer} .= $buf; + + # Bereits vollständige JSON-Strings verarbeiten... + my @groups = $hash->{helper}{buffer} =~ m/({(?:[^{}]++|(?1))*})/xg; + for my $elem (@groups) { + MEDIAPORTAL_ProcessMessage($hash, $elem); + } + + # Bereits verarbeitetes aus dem Buffer wieder entfernen... + $hash->{helper}{buffer} =~ s/[ \r\n]*({(?:[^{}]++|(?1))*})[ \r\n]*//xg; + + return undef; +} + +######################################################################################## +# +# MEDIAPORTAL_ProcessMessage +# +######################################################################################## +sub MEDIAPORTAL_ProcessMessage($$) { + my ($hash, $msg) = @_; + + MEDIAPORTAL_Log $hash->{NAME}, 5, "Message received: $msg"; + + my $json = {}; + eval { + $json = from_json($msg); + }; + if ($@) { + MEDIAPORTAL_Log $hash->{NAME}, 5, "Error during JSON-Parser with 'from_json()'-call (but just keep trying another way): $@"; + + eval { + $json = decode_json(decode('iso8859-1', $msg)); + }; + if ($@) { + MEDIAPORTAL_Log $hash->{NAME}, 1, "Final Error during JSON-Parser: $@"; + return; + } + } + + if (defined($json->{Type})) { + if ($json->{Type} eq "welcome") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'WELCOME received. Sending identify message.'; + + DevIo_SimpleWrite($hash, MEDIAPORTAL_GetMSG_identify($hash), 0); + } elsif ($json->{Type} eq "authenticationresponse") { + MEDIAPORTAL_Log $hash->{NAME}, 4, "AUTHRESPONSE received. SUCCESS=$json->{Success}"; + + $hash->{STATE} = "Authenticated. Processing messages."; + } elsif ($json->{Type} eq "status") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'STATUS received.'; + + my $playStatus = 'Stopped'; + $playStatus = 'Playing' if ($json->{IsPlaying}); + $playStatus = 'Paused' if ($json->{IsPaused}); + + my $title = ''; + $title = $json->{Title} if (defined($json->{Title}) && $json->{Title}); + if (defined($json->{SelectedItem}) && $json->{SelectedItem} ne '' && $title eq '') { + $title = 'Auswahl: '.$json->{SelectedItem}; + + # Wenn der Titel während des Abspielens nicht mitgeliefert wurde, dann für später nochmal anfordern... + # Das ist ein Bug in Wifiremote, das den Titel beim Start nicht immer mitliefert. + if ($json->{IsPlaying}) { + InternalTimer(gettimeofday() + 5, 'MEDIAPORTAL_GetNowPlaying', $hash, 0); + } + } + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'IsPlaying', $json->{IsPlaying}); + readingsBulkUpdate($hash, 'IsPaused', $json->{IsPaused}); + readingsBulkUpdate($hash, 'playStatus', $playStatus); + readingsBulkUpdate($hash, 'CurrentModule', $json->{CurrentModule}); + readingsBulkUpdate($hash, 'Title', $title); + readingsEndUpdate($hash, 1); + + $hash->{helper}{LastStatusTitle} = $title; + $hash->{helper}{LastStatusTimestamp} = time(); + } elsif ($json->{Type} eq "volume") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'VOLUME received.'; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'Volume', $json->{Volume}); + readingsBulkUpdate($hash, 'IsMuted', $json->{IsMuted}); + readingsEndUpdate($hash, 1); + } elsif ($json->{Type} eq "nowplaying") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'NOWPLAYING received.'; + + readingsBeginUpdate($hash); + + readingsBulkUpdate($hash, 'Duration', MEDIAPORTAL_ConvertSecondsToTime($json->{Duration})); + readingsBulkUpdate($hash, 'Position', MEDIAPORTAL_ConvertSecondsToTime($json->{Position})); + readingsBulkUpdate($hash, 'File', $json->{File}); + + readingsBulkUpdate($hash, 'Title', ''); + readingsBulkUpdate($hash, 'Description', ''); + readingsBulkUpdate($hash, 'nextTitle', ''); + readingsBulkUpdate($hash, 'nextDescription', ''); + + if (defined($json->{MediaInfo})) { + if ($json->{MediaInfo}{MediaType} eq 'tv') { + readingsBulkUpdate($hash, 'Title', $json->{MediaInfo}{ChannelName}.' - '.$json->{MediaInfo}{CurrentProgramName}); + readingsBulkUpdate($hash, 'Description', $json->{MediaInfo}{CurrentProgramDescription}); + + if (defined($json->{MediaInfo}{NextProgramName})) { + readingsBulkUpdate($hash, 'nextTitle', $json->{MediaInfo}{ChannelName}.' - '.$json->{MediaInfo}{NextProgramName}); + readingsBulkUpdate($hash, 'nextDescription', $json->{MediaInfo}{NextProgramDescription}); + } + } elsif ($json->{MediaInfo}{MediaType} eq 'movie') { + readingsBulkUpdate($hash, 'Title', $json->{MediaInfo}{Title}); + readingsBulkUpdate($hash, 'Description', $json->{MediaInfo}{Summary}); + } elsif ($json->{MediaInfo}{MediaType} eq 'series') { + readingsBulkUpdate($hash, 'Title', $json->{MediaInfo}{Series}.' '.$json->{MediaInfo}{Season}.'x'.$json->{MediaInfo}{Episode}.' - '.$json->{MediaInfo}{Title}); + readingsBulkUpdate($hash, 'Description', $json->{MediaInfo}{Plot}); + } else { + MEDIAPORTAL_Log $hash->{NAME}, 0, 'MediaInfo für "'.$json->{MediaInfo}{MediaType}.'" geliefert, aber nicht verarbeitet. Bitte diese komplette Information ins Forum einstellen: '.Dumper($json->{MediaInfo}); + } + } else { + # Die MediaInfos wurden nicht mitgeliefert... + # Hier nochmal versuchen, den Titel zu extrahieren... + my $title = ''; + $title = $json->{Title} if (defined($json->{Title}) && $json->{Title}); + + if ($title eq '') { + readingsBulkUpdate($hash, 'Title', $hash->{helper}{LastStatusTitle}); + } else { + readingsBulkUpdate($hash, 'Title', $title); + } + } + + readingsEndUpdate($hash, 1); + } elsif ($json->{Type} eq "nowplayingupdate") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'NOWPLAYINGUPDATE received.'; + + readingsBeginUpdate($hash); + readingsBulkUpdate($hash, 'Duration', MEDIAPORTAL_ConvertSecondsToTime($json->{Duration})); + readingsBulkUpdate($hash, 'Position', MEDIAPORTAL_ConvertSecondsToTime($json->{Position})); + readingsEndUpdate($hash, AttrVal($hash->{NAME}, 'generateNowPlayingUpdateEvents', 0)); + } elsif ($json->{Type} eq "properties") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'PROPERTIES received.'; + + MEDIAPORTAL_Log undef, 4, 'JSON: '.Dumper($json); + } elsif ($json->{Type} eq "facadeinfo") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'FACADEINFO5 received.'; + } elsif ($json->{Type} eq "dialog") { + MEDIAPORTAL_Log $hash->{NAME}, 4, 'DIALOG received.'; + } else { + MEDIAPORTAL_Log $hash->{NAME}, 1, "Unhandled message received: MessageType '$json->{Type}'"; + } + } else { + MEDIAPORTAL_Log $hash->{NAME}, 1, 'Unhandled message received without any Messagetype: '.$msg; + } +} + +######################################################################################## +# +# MEDIAPORTAL_DoInit +# +######################################################################################## +sub MEDIAPORTAL_DoInit($) { + my ($hash) = @_; + + $hash->{STATE} = 'Connecting...'; + $hash->{helper}{buffer} = ''; + $hash->{helper}{LastStatusTimestamp} = time(); + + # Versuch, die MAC-Adresse des Ziels selber herauszufinden... + if (AttrVal($hash->{NAME}, 'macaddress', '') eq '') { + my $newmac = MEDIAPORTAL_GetMAC($hash); + + if (defined($newmac)) { + CommandAttr(undef, $hash->{NAME}.' macaddress '.$newmac); + } + } + + RemoveInternalTimer($hash); + InternalTimer(gettimeofday() + $MEDIAPORTAL_Interval, 'MEDIAPORTAL_GetIntervalStatus', $hash, 0); + + return undef; +} + +######################################################################################## +# +# MEDIAPORTAL_Ready +# +######################################################################################## +sub MEDIAPORTAL_Ready($) { + my ($hash) = @_; + + return DevIo_OpenDev($hash, 1, 'MEDIAPORTAL_DoInit'); +} + +######################################################################################## +# +# MEDIAPORTAL_Attr +# +######################################################################################## +#sub MEDIAPORTAL_Attr($@) { +# my (@a) = @_; +# +# my $hash = $defs{$a[1]}; +# my $name = $hash->{NAME}; +# +# MEDIAPORTAL_Log $hash->{NAME}, 5,"********** 1 $a[1] 2 $a[2]"; +# if($a[0] eq 'set') { +# } +# +# return undef; +#} + +######################################################################################## +# +# MEDIAPORTAL_GetMSG_identify +# +######################################################################################## +sub MEDIAPORTAL_GetMSG_identify($) { + my ($hash) = @_; + + my $authmethod=AttrVal($hash->{NAME}, 'authmethod', 'none'); + my $uid = AttrVal($hash->{NAME}, 'username', ''); + my $pwd = AttrVal($hash->{NAME}, 'password', ''); + my $cmd = { + Type => 'identify', + Name => 'MP_Connector', + Application => 'FHEM', + Version => '1.0' + }; + if ($authmethod ne "none") { + $cmd->{Authenticate}{AuthMethod} = $authmethod; + $cmd->{Authenticate}{User} = $uid; + $cmd->{Authenticate}{Password} = $pwd; + } + my $strcmd = encode_json($cmd)."\r\n"; + + return $strcmd; +} + +######################################################################################## +# +# MEDIAPORTAL_GetMAC +# +######################################################################################## +sub MEDIAPORTAL_GetMAC($) { + my ($hash) = @_; + my $mac = undef; + + eval { + my ($host, $port) = split(/:/, $hash->{DeviceName}); + + my $result = qx/arp -a $host/; + MEDIAPORTAL_Log undef, 5, 'ARP-SysCall: '.$result; + + $mac = uc($1) if ($result =~ m/([0-9a-fA-F]{2}(:|-)[0-9a-fA-F]{2}(:|-)[0-9a-fA-F]{2}(:|-)[0-9a-fA-F]{2}(:|-)[0-9a-fA-F]{2}(:|-)[0-9a-fA-F]{2})/s); + $mac =~ s/-/:/g if (defined($mac)); # Korrektur für Windows-Rechner + + if (defined($mac)) { + MEDIAPORTAL_Log undef, 5, 'Found Mac: '.$mac; + } else { + MEDIAPORTAL_Log undef, 5, 'No Mac Found!'; + } + }; + if ($@) { + return undef; + } + + return undef if (defined($mac) && ($mac eq '00:00:00:00:00:00')); # Unter Windows wird im Fehlerfall diese Adresse zurückgegeben. + return $mac; +} + +######################################################################################## +# +# MEDIAPORTAL_Wakeup +# +######################################################################################## +sub MEDIAPORTAL_Wakeup($;$$) { + my ($hwaddr, $ipaddr, $port) = @_; + + $ipaddr = '255.255.255.255' if (!defined($ipaddr)); + $port = getservbyname('discard', 'udp') if (!defined($port)); + + # Zur Sicherheit zweimal senden... + return MEDIAPORTAL_DoWakeup($hwaddr, $ipaddr, $port) || MEDIAPORTAL_DoWakeup($hwaddr, $ipaddr, $port); +} + +######################################################################################## +# +# MEDIAPORTAL_DoWakeup +# +######################################################################################## +sub MEDIAPORTAL_DoWakeup($;$$) { + my ($hwaddr, $ipaddr, $port) = @_; + + $ipaddr = '255.255.255.255' if (!defined($ipaddr)); + $port = getservbyname('discard', 'udp') if (!defined($port)); + + # Validate hardware address (ethernet address) + my $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6); + if ($hwaddr !~ m/^$hwaddr_re$/) { + warn "Invalid hardware address: $hwaddr\n"; + return undef; + } + + # Generate magic sequence + my $pkt = ''; + foreach (split /:/, $hwaddr) { + $pkt .= chr(hex($_)); + } + $pkt = chr(0xFF) x 6 . $pkt x 16; + + # Allocate socket and send packet + my $raddr = gethostbyname($ipaddr); + my $them = pack_sockaddr_in($port, $raddr); + my $proto = getprotobyname('udp'); + + socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!"; + setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!"; + + send(S, $pkt, 0, $them) or die "send : $!"; + close S; + + return 1; +} + +######################################################################################## +# +# MEDIAPORTAL_ConvertSecondsToTime +# +######################################################################################## +sub MEDIAPORTAL_ConvertSecondsToTime($) { + my ($seconds) = @_; + + return sprintf('%01d:%02d:%02d', $seconds / 3600, ($seconds%3600) / 60, $seconds%60) if ($seconds > 0); + return '0:00:00'; +} + +######################################################################################## +# +# MEDIAPORTAL_isInList +# +######################################################################################## +sub MEDIAPORTAL_isInList($@) { + my($search, @list) = @_; + + return 1 if MEDIAPORTAL_posInList($search, @list) >= 0; + return 0; +} + +######################################################################################## +# +# MEDIAPORTAL_posInList +# +######################################################################################## +sub MEDIAPORTAL_posInList($@) { + my($search, @list) = @_; + + for (my $i = 0; $i <= $#list; $i++) { + return $i if ($list[$i] && $search eq $list[$i]); + } + + return -1; +} + +######################################################################################## +# +# MEDIAPORTAL_Log - Log to the normal Log-command with the prefix 'SONOSPLAYER' +# +######################################################################################## +sub MEDIAPORTAL_Log($$$) { + my ($devicename, $level, $text) = @_; + + Log3 $devicename, $level, 'MEDIAPORTAL: '.$text; +} + +1; + +=pod +=begin html + + +

MEDIAPORTAL

+

Connects to a running MediaPortal instance via the WifiRemote plugin

+

Example

+

+define wohnzimmer_Mediaportal MEDIAPORTAL 192.168.0.47:8017 +

+ +

Define

+define <name> MEDIAPORTAL host[:port] +

Define a Mediaportal interface to communicate with a Wifiremote-Plugin of a Mediaportal-System.
+

+host[:port]
The name and port of the Mediaportal-Wifiremote-Plugin. If Port is not given, the default of 8017 will be used.

+ +

Set

+ + +

Get

+ + +

Attributes

+ + +=end html + +=begin html_DE + + +

MEDIAPORTAL

+

Verbindet sich über das Wifiremote-Plugin mit einer laufenden Mediaportal-Instanz.

+

Beispiel

+

+define wohnzimmer_Mediaportal MEDIAPORTAL 192.168.0.47:8017 +

+ +

Define

+define <name> MEDIAPORTAL host[:port] +

Definiert ein Mediaportal Interface für die Kommunikation mit einem Wifiremote-Plugin einer Mediaportal Installation.
+

+host[:port]
Der Hostname und der Port eines laufenden Mediaportal-Wifiremote-Plugins. Wenn der Port nicht angegeben wurde, wird 8017 als Standard verwendet.

+ +

Set

+ + +

Get

+ + +

Attribute

+ + +=end html_DE +=cut \ No newline at end of file