From e84c966291627da45f187ef8461884706f4c7b77 Mon Sep 17 00:00:00 2001 From: Reinerlein <> Date: Tue, 30 Jun 2020 10:49:29 +0000 Subject: [PATCH] SONOS: New release with many bugfixes and features. See changelog for details. git-svn-id: https://svn.fhem.de/fhem/trunk@22308 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/00_SONOS.pm | 618 ++++++++++++++++++----------- fhem/FHEM/21_SONOSPLAYER.pm | 466 +++++++++++----------- fhem/FHEM/lib/UPnP/ControlPoint.pm | 49 ++- 3 files changed, 648 insertions(+), 485 deletions(-) diff --git a/fhem/FHEM/00_SONOS.pm b/fhem/FHEM/00_SONOS.pm index 0136d0b13..30b98a9bc 100755 --- a/fhem/FHEM/00_SONOS.pm +++ b/fhem/FHEM/00_SONOS.pm @@ -1,6 +1,6 @@ ######################################################################################## # -# SONOS.pm (c) by Reiner Leins, March 2018 +# SONOS.pm (c) by Reiner Leins, June 2020 # rleins at lmsoft dot de # # $Id$ @@ -51,6 +51,24 @@ # Changelog (last 4 entries only, see Wiki for complete changelog) # # SVN-History: +# 30.06.2020 +# Sonos Playbase und Beam (S11 und S14) zu den Playern mit Optischer Eingangswahl hinzugefügt. +# Bei "setCurrentTrackPosition" kann man nun auch Sekunden angeben (auch mit Vorzeichen-Prefix und/oder Prozent-Suffix). +# Beim Aktualisieren von "DailyIndexRefreshTime" wurde ein bestehender Bulk-Update unterbrochen. +# In der ControlPoint.pm wurde eine Anpassung für den Standardmäßigen Namespace-Prefix eingebaut. Dieser kann nun beim Initialisieren des ControlPoint-Proxy über den Parameter 'EnvNamespace' definiert werden. Wenn der Parameter nicht mit angegeben wird, dann wird wie bisher 'u' verwendet, wird '' (als Text) angegeben, dann wird der ganze Namespace weggelassen. (nicht für das Sonos-Modul relevant). +# Beim Verbindungsverlust wird nun wirklich versucht einen SubProzess neuzustarten. Das konnte schiefgehen, wenn der Port vom Betriebssystem noch nicht wieder freigegeben wurde. +# Bei der Auswertung der MusicServiceList gibt es jetzt Manifest-Verweise, die gesondert bearbeitet werden müssen (z.B. fehlten die Spotify-Informationen) +# Bei der Auswertung der MusicServiceList wurden SSL-Dokumente nicht korrekt nachgeladen. +# Beim Abbau von Sockets wird nun immer SO_LINGER gesetzt, sodass alle Port sofort freigegeben werden sollten +# Der Timeout von Renew-Anforderungen und Befehlsübergaben in der ControlPoint an die Player wurde von 20s auf 5s heruntergesetzt. +# Der PingType "SYN" wurde korrigiert, und sollte nun korrekte Ergebnisse liefern. +# In der ControlPoint wurde nun konsequent ReusePort eingebaut (sofern gesetzt). +# Fehler aus den ControlPoint-Methoden werden nun sauber gefangen und ausgewertet. +# Das Erneuern der Subscriptions am Player wurde nicht zu FHEM durchgemeldet, und zu selten versucht. +# Es gibt ein neues Attribut "allowedWebAccess", mit dem man den Zugriff auf Nachladeadressen beschränken kann. +# Commandref angepasst, sodass nun bei der Auswahl eines Attributes/Getter/Setter in FHEMWEB der passende Teil der Commandref daneben angezeigt wird. +# Der fork()-Befehl wurde durch den von FHEM bereitgestellten fhemFork()-Befehl ersetzt. Dort werden noch offene Filehandles und Sockets behandelt. +# Wenn man Player von der Erkennung ausgeschlossen hatte, dann gab es Fehlermeldungen bzgl. unbekannter Player bei der Gruppierungsübermittlung (da Sonos davon ja nichts weiß) # 25.04.2018 # "Deep Recursion"-Warnung beim loggen wird nun verhindert # Beim Erzeugen der Gruppen-ReadingsGroup ist bei der Verwendung eines Boosts ab und zu ein Fehler aufgetreten @@ -63,13 +81,6 @@ # Bei einigen Positionsabfragen an die Player wurden Sonderfälle (wie NOT_IMPLEMENTED) nicht berücksichtigt. # Slider-Wertebereich für Bass und Treble auf den Bereich -10..10 korrigiert. # Es gibt jetzt ein Reading "IsZoneBridge", das 0 oder 1 sein kann. Danach wird jetzt auch entschieden, ob eine Playersteuerung dargestellt wird, oder nicht. -# 10.03.2018 -# Die PlayBase kann nun auch den SPDIF-Eingang aktivieren (wie die PlayBar) -# Wenn man über Alexa Musik hört, wird das aktuelle Cover nun auch angezeigt. -# Wenn ein Player nach einem Neustart disabled war, und während des Betriebs enabled wird, wird nun versucht ihn wiederzufinden. -# Wenn ein Player disabled oder disappeared ist, wird ein Proxy-Cover-Zugriffsversuch auf diesen Player unterbunden. -# Ein Modify-Befehlsaufruf wird nun am Vorhandensein von $hash->{OLDDEF} erkannt. -# Bei einigen PERL-Installationen stand im Reading 'currentTrackPositionSimulatedSec' eine Kommazahl (da sie von time() aus berechnet wird). Diese Zahl wird nun gerundet. # ######################################################################################## # @@ -116,7 +127,6 @@ use feature 'unicode_strings'; use Digest::MD5 qw(md5_hex); use File::Temp; use File::Copy; -# use Encode::Guess; use Data::Dumper; $Data::Dumper::Terse = 1; @@ -174,19 +184,11 @@ if (lc(substr($0, -7)) eq 'fhem.pl') { $gPath = $attr{global}{modpath}.'/FHEM'; } use lib ($gPath.'/lib', $gPath.'/FHEM/lib', './FHEM/lib', './lib', './FHEM', './', '/usr/local/FHEM/share/fhem/FHEM/lib'); -# print 'Current: "'.$0.'", gPath: "'.$gPath."\"\n"; if (lc(substr($0, -7)) eq 'fhem.pl') { require 'DevIo.pm'; } else { use UPnP::ControlPoint; - - ######################################################## - # Change all carp-calls in the UPnP-Module to croak-calls - # This will ensure you can "catch" carp with an enclosing - # "eval{}"-Block - ######################################################## - #*UPnP::ControlPoint::carp = \&UPnP::ControlPoint::croak; } my $startedbyfhem = SONOS_isInList('startedbyfhem', @ARGV); @@ -217,13 +219,14 @@ my %sets = ( ); my @SONOS_PossibleDefinitions = qw(NAME INTERVAL); -my @SONOS_PossibleAttributes = qw(targetSpeakFileHashCache targetSpeakFileTimestamp targetSpeakDir targetSpeakURL targetSpeakMP3FileDir targetSpeakMP3FileConverter SpeakGoogleURL Speak0 Speak1 Speak2 Speak3 Speak4 SpeakCover Speak1Cover Speak2Cover Speak3Cover Speak4Cover minVolume maxVolume minVolumeHeadphone maxVolumeHeadphone getAlarms disable generateVolumeEvent buttonEvents generateProxyAlbumArtURLs proxyCacheTime bookmarkSaveDir bookmarkTitleDefinition bookmarkPlaylistDefinition coverLoadTimeout getListsDirectlyToReadings getFavouritesListAtNewVersion getPlaylistsListAtNewVersion getRadiosListAtNewVersion getQueueListAtNewVersion getTitleInfoFromMaster stopSleeptimerInAction saveSleeptimerInAction webname SubProcessLogfileName); +my @SONOS_PossibleAttributes = qw(verbose targetSpeakFileHashCache targetSpeakFileTimestamp targetSpeakDir targetSpeakURL targetSpeakMP3FileDir targetSpeakMP3FileConverter SpeakGoogleURL Speak0 Speak1 Speak2 Speak3 Speak4 SpeakCover Speak1Cover Speak2Cover Speak3Cover Speak4Cover minVolume maxVolume minVolumeHeadphone maxVolumeHeadphone getAlarms disable generateVolumeEvent buttonEvents generateProxyAlbumArtURLs proxyCacheTime bookmarkSaveDir bookmarkTitleDefinition bookmarkPlaylistDefinition coverLoadTimeout getListsDirectlyToReadings getFavouritesListAtNewVersion getPlaylistsListAtNewVersion getRadiosListAtNewVersion getQueueListAtNewVersion getTitleInfoFromMaster stopSleeptimerInAction saveSleeptimerInAction webname SubProcessLogfileName allowedWebAccess); my @SONOS_PossibleReadings = qw(AlarmList AlarmListIDs UserID_Spotify UserID_Napster location SleepTimerVersion Mute OutputFixed HeadphoneConnected Balance Volume Loudness Bass Treble TruePlay SurroundEnable SurroundLevel SubEnable SubGain SubPolarity AudioDelay AudioDelayLeftRear AudioDelayRightRear NightMode DialogLevel AlarmListVersion ZonePlayerUUIDsInGroup ZoneGroupState ZoneGroupID fieldType IsBonded ZoneGroupName roomName roomNameAlias roomIcon currentTransportState transportState TransportState LineInConnected presence currentAlbum currentArtist currentTitle currentStreamAudio GroupVolume GroupMute FavouritesVersion RadiosVersion PlaylistsVersion QueueVersion QueueHash GroupMasterPlayer ShareIndexInProgress DirectControlClientID DirectControlIsSuspended DirectControlAccountID IsMaster MasterPlayer SlavePlayer ButtonState ButtonLockState AllPlayer LineInName LineInIcon MusicServicesListVersion MusicServicesList WifiEnabled WirelessMode Orientation); -# Communication between the two "levels" of threads +# Communication between the different "levels" of threads my $SONOS_Client_ReceiveQueue = Thread::Queue->new(); my %SONOS_PlayerRestoreRunningUDN :shared = (); +my $SONOS_PlayerRestoreRunningQueue = Thread::Queue->new(); my $SONOS_PlayerRestoreQueue = Thread::Queue->new(); my $SONOS_LongJobsQueue = Thread::Queue->new(); @@ -355,7 +358,7 @@ sub SONOS_Initialize ($) { eval { no strict; no warnings; - $hash->{AttrList}= 'disable:1,0 pingType:'.join(',', @SONOS_PINGTYPELIST).' usedonlyIPs ignoredIPs targetSpeakDir targetSpeakURL targetSpeakFileTimestamp:1,0 targetSpeakFileHashCache:1,0 targetSpeakMP3FileDir targetSpeakMP3FileConverter SpeakGoogleURL Speak1 Speak2 Speak3 Speak4 SpeakCover Speak1Cover Speak2Cover Speak3Cover Speak4Cover generateProxyAlbumArtURLs:1,0 proxyCacheTime proxyCacheDir bookmarkSaveDir bookmarkTitleDefinition bookmarkPlaylistDefinition coverLoadTimeout:1,2,3,4,5,6,7,8,9,10,15,20,25,30 getListsDirectlyToReadings:1,0 getFavouritesListAtNewVersion:1,0 getPlaylistsListAtNewVersion:1,0 getRadiosListAtNewVersion:1,0 getQueueListAtNewVersion:1,0 deviceRoomView:Both,DeviceLineOnly reusePort:1,0 webname SubProcessLogfileName getLocalCoverArt '.$readingFnAttributes; + $hash->{AttrList}= 'disable:1,0 pingType:'.join(',', @SONOS_PINGTYPELIST).' usedonlyIPs ignoredIPs targetSpeakDir targetSpeakURL targetSpeakFileTimestamp:1,0 targetSpeakFileHashCache:1,0 targetSpeakMP3FileDir targetSpeakMP3FileConverter SpeakGoogleURL Speak1 Speak2 Speak3 Speak4 SpeakCover Speak1Cover Speak2Cover Speak3Cover Speak4Cover generateProxyAlbumArtURLs:1,0 proxyCacheTime proxyCacheDir bookmarkSaveDir bookmarkTitleDefinition bookmarkPlaylistDefinition coverLoadTimeout:1,2,3,4,5,6,7,8,9,10,15,20,25,30 getListsDirectlyToReadings:1,0 getFavouritesListAtNewVersion:1,0 getPlaylistsListAtNewVersion:1,0 getRadiosListAtNewVersion:1,0 getQueueListAtNewVersion:1,0 deviceRoomView:Both,DeviceLineOnly reusePort:1,0 webname SubProcessLogfileName getLocalCoverArt allowedWebAccess '.$readingFnAttributes; use strict; use warnings; }; @@ -774,7 +777,7 @@ sub SONOS_FhemWebCallback($) { } # Bild vom Player holen... - my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT); + my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT, timeout => $SONOS_DEFAULTCOVERLOADTIMEOUT); my $response = $ua->get($albumurl); if ($response->is_success) { SONOS_Log undef, 5, 'Cover wurde neu geladen: '.$albumurl; @@ -861,7 +864,6 @@ sub SONOS_FhemWebCallback($) { FW_serveSpecial('sonos_playbar_round', 'png', $attr{global}{modpath}.'/FHEM/lib/UPnP', 1); return (undef, undef); } - if ($URL =~ m/^\/empty.jpg/i) { FW_serveSpecial('sonos_empty', 'jpg', $attr{global}{modpath}.'/FHEM/lib/UPnP', 1); @@ -1083,6 +1085,7 @@ sub SONOS_StopSubProcess($) { } else { DevIo_SimpleWrite($hash, "disconnect\n", 2); } + $hash->{TCPDev}->sockopt(SO_LINGER, pack("ii", 1, 0)) if (defined($hash->{TCPDev})); DevIo_CloseDev($hash); setReadingsVal($hash, "state", 'disabled', TimeNow()); @@ -1165,6 +1168,8 @@ sub SONOS_Read($) { # Checker aktivieren... InternalTimer(gettimeofday() + $hash->{INTERVAL}, 'SONOS_IsSubprocessAliveChecker', $hash, 0); + SONOS_Log undef, 1, "ReadFn-Aufruf durch FHEM, aber keine Daten bekommen..."; + select(undef, undef, undef, 0.001); return undef; } @@ -1178,6 +1183,8 @@ sub SONOS_Read($) { # Checker aktivieren... InternalTimer(gettimeofday() + $hash->{INTERVAL}, 'SONOS_IsSubprocessAliveChecker', $hash, 0); + SONOS_Log undef, 1, "ReadFn-Aufruf durch FHEM, aber keine weiteren Daten bekommen. Buffer bisher (kein abschließendes Newline): '$buf'"; + return undef; } @@ -1641,12 +1648,11 @@ sub SONOS_Read($) { } } elsif ($tempURI =~ m/getaa.*?x-sonosapi-stream%3a(.+?)%3f/i) { $srcURI = SONOS_GetRadioMediaMetadata($hash->{UDN}, $1); - eval { - my $result = SONOS_ReadURL($srcURI); - if (!defined($result) || ($result =~ m/.*<\/Error>/i)) { - $srcURI = $groundURL.$tempURI; - } - }; + + my $result = SONOS_ReadURL($srcURI); + if (!defined($result) || ($result =~ m/.*<\/Error>/i)) { + $srcURI = $groundURL.$tempURI; + } if ($getLocalCoverArt) { $currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.png'; @@ -1698,8 +1704,10 @@ sub SONOS_Read($) { if ($nextReading eq 'next') { $current{nextAlbumArtURL} = $URL; + SONOS_Log undef, 5, 'ProcessCover: SetCurrent:nextAlbumArtURL: '.$current{nextAlbumArtURL}; } else { $current{AlbumArtURL} = $URL; + SONOS_Log undef, 5, 'ProcessCover: SetCurrent:AlbumArtURL: '.$current{AlbumArtURL}; } # This URI change rarely, but the File itself change nearly with every song, so trigger it everytime the content was different to the old one @@ -1740,7 +1748,7 @@ sub SONOS_Read($) { SONOS_Log undef, 0, "Fehlerhafter Aufruf von DoWorkAnswer: $1:$2:$3"; } } elsif ($line =~ m/rePing:/) { - # Zunächst mal nichts weiter tun, hier geht es nur um die Aktualisierung der letzten Prozessantwort... + # Zunächst mal nichts weiter tun, hier geht es nur um die Aktualisierung der letzten Prozessantwort (die ja bei jeder Nachricht vom SubProcess aktualisiert wird)... } else { SONOS_DoTriggerInternal('Main', $line); } @@ -1819,36 +1827,55 @@ sub SONOS_StartClientProcessIfNeccessary($) { my ($upnplistener) = @_; my ($host, $port) = split(/:/, $upnplistener); - my $socket = new IO::Socket::INET(PeerAddr => $upnplistener, Proto => 'tcp'); + # Sonos-Device ermitteln... + my $hash = SONOS_getSonosPlayerByName(); + + my $socket = new IO::Socket::INET(PeerAddr => $upnplistener, Proto => 'tcp', Timeout => $SONOS_DEFAULTCOVERLOADTIMEOUT); if (!$socket) { - # Sonos-Device ermitteln... - my $hash = SONOS_getSonosPlayerByName(); - SONOS_Log undef, 1, 'Kein UPnP-Server gefunden... Starte selber einen und warte '.$hash->{WAITTIME}.' Sekunde(n) darauf...'; $SONOS_StartedOwnUPnPServer = 1; - if (fork() == 0) { + if (main::fhemFork() == 0) { # Zuständigen Verbose-Level ermitteln... - # Allerdings sind die Attribute (momentan) zu diesem Zeitpunkt noch nicht gesetzt, sodass nur das globale Attribut verwendet werden kann... - my $verboselevel = AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'verbose', $attr{global}{verbose}); + # Allerdings sind die Attribute u.U. (momentan) zu diesem Zeitpunkt noch nicht gesetzt, sodass nur das globale Attribut verwendet werden kann... + my $verboselevel = AttrVal($hash->{NAME}, 'verbose', $attr{global}{verbose}); # Prozess anstarten... exec("$^X $attr{global}{modpath}/FHEM/00_SONOS.pm $port $verboselevel ".(($attr{global}{mseclog}) ? '1' : '0').' startedbyfhem'); exit(0); } } else { - $socket->sockopt(SO_LINGER, pack("ii", 1, 0)); + SONOS_Log undef, 1, 'UPnP-Server gefunden... versuche zu verbinden...'; - # Antwort vom Client weglesen... - my $answer; - $socket->recv($answer, 5000); - $socket->send("Test\r\n", 0); - - # Hiermit wird eine etwaig bestehende Thread-Struktur beendet und diese Verbindung selbst geschlossen... - eval{ - $socket->shutdown(2); - $socket->close(); + # Timeout über Alarm festlegen... + eval { + local $SIG{ALRM} = sub { die 'Timed Out'; }; + alarm $SONOS_DEFAULTCOVERLOADTIMEOUT; + + # Antwort vom Client weglesen... + my $answer; + $socket->recv($answer, 5000); + SONOS_Log $hash, 5, 'Antwort vom SubProzess: '.$answer; + if ($answer eq 'This is UPnP-Server listening for commands\r\n') { + $socket->send("Test\r\n", 0); + + # Hiermit wird eine etwaig bestehende Thread-Struktur beendet und diese Verbindung selbst geschlossen... + eval{ + $socket->sockopt(SO_LINGER, pack("ii", 1, 0)); + $socket->shutdown(2); + $socket->close(); + }; + } else { + # Wenn keine ordentliche Antwort kam... + SONOS_Log undef, 1, 'Es wurde ein Server gefunden, der aber nicht als UPnP-Server antwortet... Anderes System? Falsche IP angegeben? Falschen Port angegeben? Zieladresse: '.$upnplistener; + } + + alarm 0; }; + alarm 0; # race condition protection + if ($@) { + SONOS_Log undef, 1, 'Es wurde ein Server gefunden. Dieser hat aber nicht geantwortet. Zieladresse: '.$upnplistener.', Fehler: '.$@; + } } return undef; @@ -1947,6 +1974,7 @@ sub SONOS_InitClientProcess($) { } } + # Alle Informationen sind drüben, dann Threads dort drüben starten DevIo_SimpleWrite($hash, "StartThread\n", 2); @@ -1969,11 +1997,15 @@ sub SONOS_IsSubprocessAliveChecker() { my $lastProcessAnswer = ReadingsVal(SONOS_getSonosPlayerByName()->{NAME}, 'LastProcessAnswer', '0'); - # Wenn länger nichts passiert ist, dann eine Aktualisierung anfordern... - SONOS_DoWork('undef', 'refreshProcessAnswer') if ($lastProcessAnswer < time() - $hash->{INTERVAL}); + # Wenn länger nichts passiert ist, dann erstmal eine Aktualisierung anfordern... + if ($lastProcessAnswer < time() - $hash->{INTERVAL}) { + # Loggen und anfordern... + SONOS_Log $hash->{UDN}, 5, 'Long time, no hear from SubProcess... sending ping-request'; + SONOS_DoWork('undef', 'refreshProcessAnswer'); + } # Wenn die letzte Antwort zu lange her ist, dann den SubProzess neustarten... - if (($lastProcessAnswer != 0) && ($lastProcessAnswer < time() - (4 * $hash->{INTERVAL}))) { + if (($lastProcessAnswer != 0) && ($lastProcessAnswer < time() - 5 - (4 * $hash->{INTERVAL}))) { # Verbindung beenden, damit der SubProzess die Chance hat neu initialisiert zu werden... SONOS_Log $hash->{UDN}, 2, 'LastProcessAnswer way too old (Lastanswer: '.$lastProcessAnswer.' ~ '.SONOS_GetTimeString($lastProcessAnswer).')... try to restart the process and connection...'; @@ -1987,7 +2019,7 @@ sub SONOS_IsSubprocessAliveChecker() { SONOS_readingsEndUpdate($sHash, 1); # Stoppen... - InternalTimer(gettimeofday() + 1, 'SONOS_StopSubProcess', $hash, 0); + SONOS_StopSubProcess($hash); # Starten... InternalTimer(gettimeofday() + 30, 'SONOS_DelayStart', $hash, 0); @@ -2091,12 +2123,23 @@ sub SONOS_ConvertZoneGroupState($) { my @groups = (); while ($zoneGroupState =~ m/(.*?)<\/ZoneGroup>/gi) { + # Wenn der Coordinator-Player in Fhem unbekannt ist (weil er z.B. bei der Erkennung ausgeschlossen wurde) dann übergehen... + if (!SONOS_getSonosPlayerByUDN($1.'_MR')) { + next; + } + my @group = ($1.'_MR'); my $groupMember = $2; - + while ($groupMember =~ m//gi) { + # Wenn der Member-Player in Fhem unbekannt ist (weil er z.B. bei der Erkennung ausgeschlossen wurde) dann übergehen... + if (!SONOS_getSonosPlayerByUDN($1.'_MR')) { + next; + } + my $udn = $1; my $string = $2; + push @group, $udn.'_MR' if (!($string =~ m/IsZoneBridge="."/) && !SONOS_isInList($udn.'_MR', @group)); # Etwaig von vorher enthaltene Bridges wieder entfernen (wenn sie bereits als Koordinator eingesetzt wurde) @@ -2370,7 +2413,7 @@ sub SONOS_DoWork($$;@) { eval { my $hash = SONOS_getSonosPlayerByName(); if (defined($hash->{TCPDev}) && ($hash->{TCPDev}->connected())) { - DevIo_SimpleWrite($hash, 'DoWork:'.$udn.':'.$method.':'.encode_utf8(join('--#--', @params))."\r\n", 2); + DevIo_SimpleWrite($hash, 'DoWork:'.$udn.':'.$method.':'.encode_utf8(join('--#--', @params))."\n", 2); } }; if ($@) { @@ -2463,7 +2506,7 @@ sub SONOS_Discover() { ######################################################################################## # -# SONOS_Discover_DoQueue - Do the working job (command from Fhem -> Sonosplayer) +# SONOS_Discover_DoQueue - Do the working job (command from Fhem -> SubProcess (e.g. Sonosplayer)) # ######################################################################################## sub SONOS_Discover_DoQueue($) { @@ -2568,30 +2611,36 @@ sub SONOS_Discover_DoQueue($) { } elsif ($workType eq 'setCurrentTrackPosition') { my $value1 = $params[0]; - # Wenn eine Sekundenangabe gemacht wurde, dann in einen Zeitstring umwandeln, damit der Rest so bleiben kann - $value1 = $1.SONOS_ConvertSecondsToTime($2).$3 if ($value1 =~ m/^([+-]{0,1})(\d+)(\%{0,1})$/); - if (SONOS_CheckProxyObject($udn, $SONOS_AVTransportControlProxy{$udn})) { - if ($value1 =~ m/([+-])(\d+:\d+:\d+|\d+:\d+|\d+)(\%{0,1})/) { - # Relative-(Prozent)-Angabe + if ($value1 =~ m/([+-]{0,1})(\d+:\d+:\d+|\d+:\d+|\d+)(\%{0,1})/) { + my $sign = $1; my $value1Sec = SONOS_GetTimeSeconds(SONOS_ExpandTimeString($2)); + my $percent = $3; + + $sign = '' if (!defined($sign)); + $percent = '' if (!defined($percent)); # Positionswerte abfragen... my $result = $SONOS_AVTransportControlProxy{$udn}->GetPositionInfo(0); - my $pos = SONOS_GetTimeSeconds($result->getValue('RelTime')); + my $pos = $result->getValue('RelTime'); if ($pos !~ /\d+:\d+:\d+/i) { # e.g. NOT_IMPLEMENTED $pos = '0:00:00'; } + $pos = SONOS_GetTimeSeconds($pos); my $duration = SONOS_GetTimeSeconds($result->getValue('TrackDuration')); # Neue Position berechnen... my $newPos = 0; - if ($3 eq '') { - $newPos = ($pos + $value1Sec) if ($1 eq '+'); - $newPos = ($pos - $value1Sec) if ($1 eq '-'); + if ($sign =~ m/[+-]/) { + if ($percent eq '') { + $newPos = ($pos + $value1Sec) if ($sign eq '+'); + $newPos = ($pos - $value1Sec) if ($sign eq '-'); + } else { + $newPos = ($pos + ($value1Sec * $duration / 100)) if ($sign eq '+'); + $newPos = ($pos - ($value1Sec * $duration / 100)) if ($sign eq '-'); + } } else { - $newPos = ($pos + ($value1Sec * $duration / 100)) if ($1 eq '+'); - $newPos = ($pos - ($value1Sec * $duration / 100)) if ($1 eq '-'); + $newPos = $value1Sec; } # Sicherstellen, dass wir im Bereich des Titels bleiben... @@ -2600,8 +2649,6 @@ sub SONOS_Discover_DoQueue($) { # Neue Position setzen $SONOS_AVTransportControlProxy{$udn}->Seek(0, 'REL_TIME', SONOS_ConvertSecondsToTime($newPos)); - } else { - $SONOS_AVTransportControlProxy{$udn}->Seek(0, 'REL_TIME', $value1); } my $trackPosition = $SONOS_AVTransportControlProxy{$udn}->GetPositionInfo(0)->getValue('RelTime'); @@ -4164,7 +4211,7 @@ sub SONOS_Discover_DoQueue($) { ######################################################################################## # -# SONOS_ProcessRenew - Process the renewal of sbscriptions +# SONOS_ProcessRenew - Process the renewal of subscriptions # ######################################################################################## sub SONOS_ProcessRenew($$$) { @@ -4172,13 +4219,18 @@ sub SONOS_ProcessRenew($$$) { if (defined($subscriptionHash->{$udn}) && (Time::HiRes::time() - $subscriptionHash->{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) { eval { - $SIG{__WARN__} = sub { $_ = shift; }; + local $SIG{__WARN__} = sub { die $_[0] }; $subscriptionHash->{$udn}->renew(); + SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'LastSubscriptionsRenew', $subscriptionName.': '.SONOS_TimeNow()); + SONOS_Log $udn, 3, $subscriptionName.'-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.'; }; if ($@) { - SONOS_Log $udn, 3, 'Error! '.$subscriptionName.'-Subscription for ZonePlayer "'.$udn.'" has expired and could not be renewed: '.$@; + # Den 412er Fehler ignorieren, angeblich regeneriert sich der Player irgendwann selbst... + if ($@ !~ m/412 Precondition Failed/i) { + SONOS_Log $udn, 1, 'Error! '.$subscriptionName.'-Subscription for ZonePlayer "'.$udn.'" has expired and could not be renewed: '.$@; + } # Wenn der Player nicht erreichbar war, dann entsprechend entfernen... # Hier aber nur eine kleine Lösung, da es nur ein Notbehelf sein soll... @@ -4222,10 +4274,27 @@ sub SONOS_GetBrowseStructuredResult($$@) { my $result = $SONOS_ContentDirectoryControlProxy{$udn}->Browse($searchValue, 'BrowseDirectChildren', '', 0, 0, ''); my $tmp = $result->getValue('Result'); - while ($tmp =~ m/<$container id="(.+?)".*?>(.*?)<\/dc:title>.*?(.*?)<\/res>.*?<\/$container>/ig) { - $resultHash{$1}->{Title} = $2; - $resultHash{$1}->{Cover} = SONOS_MakeCoverURL($udn, $3); - $resultHash{$1}->{Ressource} = decode_entities($3); + while ($tmp =~ m/(<$container id="(.+?)".*?>(.*?)<\/dc:title>.*?(.*?)<\/res>.*?<\/$container>)/ig) { + my $itemElem = $1; + my $id = $2; + $resultHash{$id}->{Title} = $3; + $resultHash{$id}->{Cover} = SONOS_MakeCoverURL($udn, $4); + $resultHash{$id}->{Ressource} = decode_entities($4); + + # Typ + if ($itemElem =~ m/.*?<upnp:class>(.+)<\/upnp:class>.*?<\/r:resMD>/i) { + $resultHash{$id}->{Typ} = $1 if ($1 =~ m/\.([^.]*)$/); + } + + # Reihenfolge + if ($itemElem =~ m/(\d+)<\/r:ordinal>/i) { + $resultHash{$id}->{Position} = $1; + } + + # Beschreibung + if ($itemElem =~ m/(.+)<\/r:description>/i) { + $resultHash{$id}->{Description} = $1; + } } } @@ -4309,7 +4378,7 @@ sub SONOS_GetQueueStructuredResult($$$) { # SONOS_StartMetadata - Starts any kind of Metadata # ######################################################################################## -sub SONOS_StartMetadata($$$$) { +sub SONOS_StartMetadata($$$$$) { my ($workType, $udn, $res, $meta, $nostart) = @_; if (SONOS_CheckProxyObject($udn, $SONOS_AVTransportControlProxy{$udn})) { @@ -4578,12 +4647,19 @@ sub SONOS_MakeCoverURL($$) { $resURL = SONOS_getSpotifyCoverURL($1); } elsif ($resURL =~ m/^x-sonosapi-stream:(.+?)\?/i) { my $resURLtemp = SONOS_GetRadioMediaMetadata($udn, $1); + SONOS_Log undef, 5, 'Stream-Cover-Ermittlung 1 erfolgreich: '.$resURLtemp; + eval { my $result = SONOS_ReadURL($resURLtemp); if (!defined($result) || ($result =~ m/.*<\/Error>/i)) { $resURLtemp = $1.'/getaa?s=1&u='.SONOS_URI_Escape($resURL) if (SONOS_Client_Data_Retreive($udn, 'reading', 'location', '') =~ m/^(http:\/\/.*?:.*?)\//i); } }; + if ($@) { + SONOS_Log $udn, 2, 'Beim Laden der Ressourcen vom Player ist ein Fehler aufgetreten: '.$@; + } + SONOS_Log undef, 5, 'Stream-Cover-Ermittlung 2 erfolgreich: '.$resURLtemp; + $resURL = $resURLtemp; } elsif (($resURL =~ m/x-rincon-playlist:.*?#(.*)/i) || ($resURL =~ m/savedqueues.rsq(#\d+)/i)) { my $search = $1; @@ -4661,10 +4737,10 @@ sub SONOS_getSpotifyCoverURL($;$) { my $infos = ''; if ($oldStyle) { - my $result = get('https://embed.spotify.com/oembed/?url='.$trackID); + my $result = SONOS_ReadURL('https://embed.spotify.com/oembed/?url='.$trackID); $infos = $1 if ($result && ($result =~ m/"thumbnail_url":"(.*?)"/i)); } else { - my $result = get('https://api.spotify.com/v1/tracks/'.$trackID); + my $result = SONOS_ReadURL('https://api.spotify.com/v1/tracks/'.$trackID); $infos = $1 if ($result && ($result =~ m/"images".*?:.*?\[.*?{.*?"height".*?:.*?\d{3},.*?"url".*?:.*?"(.*?)",.*?"width"/is)); } @@ -4775,7 +4851,7 @@ sub SONOS_GetSpeakFile($$$$$) { SONOS_Log $udn, 3, 'Load Google generated MP3 ('.$counter.'. Element) from "'.$url.'" to "'.$destFileName.$counter.'"'; - my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT); + my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT, timeout => $SONOS_DEFAULTCOVERLOADTIMEOUT); my $response = $ua->get($url, ':content_file' => $destFileName.$counter); if (!$response->is_success) { SONOS_Log $udn, 1, 'MP3 Download-Error: '.$response->status_line; @@ -5744,6 +5820,8 @@ sub SONOS_Discover_Callback($$$) { if ($action eq 'deviceAdded') { my $descriptionDocument; eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $descriptionDocument = $device->descriptionDocument(); }; if ($@) { @@ -5900,6 +5978,8 @@ sub SONOS_Discover_Callback($$$) { if (!$isZoneBridge) { if ($SONOS_RenderingControlProxy{$udn}) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $currentVolume = $SONOS_RenderingControlProxy{$udn}->GetVolume(0, 'Master')->getValue('CurrentVolume'); # Balance ermitteln @@ -6029,6 +6109,8 @@ sub SONOS_Discover_Callback($$$) { if (!$isZoneBridge) { if (SONOS_CheckProxyObject($udn, $SONOS_AVTransportControlProxy{$udn})) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + my $result = $SONOS_AVTransportControlProxy{$udn}->GetTransportInfo(0); SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', $udn, 'transportState', $result->getValue('CurrentTransportState')); @@ -6066,7 +6148,7 @@ sub SONOS_Discover_Callback($$$) { } } - SONOS_Client_Data_Refresh('', $udn, 'LastSubscriptionsRenew', SONOS_TimeNow()); + SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', $udn, 'LastSubscriptionsRenew', SONOS_TimeNow()); SONOS_Client_Notifier('ReadingsEndUpdate:'.$udn); SONOS_Client_Notifier('CommandAttrWithUDN:'.$udn.':model Sonos_'.$modelNumber); @@ -6095,6 +6177,8 @@ sub SONOS_Discover_Callback($$$) { || SONOS_Client_Data_Retreive($udn, 'attr', 'minVolumeHeadphone', -1) != -1 || SONOS_Client_Data_Retreive($udn, 'attr', 'maxVolumeHeadphone', -1) != -1)) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_RenderingSubscriptions{$udn} = $renderingService->subscribe(\&SONOS_RenderingCallback); }; $SONOS_ButtonPressQueue{$udn} = Thread::Queue->new(); @@ -6122,6 +6206,8 @@ sub SONOS_Discover_Callback($$$) { # ContentDirectory-Subscription if ($contentDirectoryService) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_ContentDirectorySubscriptions{$udn} = $contentDirectoryService->subscribe(\&SONOS_ContentDirectoryCallback); if (defined($SONOS_ContentDirectorySubscriptions{$udn})) { SONOS_Log undef, 2, 'ContentDirectory-Service-subscribing successful with SID='.$SONOS_ContentDirectorySubscriptions{$udn}->SID; @@ -6139,6 +6225,8 @@ sub SONOS_Discover_Callback($$$) { # Alarm-Subscription if ($alarmService && (SONOS_Client_Data_Retreive($udn, 'attr', 'getAlarms', 0) != 0)) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_AlarmSubscriptions{$udn} = $alarmService->subscribe(\&SONOS_AlarmCallback); if (defined($SONOS_AlarmSubscriptions{$udn})) { SONOS_Log undef, 2, 'Alarm-Service-subscribing successful with SID='.$SONOS_AlarmSubscriptions{$udn}->SID; @@ -6156,6 +6244,8 @@ sub SONOS_Discover_Callback($$$) { # ZoneGroupTopology-Subscription if ($zoneGroupTopologyService) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_ZoneGroupTopologySubscriptions{$udn} = $zoneGroupTopologyService->subscribe(\&SONOS_ZoneGroupTopologyCallback); if (defined($SONOS_ZoneGroupTopologySubscriptions{$udn})) { SONOS_Log undef, 2, 'ZoneGroupTopology-Service-subscribing successful with SID='.$SONOS_ZoneGroupTopologySubscriptions{$udn}->SID; @@ -6173,6 +6263,8 @@ sub SONOS_Discover_Callback($$$) { # DeviceProperties-Subscription if ($devicePropertiesService) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_DevicePropertiesSubscriptions{$udn} = $devicePropertiesService->subscribe(\&SONOS_DevicePropertiesCallback); if (defined($SONOS_DevicePropertiesSubscriptions{$udn})) { SONOS_Log undef, 2, 'DeviceProperties-Service-subscribing successful with SID='.$SONOS_DevicePropertiesSubscriptions{$udn}->SID; @@ -6190,6 +6282,8 @@ sub SONOS_Discover_Callback($$$) { # AudioIn-Subscription if ($audioInService) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_AudioInSubscriptions{$udn} = $audioInService->subscribe(\&SONOS_AudioInCallback); if (defined($SONOS_AudioInSubscriptions{$udn})) { SONOS_Log undef, 2, 'AudioIn-Service-subscribing successful with SID='.$SONOS_AudioInSubscriptions{$udn}->SID; @@ -6208,6 +6302,8 @@ sub SONOS_Discover_Callback($$$) { # MusicServices-Subscription if ($musicServicesService) { eval { + local $SIG{__WARN__} = sub { die $_[0] }; + $SONOS_MusicServicesSubscriptions{$udn} = $musicServicesService->subscribe(\&SONOS_MusicServicesCallback); if (defined($SONOS_MusicServicesSubscriptions{$udn})) { SONOS_Log undef, 2, 'MusicServices-Service-subscribing successful with SID='.$SONOS_MusicServicesSubscriptions{$udn}->SID; @@ -6273,6 +6369,7 @@ sub SONOS_GetDefineStringlist($$$$$$$$$$) { push(@defs, 'CommandAttr:'.$name.' simulateCurrentTrackPosition 1'); push(@defs, 'CommandAttr:'.$name.' webCmd Volume'); + push(@defs, 'CommandAttr:'.$name.' verbose '.SONOS_Client_Data_Retreive('undef', 'attr', 'verbose', 0)) if (SONOS_Client_Data_Retreive('undef', 'attr', 'verbose', undef)); #push(@defs, 'CommandAttr:'.$name.' webCmd Play:Pause:Previous:Next:VolumeD:VolumeU:MuteT'); } else { push(@defs, 'CommandAttr:'.$name.' stateFormat presence'); @@ -6422,8 +6519,11 @@ sub SONOS_IsAlive($) { my $ping = Net::Ping->new($pingType, 1); $ping->source_verify(0); # Es ist egal, von welcher Schnittstelle des Zielsystems die Antwort kommt - $ping->port_number($port) if (lc($pingType) eq 'tcp'); # Wenn TCP verwendet werden soll, dann auf HTTP-Port des Location-Documents (Standard: 1400) des Player verbinden - if ($ping->ping($host)) { + $ping->port_number($port) if ((lc($pingType) eq 'tcp') || (lc($pingType) eq 'syn')); # Wenn TCP oder SYN verwendet werden soll, dann auf HTTP-Port des Location-Documents (Standard: 1400) des Player verbinden + $ping->ping($host, $SONOS_DEFAULTCOVERLOADTIMEOUT) if (lc($pingType) eq 'syn'); + + if (((lc($pingType) eq 'syn') && $ping->ack($host)) || + ((lc($pingType) ne 'syn') && $ping->ping($host, $SONOS_DEFAULTCOVERLOADTIMEOUT))) { # Alive SONOS_Log $udn, 4, "$host is alive"; $result = 1; @@ -8150,7 +8250,7 @@ sub SONOS_AlarmCallback($$) { } if (defined($properties{DailyIndexRefreshTime})) { - SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'DailyIndexRefreshTime', $properties{DailyIndexRefreshTime}); + SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', $udn, 'DailyIndexRefreshTime', $properties{DailyIndexRefreshTime}); } SONOS_Client_Notifier('ReadingsEndUpdate:'.$udn); @@ -8826,23 +8926,54 @@ sub SONOS_MusicServicesCallback($$) { my $serviceType = $serviceTypes[$servicepos++]; - if ($content =~ m/.*?SecureUri="(.+?)".*?Capabilities="(\d+)".*?>.*?(|).*?.*?/si) { - my $smapi = $1; - my $capabilities = $2; - my $stringsURL = $4; + my $smapi = ''; + my $capabilities = ''; + if ($content =~ m/.*?SecureUri="(.+?)".*?Capabilities="(\d+)".*?>/si) { + $smapi = $1; + $capabilities = $2; + } + + my $stringsURL = ''; + my $presentationMap = ''; + if ($content =~ m/.*?(|).*?.*?/si) { + $stringsURL = $2; + $presentationMap = $3; - my $presentationMap = $5; + SONOS_Log undef, 5, 'ID('.$id.') - XML-System for Strings('.(defined($stringsURL) ? $stringsURL : '').') and PresentationMap('.(defined($presentationMap) ? $presentationMap : '').').'; + } elsif ($content =~ m//si) { + my $manifestURL = $1; + SONOS_Log undef, 5, 'ID('.$id.') - Manifest-System('.$manifestURL.')'; + my $manifestData = encode('UTF-8', SONOS_ReadURL($manifestURL)); + + if (defined($manifestData)) { + SONOS_Log undef, 5, 'ID('.$id.') - Manifest-Data: '.$manifestData; + + if ($manifestData =~ m/"presentationMap".*?"uri":.*?"(.+?)"/si) { + $presentationMap = $1; + } + + if ($manifestData =~ m/"strings":.*?"uri":.*?"(.+?)"/si) { + $stringsURL = $1; + } + + SONOS_Log undef, 5, 'ID('.$id.') - Manifest-System: Strings('.(defined($stringsURL) ? $stringsURL : '').') and PresentationMap('.(defined($presentationMap) ? $presentationMap : '').').'; + } + } + + if ($stringsURL || $presentationMap) { my $promoString = ''; if (defined($stringsURL) && ($stringsURL ne '')) { - my $strings = encode('UTF-8', get($stringsURL)); + my $strings = encode('UTF-8', SONOS_ReadURL($stringsURL)); + SONOS_Log undef, 5, 'ID('.$id.') - Strings: '.$strings; + if (defined($strings) && ($strings ne '')) { $promoString = $1 if ($strings =~ m/.*?(.*?)<\/string>.*?<\/stringtable>/si); $promoString = $1 if (($promoString eq '') && ($strings =~ m/.*?(.*?)<\/string>.*?<\/stringtable>/si)); } } - my $presentationMapData = encode('UTF-8', get($presentationMap)); + my $presentationMapData = encode('UTF-8', SONOS_ReadURL($presentationMap)); if (defined($presentationMapData)) { SONOS_Log undef, 5, 'PresentationMap('.$id.' ~ '.$name.' ~ ServiceType: "'.$serviceType.'"): '.$presentationMapData; } else { @@ -8853,8 +8984,7 @@ sub SONOS_MusicServicesCallback($$) { if (!defined($resolution)) { ($resolution, $resolutionSubst) = SONOS_ExtractMaxResolution($presentationMapData, 'BrowseIconSizeMap'); } - - #SONOS_GetMediaMetadata($udn, $id, + SONOS_Log undef, 5, 'ID('.$id.') - ArtWork, Resolution('.(defined($resolution) ? $resolution : '').'), ResolutionSubst('.(defined($resolutionSubst) ? $resolutionSubst : '').').'; $musicServices{$id}{Name} = $name; $musicServices{$id}{ServiceType} = $serviceType; @@ -8905,7 +9035,7 @@ sub SONOS_ExtractMaxResolution($$) { return (undef, undef) if (!defined($map)); - my $artworkSizeMap = $1 if ($map =~ m/.*?.*?(.*?)<\/imageSizeMap>.*?<\/Match>.*?<\/PresentationMap>/is); + my $artworkSizeMap = $2 if ($map =~ m/.*?.*?<(imageSizeMap|browseIconSizeMap)>(.*?)<\/\1>.*?<\/Match>.*?<\/PresentationMap>/is); return (undef, undef) if (!defined($artworkSizeMap)); my @resolutions = (); @@ -9245,7 +9375,7 @@ sub SONOS_GetRadioMediaMetadata($$) { return '' if (!defined($udnKey)); - my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT); + my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT, timeout => $SONOS_DEFAULTCOVERLOADTIMEOUT); my $response = $ua->request(POST 'http://legato.radiotime.com/Radio.asmx', 'content-type' => 'text/xml; charset="utf-8"', Content => " @@ -9263,12 +9393,13 @@ sub SONOS_GetRadioMediaMetadata($$) { "); SONOS_Log $udn, 5, 'Radioservice-Metadata: '.$response->content; - my $title = $1 if ($response->content =~ m/(.*?)<\/title>/i); - my $genreId = $1 if ($response->content =~ m/<genreId>(.*?)<\/genreId>/i); - my $genre = $1 if ($response->content =~ m/<genre>(.*?)<\/genre>/i); - my $bitrate = $1 if ($response->content =~ m/<bitrate>(.*?)<\/bitrate>/i); - my $logo = $1 if ($response->content =~ m/<logo>(.*?)<\/logo>/i); $logo =~ s/(.*?)q(\..*)/$1g$2/; - my $subtitle = $1 if ($response->content =~ m/<subtitle>(.*?)<\/subtitle>/i); +# my $title = $1 if ($response->content =~ m/<title>(.*?)<\/title>/i); +# my $genreId = $1 if ($response->content =~ m/<genreId>(.*?)<\/genreId>/i); +# my $genre = $1 if ($response->content =~ m/<genre>(.*?)<\/genre>/i); +# my $bitrate = $1 if ($response->content =~ m/<bitrate>(.*?)<\/bitrate>/i); +# my $subtitle = $1 if ($response->content =~ m/<subtitle>(.*?)<\/subtitle>/i); + my $logo = $1 if ($response->content =~ m/<logo>(.*?)<\/logo>/i); + $logo =~ s/(.*?)q(\..*)/$1g$2/ if (defined($logo)); # Logo mit größerer Auflösung verwenden... return $logo; } @@ -9294,7 +9425,7 @@ sub SONOS_GetMediaMetadata($$$) { my $url = $musicService{SMAPI}; if ($url) { - my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT); + my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT, timeout => $SONOS_DEFAULTCOVERLOADTIMEOUT); my $response = $ua->request(POST $url, 'content-type' => 'text/xml; charset="utf-8"', Content => "<?xml version=\"1.0\" encoding=\"utf-8\"?> <s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"> @@ -9310,20 +9441,19 @@ sub SONOS_GetMediaMetadata($$$) { </getMediaMetadata> </s:Body> </s:Envelope>"); - SONOS_Log $udn, 0, 'MediaMetadata: '.$response->content; + SONOS_Log $udn, 5, 'MediaMetadata: '.$response->content; - my $title = $1 if ($response->content =~ m/<title>(.*?)<\/title>/i); - my $genreId = $1 if ($response->content =~ m/<genreId>(.*?)<\/genreId>/i); - my $genre = $1 if ($response->content =~ m/<genre>(.*?)<\/genre>/i); - my $bitrate = $1 if ($response->content =~ m/<bitrate>(.*?)<\/bitrate>/i); +# my $title = $1 if ($response->content =~ m/<title>(.*?)<\/title>/i); +# my $genreId = $1 if ($response->content =~ m/<genreId>(.*?)<\/genreId>/i); +# my $genre = $1 if ($response->content =~ m/<genre>(.*?)<\/genre>/i); +# my $bitrate = $1 if ($response->content =~ m/<bitrate>(.*?)<\/bitrate>/i); +# my $subtitle = $1 if ($response->content =~ m/<subtitle>(.*?)<\/subtitle>/i); my $logo = $1 if ($response->content =~ m/<logo>(.*?)<\/logo>/i); if ($musicService{ResolutionSubstitution}) { $logo =~ s//$musicService{ResolutionSubstitution}/; } - my $subtitle = $1 if ($response->content =~ m/<subtitle>(.*?)<\/subtitle>/i); - return $logo; } else { return ''; @@ -9340,13 +9470,40 @@ sub SONOS_GetMediaMetadata($$$) { sub SONOS_ReadURL($) { my ($url) = @_; - my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT); - my $response = $ua->get($url); - if ($response->is_success) { - return $response->content; + # Wenn keine URL angegeben wurde, dann still übergehen... + if (!defined($url) || $url eq '') { + SONOS_Log undef, 6, 'Es wurde versucht ein Webzugriff zu machen, aber keine URL angegeben.'; + return undef; } - return undef; + # Prüfen, ob dieses ein erlaubter Zufriff ist + my $allowedWebAccess = SONOS_Client_Data_Retreive('undef', 'attr', 'allowedWebAccess', '.*'); + if ($url !~ m/$allowedWebAccess/i) { + SONOS_Log undef, 3, 'Es wurde versucht ein Webzugriff zu machen, der aber nicht erlaubt ist. Erlaubte Adressen: '.$allowedWebAccess; + return undef; + } + + my $result = eval { + my $ua = LWP::UserAgent->new(agent => $SONOS_USERAGENT, + timeout => $SONOS_DEFAULTCOVERLOADTIMEOUT, + ssl_opts => { verify_hostname => 0, + SSL_VERIFY_NONE => 1, + SSL_cipher_list => '' }); + my $response = $ua->get($url); + if ($response->is_success) { + SONOS_Log undef, 5, 'URL('.$url.') wurde erfolgreich geladen.'; + return $response->content; + } else { + SONOS_Log undef, 1, 'Couldn\'t load data from URL('.$url.') 1: '.$response->status_line; + return undef; + } + }; + if ($@) { + SONOS_Log undef, 1, 'Couldn\'t load data from URL('.$url.') 2: '.$@; + return undef; + } + + return $result; } ######################################################################################## @@ -9639,6 +9796,7 @@ sub SONOS_Shutdown ($$) { } else { DevIo_SimpleWrite($hash, "disconnect\n", 2); } + $hash->{TCPDev}->sockopt(SO_LINGER, pack("ii", 1, 0)); DevIo_CloseDev($hash); select(undef, undef, undef, 2); @@ -9868,12 +10026,12 @@ if (defined($SONOS_ListenPort)) { eval { socket($sock, AF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "Could not create socket: $!"; bind($sock, sockaddr_in($SONOS_ListenPort, INADDR_ANY)) or die "Bind failed: $!"; - setsockopt($sock, SOL_SOCKET, SO_LINGER, pack("ii", 1, 0)) or die "Setsockopt failed: $!"; + setsockopt($sock, SOL_SOCKET, SO_LINGER, pack("ii", 1, 0)); listen($sock, 10); }; if ($@) { - SONOS_Log undef, 0, "Can't bind Port $SONOS_ListenPort: $@"; - SONOS_Log undef, 0, 'Retries left (wait 30s): '.--$retryCounter; + SONOS_Log undef, 1, "Can't bind Port $SONOS_ListenPort: $@"; + SONOS_Log undef, 1, 'Retries left (wait 30s): '.--$retryCounter; if (!$retryCounter) { die 'Bind failed...'; @@ -9888,9 +10046,9 @@ if (defined($SONOS_ListenPort)) { $SONOS_Client_Selector = IO::Select->new($sock); while ($runEndlessLoop) { - # Nachschauen, ob Subscriptions erneuert werden müssen - if (time() - $lastRenewSubscriptionCheckTime > 1800) { - $lastRenewSubscriptionCheckTime = time (); + # Alle 5 Minuten nachschauen, ob Subscriptions erneuert werden müssen + if (time() - $lastRenewSubscriptionCheckTime > 300) { + $lastRenewSubscriptionCheckTime = time(); foreach my $udn (@{$SONOS_Client_Data{PlayerUDNs}}) { my %data; @@ -9989,6 +10147,7 @@ if (defined($SONOS_ListenPort)) { # Alle Handles entfernen und schliessen... for my $cl ($SONOS_Client_Selector->handles()) { $SONOS_Client_Selector->remove($cl); + setsockopt($cl, SOL_SOCKET, SO_LINGER, pack("ii", 1, 0)); shutdown($cl, 2); close($cl); } @@ -10140,6 +10299,7 @@ sub SONOS_Client_ConsumeMessage($$) { } } + setsockopt($client, SOL_SOCKET, SO_LINGER, pack("ii", 1, 0)); shutdown($client, 2); close($client); @@ -10148,6 +10308,7 @@ sub SONOS_Client_ConsumeMessage($$) { send($client, "OK\r\n", 0); } elsif (lc($msg) eq 'goaway') { $SONOS_Client_Selector->remove($client); + setsockopt($client, SOL_SOCKET, SO_LINGER, pack("ii", 1, 0)); shutdown($client, 2); close($client); } elsif ($msg =~ m/SetData:(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*)/i) { @@ -10412,22 +10573,6 @@ sub SONOS_AddToLongJobsQueue($$;$) { } } -######################################################################################## -# SONOS_LongJobsAnswer: Adds an answer from Longjobs -######################################################################################## -#sub SONOS_LongJobsAnswer() { -# my ($udn, $workType, $params) = @_; -# -# my %data; -# $data{WorkType} = $workType; -# $data{UDN} = $udn; -# my @params = (); -# @params = split(/--#--/, decode_utf8($3)); -# $data{Params} = \@params; -# -# $SONOS_Client_ReceiveQueue->enqueue(\%data); -#} - ######################################################################################## # SONOS_Client_LongJobs_DoQueue: Do Longjobs ######################################################################################## @@ -10588,6 +10733,7 @@ sub SONOS_Client_IsAlive() { =pod +=item device =item summary Module to commmunicate with a Sonos-System via UPnP =item summary_DE Modul für die Kommunikation mit einem Sonos-System mittels UPnP =begin html @@ -10644,46 +10790,46 @@ Example with control over the used port and the isalive-checker-interval:<br /> <h4>Set</h4> <ul> <li><b>Common Tasks</b><ul> -<li><a name="SONOS_setter_RefreshShareIndex"> +<li><a name="SONOSRefreshShareIndex"> <b><code>RefreshShareIndex</code></b></a> <br />Starts the refreshing of the library.</li> -<li><a name="SONOS_setter_RescanNetwork"> +<li><a name="SONOSRescanNetwork"> <b><code>RescanNetwork</code></b></a> <br />Restarts the player discovery.</li> </ul></li> <li><b>Control-Commands</b><ul> -<li><a name="SONOS_setter_Mute"> +<li><a name="SONOSMute"> <b><code>Mute <state></code></b></a> <br />Sets the mute-state on all players.</li> -<li><a name="SONOS_setter_PauseAll"> +<li><a name="SONOSPauseAll"> <b><code>PauseAll</code></b></a> <br />Pause all Zoneplayer.</li> -<li><a name="SONOS_setter_Pause"> +<li><a name="SONOSPause"> <b><code>Pause</code></b></a> <br />Alias for PauseAll.</li> -<li><a name="SONOS_setter_StopAll"> +<li><a name="SONOSStopAll"> <b><code>StopAll</code></b></a> <br />Stops all Zoneplayer.</li> -<li><a name="SONOS_setter_Stop"> +<li><a name="SONOSStop"> <b><code>Stop</code></b></a> <br />Alias for StopAll.</li> </ul></li> <li><b>Bookmark-Commands</b><ul> -<li><a name="SONOS_setter_DisableBookmark"> +<li><a name="SONOSDisableBookmark"> <b><code>DisableBookmark <Groupname></code></b></a> <br />Disables the group with the given name.</li> -<li><a name="SONOS_setter_EnableBookmark"> +<li><a name="SONOSEnableBookmark"> <b><code>EnableBookmark <Groupname></code></b></a> <br />Enables the group with the given name.</li> -<li><a name="SONOS_setter_LoadBookmarks"> +<li><a name="SONOSLoadBookmarks"> <b><code>LoadBookmarks [Groupname]</code></b></a> <br />Loads the given group (or all if parameter not set) from the filesystem.</li> -<li><a name="SONOS_setter_SaveBookmarks"> +<li><a name="SONOSSaveBookmarks"> <b><code>SaveBookmarks [Groupname]</code></b></a> <br />Saves the given group (or all if parameter not set) to the filesystem.</li> </ul></li> <li><b>Group-Commands</b><ul> -<li><a name="SONOS_setter_Groups"> +<li><a name="SONOSGroups"> <b><code>Groups <GroupDefinition></code></b></a> <br />Sets the current groups on the whole Sonos-System. The format is the same as retreived by getter 'Groups'.<br >A reserved word is <i>Reset</i>. It can be used to directly extract all players out of their groups.</li> </ul></li> @@ -10692,7 +10838,7 @@ Example with control over the used port and the isalive-checker-interval:<br /> <h4>Get</h4> <ul> <li><b>Group-Commands</b><ul> -<li><a name="SONOS_getter_Groups"> +<li><a name="SONOSGroups"> <b><code>Groups</code></b></a> <br />Retreives the current group-configuration of the Sonos-System. The format is a comma-separated List of Lists with devicenames e.g. <code>[Sonos_Kueche], [Sonos_Wohnzimmer, Sonos_Schlafzimmer]</code>. In this example there are two groups: the first consists of one player and the second consists of two players.<br /> The order in the sublists are important, because the first entry defines the so-called group-coordinator (in this case <code>Sonos_Wohnzimmer</code>), from which the current playlist and the current title playing transferred to the other member(s).</li> @@ -10703,85 +10849,87 @@ The order in the sublists are important, because the first entry defines the so- '''Attention'''<br />The most of the attributes can only be used after a restart of fhem, because it must be initially transfered to the subprocess. <ul> <li><b>Common</b><ul> -<li><a name="SONOS_attribut_coverLoadTimeout"><b><code>coverLoadTimeout <value></code></b> +<li><a name="SONOSallowedWebAccess"><b><code>allowedWebAccess <value></code></b> +</a><br />Defines a regular Expression for allowed addresses for loading data. If not set, every access is allowed. e.g. <b>^http:\/\/192\.168\.0\.\d+.*$</b> for restricting access to local IP-addresses.</li> +<li><a name="SONOScoverLoadTimeout"><b><code>coverLoadTimeout <value></code></b> </a><br />One of (0..10,15,20,25,30). Defines the timeout for waiting of the Sonosplayer for Cover-Downloads. Defaults to 5.</li> -<li><a name="SONOS_attribut_deviceRoomView"><b><code>deviceRoomView <Both|DeviceLineOnly></code></b> +<li><a name="SONOSdeviceRoomView"><b><code>deviceRoomView <Both|DeviceLineOnly></code></b> </a><br /> Defines the style of the Device in the room overview. <code>Both</code> means "normal" Deviceline incl. Cover-/Titleview and maybe the control area, <code>DeviceLineOnly</code> means only the "normal" Deviceline-view.</li> -<li><a name="SONOS_attribut_disable"><b><code>disable <value></code></b> +<li><a name="SONOSdisable"><b><code>disable <value></code></b> </a><br />One of (0,1). With this value you can disable the whole module. Works immediatly. If set to 1 the subprocess will be terminated and no message will be transmitted. If set to 0 the subprocess is again started.<br />It is useful when you install new Sonos-Components and don't want any disgusting devices during the Sonos setup.</li> -<li><a name="SONOS_attribut_getFavouritesListAtNewVersion"><b><code>getFavouritesListAtNewVersion <value></code></b> +<li><a name="SONOSgetFavouritesListAtNewVersion"><b><code>getFavouritesListAtNewVersion <value></code></b> </a><br />One of (0,1). With this attribute set, the module will refresh the Favourites-List automatically upon changes (if the Attribute <code>getListsDirectlyToReadings</code> is set).</li> -<li><a name="SONOS_attribut_getPlaylistsListAtNewVersion"><b><code>getPlaylistsListAtNewVersion <value></code></b> +<li><a name="SONOSgetPlaylistsListAtNewVersion"><b><code>getPlaylistsListAtNewVersion <value></code></b> </a><br />One of (0,1). With this attribute set, the module will refresh the Playlists-List automatically upon changes (if the Attribute <code>getListsDirectlyToReadings</code> is set).</li> -<li><a name="SONOS_attribut_getQueueListAtNewVersion"><b><code>getQueueListAtNewVersion <value></code></b> +<li><a name="SONOSgetQueueListAtNewVersion"><b><code>getQueueListAtNewVersion <value></code></b> </a><br />One of (0,1). With this attribute set, the module will refresh the current Queue-List automatically upon changes (if the Attribute <code>getListsDirectlyToReadings</code> is set).</li> -<li><a name="SONOS_attribut_getRadiosListAtNewVersion"><b><code>getRadiosListAtNewVersion <value></code></b> +<li><a name="SONOSgetRadiosListAtNewVersion"><b><code>getRadiosListAtNewVersion <value></code></b> </a><br />One of (0,1). With this attribute set, the module will refresh the Radios-List automatically upon changes (if the Attribute <code>getListsDirectlyToReadings</code> is set).</li> -<li><a name="SONOS_attribut_getListsDirectlyToReadings"><b><code>getListsDirectlyToReadings <value></code></b> +<li><a name="SONOSgetListsDirectlyToReadings"><b><code>getListsDirectlyToReadings <value></code></b> </a><br />One of (0,1). With this attribute you can define that the module fills the readings for the lists of Favourites, Playlists, Radios and the Queue directly without the need of userReadings.</li> -<li><a name="SONOS_attribut_getLocalCoverArt"><b><code>getLocalCoverArt <value></code></b> +<li><a name="SONOSgetLocalCoverArt"><b><code>getLocalCoverArt <value></code></b> </a><br />One of (0,1). With this attribute the loads and saves the Coverart locally (default till now).</li> -<li><a name="SONOS_attribut_ignoredIPs"><b><code>ignoredIPs <IP-Address>[,IP-Address]</code></b> +<li><a name="SONOSignoredIPs"><b><code>ignoredIPs <IP-Address>[,IP-Address]</code></b> </a><br />With this attribute you can define IP-addresses, which has to be ignored by the UPnP-System of this module. e.g. "192.168.0.11,192.168.0.37"</li> -<li><a name="SONOS_attribut_pingType"><b><code>pingType <string></code></b> +<li><a name="SONOSpingType"><b><code>pingType <string></code></b> </a><br /> One of (none,tcp,udp,icmp,syn). Defines which pingType for alive-Checking has to be used. If set to 'none' no checks will be done.</li> -<li><a name="SONOS_attribut_reusePort"><b><code>reusePort <int></code></b> +<li><a name="SONOSreusePort"><b><code>reusePort <int></code></b> </a><br /> One of (0,1). If defined the socket-Attribute 'reuseport' will be used for SSDP Discovery-Port. Can solve restart-problems. If you don't have such problems don't use this attribute.</li> -<li><a name="SONOS_attribut_SubProcessLogfileName"><b><code>SubProcessLogfileName <Path></code></b> +<li><a name="SONOSSubProcessLogfileName"><b><code>SubProcessLogfileName <Path></code></b> </a><br /> If given, the subprocess logs into its own logfile. Under Windows this is a recommended way for logging, because the two Loggings (Fehm and the SubProcess) overwrite each other. If "-" is given, the logging goes to STDOUT (and therefor in the Fhem-log) as usual. The main purpose of this attribute is the short-use of separated logging. No variables are substituted. The value is used as configured.</li> -<li><a name="SONOS_attribut_usedonlyIPs"><b><code>usedonlyIPs <IP-Adresse>[,IP-Adresse]</code></b> +<li><a name="SONOSusedonlyIPs"><b><code>usedonlyIPs <IP-Adresse>[,IP-Adresse]</code></b> </a><br />With this attribute you can define IP-addresses, which has to be exclusively used by the UPnP-System of this module. e.g. "192.168.0.11,192.168.0.37"</li> </ul></li> <li><b>Bookmark Configuration</b><ul> -<li><a name="SONOS_attribut_bookmarkSaveDir"><b><code>bookmarkSaveDir <path></code></b> +<li><a name="SONOSbookmarkSaveDir"><b><code>bookmarkSaveDir <path></code></b> </a><br /> Defines a directory where the saved bookmarks can be placed. If not defined, "." will be used.</li> -<li><a name="SONOS_attribut_bookmarkTitleDefinition"><b><code>bookmarkTitleDefinition <Groupname>:<PlayerdeviceRegEx>:<TrackURIRegEx>:<MinTitleLength>:<RemainingLength>:<MaxAge>:<ReadOnly></code></b> +<li><a name="SONOSbookmarkTitleDefinition"><b><code>bookmarkTitleDefinition <Groupname>:<PlayerdeviceRegEx>:<TrackURIRegEx>:<MinTitleLength>:<RemainingLength>:<MaxAge>:<ReadOnly></code></b> </a><br /> Definition of Bookmarks for titles.</li> -<li><a name="SONOS_attribut_bookmarkPlaylistDefinition"><b><code>bookmarkPlaylistDefinition <Groupname>:<PlayerdeviceRegEx>:<MinListLength>:<MaxListLength>:<MaxAge></code></b> +<li><a name="SONOSbookmarkPlaylistDefinition"><b><code>bookmarkPlaylistDefinition <Groupname>:<PlayerdeviceRegEx>:<MinListLength>:<MaxListLength>:<MaxAge></code></b> </a><br /> Definition of bookmarks for playlists.</li> </ul></li> <li><b>Proxy Configuration</b><ul> -<li><a name="SONOS_attribut_generateProxyAlbumArtURLs"><b><code>generateProxyAlbumArtURLs <int></code></b> +<li><a name="SONOSgenerateProxyAlbumArtURLs"><b><code>generateProxyAlbumArtURLs <int></code></b> </a><br />One of (0, 1). If defined, all Cover-Links (the readings "currentAlbumArtURL" and "nextAlbumArtURL") are generated as links to the internal Sonos-Module-Proxy. It can be useful if you access Fhem over an external proxy and therefore have no access to the local network (the URLs are direct URLs to the Sonosplayer instead).</li> -<li><a name="SONOS_attribut_proxyCacheDir"><b><code>proxyCacheDir <Path></code></b> +<li><a name="SONOSproxyCacheDir"><b><code>proxyCacheDir <Path></code></b> </a><br />Defines a directory where the cached Coverfiles can be placed. If not defined "/tmp" will be used.</li> -<li><a name="SONOS_attribut_proxyCacheTime"><b><code>proxyCacheTime <int></code></b> +<li><a name="SONOSproxyCacheTime"><b><code>proxyCacheTime <int></code></b> </a><br />A time in seconds. With a definition other than "0" the caching mechanism of the internal Sonos-Module-Proxy will be activated. If the filetime of the chached cover is older than this time, it will be reloaded from the Sonosplayer.</li> -<li><a name="SONOS_attribut_webname"><b><code>webname <String></code></b> +<li><a name="SONOSwebname"><b><code>webname <String></code></b> </a><br /> With the attribute you can define the used webname for coverlinks. Defaults to 'fhem' if not given.</li> </ul></li> <li><b>Speak Configuration</b><ul> -<li><a name="SONOS_attribut_targetSpeakDir"><b><code>targetSpeakDir <string></code></b> +<li><a name="SONOStargetSpeakDir"><b><code>targetSpeakDir <string></code></b> </a><br /> Defines, which Directory has to be used for the Speakfiles</li> -<li><a name="SONOS_attribut_targetSpeakMP3FileConverter"><b><code>targetSpeakMP3FileConverter <string></code></b> +<li><a name="SONOStargetSpeakMP3FileConverter"><b><code>targetSpeakMP3FileConverter <string></code></b> </a><br /> Defines an MP3-File converter, which properly converts the resulting speaking-file. With this option you can avoid timedisplay problems. Please note that the waittime before the speaking starts can increase with this option be set.</li> -<li><a name="SONOS_attribut_targetSpeakMP3FileDir"><b><code>targetSpeakMP3FileDir <string></code></b> +<li><a name="SONOStargetSpeakMP3FileDir"><b><code>targetSpeakMP3FileDir <string></code></b> </a><br /> The directory which should be used as a default for text-embedded MP3-Files.</li> -<li><a name="SONOS_attribut_targetSpeakURL"><b><code>targetSpeakURL <string></code></b> +<li><a name="SONOStargetSpeakURL"><b><code>targetSpeakURL <string></code></b> </a><br /> Defines, which URL has to be used for accessing former stored Speakfiles as seen from the SonosPlayer</li> -<li><a name="SONOS_attribut_targetSpeakFileTimestamp"><b><code>targetSpeakFileTimestamp <int></code></b> +<li><a name="SONOStargetSpeakFileTimestamp"><b><code>targetSpeakFileTimestamp <int></code></b> </a><br /> One of (0, 1). Defines, if the Speakfile should have a timestamp in his name. That makes it possible to store all historical Speakfiles.</li> -<li><a name="SONOS_attribut_targetSpeakFileHashCache"><b><code>targetSpeakFileHashCache <int></code></b> +<li><a name="SONOStargetSpeakFileHashCache"><b><code>targetSpeakFileHashCache <int></code></b> </a><br /> One of (0, 1). Defines, if the Speakfile should have a hash-value in his name. If this value is set to one an already generated file with the same hash is re-used and not newly generated.</li> -<li><a name="SONOS_attribut_Speak1"><b><code>Speak1 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak1"><b><code>Speak1 <Fileextension>:<Commandline></code></b> </a><br />Defines a systemcall commandline for generating a speaking file out of the given text. If such an attribute is defined, an associated setter at the Sonosplayer-Device is available. The following placeholders are available:<br />'''%language%''': Will be replaced by the given language-parameter<br />'''%filename%''': Will be replaced by the complete target-filename (incl. fileextension).<br />'''%text%''': Will be replaced with the given text.<br />'''%textescaped%''': Will be replaced with the given url-encoded text.</li> -<li><a name="SONOS_attribut_Speak2"><b><code>Speak2 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak2"><b><code>Speak2 <Fileextension>:<Commandline></code></b> </a><br />See Speak1</li> -<li><a name="SONOS_attribut_Speak3"><b><code>Speak3 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak3"><b><code>Speak3 <Fileextension>:<Commandline></code></b> </a><br />See Speak1</li> -<li><a name="SONOS_attribut_Speak4"><b><code>Speak4 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak4"><b><code>Speak4 <Fileextension>:<Commandline></code></b> </a><br />See Speak1</li> -<li><a name="SONOS_attribut_SpeakCover"><b><code>SpeakCover <Filename></code></b> +<li><a name="SONOSSpeakCover"><b><code>SpeakCover <Filename></code></b> </a><br />Defines a Cover for use by the speak generation process. If not defined the Fhem-logo will be used.</li> -<li><a name="SONOS_attribut_Speak1Cover"><b><code>Speak1Cover <Filename></code></b> +<li><a name="SONOSSpeak1Cover"><b><code>Speak1Cover <Filename></code></b> </a><br />See SpeakCover</li> -<li><a name="SONOS_attribut_Speak2Cover"><b><code>Speak2Cover <Filename></code></b> +<li><a name="SONOSSpeak2Cover"><b><code>Speak2Cover <Filename></code></b> </a><br />See SpeakCover</li> -<li><a name="SONOS_attribut_Speak3Cover"><b><code>Speak3Cover <Filename></code></b> +<li><a name="SONOSSpeak3Cover"><b><code>Speak3Cover <Filename></code></b> </a><br />See SpeakCover</li> -<li><a name="SONOS_attribut_Speak4Cover"><b><code>Speak4Cover <Filename></code></b> +<li><a name="SONOSSpeak4Cover"><b><code>Speak4Cover <Filename></code></b> </a><br />See SpeakCover</li> -<li><a name="SONOS_attribut_SpeakGoogleURL"><b><code>SpeakGoogleURL <GoogleURL></code></b> +<li><a name="SONOSSpeakGoogleURL"><b><code>SpeakGoogleURL <GoogleURL></code></b> </a><br />The google-speak-url that has to be used. If empty a default will be used. You have to define placeholders for replacing the language- and text-value: %1$s -> Language, %2$s -> Text<br />The Default-URL is currently: <code>http://translate.google.com/translate_tts?tl=%1$s&client=tw-ob&q=%2$s</code></li> </ul></li> </ul> @@ -10842,46 +10990,46 @@ Definition mit Kontrolle über den verwendeten Port und das Intervall der IsAliv <h4>Set</h4> <ul> <li><b>Grundsätzliches</b><ul> -<li><a name="SONOS_setter_RefreshShareIndex"> +<li><a name="SONOSRefreshShareIndex"> <b><code>RefreshShareIndex</code></b></a> <br />Startet die Aktualisierung der Bibliothek.</li> -<li><a name="SONOS_setter_RescanNetwork"> +<li><a name="SONOSRescanNetwork"> <b><code>RescanNetwork</code></b></a> <br />Startet die Erkennung der im Netzwerk vorhandenen Player erneut.</li> </ul></li> <li><b>Steuerbefehle</b><ul> -<li><a name="SONOS_setter_Mute"> +<li><a name="SONOSMute"> <b><code>Mute <state></code></b></a> <br />Setzt den Mute-Zustand bei allen Playern.</li> -<li><a name="SONOS_setter_PauseAll"> +<li><a name="SONOSPauseAll"> <b><code>PauseAll</code></b></a> <br />Pausiert die Wiedergabe in allen Zonen.</li> -<li><a name="SONOS_setter_Pause"> +<li><a name="SONOSPause"> <b><code>Pause</code></b></a> <br />Synonym für PauseAll.</li> -<li><a name="SONOS_setter_StopAll"> +<li><a name="SONOSStopAll"> <b><code>StopAll</code></b></a> <br />Stoppt die Wiedergabe in allen Zonen.</li> -<li><a name="SONOS_setter_Stop"> +<li><a name="SONOSStop"> <b><code>Stop</code></b></a> <br />Synonym für StopAll.</li> </ul></li> <li><b>Bookmark-Befehle</b><ul> -<li><a name="SONOS_setter_DisableBookmark"> +<li><a name="SONOSDisableBookmark"> <b><code>DisableBookmark <Groupname></code></b></a> <br />Deaktiviert die angegebene Gruppe.</li> -<li><a name="SONOS_setter_EnableBookmark"> +<li><a name="SONOSEnableBookmark"> <b><code>EnableBookmark <Groupname></code></b></a> <br />Aktiviert die angegebene Gruppe.</li> -<li><a name="SONOS_setter_LoadBookmarks"> +<li><a name="SONOSLoadBookmarks"> <b><code>LoadBookmarks [Groupname]</code></b></a> <br />Lädt die angegebene Gruppe (oder alle Gruppen, wenn nicht angegeben) aus den entsprechenden Dateien.</li> -<li><a name="SONOS_setter_SaveBookmarks"> +<li><a name="SONOSSaveBookmarks"> <b><code>SaveBookmarks [Groupname]</code></b></a> <br />Speichert die angegebene Gruppe (oder alle Gruppen, wenn nicht angegeben) in die entsprechenden Dateien.</li> </ul></li> <li><b>Gruppenbefehle</b><ul> -<li><a name="SONOS_setter_Groups"> +<li><a name="SONOSGroups"> <b><code>Groups <GroupDefinition></code></b></a> <br />Setzt die aktuelle Gruppierungskonfiguration der Sonos-Systemlandschaft. Das Format ist jenes, welches auch von dem Get-Befehl 'Groups' geliefert wird.<br >Hier kann als GroupDefinition das Wort <i>Reset</i> verwendet werden, um alle Player aus ihren Gruppen zu entfernen.</li> </ul></li> @@ -10890,7 +11038,7 @@ Definition mit Kontrolle über den verwendeten Port und das Intervall der IsAliv <h4>Get</h4> <ul> <li><b>Gruppenbefehle</b><ul> -<li><a name="SONOS_getter_Groups"> +<li><a name="SONOSGroups"> <b><code>Groups</code></b></a> <br />Liefert die aktuelle Gruppierungskonfiguration der Sonos Systemlandschaft zurück. Das Format ist eine Kommagetrennte Liste von Listen mit Devicenamen, also z.B. <code>[Sonos_Kueche], [Sonos_Wohnzimmer, Sonos_Schlafzimmer]</code>. In diesem Beispiel sind also zwei Gruppen definiert, von denen die erste aus einem Player und die zweite aus Zwei Playern besteht.<br /> Dabei ist die Reihenfolge innerhalb der Unterlisten wichtig, da der erste Eintrag der sogenannte Gruppenkoordinator ist (in diesem Fall also <code>Sonos_Wohnzimmer</code>), von dem die aktuelle Abspielliste un der aktuelle Titel auf die anderen Gruppenmitglieder übernommen wird.</li> @@ -10901,87 +11049,89 @@ Dabei ist die Reihenfolge innerhalb der Unterlisten wichtig, da der erste Eintra '''Hinweis'''<br />Die Attribute werden erst bei einem Neustart von Fhem verwendet, da diese dem SubProzess initial zur Verfügung gestellt werden müssen. <ul> <li><b>Grundsätzliches</b><ul> -<li><a name="SONOS_attribut_coverLoadTimeout"><b><code>coverLoadTimeout <value></code></b> +<li><a name="SONOSallowedWebAccess"><b><code>allowedWebAccess <value></code></b> +</a><br />Definiert einen regulären Ausdruck für die erlaubten Adressen für Nachladedaten wie Musikdienste, Cover o.ä. Wenn nicht angegeben, dann wird alles zugelassen.<br />'''z.B. Mit <b>^http:\/\/192\.168\.0\.\d+.*$</b> wird der Zugriff auf das lokale Netz (z.B. beim Laden von Daten vom Sonosplayer selbst) beschränkt.<br />Alles Sperren geht über eine <i>unmögliche</i> Adresse wie <b>^xyz</b></li> +<li><a name="SONOScoverLoadTimeout"><b><code>coverLoadTimeout <value></code></b> </a><br />Eines von (0..10,15,20,25,30). Definiert den Timeout der für die Abfrage des Covers beim Sonosplayer verwendet wird. Wenn nicht angegeben, dann wird 5 verwendet.</li> -<li><a name="SONOS_attribut_deviceRoomView"><b><code>deviceRoomView <Both|DeviceLineOnly></code></b> +<li><a name="SONOSdeviceRoomView"><b><code>deviceRoomView <Both|DeviceLineOnly></code></b> </a><br /> Gibt an, was in der Raumansicht zum Sonosplayer-Device angezeigt werden soll. <code>Both</code> bedeutet "normale" Devicezeile zzgl. Cover-/Titelanzeige und u.U. Steuerbereich, <code>DeviceLineOnly</code> bedeutet nur die Anzeige der "normalen" Devicezeile.</li> -<li><a name="SONOS_attribut_disable"><b><code>disable <value></code></b> +<li><a name="SONOSdisable"><b><code>disable <value></code></b> </a><br />Eines von (0,1). Hiermit kann das Modul abgeschaltet werden. Wirkt sofort. Bei 1 wird der SubProzess beendet, und somit keine weitere Verarbeitung durchgeführt. Bei 0 wird der Prozess wieder gestartet.<br />Damit kann das Modul temporär abgeschaltet werden, um bei der Neueinrichtung von Sonos-Komponenten keine halben Zustände mitzubekommen.</li> -<li><a name="SONOS_attribut_getFavouritesListAtNewVersion"><b><code>getFavouritesListAtNewVersion <value></code></b> +<li><a name="SONOSgetFavouritesListAtNewVersion"><b><code>getFavouritesListAtNewVersion <value></code></b> </a><br />Eines von (0,1). Mit diesem Attribut kann das Modul aufgefordert werden, die Favoriten (bei definiertem Attribut <code>getListsDirectlyToReadings</code>) bei Aktualisierung automatisch herunterzuladen.</li> -<li><a name="SONOS_attribut_getPlaylistsListAtNewVersion"><b><code>getPlaylistsListAtNewVersion <value></code></b> +<li><a name="SONOSgetPlaylistsListAtNewVersion"><b><code>getPlaylistsListAtNewVersion <value></code></b> </a><br />Eines von (0,1). Mit diesem Attribut kann das Modul aufgefordert werden, die Playlisten (bei definiertem Attribut <code>getListsDirectlyToReadings</code>) bei Aktualisierung automatisch herunterzuladen.</li> -<li><a name="SONOS_attribut_getQueueListAtNewVersion"><b><code>getQueueListAtNewVersion <value></code></b> +<li><a name="SONOSgetQueueListAtNewVersion"><b><code>getQueueListAtNewVersion <value></code></b> </a><br />Eines von (0,1). Mit diesem Attribut kann das Modul aufgefordert werden, die aktuelle Abspielliste (bei definiertem Attribut <code>getListsDirectlyToReadings</code>) bei Aktualisierung automatisch herunterzuladen.</li> -<li><a name="SONOS_attribut_getRadiosListAtNewVersion"><b><code>getRadiosListAtNewVersion <value></code></b> +<li><a name="SONOSgetRadiosListAtNewVersion"><b><code>getRadiosListAtNewVersion <value></code></b> </a><br />Eines von (0,1). Mit diesem Attribut kann das Modul aufgefordert werden, die Radioliste (bei definiertem Attribut <code>getListsDirectlyToReadings</code>) bei Aktualisierung automatisch herunterzuladen.</li> -<li><a name="SONOS_attribut_getListsDirectlyToReadings"><b><code>getListsDirectlyToReadings <value></code></b> +<li><a name="SONOSgetListsDirectlyToReadings"><b><code>getListsDirectlyToReadings <value></code></b> </a><br />Eines von (0,1). Mit diesem Attribut kann das Modul aufgefordert werden, die Listen für Favoriten, Playlists, Radios und Queue direkt in die entsprechenden Readings zu schreiben. Dafür sind dann keine Userreadings mehr notwendig.</li> -<li><a name="SONOS_attribut_getLocalCoverArt"><b><code>getLocalCoverArt <value></code></b> +<li><a name="SONOSgetLocalCoverArt"><b><code>getLocalCoverArt <value></code></b> </a><br />Eines von (0,1). Mit diesem Attribut kann das Modul aufgefordert werden, die Cover lokal herunterzuladen (bisheriges Standardverhalten).</li> -<li><a name="SONOS_attribut_ignoredIPs"><b><code>ignoredIPs <IP-Adresse>[,IP-Adresse]</code></b> +<li><a name="SONOSignoredIPs"><b><code>ignoredIPs <IP-Adresse>[,IP-Adresse]</code></b> </a><br />Mit diesem Attribut können IP-Adressen angegeben werden, die vom UPnP-System ignoriert werden sollen. Z.B.: "192.168.0.11,192.168.0.37"</li> -<li><a name="SONOS_attribut_pingType"><b><code>pingType <string></code></b> +<li><a name="SONOSpingType"><b><code>pingType <string></code></b> </a><br /> Eines von (none,tcp,udp,icmp,syn). Gibt an, welche Methode für die Ping-Überprüfung verwendet werden soll. Wenn 'none' angegeben wird, dann wird keine Überprüfung gestartet.</li> -<li><a name="SONOS_attribut_reusePort"><b><code>reusePort <int></code></b> +<li><a name="SONOSreusePort"><b><code>reusePort <int></code></b> </a><br /> Eines von (0,1). Gibt an, ob die Portwiederwendung für SSDP aktiviert werden soll, oder nicht. Kann Restart-Probleme lösen. Wenn man diese Probleme nicht hat, sollte man das Attribut nicht setzen.</li> -<li><a name="SONOS_attribut_SubProcessLogfileName"><b><code>SubProcessLogfileName <Pfad></code></b> +<li><a name="SONOSSubProcessLogfileName"><b><code>SubProcessLogfileName <Pfad></code></b> </a><br /> Hiermit kann für den SubProzess eine eigene Logdatei angegeben werden. Unter Windows z.B. überschreiben sich die beiden Logausgaben (von Fhem und SubProzess) sonst gegenseitig. Wenn "-" angegeben wird, wird wie bisher auf STDOUT (und damit im Fhem-Log) geloggt. Der Hauptanwendungsfall ist die mehr oder weniger kurzfristige Fehlersuche. Es werden keinerlei Variablenwerte ersetzt, und der Wert direkt als Dateiname verwendet.</li> -<li><a name="SONOS_attribut_usedonlyIPs"><b><code>usedonlyIPs <IP-Adresse>[,IP-Adresse]</code></b> +<li><a name="SONOSusedonlyIPs"><b><code>usedonlyIPs <IP-Adresse>[,IP-Adresse]</code></b> </a><br />Mit diesem Attribut können IP-Adressen angegeben werden, die ausschließlich vom UPnP-System berücksichtigt werden sollen. Z.B.: "192.168.0.11,192.168.0.37"</li> </ul></li> <li><b>Bookmark-Einstellungen</b><ul> -<li><a name="SONOS_attribut_bookmarkSaveDir"><b><code>bookmarkSaveDir <path></code></b> +<li><a name="SONOSbookmarkSaveDir"><b><code>bookmarkSaveDir <path></code></b> </a><br /> Das Verzeichnis, in dem die Dateien für die gespeicherten Bookmarks abgelegt werden sollen. Wenn nicht festgelegt, dann wird "." verwendet.</li> -<li><a name="SONOS_attribut_bookmarkTitleDefinition"><b><code>bookmarkTitleDefinition <Groupname>:<PlayerdeviceRegEx>:<TrackURIRegEx>:<MinTitleLength>:<RemainingLength>:<MaxAge>:<ReadOnly> [...]</code></b> +<li><a name="SONOSbookmarkTitleDefinition"><b><code>bookmarkTitleDefinition <Groupname>:<PlayerdeviceRegEx>:<TrackURIRegEx>:<MinTitleLength>:<RemainingLength>:<MaxAge>:<ReadOnly> [...]</code></b> </a><br /> Die Definition für die Verwendung von Bookmarks für Titel.</li> -<li><a name="SONOS_attribut_bookmarkPlaylistDefinition"><b><code>bookmarkPlaylistDefinition <Groupname>:<PlayerdeviceRegEx>:<MinListLength>:<MaxListLength>:<MaxAge> [...]</code></b> +<li><a name="SONOSbookmarkPlaylistDefinition"><b><code>bookmarkPlaylistDefinition <Groupname>:<PlayerdeviceRegEx>:<MinListLength>:<MaxListLength>:<MaxAge> [...]</code></b> </a><br /> Die Definition für die Verwendung von Bookmarks für aktuelle Abspiellisten/Playlisten.</li> </ul></li> <li><b>Proxy-Einstellungen</b><ul> -<li><a name="SONOS_attribut_generateProxyAlbumArtURLs"><b><code>generateProxyAlbumArtURLs <int></code></b> +<li><a name="SONOSgenerateProxyAlbumArtURLs"><b><code>generateProxyAlbumArtURLs <int></code></b> </a><br /> Aus (0, 1). Wenn aktiviert, werden alle Cober-Links als Proxy-Aufrufe an Fhem generiert. Dieser Proxy-Server wird vom Sonos-Modul bereitgestellt. In der Grundeinstellung erfolgt kein Caching der Cover, sondern nur eine Durchreichung der Cover von den Sonosplayern (Damit ist der Zugriff durch einen externen Proxyserver auf Fhem möglich).</li> -<li><a name="SONOS_attribut_proxyCacheDir"><b><code>proxyCacheDir <Path></code></b> +<li><a name="SONOSproxyCacheDir"><b><code>proxyCacheDir <Path></code></b> </a><br /> Hiermit wird das Verzeichnis festgelegt, in dem die Cober zwischengespeichert werden. Wenn nicht festegelegt, so wird "/tmp" verwendet.</li> -<li><a name="SONOS_attribut_proxyCacheTime"><b><code>proxyCacheTime <int></code></b> +<li><a name="SONOSproxyCacheTime"><b><code>proxyCacheTime <int></code></b> </a><br /> Mit einer Angabe ungleich 0 wird der Caching-Mechanismus des Sonos-Modul-Proxy-Servers aktiviert. Dabei werden Cover, die im Cache älter sind als diese Zeitangabe in Sekunden, neu vom Sonosplayer geladen, alle anderen direkt ausgeliefert, ohne den Player zu fragen.</li> -<li><a name="SONOS_attribut_webname"><b><code>webname <String></code></b> +<li><a name="SONOSwebname"><b><code>webname <String></code></b> </a><br /> Hiermit kann der zu verwendende Webname für die Cover-Link-Erzeugung angegeben werden. Da vom Modul Links zu Cover u.ä. erzeugt werden, ohne dass es einen FhemWeb-Aufruf dazu gibt, kann das Modul diesen Pfad nicht selber herausfinden. Wenn das Attribut nicht angegeben wird, dann wird 'fhem' angenommen.</li> </ul></li> <li><b>Sprachoptionen</b><ul> -<li><a name="SONOS_attribut_targetSpeakDir"><b><code>targetSpeakDir <string></code></b> +<li><a name="SONOStargetSpeakDir"><b><code>targetSpeakDir <string></code></b> </a><br /> Gibt an, welches Verzeichnis für die Ablage des MP3-Files der Textausgabe verwendet werden soll</li> -<li><a name="SONOS_attribut_targetSpeakMP3FileConverter"><b><code>targetSpeakMP3FileConverter <string></code></b> +<li><a name="SONOStargetSpeakMP3FileConverter"><b><code>targetSpeakMP3FileConverter <string></code></b> </a><br /> Hiermit kann ein MP3-Konverter angegeben werden, da am Ende der Verkettung der Speak-Ansage das resultierende MP3-File nochmal sauber durchkodiert. Damit können Restzeitanzeigeprobleme behoben werden. Dadurch vegrößert sich allerdings u.U. die Ansageverzögerung.</li> -<li><a name="SONOS_attribut_targetSpeakMP3FileDir"><b><code>targetSpeakMP3FileDir <string></code></b> +<li><a name="SONOStargetSpeakMP3FileDir"><b><code>targetSpeakMP3FileDir <string></code></b> </a><br /> Das Verzeichnis, welches als Standard für MP3-Fileangaben in Speak-Texten verwendet werden soll. Wird dieses Attribut definiert, können die Angaben bei Speak ohne Verzeichnis erfolgen.</li> -<li><a name="SONOS_attribut_targetSpeakURL"><b><code>targetSpeakURL <string></code></b> +<li><a name="SONOStargetSpeakURL"><b><code>targetSpeakURL <string></code></b> </a><br /> Gibt an, unter welcher Adresse der ZonePlayer das unter targetSpeakDir angegebene Verzeichnis erreichen kann.</li> -<li><a name="SONOS_attribut_targetSpeakFileTimestamp"><b><code>targetSpeakFileTimestamp <int></code></b> +<li><a name="SONOStargetSpeakFileTimestamp"><b><code>targetSpeakFileTimestamp <int></code></b> </a><br /> One of (0, 1). Gibt an, ob die erzeugte MP3-Sprachausgabedatei einen Zeitstempel erhalten soll (1) oder nicht (0).</li> -<li><a name="SONOS_attribut_targetSpeakFileHashCache"><b><code>targetSpeakFileHashCache <int></code></b> +<li><a name="SONOStargetSpeakFileHashCache"><b><code>targetSpeakFileHashCache <int></code></b> </a><br /> One of (0, 1). Gibt an, ob die erzeugte Sprachausgabedatei einen Hashwert erhalten soll (1) oder nicht (0). Wenn dieser Wert gesetzt wird, dann wird eine bereits bestehende Datei wiederverwendet, und nicht neu erzeugt.</li> -<li><a name="SONOS_attribut_Speak1"><b><code>Speak1 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak1"><b><code>Speak1 <Fileextension>:<Commandline></code></b> </a><br />Hiermit kann ein Systemaufruf definiert werden, der zu Erzeugung einer Sprachausgabe verwendet werden kann. Sobald dieses Attribut definiert wurde, ist ein entsprechender Setter am Sonosplayer verfügbar.<br />Es dürfen folgende Platzhalter verwendet werden:<br />'''%language%''': Wird durch die eingegebene Sprache ersetzt<br />'''%filename%''': Wird durch den kompletten Dateinamen (inkl. Dateiendung) ersetzt.<br />'''%text%''': Wird durch den zu übersetzenden Text ersetzt.<br />'''%textescaped%''': Wird durch den URL-Enkodierten zu übersetzenden Text ersetzt.</li> -<li><a name="SONOS_attribut_Speak2"><b><code>Speak2 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak2"><b><code>Speak2 <Fileextension>:<Commandline></code></b> </a><br />Siehe Speak1</li> -<li><a name="SONOS_attribut_Speak3"><b><code>Speak3 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak3"><b><code>Speak3 <Fileextension>:<Commandline></code></b> </a><br />Siehe Speak1</li> -<li><a name="SONOS_attribut_Speak4"><b><code>Speak4 <Fileextension>:<Commandline></code></b> +<li><a name="SONOSSpeak4"><b><code>Speak4 <Fileextension>:<Commandline></code></b> </a><br />Siehe Speak1</li> -<li><a name="SONOS_attribut_SpeakCover"><b><code>SpeakCover <Absolute-Imagepath></code></b> +<li><a name="SONOSSpeakCover"><b><code>SpeakCover <Absolute-Imagepath></code></b> </a><br />Hiermit kann ein JPG- oder PNG-Bild als Cover für die Sprachdurchsagen definiert werden.</li> -<li><a name="SONOS_attribut_Speak1Cover"><b><code>Speak1Cover <Absolute-Imagepath></code></b> +<li><a name="SONOSSpeak1Cover"><b><code>Speak1Cover <Absolute-Imagepath></code></b> </a><br />Analog zu SpeakCover für Speak1.</li> -<li><a name="SONOS_attribut_Speak2Cover"><b><code>Speak2Cover <Absolute-Imagepath></code></b> +<li><a name="SONOSSpeak2Cover"><b><code>Speak2Cover <Absolute-Imagepath></code></b> </a><br />Analog zu SpeakCover für Speak2.</li> -<li><a name="SONOS_attribut_Speak3Cover"><b><code>Speak3Cover <Absolute-Imagepath></code></b> +<li><a name="SONOSSpeak3Cover"><b><code>Speak3Cover <Absolute-Imagepath></code></b> </a><br />Analog zu SpeakCover für Speak3.</li> -<li><a name="SONOS_attribut_Speak3Cover"><b><code>Speak3Cover <Absolute-Imagepath></code></b> +<li><a name="SONOSSpeak3Cover"><b><code>Speak3Cover <Absolute-Imagepath></code></b> </a><br />Analog zu SpeakCover für Speak3.</li> -<li><a name="SONOS_attribut_Speak4Cover"><b><code>Speak4Cover <Absolute-Imagepath></code></b> +<li><a name="SONOSSpeak4Cover"><b><code>Speak4Cover <Absolute-Imagepath></code></b> </a><br />Analog zu SpeakCover für Speak4.</li> -<li><a name="SONOS_attribut_SpeakGoogleURL"><b><code>SpeakGoogleURL <GoogleURL></code></b> +<li><a name="SONOSSpeakGoogleURL"><b><code>SpeakGoogleURL <GoogleURL></code></b> </a><br />Die zu verwendende Google-URL. Wenn dieser Parameter nicht angegeben wird, dann wird ein Standard verwendet. Hier müssen Platzhalter für die Ersetzung durch das Modul eingetragen werden: %1$s -> Sprache, %2$s -> Text<br />Die Standard-URL lautet momentan: <code>http://translate.google.com/translate_tts?tl=%1$s&client=tw-ob&q=%2$s</code></li> </ul></li> </ul> diff --git a/fhem/FHEM/21_SONOSPLAYER.pm b/fhem/FHEM/21_SONOSPLAYER.pm index d5fb0956b..d3c352c2b 100755 --- a/fhem/FHEM/21_SONOSPLAYER.pm +++ b/fhem/FHEM/21_SONOSPLAYER.pm @@ -69,6 +69,7 @@ use vars qw{%modules %defs}; # Variable Definitions ######################################################################################## my @possibleRoomIcons = qw(bathroom library office foyer dining tvroom hallway garage garden guestroom den bedroom kitchen portable media family pool masterbedroom playroom patio living); +my @SONOSPLAYER_opticalInputDeviceTypes = qw(S9 S11 S14); my %gets = ( 'CurrentTrackPosition' => '', @@ -271,7 +272,7 @@ sub SONOSPLAYER_Detail($$$;$) { # Control-Buttons if (!AttrVal($d, 'suppressControlButtons', 0) && ($withRC)) { - $html.= '<div class="rc_body" style="border: 1px solid gray; border-radius: 10px; padding: 5px;">'; + $html .= '<div class="rc_body" style="border: 1px solid gray; border-radius: 10px; padding: 5px;">'; $html .= '<table style="text-align: center;"><tr>'; $html .= '<td><a onclick="FW_cmd(\'?XHR=1&cmd.dummy=set '.$d.' Previous\')">'.FW_makeImage('rc_PREVIOUS.svg', 'Previous', 'rc-button').'</a></td> <td><a style="padding-left: 10px;" onclick="FW_cmd(\'?XHR=1&cmd.dummy=set '.$d.' Play\')">'.FW_makeImage('rc_PLAY.svg', 'Play', 'rc-button').'</a></td> @@ -338,7 +339,7 @@ sub SONOSPLAYER_State($$$$) { } # Die folgenden Readings werden nicht mehr benötigt, und werden hiermit entfernt... - return 'Reading '.$hash->{NAME}."->$name is now unused and is ignored for the future for all Zoneplayer-Types." if ($name eq 'LastGetActionName') || ($name eq 'LastGetActionResult') || ($name eq 'LastSetActionName') || ($name eq 'LastSetActionResult') || ($name eq 'LastSubscriptionsRenew') || ($name eq 'LastSubscriptionsResult') || ($name eq 'SetMakeStandaloneGroup') || ($name eq 'CurrentTempPlaying') || ($name eq 'SetWRONG'); + return 'Reading '.$hash->{NAME}."->$name is now unused and is ignored for the future for all Zoneplayer-Types." if ($name eq 'LastGetActionName') || ($name eq 'LastGetActionResult') || ($name eq 'LastSetActionName') || ($name eq 'LastSetActionResult') || ($name eq 'LastSubscriptionsResult') || ($name eq 'SetMakeStandaloneGroup') || ($name eq 'CurrentTempPlaying') || ($name eq 'SetWRONG'); return undef; } @@ -408,7 +409,7 @@ sub SONOSPLAYER_TriggerCoverTitleLater($) { # SONOSPLAYER_SimulateCurrentTrackPosition - Implements the Simulation for the currentTrackPosition # ######################################################################################## -sub SONOSPLAYER_SimulateCurrentTrackPosition() { +sub SONOSPLAYER_SimulateCurrentTrackPosition($) { my ($hash) = @_; return undef if (AttrVal($hash->{NAME}, 'disable', 0)); @@ -911,7 +912,7 @@ sub SONOSPLAYER_Set($@) { my $udnShort = $1 if ($dHash->{UDN} =~ m/(.*)_MR/); # Wenn dieses Quell-Device eine Playbar ist, dann den optischen Eingang als Quelle wählen... - if ((ReadingsVal($dHash->{NAME}, 'playerType', '') eq 'S9') || (ReadingsVal($dHash->{NAME}, 'playerType', '') eq 'S11')) { + if (SONOS_isInList(ReadingsVal($dHash->{NAME}, 'playerType', ''), @SONOSPLAYER_opticalInputDeviceTypes)) { # Das ganze geht nur bei dem eigenen Eingang, ansonsten eine Gruppenwiedergabe starten if ($dHash->{NAME} eq $hash->{NAME}) { $value = 'x-sonos-htastream:'.$udnShort.':spdif'; @@ -1311,240 +1312,240 @@ sub SONOSPLAYER_Log($$$) { <h4>Set</h4> <ul> <li><b>Common Tasks</b><ul> -<li><a name="SONOSPLAYER_setter_Alarm"> +<li><a name="SONOSPLAYERAlarm"> <b><code>Alarm (Create|Update|Delete|Enable|Disable) <ID[,ID]|All> <Datahash></code></b></a> <br />Can be used for working on alarms:<ul><li><b>Create:</b> Creates an alarm-entry with the given datahash.</li><li><b>Update:</b> Updates the alarm-entry with the given id(s) and datahash.</li><li><b>Delete:</b> Deletes the alarm-entry with the given id(s).</li><li><b>Enable:</b> Enables the alarm-entry with the given id(s).</li><li><b>Disable:</b> Disables the alarm-entry with the gven id(s).</li></ul>If the Word 'All' is given as ID, all alarms of this player are changed.<br /><b>The Datahash:</b><br />The Format is a perl-hash and is interpreted with the eval-function.<br />e.g.: { Repeat => 1 }<br /><br />The following entries are allowed/neccessary:<ul><li>StartTime</li><li>Duration</li><li>Recurrence_Once</li><li>Recurrence_Monday</li><li>Recurrence_Tuesday</li><li>Recurrence_Wednesday</li><li>Recurrence_Thursday</li><li>Recurrence_Friday</li><li>Recurrence_Saturday</li><li>Recurrence_Sunday</li><li>Enabled</li><li>ProgramURI</li><li>ProgramMetaData</li><li>Shuffle</li><li>Repeat</li><li>Volume</li><li>IncludeLinkedZones</li></ul><br />e.g.:<ul><li>set Sonos_Wohnzimmer Alarm Create 0 { Enabled => 1, Volume => 35, StartTime => '00:00:00', Duration => '00:15:00', Repeat => 0, Shuffle => 0, ProgramURI => 'x-rincon-buzzer:0', ProgramMetaData => '', Recurrence_Once => 0, Recurrence_Monday => 1, Recurrence_Tuesday => 1, Recurrence_Wednesday => 1, Recurrence_Thursday => 1, Recurrence_Friday => 1, Recurrence_Saturday => 0, Recurrence_Sunday => 0, IncludeLinkedZones => 0 }</li><li>set Sonos_Wohnzimmer Alarm Update 17 { Shuffle => 1 }</li><li>set Sonos_Wohnzimmer Alarm Delete 17 {}</li></ul></li> -<li><a name="SONOSPLAYER_setter_AudioDelay"> +<li><a name="SONOSPLAYERAudioDelay"> <b><code>AudioDelay <Level></code></b></a> <br /> Sets the audiodelay of the player to the given value. The value can range from 0 to 5.</li> -<li><a name="SONOSPLAYER_setter_AudioDelayLeftRear"> +<li><a name="SONOSPLAYERAudioDelayLeftRear"> <b><code>AudioDelayLeftRear <Level></code></b></a> <br /> Sets the audiodelayleftrear of the player to the given value. The value can range from 0 to 2. The values has the following meanings: 0: >3m, 1: >0.6m und <3m, 2: <0.6m</li> -<li><a name="SONOSPLAYER_setter_AudioDelayRightRear"> +<li><a name="SONOSPLAYERAudioDelayRightRear"> <b><code>AudioDelayRightRear <Level></code></b></a> <br /> Sets the audiodelayrightrear of the player to the given value. The value can range from 0 to 2. The values has the following meanings: 0: >3m, 1: >0.6m und <3m, 2: <0.6m</li> -<li><a name="SONOSPLAYER_setter_ButtonLockState"> +<li><a name="SONOSPLAYERButtonLockState"> <b><code>ButtonLockState <int></code></b></a> <br />One of (0, 1) Sets the current state of the ButtonLockState.</li> -<li><a name="SONOSPLAYER_setter_DailyIndexRefreshTime"> +<li><a name="SONOSPLAYERDailyIndexRefreshTime"> <b><code>DailyIndexRefreshTime <Timestring></code></b></a> <br />Sets the current DailyIndexRefreshTime for the whole bunch of Zoneplayers.</li> -<li><a name="SONOSPLAYER_setter_DialogLevel"> +<li><a name="SONOSPLAYERDialogLevel"> <b><code>DialogLevel <State></code></b></a> <br /> Sets the dialoglevel for playbar-systems.</li> -<li><a name="SONOSPLAYER_setter_ExportSonosBibliothek"> +<li><a name="SONOSPLAYERExportSonosBibliothek"> <b><code>ExportSonosBibliothek <filename></code></b></a> <br />Exports a file with a textual representation of a structure- and titlehash of the complete Sonos-Bibliothek. Warning: Will use a large amount of CPU-Time and RAM!</li> -<li><a name="SONOSPLAYER_setter_Name"> +<li><a name="SONOSPLAYERName"> <b><code>Name <Zonename></code></b></a> <br />Sets the Name for this Zone</li> -<li><a name="SONOSPLAYER_setter_NightMode"> +<li><a name="SONOSPLAYERNightMode"> <b><code>NightMode <State></code></b></a> <br /> Sets the nightmode for playbar-systems.</li> -<li><a name="SONOSPLAYER_setter_OutputFixed"> +<li><a name="SONOSPLAYEROutputFixed"> <b><code>OutputFixed <State></code></b></a> <br /> Sets the outputfixed-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_Reboot"> +<li><a name="SONOSPLAYERReboot"> <b><code>Reboot</code></b></a> <br />Initiates a reboot on the Zoneplayer.</li> -<li><a name="SONOSPLAYER_setter_ResetAttributesToDefault"> +<li><a name="SONOSPLAYERResetAttributesToDefault"> <b><code>ResetAttributesToDefault <DeleteAllOtherAttributes></code></b></a> <br />Sets the attributes to the inital state. If the parameter "DeleteAllOtherAttributes" is set to "1" or "on", all attributes will be deleted before the defaults will be newly retrieved from the player and set.</li> -<li><a name="SONOSPLAYER_setter_RoomIcon"> +<li><a name="SONOSPLAYERRoomIcon"> <b><code>RoomIcon <Iconname></code></b></a> <br />Sets the Icon for this Zone</li> -<li><a name="SONOSPLAYER_setter_SnoozeAlarm"> +<li><a name="SONOSPLAYERSnoozeAlarm"> <b><code>SnoozeAlarm <Timestring|Seconds></code></b></a> <br />Snoozes a currently playing alarm for the given time</li> -<li><a name="SONOSPLAYER_setter_SubEnable"> +<li><a name="SONOSPLAYERSubEnable"> <b><code>SubEnable <State></code></b></a> <br /> Sets the substate for sub-systems.</li> -<li><a name="SONOSPLAYER_setter_SubGain"> +<li><a name="SONOSPLAYERSubGain"> <b><code>SubGain <Level></code></b></a> <br /> Sets the sub-gain for sub-systems. The value can range from -15 to 15.</li> -<li><a name="SONOSPLAYER_setter_SubPolarity"> +<li><a name="SONOSPLAYERSubPolarity"> <b><code>SubPolarity <Level></code></b></a> <br /> Sets the sub-polarity for sub-systems. The value can range from 0 to 2.</li> -<li><a name="SONOSPLAYER_setter_SurroundEnable"> +<li><a name="SONOSPLAYERSurroundEnable"> <b><code>SurroundEnable <State></code></b></a> <br /> Sets the surround-state for surround-systems (like playbars).</li> -<li><a name="SONOSPLAYER_setter_SurroundLevel"> +<li><a name="SONOSPLAYERSurroundLevel"> <b><code>SurroundLevel <Level></code></b></a> <br /> Sets the surround-level for surround-systems (like playbars). The value can range from -15 to 15.</li> -<li><a name="SONOSPLAYER_setter_TruePlay"> +<li><a name="SONOSPLAYERTruePlay"> <b><code>TruePlay <State></code></b></a> <br />Sets the TruePlay-State of the given player.</li> -<li><a name="SONOSPLAYER_setter_Wifi"> +<li><a name="SONOSPLAYERWifi"> <b><code>Wifi <State></code></b></a> <br />Sets the WiFi-State of the given Player. Can be 'off', 'persist-off' or 'on'.</li> </ul></li> <li><b>Playing Control-Commands</b><ul> -<li><a name="SONOSPLAYER_setter_CurrentTrackPosition"> +<li><a name="SONOSPLAYERCurrentTrackPosition"> <b><code>CurrentTrackPosition <TimePosition></code></b></a> <br /> Sets the current timeposition inside the title to the given timevalue (e.g. 0:01:15) or seconds (e.g. 81). You can make relative jumps like '+0:00:10' or just '+10'. Additionally you can make a call with a percentage value like '+10%'. This relative value can be negative.</li> -<li><a name="SONOSPLAYER_setter_Pause"> +<li><a name="SONOSPLAYERPause"> <b><code>Pause</code></b></a> <br /> Pause the playing</li> -<li><a name="SONOSPLAYER_setter_Previous"> +<li><a name="SONOSPLAYERPrevious"> <b><code>Previous</code></b></a> <br /> Jumps to the beginning of the previous title.</li> -<li><a name="SONOSPLAYER_setter_Play"> +<li><a name="SONOSPLAYERPlay"> <b><code>Play</code></b></a> <br /> Starts playing</li> -<li><a name="SONOSPLAYER_setter_PlayURI"> +<li><a name="SONOSPLAYERPlayURI"> <b><code>PlayURI <songURI> [Volume]</code></b></a> <br />Plays the given MP3-File with the optional given volume.</li> -<li><a name="SONOSPLAYER_setter_PlayURITemp"> +<li><a name="SONOSPLAYERPlayURITemp"> <b><code>PlayURITemp <songURI> [Volume]</code></b></a> <br />Plays the given MP3-File with the optional given volume as a temporary file. After playing it, the whole state is reconstructed and continues playing at the former saved position and volume and so on. If the file given is a stream (exactly: a file where the running time could not be determined), the call would be identical to <code>,PlayURI</code>, e.g. nothing is restored after playing.</li> -<li><a name="SONOSPLAYER_setter_Next"> +<li><a name="SONOSPLAYERNext"> <b><code>Next</code></b></a> <br /> Jumps to the beginning of the next title</li> -<li><a name="SONOSPLAYER_setter_Speak"> +<li><a name="SONOSPLAYERSpeak"> <b><code>Speak <Volume> <Language> <Text></code></b></a> <br />Uses the Google Text-To-Speech-Engine for generating MP3-Files of the given text and plays it on the SonosPlayer. Possible languages can be obtained from Google. e.g. "de", "en", "fr", "es"...</li> -<li><a name="SONOSPLAYER_setter_StartFavourite"> +<li><a name="SONOSPLAYERStartFavourite"> <b><code>StartFavourite <Favouritename> [NoStart]</code></b></a> <br /> Starts the named sonos-favorite. The parameter should be URL-encoded for proper naming of lists with special characters. If the Word 'NoStart' is given as second parameter, than the Loading will be done, but the playing-state is leaving untouched e.g. not started.<br />Additionally it's possible to use a regular expression as the name. The first hit will be used. The format is e.g. <code>/meine.hits/</code>.</li> -<li><a name="SONOSPLAYER_setter_StartPlaylist"> +<li><a name="SONOSPLAYERStartPlaylist"> <b><code>StartPlaylist <Playlistname> [EmptyQueueBeforeImport]</code></b></a> <br /> Loads the given Playlist and starts playing immediately. For all Options have a look at "LoadPlaylist".</li> -<li><a name="SONOSPLAYER_setter_StartRadio"> +<li><a name="SONOSPLAYERStartRadio"> <b><code>StartRadio <Radiostationname></code></b></a> <br /> Loads the named radiostation (favorite) and starts playing immediately. For all Options have a look at "LoadRadio".</li> -<li><a name="SONOSPLAYER_setter_StartSearchlist"> +<li><a name="SONOSPLAYERStartSearchlist"> <b><code>StartSearchlist <Categoryname> <CategoryElement> [[TitlefilterRegEx]/[AlbumfilterRegEx]/[ArtistfilterRegEx] [maxElem]]</code></b></a> <br /> Loads the searchlist and starts playing immediately. For all Options have a look at "LoadSearchlist".</li> -<li><a name="SONOSPLAYER_setter_Stop"> +<li><a name="SONOSPLAYERStop"> <b><code>Stop</code></b></a> <br /> Stops the playing</li> -<li><a name="SONOSPLAYER_setter_Track"> +<li><a name="SONOSPLAYERTrack"> <b><code>Track <TrackNumber|Random></code></b></a> <br /> Sets the track with the given tracknumber as the current title. If the tracknumber is the word <code>Random</code> a random track will be selected.</li> </ul></li> <li><b>Playing Settings</b><ul> -<li><a name="SONOSPLAYER_setter_Balance"> +<li><a name="SONOSPLAYERBalance"> <b><code>Balance <BalanceValue></code></b></a> <br /> Sets the balance to the given value. The value can range from -100 (full left) to 100 (full right). Retrieves the new balancevalue as the result.</li> -<li><a name="SONOSPLAYER_setter_Bass"> +<li><a name="SONOSPLAYERBass"> <b><code>Bass <BassValue></code></b></a> <br /> Sets the bass to the given value. The value can range from -10 to 10. Retrieves the new bassvalue as the result.</li> -<li><a name="SONOSPLAYER_setter_CrossfadeMode"> +<li><a name="SONOSPLAYERCrossfadeMode"> <b><code>CrossfadeMode <State></code></b></a> <br /> Sets the crossfade-mode. Retrieves the new mode as the result.</li> -<li><a name="SONOSPLAYER_setter_LEDState"> +<li><a name="SONOSPLAYERLEDState"> <b><code>LEDState <State></code></b></a> <br /> Sets the LED state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_Loudness"> +<li><a name="SONOSPLAYERLoudness"> <b><code>Loudness <State></code></b></a> <br /> Sets the loudness-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_Mute"> +<li><a name="SONOSPLAYERMute"> <b><code>Mute <State></code></b></a> <br /> Sets the mute-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_MuteT"> +<li><a name="SONOSPLAYERMuteT"> <b><code>MuteT</code></b></a> <br /> Toggles the mute state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_Repeat"> +<li><a name="SONOSPLAYERRepeat"> <b><code>Repeat <State></code></b></a> <br /> Sets the repeat-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_RepeatOne"> +<li><a name="SONOSPLAYERRepeatOne"> <b><code>RepeatOne <State></code></b></a> <br /> Sets the repeatOne-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_RepeatOneT"> +<li><a name="SONOSPLAYERRepeatOneT"> <b><code>RepeatOneT</code></b></a> <br /> Toggles the repeatOne-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_RepeatT"> +<li><a name="SONOSPLAYERRepeatT"> <b><code>RepeatT</code></b></a> <br /> Toggles the repeat-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_Shuffle"> +<li><a name="SONOSPLAYERShuffle"> <b><code>Shuffle <State></code></b></a> <br /> Sets the shuffle-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_ShuffleT"> +<li><a name="SONOSPLAYERShuffleT"> <b><code>ShuffleT</code></b></a> <br /> Toggles the shuffle-state. Retrieves the new state as the result.</li> -<li><a name="SONOSPLAYER_setter_SleepTimer"> +<li><a name="SONOSPLAYERSleepTimer"> <b><code>SleepTimer <Timestring|Seconds></code></b></a> <br /> Sets the Sleeptimer to the given Time. It must be in the full format of "HH:MM:SS". Deactivate with "00:00:00" or "off".</li> -<li><a name="SONOSPLAYER_setter_Treble"> +<li><a name="SONOSPLAYERTreble"> <b><code>Treble <TrebleValue></code></b></a> <br /> Sets the treble to the given value. The value can range from -10 to 10. Retrieves the new treblevalue as the result.</li> -<li><a name="SONOSPLAYER_setter_Volume"> +<li><a name="SONOSPLAYERVolume"> <b><code>Volume <VolumeLevel> [RampType]</code></b></a> <br /> Sets the volume to the given value. The value could be a relative value with + or - sign. In this case the volume will be increased or decreased according to this value. Retrieves the new volume as the result.<br />Optional can be a RampType defined with a value between 1 and 3 which describes different templates defined by the Sonos-System.</li> -<li><a name="SONOSPLAYER_setter_VolumeD"> +<li><a name="SONOSPLAYERVolumeD"> <b><code>VolumeD</code></b></a> <br /> Turns the volume by volumeStep-ticks down.</li> -<li><a name="SONOSPLAYER_setter_VolumeRestore"> +<li><a name="SONOSPLAYERVolumeRestore"> <b><code>VolumeRestore</code></b></a> <br /> Restores the volume of a formerly saved volume.</li> -<li><a name="SONOSPLAYER_setter_VolumeSave"> +<li><a name="SONOSPLAYERVolumeSave"> <b><code>VolumeSave <VolumeLevel></code></b></a> <br /> Sets the volume to the given value. The value could be a relative value with + or - sign. In this case the volume will be increased or decreased according to this value. Retrieves the new volume as the result. Additionally it saves the old volume to a reading for restoreing.</li> -<li><a name="SONOSPLAYER_setter_VolumeU"> +<li><a name="SONOSPLAYERVolumeU"> <b><code>VolumeU</code></b></a> <br /> Turns the volume by volumeStep-ticks up.</li> </ul></li> <li><b>Control the current Playlist</b><ul> -<li><a name="SONOSPLAYER_setter_AddURIToQueue"> +<li><a name="SONOSPLAYERAddURIToQueue"> <b><code>AddURIToQueue <songURI></code></b></a> <br />Adds the given MP3-File at the current position into the queue.</li> -<li><a name="SONOSPLAYER_setter_CurrentPlaylist"> +<li><a name="SONOSPLAYERCurrentPlaylist"> <b><code>CurrentPlaylist</code></b></a> <br /> Sets the current playing to the current queue, but doesn't start playing (e.g. after hearing of a radiostream, where the current playlist still exists but is currently "not in use")</li> -<li><a name="SONOSPLAYER_setter_DeleteFromQueue"> +<li><a name="SONOSPLAYERDeleteFromQueue"> <b><code>DeleteFromQueue <index_of_elems></code></b></a> <br /> Deletes the elements from the current queue with the given indices. You can use the ususal perl-array-formats like "1..12,17,20..22". The indices reference to the position in the current view of the list (this usually differs between the normal playmode and the shuffleplaymode).</li> -<li><a name="SONOSPLAYER_setter_DeletePlaylist"> +<li><a name="SONOSPLAYERDeletePlaylist"> <b><code>DeletePlaylist</code></b></a> <br /> Deletes the Sonos-Playlist with the given name. According to the possibilities of the playlistname have a close look at LoadPlaylist.</li> -<li><a name="SONOSPLAYER_setter_EmptyPlaylist"> +<li><a name="SONOSPLAYEREmptyPlaylist"> <b><code>EmptyPlaylist</code></b></a> <br /> Clears the current queue</li> -<li><a name="SONOSPLAYER_setter_LoadFavourite"> +<li><a name="SONOSPLAYERLoadFavourite"> <b><code>LoadFavourite <Favouritename></code></b></a> <br /> Loads the named sonos-favorite. The parameter should be URL-encoded for proper naming of lists with special characters.<br />Additionally it's possible to use a regular expression as the name. The first hit will be used. The format is e.g. <code>/meine.hits/</code>.</li> -<li><a name="SONOSPLAYER_setter_LoadPlaylist"> +<li><a name="SONOSPLAYERLoadPlaylist"> <b><code>LoadPlaylist <Playlistname|Fhem-Devicename> [EmptyQueueBeforeImport]</code></b></a> <br /> Loads the named playlist to the current playing queue. The parameter should be URL-encoded for proper naming of lists with special characters. The Playlistnamen can be an Fhem-Devicename, then the current playlist of this referenced player will be copied. The Playlistname can also be a filename and then must be startet with 'file:' (e.g. 'file:c:/Test.m3u')<br />If EmptyQueueBeforeImport is given and set to 1, the queue will be emptied before the import process. If not given, the parameter will be interpreted as 1.<br />Additionally it's possible to use a regular expression as the name. The first hit will be used. The format is e.g. <code>/hits.2014/</code>.</li> -<li><a name="SONOSPLAYER_setter_LoadRadio"> +<li><a name="SONOSPLAYERLoadRadio"> <b><code>LoadRadio <Radiostationname></code></b></a> <br /> Loads the named radiostation (favorite). The current queue will not be touched but deactivated. The parameter should be URL-encoded for proper naming of lists with special characters.<br />Additionally it's possible to use a regular expression as the name. The first hit will be used. The format is e.g. <code>/radio/</code>.</li> -<li><a name="SONOSPLAYER_setter_LoadSearchlist"> +<li><a name="SONOSPLAYERLoadSearchlist"> <b><code>LoadSearchlist <Categoryname> <CategoryElement> [[TitlefilterRegEx]/[AlbumfilterRegEx]/[ArtistfilterRegEx] [[*]maxElem[+|-]]]</code></b></a> <br /> Loads titles from the Sonos-Bibliothek into the current playlist according to the given category and filtervalues. Please consult the (german) Wiki for detailed informations.</li> -<li><a name="SONOSPLAYER_setter_SavePlaylist"> +<li><a name="SONOSPLAYERSavePlaylist"> <b><code>SavePlaylist <Playlistname></code></b></a> <br /> Saves the current queue as a playlist with the given name. An existing playlist with the same name will be overwritten. The parameter should be URL-encoded for proper naming of lists with special characters. The Playlistname can be a filename and then must be startet with 'file:' (e.g. 'file:c:/Test.m3u')</li> </ul></li> <li><b>Groupcontrol</b><ul> -<li><a name="SONOSPLAYER_setter_AddMember"> +<li><a name="SONOSPLAYERAddMember"> <b><code>AddMember <devicename></code></b></a> <br />Adds the given devicename to the current device as a groupmember. The current playing of the current device goes on and will be transfered to the given device (the new member).</li> -<li><a name="SONOSPLAYER_setter_CreateStereoPair"> +<li><a name="SONOSPLAYERCreateStereoPair"> <b><code>CreateStereoPair <rightPlayerDevicename></code></b></a> <br />Adds the given devicename to the current device as the right speaker of a stereopair. The current playing of the current device goes on (as left-side speaker) and will be transfered to the given device (as right-side speaker).</li> -<li><a name="SONOSPLAYER_setter_GroupMute"> +<li><a name="SONOSPLAYERGroupMute"> <b><code>GroupMute <State></code></b></a> <br />Sets the mute state of the complete group in one step. The value can be on or off.</li> -<li><a name="SONOSPLAYER_setter_GroupVolume"> +<li><a name="SONOSPLAYERGroupVolume"> <b><code>GroupVolume <VolumeLevel></code></b></a> <br />Sets the group-volume in the way the original controller does. This means, that the relative volumelevel between the different players will be saved during change.</li> -<li><a name="SONOSPLAYER_setter_GroupVolumeD"> +<li><a name="SONOSPLAYERGroupVolumeD"> <b><code>GroupVolumeD</code></b></a> <br /> Turns the group volume by volumeStep-ticks down.</li> -<li><a name="SONOSPLAYER_setter_GroupVolumeU"> +<li><a name="SONOSPLAYERGroupVolumeU"> <b><code>GroupVolumeU</code></b></a> <br /> Turns the group volume by volumeStep-ticks up.</li> -<li><a name="SONOSPLAYER_setter_MakeStandaloneGroup"> +<li><a name="SONOSPLAYERMakeStandaloneGroup"> <b><code>MakeStandaloneGroup</code></b></a> <br />Makes this Player a standalone group.</li> -<li><a name="SONOSPLAYER_setter_RemoveMember"> +<li><a name="SONOSPLAYERRemoveMember"> <b><code>RemoveMember <devicename></code></b></a> <br />Removes the given device, so that they both are not longer a group. The current playing of the current device goes on normally. The cutted device stops his playing and has no current playlist anymore (since Sonos Version 4.2 the old playlist will be restored).</li> -<li><a name="SONOSPLAYER_setter_SeparateStereoPair"> +<li><a name="SONOSPLAYERSeparateStereoPair"> <b><code>SeparateStereoPair</code></b></a> <br />Divides the stereo-pair into two independant devices.</li> -<li><a name="SONOSPLAYER_setter_SnapshotGroupVolume"> +<li><a name="SONOSPLAYERSnapshotGroupVolume"> <b><code>SnapshotGroupVolume</code></b></a> <br /> Save the current volume-relation of all players of the same group. It's neccessary for the use of "GroupVolume" and is stored until the next call of "SnapshotGroupVolume".</li> </ul></li> @@ -1553,53 +1554,53 @@ sub SONOSPLAYER_Log($$$) { <h4>Get</h4> <ul> <li><b>Common</b><ul> -<li><a name="SONOSPLAYER_getter_Alarm"> +<li><a name="SONOSPLAYERAlarm"> <b><code>Alarm <ID></code></b></a> <br /> It's an exception to the normal getter semantics. Returns directly a Perl-Hash with the Alarm-Informations to the given id. It's just a shorthand for <code>eval(ReadingsVal(<Devicename>, 'Alarmlist', ()))->{<ID>};</code>.</li> -<li><a name="SONOSPLAYER_getter_EthernetPortStatus"> +<li><a name="SONOSPLAYEREthernetPortStatus"> <b><code>EthernetPortStatus <PortNumber></code></b></a> <br /> Gets the Ethernet-Portstatus of the given Port. Can be 'Active' or 'Inactive'.</li> -<li><a name="SONOSPLAYER_getter_PossibleRoomIcons"> +<li><a name="SONOSPLAYERPossibleRoomIcons"> <b><code>PossibleRoomIcons</code></b></a> <br /> Retreives a list of all possible Roomiconnames for the use with "set RoomIcon".</li> -<li><a name="SONOSPLAYER_getter_SupportLinks"> +<li><a name="SONOSPLAYERSupportLinks"> <b><code>SupportLinks</code></b></a> <br /> Shows a list with direct links to the player-support-sites.</li> -<li><a name="SONOSPLAYER_getter_WifiPortStatus"> +<li><a name="SONOSPLAYERWifiPortStatus"> <b><code>WifiPortStatus</code></b></a> <br /> Gets the Wifi-Portstatus. Can be 'Active' or 'Inactive'.</li> </ul></li> <li><b>Lists</b><ul> -<li><a name="SONOSPLAYER_getter_Favourites"> +<li><a name="SONOSPLAYERFavourites"> <b><code>Favourites</code></b></a> <br /> Retrieves a list with the names of all sonos favourites. This getter retrieves the same list on all Zoneplayer. The format is a comma-separated list with quoted names of favourites. e.g. "Liste 1","Entry 2","Test"</li> -<li><a name="SONOSPLAYER_getter_FavouritesWithCovers"> +<li><a name="SONOSPLAYERFavouritesWithCovers"> <b><code>FavouritesWithCovers</code></b></a> <br /> Retrieves a list with the stringrepresentation of a perl-hash which can easily be converted with "eval". It consists of the names and coverlinks of all of the favourites stored in Sonos e.g. {'FV:2/22' => {'Cover' => 'urlzumcover', 'Title' => '1. Favorit'}}</li> -<li><a name="SONOSPLAYER_getter_Playlists"> +<li><a name="SONOSPLAYERPlaylists"> <b><code>Playlists</code></b></a> <br /> Retrieves a list with the names of all saved queues (aka playlists). This getter retrieves the same list on all Zoneplayer. The format is a comma-separated list with quoted names of playlists. e.g. "Liste 1","Liste 2","Test"</li> -<li><a name="SONOSPLAYER_getter_PlaylistsWithCovers"> +<li><a name="SONOSPLAYERPlaylistsWithCovers"> <b><code>PlaylistsWithCovers</code></b></a> <br /> Retrieves a list with the stringrepresentation of a perl-hash which can easily be converted with "eval". It consists of the names and coverlinks of all of the playlists stored in Sonos e.g. {'SQ:14' => {'Cover' => 'urlzumcover', 'Title' => '1. Playlist'}}</li> -<li><a name="SONOSPLAYER_getter_Queue"> +<li><a name="SONOSPLAYERQueue"> <b><code>Queue</code></b></a> <br /> Retrieves a list with the names of all titles in the current queue. This getter retrieves the same list on all Zoneplayer. The format is a comma-separated list with quoted names of the titles. e.g. "1. Liste 1 [0:02:14]","2. Eintrag 2 [k.A.]","3. Test [0:14:00]"</li> -<li><a name="SONOSPLAYER_getter_QueueWithCovers"> +<li><a name="SONOSPLAYERQueueWithCovers"> <b><code>QueueWithCovers</code></b></a> <br /> Retrieves a list with the stringrepresentation of a perl-hash which can easily be converted with "eval". It consists of the names and coverlinks of all of the titles in the current queue. e.g.: {'Q:0/22' => {'Cover' => 'urlzumcover', 'Title' => '1. Titel'}}.</li> -<li><a name="SONOSPLAYER_getter_Radios"> +<li><a name="SONOSPLAYERRadios"> <b><code>Radios</code></b></a> <br /> Retrieves a list with the names of all saved radiostations (favorites). This getter retrieves the same list on all Zoneplayer. The format is a comma-separated list with quoted names of radiostations. e.g. "Sender 1","Sender 2","Test"</li> -<li><a name="SONOSPLAYER_getter_RadiosWithCovers"> +<li><a name="SONOSPLAYERRadiosWithCovers"> <b><code>RadiosWithCovers</code></b></a> <br /> Retrieves a list with the stringrepresentation of a perl-hash which can easily be converted with "eval". It consists of the names and coverlinks of all of the radiofavourites stored in Sonos e.g. {'R:0/0/2' => {'Cover' => 'urlzumcover', 'Title' => '1. Radiosender'}}</li> -<li><a name="SONOSPLAYER_getter_SearchlistCategories"> +<li><a name="SONOSPLAYERSearchlistCategories"> <b><code>SearchlistCategories</code></b></a> <br /> Retrieves a list with the possible categories for the setter "LoadSearchlist". The Format is a comma-separated list with quoted names of categories.</li> </ul></li> <li><b>Informations on the current Title</b><ul> -<li><a name="SONOSPLAYER_getter_CurrentTrackPosition"> +<li><a name="SONOSPLAYERCurrentTrackPosition"> <b><code>CurrentTrackPosition</code></b></a> <br /> Retrieves the current timeposition inside a title</li> </ul></li> @@ -1609,58 +1610,58 @@ sub SONOSPLAYER_Log($$$) { '''Attention'''<br />The attributes can only be used after a restart of fhem, because it must be initially transfered to the subprocess. <ul> <li><b>Common</b><ul> -<li><a name="SONOSPLAYER_attribut_disable"><b><code>disable <int></code></b> +<li><a name="SONOSPLAYERdisable"><b><code>disable <int></code></b> </a><br /> One of (0,1). Disables the event-worker for this Sonosplayer.</li> -<li><a name="SONOSPLAYER_attribut_generateSomethingChangedEvent"><b><code>generateSomethingChangedEvent <int></code></b> +<li><a name="SONOSPLAYERgenerateSomethingChangedEvent"><b><code>generateSomethingChangedEvent <int></code></b> </a><br /> One of (0,1). 1 if a 'SomethingChanged'-Event should be generated. This event is thrown every time an event is generated. This is useful if you wants to be notified on every change with a single event.</li> -<li><a name="SONOSPLAYER_attribut_generateVolumeEvent"><b><code>generateVolumeEvent <int></code></b> +<li><a name="SONOSPLAYERgenerateVolumeEvent"><b><code>generateVolumeEvent <int></code></b> </a><br /> One of (0,1). Enables an event generated at volumechanges if minVolume or maxVolume is set.</li> -<li><a name="SONOSPLAYER_attribut_generateVolumeSlider"><b><code>generateVolumeSlider <int></code></b> +<li><a name="SONOSPLAYERgenerateVolumeSlider"><b><code>generateVolumeSlider <int></code></b> </a><br /> One of (0,1). Enables a slider for volumecontrol in detail view.</li> -<li><a name="SONOSPLAYER_attribut_getAlarms"><b><code>getAlarms <int></code></b> +<li><a name="SONOSPLAYERgetAlarms"><b><code>getAlarms <int></code></b> </a><br /> One of (0..1). Initializes a callback-method for Alarms. This included the information of the DailyIndexRefreshTime.</li> -<li><a name="SONOSPLAYER_attribut_suppressControlButtons"><b><code>suppressControlButtons <int></code></b> +<li><a name="SONOSPLAYERsuppressControlButtons"><b><code>suppressControlButtons <int></code></b> </a><br /> One of (0,1). Enables the control-section shown under the Cover-/Titleview.</li> -<li><a name="SONOSPLAYER_attribut_volumeStep"><b><code>volumeStep <int></code></b> +<li><a name="SONOSPLAYERvolumeStep"><b><code>volumeStep <int></code></b> </a><br /> One of (0..100). Defines the stepwidth for subsequent calls of <code>VolumeU</code> and <code>VolumeD</code>.</li> </ul></li> <li><b>Information Generation</b><ul> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize1"><b><code>generateInfoSummarize1 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize1"><b><code>generateInfoSummarize1 <string></code></b> </a><br /> Generates the reading 'InfoSummarize1' with the given format. More Information on this in the examples-section.</li> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize2"><b><code>generateInfoSummarize2 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize2"><b><code>generateInfoSummarize2 <string></code></b> </a><br /> Generates the reading 'InfoSummarize2' with the given format. More Information on this in the examples-section.</li> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize3"><b><code>generateInfoSummarize3 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize3"><b><code>generateInfoSummarize3 <string></code></b> </a><br /> Generates the reading 'InfoSummarize3' with the given format. More Information on this in the examples-section.</li> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize4"><b><code>generateInfoSummarize4 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize4"><b><code>generateInfoSummarize4 <string></code></b> </a><br /> Generates the reading 'InfoSummarize4' with the given format. More Information on this in the examples-section.</li> -<li><a name="SONOSPLAYER_attribut_getTitleInfoFromMaster"><b><code>getTitleInfoFromMaster <int></code></b> +<li><a name="SONOSPLAYERgetTitleInfoFromMaster"><b><code>getTitleInfoFromMaster <int></code></b> </a><br /> One of (0, 1). Gets the current Playing-Informations from the Masterplayer (if one is present).</li> -<li><a name="SONOSPLAYER_attribut_simulateCurrentTrackPosition"><b><code>simulateCurrentTrackPosition <int></code></b> +<li><a name="SONOSPLAYERsimulateCurrentTrackPosition"><b><code>simulateCurrentTrackPosition <int></code></b> </a><br /> One of (0,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60). Starts an internal Timer which refreshs the current trackposition into the Readings <code>currentTrackPositionSimulated</code> and <code>currentTrackPositionSimulatedSec</code>. At the same time the Reading <code>currentTrackPositionSimulatedPercent</code> (between 0.0 and 100.0) will also be refreshed.</li> -<li><a name="SONOSPLAYER_attribut_simulateCurrentTrackPositionPercentFormat"><b><code>simulateCurrentTrackPositionPercentFormat <Format></code></b> +<li><a name="SONOSPLAYERsimulateCurrentTrackPositionPercentFormat"><b><code>simulateCurrentTrackPositionPercentFormat <Format></code></b> </a><br /> Defines the format of the percentformat in the Reading <code>currentTrackPositionSimulatedPercent</code>.</li> -<li><a name="SONOSPLAYER_attribut_stateVariable"><b><code>stateVariable <string></code></b> +<li><a name="SONOSPLAYERstateVariable"><b><code>stateVariable <string></code></b> </a><br /> One of (TransportState,NumberOfTracks,Track,TrackURI,TrackDuration,Title,Artist,Album,OriginalTrackNumber,AlbumArtist,<br />Sender,SenderCurrent,SenderInfo,StreamAudio,NormalAudio,AlbumArtURI,nextTrackDuration,nextTrackURI,nextAlbumArtURI,<br />nextTitle,nextArtist,nextAlbum,nextAlbumArtist,nextOriginalTrackNumber,Volume,Mute,Shuffle,Repeat,RepeatOne,CrossfadeMode,Balance,<br />HeadphoneConnected,SleepTimer,Presence,RoomName,SaveRoomName,PlayerType,Location,SoftwareRevision,SerialNum,InfoSummarize1,<br />InfoSummarize2,InfoSummarize3,InfoSummarize4). Defines, which variable has to be copied to the content of the state-variable.</li> </ul></li> <li><b>Controloptions</b><ul> -<li><a name="SONOSPLAYER_attribut_maxVolume"><b><code>maxVolume <int></code></b> +<li><a name="SONOSPLAYERmaxVolume"><b><code>maxVolume <int></code></b> </a><br /> One of (0..100). Define a maximal volume for this Zoneplayer</li> -<li><a name="SONOSPLAYER_attribut_minVolume"><b><code>minVolume <int></code></b> +<li><a name="SONOSPLAYERminVolume"><b><code>minVolume <int></code></b> </a><br /> One of (0..100). Define a minimal volume for this Zoneplayer</li> -<li><a name="SONOSPLAYER_attribut_maxVolumeHeadphone"><b><code>maxVolumeHeadphone <int></code></b> +<li><a name="SONOSPLAYERmaxVolumeHeadphone"><b><code>maxVolumeHeadphone <int></code></b> </a><br /> One of (0..100). Define a maximal volume for this Zoneplayer for use with headphones</li> -<li><a name="SONOSPLAYER_attribut_minVolumeHeadphone"><b><code>minVolumeHeadphone <int></code></b> +<li><a name="SONOSPLAYERminVolumeHeadphone"><b><code>minVolumeHeadphone <int></code></b> </a><br /> One of (0..100). Define a minimal volume for this Zoneplayer for use with headphones</li> -<li><a name="SONOSPLAYER_attribut_buttonEvents"><b><code>buttonEvents <Time:Pattern>[ <Time:Pattern> ...]</code></b> +<li><a name="SONOSPLAYERbuttonEvents"><b><code>buttonEvents <Time:Pattern>[ <Time:Pattern> ...]</code></b> </a><br /> Defines that after pressing a specified sequence of buttons at the player an event has to be thrown. The definition itself is a tupel: the first part (before the colon) is the time in seconds, the second part (after the colon) is the button sequence of this event.<br /> The following button-shortcuts are possible: <ul><li><b>M</b>: The Mute-Button</li><li><b>H</b>: The Headphone-Connector</li><li><b>U</b>: Up-Button (Volume Up)</li><li><b>D</b>: Down-Button (Volume Down)</li></ul><br /> The event thrown is named <code>ButtonEvent</code>, the value is the defined button-sequence.<br /> E.G.: <code>2:MM</code><br /> Here an event is defined, where in time of 2 seconds the Mute-Button has to be pressed 2 times. The created event is named <code>ButtonEvent</code> and has the value <code>MM</code>.</li> </ul></li> -<li><a name="SONOSPLAYER_attribut_saveSleeptimerInAction"><b><code>saveSleeptimerInAction <int></code></b> +<li><a name="SONOSPLAYERsaveSleeptimerInAction"><b><code>saveSleeptimerInAction <int></code></b> </a><br /> One of (0..1). If set, a possibly set Attribute "stopSleeptimerInAction" will be ignored.</li> -<li><a name="SONOSPLAYER_attribut_stopSleeptimerInAction"><b><code>stopSleeptimerInAction <int></code></b> +<li><a name="SONOSPLAYERstopSleeptimerInAction"><b><code>stopSleeptimerInAction <int></code></b> </a><br /> One of (0..1). If set, a change of the current transportState to "PAUSED_PLAYBACK" or "STOPPED" will cause a stopping of an eventually running SleepTimer.</li> </ul> <a name="SONOSPLAYERexamples"></a> @@ -1695,240 +1696,240 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p <h4>Set</h4> <ul> <li><b>Grundsätzliche Einstellungen</b><ul> -<li><a name="SONOSPLAYER_setter_Alarm"> +<li><a name="SONOSPLAYERAlarm"> <b><code>Alarm (Create|Update|Delete|Enable|Disable) <ID[,ID]|All> <Datahash></code></b></a> <br />Diese Anweisung wird für die Bearbeitung der Alarme verwendet:<ul><li><b>Create:</b> Erzeugt einen neuen Alarm-Eintrag mit den übergebenen Hash-Daten.</li><li><b>Update:</b> Aktualisiert die Alarme mit den übergebenen IDs und den angegebenen Hash-Daten.</li><li><b>Delete:</b> Löscht die Alarm-Einträge mit den übergebenen IDs.</li><li><b>Enable:</b> Aktiviert die Alarm-Einträge mit den übergebenen IDs.</li><li><b>Disable:</b> Deaktiviert die Alarm-Einträge mit den übergebenen IDs.</li></ul>Bei Angabe des Wortes 'All' als ID, werden alle Alarme dieses Players bearbeitet.<br /><b>Die Hash-Daten:</b><br />Das Format ist ein Perl-Hash und wird mittels der eval-Funktion interpretiert.<br />e.g.: { Repeat => 1 }<br /><br />Die folgenden Schlüssel sind zulässig/notwendig:<ul><li>StartTime</li><li>Duration</li><li>Recurrence_Once</li><li>Recurrence_Monday</li><li>Recurrence_Tuesday</li><li>Recurrence_Wednesday</li><li>Recurrence_Thursday</li><li>Recurrence_Friday</li><li>Recurrence_Saturday</li><li>Recurrence_Sunday</li><li>Enabled</li><li>ProgramURI</li><li>ProgramMetaData</li><li>Shuffle</li><li>Repeat</li><li>Volume</li><li>IncludeLinkedZones</li></ul><br />z.B.:<ul><li>set Sonos_Wohnzimmer Alarm Create 0 { Enabled => 1, Volume => 35, StartTime => '00:00:00', Duration => '00:15:00', Repeat => 0, Shuffle => 0, ProgramURI => 'x-rincon-buzzer:0', ProgramMetaData => '', Recurrence_Once => 0, Recurrence_Monday => 1, Recurrence_Tuesday => 1, Recurrence_Wednesday => 1, Recurrence_Thursday => 1, Recurrence_Friday => 1, Recurrence_Saturday => 0, Recurrence_Sunday => 0, IncludeLinkedZones => 0 }</li><li>set Sonos_Wohnzimmer Alarm Update 17 { Shuffle => 1 }</li><li>set Sonos_Wohnzimmer Alarm Delete 17 {}</li></ul></li> -<li><a name="SONOSPLAYER_setter_AudioDelay"> +<li><a name="SONOSPLAYERAudioDelay"> <b><code>AudioDelay <Level></code></b></a> <br /> Setzt den AudioDelay der Playbar auf den angegebenen Wert. Der Wert kann zwischen 0 und 5 liegen.</li> -<li><a name="SONOSPLAYER_setter_AudioDelayLeftRear"> +<li><a name="SONOSPLAYERAudioDelayLeftRear"> <b><code>AudioDelayLeftRear <Level></code></b></a> <br /> Setzt den AudioDelayLeftRear des Players auf den angegebenen Wert. Der Wert kann zwischen 0 und 2 liegen. Wobei die Werte folgende Bedeutung haben: 0: >3m, 1: >0.6m und <3m, 2: <0.6m</li> -<li><a name="SONOSPLAYER_setter_AudioDelayRightRear"> +<li><a name="SONOSPLAYERAudioDelayRightRear"> <b><code>AudioDelayRightRear <Level></code></b></a> <br /> Setzt den AudioDelayRightRear des Players auf den angegebenen Wert. Der Wert kann zwischen 0 und 2 liegen. Wobei die Werte folgende Bedeutung haben: 0: >3m, 1: >0.6m und <3m, 2: <0.6m</li> -<li><a name="SONOSPLAYER_setter_ButtonLockState"> +<li><a name="SONOSPLAYERButtonLockState"> <b><code>ButtonLockState <int></code></b></a> <br />One of (0, 1). Setzt den aktuellen Button-Sperr-Zustand.</li> -<li><a name="SONOSPLAYER_setter_DailyIndexRefreshTime"> +<li><a name="SONOSPLAYERDailyIndexRefreshTime"> <b><code>DailyIndexRefreshTime <Timestring></code></b></a> <br />Setzt die aktuell gültige DailyIndexRefreshTime für alle Zoneplayer.</li> -<li><a name="SONOSPLAYER_setter_DialogLevel"> +<li><a name="SONOSPLAYERDialogLevel"> <b><code>DialogLevel <State></code></b></a> <br /> Legt den Zustand der Sprachverbesserung der Playbar fest.</li> -<li><a name="SONOSPLAYER_setter_ExportSonosBibliothek"> +<li><a name="SONOSPLAYERExportSonosBibliothek"> <b><code>ExportSonosBibliothek <filename></code></b></a> <br />Exportiert eine Datei mit der textuellen Darstellung eines Struktur- und Titelhashs, das die komplette Navigationsstruktur aus der Sonos-Bibliothek abbildet. Achtung: Benötigt eine große Menge CPU-Zeit und Arbeitsspeicher für die Ausführung!</li> -<li><a name="SONOSPLAYER_setter_Name"> +<li><a name="SONOSPLAYERName"> <b><code>Name <Zonename></code></b></a> <br />Legt den Namen der Zone fest.</li> -<li><a name="SONOSPLAYER_setter_NightMode"> +<li><a name="SONOSPLAYERNightMode"> <b><code>NightMode <State></code></b></a> <br /> Legt den Zustand des Nachtsounds der Playbar fest.</li> -<li><a name="SONOSPLAYER_setter_OutputFixed"> +<li><a name="SONOSPLAYEROutputFixed"> <b><code>OutputFixed <State></code></b></a> <br /> Setzt den angegebenen OutputFixed-Zustand. Liefert den aktuell gültigen OutputFixed-Zustand.</li> -<li><a name="SONOSPLAYER_setter_Reboot"> +<li><a name="SONOSPLAYERReboot"> <b><code>Reboot</code></b></a> <br />Führt für den Zoneplayer einen Neustart durch.</li> -<li><a name="SONOSPLAYER_setter_ResetAttributesToDefault"> +<li><a name="SONOSPLAYERResetAttributesToDefault"> <b><code>ResetAttributesToDefault <DeleteAllOtherAttributes></code></b></a> <br />Setzt die Attribute eines Players auf die Voreinstellung zurück, wie sie beim Anlegen des Players gesetzt waren. Wenn der Parameter "DeleteAllOtherAttributes" mit "1" oder "on" angegeben wurde, werden vor dem Setzen alle Attribute gelöscht.</li> -<li><a name="SONOSPLAYER_setter_RoomIcon"> +<li><a name="SONOSPLAYERRoomIcon"> <b><code>RoomIcon <Iconname></code></b></a> <br />Legt das Icon für die Zone fest</li> -<li><a name="SONOSPLAYER_setter_SnoozeAlarm"> +<li><a name="SONOSPLAYERSnoozeAlarm"> <b><code>SnoozeAlarm <Timestring|Seconds></code></b></a> <br />Unterbricht eine laufende Alarmwiedergabe für den übergebenen Zeitraum.</li> -<li><a name="SONOSPLAYER_setter_SubEnable"> +<li><a name="SONOSPLAYERSubEnable"> <b><code>SubEnable <State></code></b></a> <br /> Legt den Zustand des Sub-Zustands fest.</li> -<li><a name="SONOSPLAYER_setter_SubGain"> +<li><a name="SONOSPLAYERSubGain"> <b><code>SubGain <Level></code></b></a> <br /> Setzt den SubGain auf den angegebenen Wert. Der Wert kann zwischen -15 und 15 liegen.</li> -<li><a name="SONOSPLAYER_setter_SubPolarity"> +<li><a name="SONOSPLAYERSubPolarity"> <b><code>SubPolarity <Level></code></b></a> <br /> Setzt den SubPolarity auf den angegebenen Wert. Der Wert kann zwischen 0 und 2 liegen.</li> -<li><a name="SONOSPLAYER_setter_SurroundEnable"> +<li><a name="SONOSPLAYERSurroundEnable"> <b><code>SurroundEnable <State></code></b></a> <br />Setzt den SurroundEnable-Zustand.</li> -<li><a name="SONOSPLAYER_setter_SurroundLevel"> +<li><a name="SONOSPLAYERSurroundLevel"> <b><code>SurroundLevel <Level></code></b></a> <br /> Setzt den Surroundlevel auf den angegebenen Wert. Der Wert kann zwischen -15 und 15 liegen.</li> -<li><a name="SONOSPLAYER_setter_TruePlay"> +<li><a name="SONOSPLAYERTruePlay"> <b><code>TruePlay <State></code></b></a> <br />Setzt den TruePlay-Zustand.</li> -<li><a name="SONOSPLAYER_setter_Wifi"> +<li><a name="SONOSPLAYERWifi"> <b><code>Wifi <State></code></b></a> <br />Setzt den WiFi-Zustand des Players. Kann 'off', 'persist-off' oder 'on' sein.</li> </ul></li> <li><b>Abspiel-Steuerbefehle</b><ul> -<li><a name="SONOSPLAYER_setter_CurrentTrackPosition"> +<li><a name="SONOSPLAYERCurrentTrackPosition"> <b><code>CurrentTrackPosition <TimePosition></code></b></a> <br /> Setzt die Abspielposition innerhalb des Liedes auf den angegebenen Zeitwert (z.B. 0:01:15) oder eine Sekundenangabe (z.B. 81). Man kann hier auch relative Angaben machen wie '+0:00:10' oder nur '+10'. Zusätzlich kann man auch Prozentwerte angeben wie z.B. '+10%'. Natürlich können diese Angaben auch negativ sein.</li> -<li><a name="SONOSPLAYER_setter_Pause"> +<li><a name="SONOSPLAYERPause"> <b><code>Pause</code></b></a> <br /> Pausiert die Wiedergabe</li> -<li><a name="SONOSPLAYER_setter_Previous"> +<li><a name="SONOSPLAYERPrevious"> <b><code>Previous</code></b></a> <br /> Springt an den Anfang des vorherigen Titels.</li> -<li><a name="SONOSPLAYER_setter_Play"> +<li><a name="SONOSPLAYERPlay"> <b><code>Play</code></b></a> <br /> Startet die Wiedergabe</li> -<li><a name="SONOSPLAYER_setter_PlayURI"> +<li><a name="SONOSPLAYERPlayURI"> <b><code>PlayURI <songURI> [Volume]</code></b></a> <br /> Spielt die angegebene MP3-Datei ab. Dabei kann eine Lautstärke optional mit angegeben werden.</li> -<li><a name="SONOSPLAYER_setter_PlayURITemp"> +<li><a name="SONOSPLAYERPlayURITemp"> <b><code>PlayURITemp <songURI> [Volume]</code></b></a> <br /> Spielt die angegebene MP3-Datei mit der optionalen Lautstärke als temporäre Wiedergabe ab. Nach dem Abspielen wird der vorhergehende Zustand wiederhergestellt, und läuft an der unterbrochenen Stelle weiter. Wenn die Länge der Datei nicht ermittelt werden kann (z.B. bei Streams), läuft die Wiedergabe genauso wie bei <code>PlayURI</code> ab, es wird also nichts am Ende (wenn es eines geben sollte) wiederhergestellt.</li> -<li><a name="SONOSPLAYER_setter_Next"> +<li><a name="SONOSPLAYERNext"> <b><code>Next</code></b></a> <br /> Springt an den Anfang des nächsten Titels</li> -<li><a name="SONOSPLAYER_setter_Speak"> +<li><a name="SONOSPLAYERSpeak"> <b><code>Speak <Volume> <Language> <Text></code></b></a> <br /> Verwendet die Google Text-To-Speech-Engine um den angegebenen Text in eine MP3-Datei umzuwandeln und anschließend mittels <code>PlayURITemp</code> als Durchsage abzuspielen. Mögliche Sprachen können auf der Google-Seite nachgesehen werden. Möglich sind z.B. "de", "en", "fr", "es"...</li> -<li><a name="SONOSPLAYER_setter_StartFavourite"> +<li><a name="SONOSPLAYERStartFavourite"> <b><code>StartFavourite <FavouriteName> [NoStart]</code></b></a> <br /> Startet den angegebenen Favoriten. Der Name bezeichnet einen Eintrag in der Sonos-Favoritenliste. Der Parameter sollte/kann URL-Encoded werden um auch Spezialzeichen zu ermöglichen. Wenn das Wort 'NoStart' als zweiter Parameter angegeben wurde, dann wird der Favorit geladen und fertig vorbereitet, aber nicht explizit gestartet.<br />Zusätzlich kann ein regulärer Ausdruck für den Namen verwendet werden. Der erste Treffer wird verwendet. Das Format ist z.B. <code>/meine.hits/</code>.</li> -<li><a name="SONOSPLAYER_setter_StartPlaylist"> +<li><a name="SONOSPLAYERStartPlaylist"> <b><code>StartPlaylist <Playlistname> [EmptyQueueBeforeImport]</code></b></a> <br /> Lädt die benannte Playlist und startet sofort die Wiedergabe. Zu den Parametern und Bemerkungen bitte unter "LoadPlaylist" nachsehen.</li> -<li><a name="SONOSPLAYER_setter_StartRadio"> +<li><a name="SONOSPLAYERStartRadio"> <b><code>StartRadio <Radiostationname></code></b></a> <br /> Lädt den benannten Radiosender, genauer gesagt, den benannten Radiofavoriten und startet sofort die Wiedergabe. Dabei wird die bestehende Abspielliste beibehalten, aber deaktiviert. Der Parameter kann/muss URL-Encoded sein, um auch Leer- und Sonderzeichen angeben zu können.</li> -<li><a name="SONOSPLAYER_setter_StartSearchlist"> +<li><a name="SONOSPLAYERStartSearchlist"> <b><code>StartSearchlist <Kategoriename> <KategorieElement> [[TitelfilterRegEx]/[AlbumfilterRegEx]/[ArtistfilterRegEx] [maxElem]]</code></b></a> <br /> Lädt die Searchlist und startet sofort die Wiedergabe. Für nähere Informationen bitte unter "LoadSearchlist" nachschlagen.</li> -<li><a name="SONOSPLAYER_setter_Stop"> +<li><a name="SONOSPLAYERStop"> <b><code>Stop</code></b></a> <br /> Stoppt die Wiedergabe</li> -<li><a name="SONOSPLAYER_setter_Track"> +<li><a name="SONOSPLAYERTrack"> <b><code>Track <TrackNumber|Random></code></b></a> <br /> Aktiviert den angebenen Titel der aktuellen Abspielliste. Wenn als Tracknummer der Wert <code>Random</code> angegeben wird, dann wird eine zufällige Trackposition ausgewählt.</li> </ul></li> <li><b>Einstellungen zum Abspielen</b><ul> -<li><a name="SONOSPLAYER_setter_Balance"> +<li><a name="SONOSPLAYERBalance"> <b><code>Balance <BalanceValue></code></b></a> <br /> Setzt die Balance auf den angegebenen Wert. Der Wert kann zwischen -100 (voll links) bis 100 (voll rechts) sein. Gibt die wirklich eingestellte Balance als Ergebnis zurück.</li> -<li><a name="SONOSPLAYER_setter_Bass"> +<li><a name="SONOSPLAYERBass"> <b><code>Bass <BassValue></code></b></a> <br /> Setzt den Basslevel auf den angegebenen Wert. Der Wert kann zwischen -10 bis 10 sein. Gibt den wirklich eingestellten Basslevel als Ergebnis zurück.</li> -<li><a name="SONOSPLAYER_setter_CrossfadeMode"> +<li><a name="SONOSPLAYERCrossfadeMode"> <b><code>CrossfadeMode <State></code></b></a> <br /> Legt den Zustand des Crossfade-Mode fest. Liefert den aktuell gültigen Crossfade-Mode.</li> -<li><a name="SONOSPLAYER_setter_LEDState"> +<li><a name="SONOSPLAYERLEDState"> <b><code>LEDState <State></code></b></a> <br /> Legt den Zustand der LED fest. Liefert den aktuell gültigen Zustand.</li> -<li><a name="SONOSPLAYER_setter_Loudness"> +<li><a name="SONOSPLAYERLoudness"> <b><code>Loudness <State></code></b></a> <br /> Setzt den angegebenen Loudness-Zustand. Liefert den aktuell gültigen Loudness-Zustand.</li> -<li><a name="SONOSPLAYER_setter_Mute"> +<li><a name="SONOSPLAYERMute"> <b><code>Mute <State></code></b></a> <br /> Setzt den angegebenen Mute-Zustand. Liefert den aktuell gültigen Mute-Zustand.</li> -<li><a name="SONOSPLAYER_setter_MuteT"> +<li><a name="SONOSPLAYERMuteT"> <b><code>MuteT</code></b></a> <br /> Schaltet den Zustand des Mute-Zustands um. Liefert den aktuell gültigen Mute-Zustand.</li> -<li><a name="SONOSPLAYER_setter_Repeat"> +<li><a name="SONOSPLAYERRepeat"> <b><code>Repeat <State></code></b></a> <br /> Legt den Zustand des Repeat-Zustands fest. Liefert den aktuell gültigen Repeat-Zustand.</li> -<li><a name="SONOSPLAYER_setter_RepeatOne"> +<li><a name="SONOSPLAYERRepeatOne"> <b><code>RepeatOne <State></code></b></a> <br /> Legt den Zustand des RepeatOne-Zustands fest. Liefert den aktuell gültigen RepeatOne-Zustand.</li> -<li><a name="SONOSPLAYER_setter_RepeatOneT"> +<li><a name="SONOSPLAYERRepeatOneT"> <b><code>RepeatOneT</code></b></a> <br /> Schaltet den Zustand des RepeatOne-Zustands um. Liefert den aktuell gültigen RepeatOne-Zustand.</li> -<li><a name="SONOSPLAYER_setter_RepeatT"> +<li><a name="SONOSPLAYERRepeatT"> <b><code>RepeatT</code></b></a> <br /> Schaltet den Zustand des Repeat-Zustands um. Liefert den aktuell gültigen Repeat-Zustand.</li> -<li><a name="SONOSPLAYER_setter_Shuffle"> +<li><a name="SONOSPLAYERShuffle"> <b><code>Shuffle <State></code></b></a> <br /> Legt den Zustand des Shuffle-Zustands fest. Liefert den aktuell gültigen Shuffle-Zustand.</li> -<li><a name="SONOSPLAYER_setter_ShuffleT"> +<li><a name="SONOSPLAYERShuffleT"> <b><code>ShuffleT</code></b></a> <br /> Schaltet den Zustand des Shuffle-Zustands um. Liefert den aktuell gültigen Shuffle-Zustand.</li> -<li><a name="SONOSPLAYER_setter_SleepTimer"> +<li><a name="SONOSPLAYERSleepTimer"> <b><code>SleepTimer <Timestring|Seconds></code></b></a> <br /> Legt den aktuellen SleepTimer fest. Der Wert muss ein kompletter Zeitstempel sein (HH:MM:SS). Zum Deaktivieren darf der Zeitstempel nur Nullen enthalten oder das Wort 'off'.</li> -<li><a name="SONOSPLAYER_setter_Treble"> +<li><a name="SONOSPLAYERTreble"> <b><code>Treble <TrebleValue></code></b></a> <br /> Setzt den Treblelevel auf den angegebenen Wert. Der Wert kann zwischen -10 bis 10 sein. Gibt den wirklich eingestellten Treblelevel als Ergebnis zurück.</li> -<li><a name="SONOSPLAYER_setter_Volume"> +<li><a name="SONOSPLAYERVolume"> <b><code>Volume <VolumeLevel> [RampType]</code></b></a> <br /> Setzt die aktuelle Lautstärke auf den angegebenen Wert. Der Wert kann ein relativer Wert mittels + oder - Zeichen sein. Liefert den aktuell gültigen Lautstärkewert zurück.<br />Optional kann ein RampType übergeben werden, der einen Wert zwischen 1 und 3 annehmen kann, und verschiedene von Sonos festgelegte Muster beschreibt.</li> -<li><a name="SONOSPLAYER_setter_VolumeD"> +<li><a name="SONOSPLAYERVolumeD"> <b><code>VolumeD</code></b></a> <br /> Verringert die aktuelle Lautstärke um volumeStep-Einheiten.</li> -<li><a name="SONOSPLAYER_setter_VolumeRestore"> +<li><a name="SONOSPLAYERVolumeRestore"> <b><code>VolumeRestore</code></b></a> <br /> Stellt die mittels <code>VolumeSave</code> gespeicherte Lautstärke wieder her.</li> -<li><a name="SONOSPLAYER_setter_VolumeSave"> +<li><a name="SONOSPLAYERVolumeSave"> <b><code>VolumeSave <VolumeLevel></code></b></a> <br /> Setzt die aktuelle Lautstärke auf den angegebenen Wert. Der Wert kann ein relativer Wert mittels + oder - Zeichen sein. Liefert den aktuell gültigen Lautstärkewert zurück. Zusätzlich wird der alte Lautstärkewert gespeichert und kann mittels <code>VolumeRestore</code> wiederhergestellt werden.</li> -<li><a name="SONOSPLAYER_setter_VolumeU"> +<li><a name="SONOSPLAYERVolumeU"> <b><code>VolumeU</code></b></a> <br /> Erhöht die aktuelle Lautstärke um volumeStep-Einheiten.</li> </ul></li> <li><b>Steuerung der aktuellen Abspielliste</b><ul> -<li><a name="SONOSPLAYER_setter_AddURIToQueue"> +<li><a name="SONOSPLAYERAddURIToQueue"> <b><code>AddURIToQueue <songURI></code></b></a> <br /> Fügt die angegebene MP3-Datei an der aktuellen Stelle in die Abspielliste ein.</li> -<li><a name="SONOSPLAYER_setter_CurrentPlaylist"> +<li><a name="SONOSPLAYERCurrentPlaylist"> <b><code>CurrentPlaylist</code></b></a> <br /> Setzt den Abspielmodus auf die aktuelle Abspielliste, startet aber keine Wiedergabe (z.B. nach dem Hören eines Radiostreams, wo die aktuelle Abspielliste noch existiert, aber gerade "nicht verwendet" wird)</li> -<li><a name="SONOSPLAYER_setter_DeleteFromQueue"> +<li><a name="SONOSPLAYERDeleteFromQueue"> <b><code>DeleteFromQueue <index_of_elems></code></b></a> <br /> Löscht die angegebenen Elemente aus der aktuellen Abspielliste. Die Angabe erfolgt über die Indizies der Titel. Es können die bei Perl-Array-üblichen Formate verwendet werden: "1..12,17,20..22". Die Indizies beziehen sich auf die aktuell angezeigte Reihenfolge (diese unterscheidet sich zwischen der normalen Abspielweise und dem Shufflemodus).</li> -<li><a name="SONOSPLAYER_setter_DeletePlaylist"> +<li><a name="SONOSPLAYERDeletePlaylist"> <b><code>DeletePlaylist</code></b></a> <br /> Löscht die bezeichnete Playliste. Zum möglichen Format des Playlistenamen unter LoadPlaylist nachsehen.</li> -<li><a name="SONOSPLAYER_setter_EmptyPlaylist"> +<li><a name="SONOSPLAYEREmptyPlaylist"> <b><code>EmptyPlaylist</code></b></a> <br /> Leert die aktuelle Abspielliste</li> -<li><a name="SONOSPLAYER_setter_LoadFavourite"> +<li><a name="SONOSPLAYERLoadFavourite"> <b><code>LoadFavourite <FavouriteName></code></b></a> <br /> Lädt den angegebenen Favoriten. Der Name bezeichnet einen Eintrag in der Sonos-Favoritenliste. Der Parameter sollte/kann URL-Encoded werden um auch Spezialzeichen zu ermöglichen.<br />Zusätzlich kann ein regulärer Ausdruck für den Namen verwendet werden. Der erste Treffer wird verwendet. Das Format ist z.B. <code>/meine.hits/</code>.</li> -<li><a name="SONOSPLAYER_setter_LoadPlaylist"> +<li><a name="SONOSPLAYERLoadPlaylist"> <b><code>LoadPlaylist <Playlistname|Fhem-Devicename> [EmptyQueueBeforeImport]</code></b></a> <br /> Lädt die angegebene Playlist in die aktuelle Abspielliste. Der Parameter sollte/kann URL-Encoded werden um auch Spezialzeichen zu ermöglichen. Der Playlistname kann ein Fhem-Sonosplayer-Devicename sein, dann wird dessen aktuelle Abpielliste kopiert. Der Playlistname kann aber auch ein Dateiname sein. Dann muss dieser mit 'file:' beginnen (z.B. 'file:c:/Test.m3u).<br />Wenn der Parameter EmptyQueueBeforeImport mit ''1'' angegeben wirde, wird die aktuelle Abspielliste vor dem Import geleert. Standardmäßig wird hier ''1'' angenommen.<br />Zusätzlich kann ein regulärer Ausdruck für den Namen verwendet werden. Der erste Treffer wird verwendet. Das Format ist z.B. <code>/hits.2014/</code>.</li> -<li><a name="SONOSPLAYER_setter_LoadRadio"> +<li><a name="SONOSPLAYERLoadRadio"> <b><code>LoadRadio <Radiostationname></code></b></a> <br /> Startet den angegebenen Radiostream. Der Name bezeichnet einen Sender in der Radiofavoritenliste. Die aktuelle Abspielliste wird nicht verändert. Der Parameter sollte/kann URL-Encoded werden um auch Spezialzeichen zu ermöglichen.<br />Zusätzlich kann ein regulärer Ausdruck für den Namen verwendet werden. Der erste Treffer wird verwendet. Das Format ist z.B. <code>/radio/</code>.</li> -<li><a name="SONOSPLAYER_setter_LoadSearchlist"> +<li><a name="SONOSPLAYERLoadSearchlist"> <b><code>LoadSearchlist <Kategoriename> <KategorieElement> [[TitelfilterRegEx]/[AlbumfilterRegEx]/[ArtistfilterRegEx] [[*]maxElem[+|-]]]</code></b></a> <br /> Lädt Titel nach diversen Kriterien in die aktuelle Abspielliste. Nähere Beschreibung bitte im Wiki nachlesen.</li> -<li><a name="SONOSPLAYER_setter_SavePlaylist"> +<li><a name="SONOSPLAYERSavePlaylist"> <b><code>SavePlaylist <Playlistname></code></b></a> <br /> Speichert die aktuelle Abspielliste unter dem angegebenen Namen. Eine bestehende Playlist mit diesem Namen wird überschrieben. Der Parameter sollte/kann URL-Encoded werden um auch Spezialzeichen zu ermöglichen. Der Playlistname kann auch ein Dateiname sein. Dann muss dieser mit 'file:' beginnen (z.B. 'file:c:/Test.m3u).</li> </ul></li> <li><b>Gruppenbefehle</b><ul> -<li><a name="SONOSPLAYER_setter_AddMember"> +<li><a name="SONOSPLAYERAddMember"> <b><code>AddMember <devicename></code></b></a> <br />Fügt dem Device das übergebene Device als Gruppenmitglied hinzu. Die Wiedergabe des aktuellen Devices bleibt erhalten, und wird auf das angegebene Device mit übertragen.</li> -<li><a name="SONOSPLAYER_setter_CreateStereoPair"> +<li><a name="SONOSPLAYERCreateStereoPair"> <b><code>CreateStereoPair <rightPlayerDevicename></code></b></a> <br />Fügt dem Device das übergebene Device als rechtes Stereopaar-Element hinzu. Die Wiedergabe des aktuellen Devices bleibt erhalten (als linker Lautsprecher), und wird auf das angegebene Device mit übertragen (als rechter Lautsprecher).</li> -<li><a name="SONOSPLAYER_setter_GroupMute"> +<li><a name="SONOSPLAYERGroupMute"> <b><code>GroupMute <State></code></b></a> <br />Setzt den Mute-Zustand für die komplette Gruppe in einem Schritt. Der Wert kann on oder off sein.</li> -<li><a name="SONOSPLAYER_setter_GroupVolume"> +<li><a name="SONOSPLAYERGroupVolume"> <b><code>GroupVolume <VolumeLevel></code></b></a> <br />Setzt die Gruppenlautstärke in der Art des Original-Controllers. Das bedeutet, dass das Lautstärkeverhältnis der Player zueinander beim Anpassen erhalten bleibt.</li> -<li><a name="SONOSPLAYER_setter_GroupVolumeD"> +<li><a name="SONOSPLAYERGroupVolumeD"> <b><code>GroupVolumeD</code></b></a> <br /> Verringert die aktuelle Gruppenlautstärke um volumeStep-Einheiten.</li> -<li><a name="SONOSPLAYER_setter_GroupVolumeU"> +<li><a name="SONOSPLAYERGroupVolumeU"> <b><code>GroupVolumeU</code></b></a> <br /> Erhöht die aktuelle Gruppenlautstärke um volumeStep-Einheiten.</li> -<li><a name="SONOSPLAYER_setter_MakeStandaloneGroup"> +<li><a name="SONOSPLAYERMakeStandaloneGroup"> <b><code>MakeStandaloneGroup</code></b></a> <br />Macht diesen Player zu seiner eigenen Gruppe.</li> -<li><a name="SONOSPLAYER_setter_RemoveMember"> +<li><a name="SONOSPLAYERRemoveMember"> <b><code>RemoveMember <devicename></code></b></a> <br />Entfernt dem Device das übergebene Device, sodass die beiden keine Gruppe mehr bilden. Die Wiedergabe des aktuellen Devices läuft normal weiter. Das abgetrennte Device stoppt seine Wiedergabe, und hat keine aktuelle Abspielliste mehr (seit Sonos Version 4.2 hat der Player wieder die Playliste von vorher aktiv).</li> -<li><a name="SONOSPLAYER_setter_SeparateStereoPair"> +<li><a name="SONOSPLAYERSeparateStereoPair"> <b><code>SeparateStereoPair</code></b></a> <br />Trennt das Stereopaar wieder auf.</li> -<li><a name="SONOSPLAYER_setter_SnapshotGroupVolume"> +<li><a name="SONOSPLAYERSnapshotGroupVolume"> <b><code>SnapshotGroupVolume</code></b></a> <br /> Legt das Lautstärkeverhältnis der aktuellen Player der Gruppe für folgende '''GroupVolume'''-Aufrufe fest. Dieses festgelegte Verhältnis wird bis zum nächsten Aufruf von '''SnapshotGroupVolume''' beibehalten.</li> </ul></li> @@ -1937,53 +1938,53 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p <h4>Get</h4> <ul> <li><b>Grundsätzliches</b><ul> -<li><a name="SONOSPLAYER_getter_Alarm"> +<li><a name="SONOSPLAYERAlarm"> <b><code>Alarm <ID></code></b></a> <br /> Ausnahmefall. Diese Get-Anweisung liefert direkt ein Hash zurück, in welchem die Informationen des Alarms mit der gegebenen ID enthalten sind. Es ist die Kurzform für <code>eval(ReadingsVal(<Devicename>, 'Alarmlist', ()))->{<ID>};</code>, damit sich nicht jeder ausdenken muss, wie er jetzt am einfachsten an die Alarm-Informationen rankommen kann.</li> -<li><a name="SONOSPLAYER_getter_EthernetPortStatus"> +<li><a name="SONOSPLAYEREthernetPortStatus"> <b><code>EthernetPortStatus <PortNumber></code></b></a> <br /> Liefert den Ethernet-Portstatus des gegebenen Ports. Kann 'Active' oder 'Inactive' liefern.</li> -<li><a name="SONOSPLAYER_getter_PossibleRoomIcons"> +<li><a name="SONOSPLAYERPossibleRoomIcons"> <b><code>PossibleRoomIcons</code></b></a> <br /> Liefert eine Liste aller möglichen RoomIcon-Bezeichnungen zurück.</li> -<li><a name="SONOSPLAYER_getter_SupportLinks"> +<li><a name="SONOSPLAYERSupportLinks"> <b><code>SupportLinks</code></b></a> <br /> Ausnahmefall. Diese Get-Anweisung liefert eine Liste mit passenden Links zu den Supportseiten des Player.</li> -<li><a name="SONOSPLAYER_getter_WifiPortStatus"> +<li><a name="SONOSPLAYERWifiPortStatus"> <b><code>WifiPortStatus</code></b></a> <br /> Liefert den Wifi-Portstatus. Kann 'Active' oder 'Inactive' liefern.</li> </ul></li> <li><b>Listen</b><ul> -<li><a name="SONOSPLAYER_getter_Favourites"> +<li><a name="SONOSPLAYERFavourites"> <b><code>Favourites</code></b></a> <br /> Liefert eine Liste mit den Namen aller gespeicherten Sonos-Favoriten. Das Format der Liste ist eine Komma-Separierte Liste, bei der die Namen in doppelten Anführungsstrichen stehen. z.B. "Liste 1","Eintrag 2","Test"</li> -<li><a name="SONOSPLAYER_getter_FavouritesWithCovers"> +<li><a name="SONOSPLAYERFavouritesWithCovers"> <b><code>FavouritesWithCovers</code></b></a> <br /> Liefert die Stringrepräsentation eines Hash mit den Namen und Covern aller gespeicherten Sonos-Favoriten. Z.B.: {'FV:2/22' => {'Cover' => 'urlzumcover', 'Title' => '1. Favorit'}}. Dieser String kann einfach mit '''eval''' in eine Perl-Datenstruktur umgewandelt werden.</li> -<li><a name="SONOSPLAYER_getter_Playlists"> +<li><a name="SONOSPLAYERPlaylists"> <b><code>Playlists</code></b></a> <br /> Liefert eine Liste mit den Namen aller gespeicherten Playlists. Das Format der Liste ist eine Komma-Separierte Liste, bei der die Namen in doppelten Anführungsstrichen stehen. z.B. "Liste 1","Liste 2","Test"</li> -<li><a name="SONOSPLAYER_getter_PlaylistsWithCovers"> +<li><a name="SONOSPLAYERPlaylistsWithCovers"> <b><code>PlaylistsWithCovers</code></b></a> <br /> Liefert die Stringrepräsentation eines Hash mit den Namen und Covern aller gespeicherten Sonos-Playlisten. Z.B.: {'SQ:14' => {'Cover' => 'urlzumcover', 'Title' => '1. Playlist'}}. Dieser String kann einfach mit '''eval''' in eine Perl-Datenstruktur umgewandelt werden.</li> -<li><a name="SONOSPLAYER_getter_Queue"> +<li><a name="SONOSPLAYERQueue"> <b><code>Queue</code></b></a> <br /> Liefert eine Liste mit den Namen aller Titel in der aktuellen Abspielliste. Das Format der Liste ist eine Komma-Separierte Liste, bei der die Namen in doppelten Anführungsstrichen stehen. z.B. "1. Liste 1 [0:02:14]","2. Eintrag 2 [k.A.]","3. Test [0:14:00]"</li> -<li><a name="SONOSPLAYER_getter_QueueWithCovers"> +<li><a name="SONOSPLAYERQueueWithCovers"> <b><code>QueueWithCovers</code></b></a> <br /> Liefert die Stringrepräsentation eines Hash mit den Namen und Covern aller Titel der aktuellen Abspielliste. Z.B.: {'Q:0/22' => {'Cover' => 'urlzumcover', 'Title' => '1. Titel'}}. Dieser String kann einfach mit '''eval''' in eine Perl-Datenstruktur umgewandelt werden.</li> -<li><a name="SONOSPLAYER_getter_Radios"> +<li><a name="SONOSPLAYERRadios"> <b><code>Radios</code></b></a> <br /> Liefert eine Liste mit den Namen aller gespeicherten Radiostationen (Favoriten). Das Format der Liste ist eine Komma-Separierte Liste, bei der die Namen in doppelten Anführungsstrichen stehen. z.B. "Sender 1","Sender 2","Test"</li> -<li><a name="SONOSPLAYER_getter_RadiosWithCovers"> +<li><a name="SONOSPLAYERRadiosWithCovers"> <b><code>RadiosWithCovers</code></b></a> <br /> Liefert die Stringrepräsentation eines Hash mit den Namen und Covern aller gespeicherten Sonos-Radiofavoriten. Z.B.: {'R:0/0/2' => {'Cover' => 'urlzumcover', 'Title' => '1. Radiosender'}}. Dieser String kann einfach mit '''eval''' in eine Perl-Datenstruktur umgewandelt werden.</li> -<li><a name="SONOSPLAYER_getter_SearchlistCategories"> +<li><a name="SONOSPLAYERSearchlistCategories"> <b><code>SearchlistCategories</code></b></a> <br /> Liefert eine Liste mit den Namen alle möglichen Kategorien für den Aufruf von "LoadSearchlist". Das Format der Liste ist eine Komma-Separierte Liste, bei der die Namen in doppelten Anführungsstrichen stehen.</li> </ul></li> <li><b>Informationen zum aktuellen Titel</b><ul> -<li><a name="SONOSPLAYER_getter_CurrentTrackPosition"> +<li><a name="SONOSPLAYERCurrentTrackPosition"> <b><code>CurrentTrackPosition</code></b></a> <br /> Liefert die aktuelle Position innerhalb des Titels.</li> </ul></li> @@ -1993,61 +1994,60 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p '''Hinweis'''<br />Die Attribute werden erst bei einem Neustart von Fhem verwendet, da diese dem SubProzess initial zur Verfügung gestellt werden müssen. <ul> <li><b>Grundsätzliches</b><ul> -<li><a name="SONOSPLAYER_attribut_disable"><b><code>disable <int></code></b> +<li><a name="SONOSPLAYERdisable"><b><code>disable <int></code></b> </a><br /> One of (0,1). Deaktiviert die Event-Verarbeitung für diesen Zoneplayer.</li> -<li><a name="SONOSPLAYER_attribut_generateSomethingChangedEvent"><b><code>generateSomethingChangedEvent <int></code></b> +<li><a name="SONOSPLAYERgenerateSomethingChangedEvent"><b><code>generateSomethingChangedEvent <int></code></b> </a><br /> One of (0,1). 1 wenn ein 'SomethingChanged'-Event erzeugt werden soll. Dieses Event wird immer dann erzeugt, wenn sich irgendein Wert ändert. Dies ist nützlich, wenn man immer informiert werden möchte, egal, was sich geändert hat.</li> -<li><a name="SONOSPLAYER_attribut_generateVolumeEvent"><b><code>generateVolumeEvent <int></code></b> +<li><a name="SONOSPLAYERgenerateVolumeEvent"><b><code>generateVolumeEvent <int></code></b> </a><br /> One of (0,1). Aktiviert die Generierung eines Events bei Lautstärkeänderungen, wenn minVolume oder maxVolume definiert sind.</li> -<li><a name="SONOSPLAYER_attribut_generateVolumeSlider"><b><code>generateVolumeSlider <int></code></b> +<li><a name="SONOSPLAYERgenerateVolumeSlider"><b><code>generateVolumeSlider <int></code></b> </a><br /> One of (0,1). Aktiviert einen Slider für die Lautstärkekontrolle in der Detailansicht.</li> -<li><a name="SONOSPLAYER_attribut_getAlarms"><b><code>getAlarms <int></code></b> +<li><a name="SONOSPLAYERgetAlarms"><b><code>getAlarms <int></code></b> </a><br /> One of (0..1). Richtet eine Callback-Methode für Alarme ein. Damit wird auch die DailyIndexRefreshTime automatisch aktualisiert.</li> -<li><a name="SONOSPLAYER_attribut_suppressControlButtons"><b><code>suppressControlButtons <int></code></b> +<li><a name="SONOSPLAYERsuppressControlButtons"><b><code>suppressControlButtons <int></code></b> </a><br /> One of (0,1). Gibt an, ob die Steuerbuttons unter der Cover-/Titelanzeige angezeigt werden sollen (=1) oder nicht (=0).</li> -</ul></li> -<li><a name="SONOSPLAYER_attribut_volumeStep"><b><code>volumeStep <int></code></b> +<li><a name="SONOSPLAYERvolumeStep"><b><code>volumeStep <int></code></b> </a><br /> One of (0..100). Definiert die Schrittweite für die Aufrufe von <code>VolumeU</code> und <code>VolumeD</code>.</li> -</ul> +</ul></li> <li><b>Informationen generieren</b><ul> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize1"><b><code>generateInfoSummarize1 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize1"><b><code>generateInfoSummarize1 <string></code></b> </a><br /> Erzeugt das Reading 'InfoSummarize1' mit dem angegebenen Format. Mehr Informationen dazu im Bereich Beispiele.</li> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize2"><b><code>generateInfoSummarize2 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize2"><b><code>generateInfoSummarize2 <string></code></b> </a><br /> Erzeugt das Reading 'InfoSummarize2' mit dem angegebenen Format. Mehr Informationen dazu im Bereich Beispiele.</li> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize3"><b><code>generateInfoSummarize3 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize3"><b><code>generateInfoSummarize3 <string></code></b> </a><br /> Erzeugt das Reading 'InfoSummarize3' mit dem angegebenen Format. Mehr Informationen dazu im Bereich Beispiele.</li> -<li><a name="SONOSPLAYER_attribut_generateInfoSummarize4"><b><code>generateInfoSummarize4 <string></code></b> +<li><a name="SONOSPLAYERgenerateInfoSummarize4"><b><code>generateInfoSummarize4 <string></code></b> </a><br /> Erzeugt das Reading 'InfoSummarize4' mit dem angegebenen Format. Mehr Informationen dazu im Bereich Beispiele.</li> -<li><a name="SONOSPLAYER_attribut_getTitleInfoFromMaster"><b><code>getTitleInfoFromMaster <int></code></b> +<li><a name="SONOSPLAYERgetTitleInfoFromMaster"><b><code>getTitleInfoFromMaster <int></code></b> </a><br /> Eins aus (0,1,2,3,4,5,6,7,8,9,10,15,20,25,30,45,60). Bringt das Device dazu, seine aktuellen Abspielinformationen vom aktuellen Gruppenmaster zu holen, wenn es einen solchen gibt.</li> -<li><a name="SONOSPLAYER_attribut_simulateCurrentTrackPosition"><b><code>simulateCurrentTrackPosition <int></code></b> +<li><a name="SONOSPLAYERsimulateCurrentTrackPosition"><b><code>simulateCurrentTrackPosition <int></code></b> </a><br /> Eins aus (0, 1). Bringt das Device dazu, seine aktuelle Abspielposition simuliert weiterlaufen zu lassen. Dazu werden die Readings <code>currentTrackPositionSimulated</code> und <code>currentTrackPositionSimulatedSec</code> gesetzt. Gleichzeitig wird auch das Reading <code>currentTrackPositionSimulatedPercent</code> (zwischen 0.0 und 100.0) gesetzt.</li> -<li><a name="SONOSPLAYER_attribut_simulateCurrentTrackPositionPercentFormat"><b><code>simulateCurrentTrackPositionPercentFormat <Format></code></b> +<li><a name="SONOSPLAYERsimulateCurrentTrackPositionPercentFormat"><b><code>simulateCurrentTrackPositionPercentFormat <Format></code></b> </a><br /> Definiert das Format für die sprintf-Prozentausgabe im Reading <code>currentTrackPositionSimulatedPercent</code>.</li> -<li><a name="SONOSPLAYER_attribut_stateVariable"><b><code>stateVariable <string></code></b> +<li><a name="SONOSPLAYERstateVariable"><b><code>stateVariable <string></code></b> </a><br /> One of (TransportState,NumberOfTracks,Track,TrackURI,TrackDuration,Title,Artist,Album,OriginalTrackNumber,AlbumArtist,<br />Sender,SenderCurrent,SenderInfo,StreamAudio,NormalAudio,AlbumArtURI,nextTrackDuration,nextTrackURI,nextAlbumArtURI,<br />nextTitle,nextArtist,nextAlbum,nextAlbumArtist,nextOriginalTrackNumber,Volume,Mute,Shuffle,Repeat,RepeatOne,CrossfadeMode,Balance,<br />HeadphoneConnected,SleepTimer,Presence,RoomName,SaveRoomName,PlayerType,Location,SoftwareRevision,SerialNum,InfoSummarize1,I<br />nfoSummarize2,InfoSummarize3,InfoSummarize4). Gibt an, welche Variable in das Reading <code>state</code> kopiert werden soll.</li> </ul></li> <li><b>Steueroptionen</b><ul> -<li><a name="SONOSPLAYER_attribut_maxVolume"><b><code>maxVolume <int></code></b> +<li><a name="SONOSPLAYERmaxVolume"><b><code>maxVolume <int></code></b> </a><br /> One of (0..100). Definiert die maximale Lautstärke dieses Zoneplayer.</li> -<li><a name="SONOSPLAYER_attribut_minVolume"><b><code>minVolume <int></code></b> +<li><a name="SONOSPLAYERminVolume"><b><code>minVolume <int></code></b> </a><br /> One of (0..100). Definiert die minimale Lautstärke dieses Zoneplayer.</li> -<li><a name="SONOSPLAYER_attribut_maxVolumeHeadphone"><b><code>maxVolumeHeadphone <int></code></b> +<li><a name="SONOSPLAYERmaxVolumeHeadphone"><b><code>maxVolumeHeadphone <int></code></b> </a><br /> One of (0..100). Definiert die maximale Lautstärke dieses Zoneplayer im Kopfhörerbetrieb.</li> -<li><a name="SONOSPLAYER_attribut_minVolumeHeadphone"><b><code>minVolumeHeadphone <int></code></b> +<li><a name="SONOSPLAYERminVolumeHeadphone"><b><code>minVolumeHeadphone <int></code></b> </a><br /> One of (0..100). Definiert die minimale Lautstärke dieses Zoneplayer im Kopfhörerbetrieb.</li> -<li><a name="SONOSPLAYER_attribut_buttonEvents"><b><code>buttonEvents <Time:Pattern>[ <Time:Pattern> ...]</code></b> +<li><a name="SONOSPLAYERbuttonEvents"><b><code>buttonEvents <Time:Pattern>[ <Time:Pattern> ...]</code></b> </a><br /> Definiert, dass bei einer bestimten Tastenfolge am Player ein Event erzeugt werden soll. Die Definition der Events erfolgt als Tupel: Der erste Teil vor dem Doppelpunkt ist die Zeit in Sekunden, die berücksichtigt werden soll, der zweite Teil hinter dem Doppelpunkt definiert die Abfolge der Buttons, die für dieses Event notwendig sind.<br /> Folgende Button-Kürzel sind zulässig: <ul><li><b>M</b>: Der Mute-Button</li><li><b>H</b>: Die Headphone-Buchse</li><li><b>U</b>: Up-Button (Lautstärke Hoch)</li><li><b>D</b>: Down-Button (Lautstärke Runter)</li></ul><br /> Das Event, das geworfen wird, heißt <code>ButtonEvent</code>, der Wert ist die definierte Tastenfolge<br /> Z.B.: <code>2:MM</code><br /> Hier wird definiert, dass ein Event erzeugt werden soll, wenn innerhalb von 2 Sekunden zweimal die Mute-Taste gedrückt wurde. Das damit erzeugte Event hat dann den Namen <code>ButtonEvent</code>, und den Wert <code>MM</code>.</li> </ul></li> -<li><a name="SONOSPLAYER_attribut_saveSleeptimerInAction"><b><code>saveSleeptimerInAction <int></code></b> +<li><a name="SONOSPLAYERsaveSleeptimerInAction"><b><code>saveSleeptimerInAction <int></code></b> </a><br /> One of (0..1). Wenn gesetzt, wird ein etwaig gesetztes Attribut "stopSleeptimerInAction" ignoriert.</li> -<li><a name="SONOSPLAYER_attribut_stopSleeptimerInAction"><b><code>stopSleeptimerInAction <int></code></b> +<li><a name="SONOSPLAYERstopSleeptimerInAction"><b><code>stopSleeptimerInAction <int></code></b> </a><br /> One of (0..1). Wenn gesetzt, wird bei einem Wechsel des transportState auf "PAUSED_PLAYBACK" oder "STOPPED" ein etwaig definierter SleepTimer deaktiviert.</li> - +</ul> <a name="SONOSPLAYERexamples"></a> <h4>Beispiele / Hinweise</h4> <ul> diff --git a/fhem/FHEM/lib/UPnP/ControlPoint.pm b/fhem/FHEM/lib/UPnP/ControlPoint.pm index 1d5f275e9..b5bca5067 100644 --- a/fhem/FHEM/lib/UPnP/ControlPoint.pm +++ b/fhem/FHEM/lib/UPnP/ControlPoint.pm @@ -111,7 +111,11 @@ sub new { $reuseport = 0 if (!defined($reuseport)); # Create the socket on which search requests go out - $self->{_searchSocket} = IO::Socket::INET->new(Proto => 'udp', LocalPort => $searchPort) || carp("Error creating search socket: $!\n"); + $self->{_searchSocket} = IO::Socket::INET->new(Proto => 'udp', + Reuse => 1, + ReuseAddr => 1, + ReusePort => (defined(&ReusePort) ? 1 : 0), + LocalPort => $searchPort) || carp("Error creating search socket: $!\n"); setsockopt($self->{_searchSocket}, IP_LEVEL, IP_MULTICAST_TTL, @@ -120,9 +124,13 @@ sub new { # Create the socket on which we'll listen for events to which we are # subscribed. - $self->{_subscriptionSocket} = HTTP::Daemon->new(LocalPort => $subscriptionPort, Reuse=>1, Listen=>20) || carp("Error creating subscription socket: $!\n"); + $self->{_subscriptionSocket} = HTTP::Daemon->new(LocalPort => $subscriptionPort, + Reuse=>1, + ReuseAddr => 1, + ReusePort => (defined(&ReusePort) ? 1 : 0), + Listen=>20) || carp("Error creating subscription socket: $!\n"); $self->{_subscriptionURL} = $args{SubscriptionURL} || DEFAULT_SUBSCRIPTION_URL; - $self->{_subscriptionPort} = $self->{_subscriptionSocket}->sockport();; + $self->{_subscriptionPort} = $self->{_subscriptionSocket}->sockport(); # Create the socket on which we'll listen for SSDP Notifications. # First try with ReusePort (if given as parameter)... @@ -130,16 +138,18 @@ sub new { $self->{_ssdpMulticastSocket} = IO::Socket::INET->new( Proto => 'udp', Reuse => 1, - ReusePort => $reuseport, + ReuseAddr => 1, + ReusePort => (defined(&ReusePort) ? 1 : 0), LocalPort => SSDP_PORT) || - croak("Error creating SSDP multicast listen socket: $!\n"); + croak("Error creating SSDP multicast listen socket (1): $!\n"); }; - if ($@ =~ /Your vendor has not defined Socket macro SO_REUSEPORT/i) { + if ($@ =~ /Your vendor has not defined Socket macro/i) { $self->{_ssdpMulticastSocket} = IO::Socket::INET->new( Proto => 'udp', Reuse => 1, + ReuseAddr => 1, LocalPort => SSDP_PORT) || - croak("Error creating SSDP multicast listen socket: $!\n"); + croak("Error creating SSDP multicast listen socket (2): $!\n"); } elsif($@) { # Weiterwerfen... croak($@); @@ -778,7 +788,7 @@ sub subscribe { $request->header('Callback', '<' . $cp->subscriptionURL . '>'); $request->header('Timeout', 'Second-' . defined($timeout) ? $timeout : 'infinite'); - my $ua = LWP::UserAgent->new(timeout => 20); + my $ua = LWP::UserAgent->new(timeout => 5); my $response = $ua->request($request); if ($response->is_success) { @@ -816,7 +826,7 @@ sub unsubscribe { my $request = HTTP::Request->new('UNSUBSCRIBE', "$url"); $request->header('SID', $subscription->SID); - my $ua = LWP::UserAgent->new(timeout => 20); + my $ua = LWP::UserAgent->new(timeout => 5); my $response = $ua->request($request); if ($response->is_success) { @@ -1127,21 +1137,24 @@ sub renew { $request->header('Timeout', 'Second-' . defined($timeout) ? $timeout : 'infinite'); - my $ua = LWP::UserAgent->new(timeout => 20); + my $ua = LWP::UserAgent->new(timeout => 5); my $response = $ua->request($request); if ($response->is_success) { - $timeout = $response->header('Timeout'); - if ($timeout =~ /^Second-(\d+)$/) { - $timeout = $1; + if ($response->code == 200) { + $timeout = $response->header('Timeout'); + if ($timeout =~ /^Second-(\d+)$/) { + $timeout = $1; + } + + $self->{_timeout} = $timeout; + $self->{_startTime} = Time::HiRes::time(); + } else { + carp("Renewal of subscription successful but answered with error: " . $response->code . " " . $response->message); } - - $self->{_timeout} = $timeout; - $self->{_startTime} = Time::HiRes::time(); } else { - carp("Renewal of subscription failed with error: " . - $response->code . " " . $response->message); + carp("Renewal of subscription failed with error: " . $response->code . " " . $response->message); } return $self;