diff --git a/fhem/CHANGED b/fhem/CHANGED index 1301a46cb..ddcc29f96 100644 --- a/fhem/CHANGED +++ b/fhem/CHANGED @@ -1,5 +1,6 @@ # Add changes at the top of the list. Keep it in ASCII, and 80-char wide. # Do not insert empty lines here, update check depends on it. + - feature: 00_SONOS: New lists for groupinformations - bugfix: 88_HMCCU: Fixed toggle function for HMCCUCHN devices - change: 93_DbLog: new version 2.14.4, new set-commands exportCache, importCachefile, some fixes (e.g. many SVGs from SQLite diff --git a/fhem/FHEM/00_SONOS.pm b/fhem/FHEM/00_SONOS.pm index d875a5a66..1ed57e10d 100755 --- a/fhem/FHEM/00_SONOS.pm +++ b/fhem/FHEM/00_SONOS.pm @@ -1,6 +1,6 @@ ######################################################################################## # -# SONOS.pm (c) by Reiner Leins, March 2017 +# SONOS.pm (c) by Reiner Leins, April 2017 # rleins at lmsoft dot de # # $Id$ @@ -51,9 +51,26 @@ # Changelog (last 4 entries only, see Wiki for complete changelog) # # SVN-History: +# 04.04.2017 +# Es gibt zwei neue Readings "AvailablePlayerList" und "AvailablePlayerListAlias" am Sonosplayer-Device, wenn das Attribut "getListsDirectlyToReadings" am Sonos-Device gesetzt wurde. Diese Reading geben die anderen noch verfügbaren, nicht gebundenen, Player an. Das ist die Grundlage für eine Player-zur-Abspielgruppe-hinzufügen-Funktion als Listendarstellung. +# Es gibt zwei neue Readings "AllPlayerNotBonded" und "AllPlayerNotBondedCount" am Sonos-Device, welche alle Masterplayer angibt, die nicht gebunden sind. +# Es gibt ein neues Reading "IsBonded" am Sonosplayer-Device, welches angibt, ob der Player in einer Bindung zum Masterplayer steht (anstatt ein einfaches Gruppenmitglied zu sein). Gebundene Player sind z.B. der rechte Player im Stereoverbund, sowie die Satellitenplayer in einem 5.1er Surroundsystem (also Subwoofer, hintere Lautsprecher und vordere Lautsprecher). +# Es gibt drei neue Readings "SlavePlayerNotBonded", "SlavePlayerNotBondedList" und "SlavePlayerNotBondedListAlias" am Sonosplayer-Device, wobei die beiden letzteren nur erzeugt werden, wenn das Attribut "getListsDirectlyToReadings" am Sonos-Device gesetzt wurde. +# Es gibt jetzt ein Attribut "getTitleInfoFromMaster" am Sonosplayer-Device, mit welchem man ein Slave-Device (auch gebundene) dazu bringen kann, die wichtigsten Abspielreadings automatisch vom Master zu duplizieren. +# Die Oberflächenauswahl für "AddMember", "RemoveMember" und "CreateStereoPair" wurde auf die nicht bereits gebundenen Player bzw. die Teilnehmer deer Gruppe beschränkt. +# Es gibt zwei neue Readings "AllPlayer" und "AllPlayerCount" am Sonos-Device. Damit erhält man eine komplette Liste aller Player (und deren Anzahl), egal wie sie gerade verwendet werden. +# Die mitgelieferte Prozedur für die ReadingsGroup-Anzeigen wurde für das neue Reading "Queue" erweitert, außerdem wurde ein Darstellungsproblem der Gruppierungsanzeige mit aktuellen Versionen von FHEMWEB behoben +# Es gibt zwei neue Readings "ButtonState" und "ButtonLockState", sowie einen Setter für "ButtonLockState". +# Es gibt ein neues Reading "LineInPlayer" am Sonos-Device, und wenn das Attribut "getListsDirectlyToReadings" gesetzt ist, auch "LineInPlayerList" und "LineInPlayerListAlias". Diese Liste enthält die gültigen LineIn-Eingänge aller Player, die für die Wiedergabe ausgewählt werden können. +# Es gibt zwei neue Attribute "stopSleeptimerInAction" und "saveSleeptimerInAction" am Sonosplayer-Device. Mit "stopSleeptimerInAction" wird das Modul dazu angehalten, bei einem Wechsel des TransportState auf "STOPPED" oder "PAUSED_PLAYBACK" einen etwaig aktivierten SleepTimer zu deaktivieren. Mit dem Attribut "saveSleeptimerInAction" kann man dieses Verhalten (z.B. temporär) wieder unterdrücken. +# Es gibt jetzt ein Reading "ZoneGroupNameDetails" am Sonosplayer-Device, welches die Slavezonen als textuelle Auflistung mittels '+' enthält. Ist leer, wenn es keine Slaveplayer gibt. Enthält den Namen des Gruppenmasters, wenn es einen solchen gibt. +# Interne Aufräumarbeiten: Mittlerweile überflüssige Codeteile wurden entfernt, und einige Single-Readingsupdates zu einem Bulk-Readingsupdate zusammengefasst. +# Die Readings "currentFavouriteNameMasked", "currentPlaylistNameMasked" und "currentRadioNameMasked" werden automatisch gesetzt, wenn das Attribut "getListsDirectlyToReadings" gesetzt ist. +# Tippfehler bei den Readings "RadioList" und "RadioListAlias" korrigiert. Korrekt ist nun "RadiosList" und "RadiosListAlias" (also Mehrzahl bei Radios). # 19.03.2017 # Es gibt ein neues Attribut "getListsDirectlyToReadings", mit welchem die UserReadings bzgl. Favourites, Playlists und Radios (sowie currentTrackPosition) obsolet werden, da sie dann direkt in die passenden Readings geschrieben werden. Wenn man selber beeinflussen möchte, auf welche Weise und mit welchem Namen diese Readings gefüllt werden, dann darf dieses Attribut nicht gesetzt werden (es bleibt dann das bisherige Verhalten) # Es gibt zwei neue Getter "Queue" und "QueueWithCovers", welche die aktuelle Abspielliste liefern. Diese können wieder mit UserReadings oder dem neuen Attribut "getListsDirectlyToReadings" in entsprechende Readings übertragen werden. Hierbei werden auch zwei neue Readings "QueueDuration" und "QueueDurationSec" gefüllt, die die gesamte Abspieldauer der Abspielliste enthalten. +# Die Prozedur "SONOS_getGroupsRG()" wurde umgebaut, da FW_makeImage von FhemWeb scheinbar die Variable "$_" verändert. # 13.03.2017 # Saubere Fehlerbehandlung bei der Verarbeitung von currentFavouriteName, currentPlaylistName und currentRadioName. # 12.03.2017 @@ -70,19 +87,6 @@ # Es gibt ein neues Reading "currentFavouriteName", welches versucht den "currentEnqueuedTransportURI" in den Favoriten zu finden, und enthält dann den gefundenen Favoritennamen. Dazu müssen die Favoriten einmal mittels "get FavouritesWithCover" ermittelt worden sein, und im Reading "Favourites" bereitstehen. # Es gibt ein neues Reading "currentPlaylistName", welches versucht den "currentEnqueuedTransportURI" in den Playlisten zu finden, und enthält dann den gefundenen Playlistnamen. Dazu müssen die Playlisten einmal mittels "get PlaylistsWithCover" ermittelt worden sein, und im Reading "Playlists" bereitstehen. # Es gibt ein neues Reading "currentRadioName", welches versucht den "currentEnqueuedTransportURI" in den Radios zu finden, und enthält dann den gefundenen Radionamen. Dazu müssen die Radios einmal mittels "get RadiosWithCover" ermittelt worden sein, und im Reading "Radios" bereitstehen. -# 19.03.2016 -# Bei der Alarmbearbeitung kann man nun mehrere Alarm-IDs mit Komma getrennt angeben, und das Schlüsselwort "All" verwenden, um alle Alarme dieses Players anzusprechen. -# Man kann bei der Alarmbearbeitung nun auch zwei neue, direkte und kürzere, Befehle für Standardaufgaben verwenden: "Enable", "Disable". -# 06.02.2016 -# Zusätzlich zu "Mute" (mit Parameter) am Sonos-Device gibt es jetzt auch "MuteOn" und "MuteOff" (jeweils ohne Parameter) zur Verwendung als WebCmd. -# Es gibt ein neues Reading "IsMaster" am Sonosplayer-Device, welches angibt, ob der Player gerade ein Masterplayer ist -# Es gibt ein neues Reading "MasterPlayer" am Sonosplayer-Device, welches angibt, wie der aktuelle MasterPlayer zu diesem Player heißt. Ist der Player selber der Master (also IsMaster = 1), so steht dort der eigene Name drin. -# Es gibt ein neues Reading "SlavePlayer" am Sonosplayer-Device, welches angibt, welche Slaveplayer zu diesem Player zugeordnet sind. Enthält nur Playerdevicenamen, wenn dieser Player ein Masterplayer ist, und dann auch nicht sich selber. -# 31.01.2016 -# Im Modul ControlPoint.pm gab es eine fehlerhafte Bearbeitung des Arrays @LWP::Protocol::http::EXTRA_SOCK_OPTS, welche in manchen Fällen zu der Fehlermeldung "Odd number of elements in hash assignment" geführt hat. -# Der Anbieter SoundCloud wird als Quelle erkannt und angezeigt -# Es gibt drei neue Readings "MasterPlayer", "MasterPlayerPlaying" und "MasterPlayerNotPlaying" am Sonos-Device (zzgl. der jeweiligen Angabe der Anzahl) -# Die Ausgabe von "get Sonos Groups" liefert nun stets eine normalisierte Liste (also sortiert). # ######################################################################################## # @@ -184,7 +188,7 @@ 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"; +# print 'Current: "'.$0.'", gPath: "'.$gPath."\"\n"; if (lc(substr($0, -7)) eq 'fhem.pl') { require 'DevIo.pm'; @@ -234,8 +238,8 @@ my %SONOS_ProviderList = ('^http:(\/\/.*)' => 'Radio', '^x-sonosapi-hls-static:' => 'Amazon Music'); 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); -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 ZoneGroupName roomName roomNameAlias roomIcon transportState TransportState LineInConnected presence currentAlbum currentArtist currentTitle GroupVolume GroupMute FavouritesVersion RadiosVersion PlaylistsVersion QueueVersion QueueHash GroupMasterPlayer ShareIndexInProgress DirectControlClientID DirectControlIsSuspended DirectControlAccountID); +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 getTitleInfoFromMaster stopSleeptimerInAction saveSleeptimerInAction); +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 GroupVolume GroupMute FavouritesVersion RadiosVersion PlaylistsVersion QueueVersion QueueHash GroupMasterPlayer ShareIndexInProgress DirectControlClientID DirectControlIsSuspended DirectControlAccountID IsMaster MasterPlayer SlavePlayer ButtonState ButtonLockState AllPlayer LineInName LineInIcon); # Obsolete Einstellungen... my $SONOS_UseTelnetForQuestions = 1; @@ -621,23 +625,27 @@ sub SONOS_getListRG($$;$) { my $resultString = ''; # Manchmal ist es etwas komplizierter mit den Zeichensätzen... - my %elems = %{eval(decode('CP1252', ReadingsVal($device, $reading, '{}')))}; + #my %elems = %{eval(decode('CP1252', ReadingsVal($device, $reading, '{}')))}; + my %elems = %{eval(ReadingsVal($device, $reading, '{}'))}; - for my $key (keys %elems) { - my $command; + for my $key (sort keys %elems) { + my $command = ''; if ($reading eq 'Favourites') { $command = 'cmd.'.$device.SONOS_URI_Escape('=set '.$device.' StartFavourite '.SONOS_URI_Escape($elems{$key}->{Title})); } elsif ($reading eq 'Playlists') { $command = 'cmd.'.$device.SONOS_URI_Escape('=set '.$device.' StartPlaylist '.SONOS_URI_Escape($elems{$key}->{Title})); } elsif ($reading eq 'Radios') { $command = 'cmd.'.$device.SONOS_URI_Escape('=set '.$device.' StartRadio '.SONOS_URI_Escape($elems{$key}->{Title})); + } elsif ($reading eq 'Queue') { + next if (($key eq 'Duration') || ($key eq 'DurationSec')); + $command = 'cmd.'.$device.SONOS_URI_Escape('=set '.$device.' Track '.$elems{$key}->{Position}); } $command = "FW_cmd('/fhem?XHR=1&$command')"; if ($ul) { $resultString .= '
  • '; } else { - $resultString .= ''.$elems{$key}->{Title}."\n"; + $resultString .= ''.(($reading eq 'Queue') ? $elems{$key}->{ShowTitle} : $elems{$key}->{Title})."\n"; } } @@ -654,13 +662,14 @@ sub SONOS_getListRG($$;$) { # ######################################################################################## sub SONOS_getGroupsRG() { - my $groups = CommandGet(undef, SONOS_getDeviceDefHash(undef)->{NAME}.' Groups'); + my $groups = CommandGet(undef, SONOS_getSonosPlayerByName()->{NAME}.' Groups'); my $result = ' @@ -1465,6 +1486,10 @@ The event thrown is named ButtonEvent, the value is the defined but E.G.: 2:MM
    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 ButtonEvent and has the value MM. +
  • saveSleeptimerInAction <int> +
    One of (0..1). If set, a possibly set Attribute "stopSleeptimerInAction" will be ignored.
  • +
  • stopSleeptimerInAction <int> +
    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.
  • Examples / Tips

    @@ -1510,6 +1535,9 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p
  • AudioDelayRightRear <Level>
    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
  • +
  • +ButtonLockState <int> +
    One of (0, 1). Setzt den aktuellen Button-Sperr-Zustand.
  • DailyIndexRefreshTime <Timestring>
    Setzt die aktuell gültige DailyIndexRefreshTime für alle Zoneplayer.
  • @@ -1812,6 +1840,8 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p
    Erzeugt das Reading 'InfoSummarize3' mit dem angegebenen Format. Mehr Informationen dazu im Bereich Beispiele.
  • generateInfoSummarize4 <string>
    Erzeugt das Reading 'InfoSummarize4' mit dem angegebenen Format. Mehr Informationen dazu im Bereich Beispiele.
  • +
  • getTitleInfoFromMaster <int> +
    Eins aus (0, 1). Bringt das Device dazu, seine aktuellen Abspielinformationen vom aktuellen Gruppenmaster zu holen, wenn es einen solchen gibt.
  • stateVariable <string>
    One of (TransportState,NumberOfTracks,Track,TrackURI,TrackDuration,Title,Artist,Album,OriginalTrackNumber,AlbumArtist,
    Sender,SenderCurrent,SenderInfo,StreamAudio,NormalAudio,AlbumArtURI,nextTrackDuration,nextTrackURI,nextAlbumArtURI,
    nextTitle,nextArtist,nextAlbum,nextAlbumArtist,nextOriginalTrackNumber,Volume,Mute,Shuffle,Repeat,RepeatOne,CrossfadeMode,Balance,
    HeadphoneConnected,SleepTimer,Presence,RoomName,SaveRoomName,PlayerType,Location,SoftwareRevision,SerialNum,InfoSummarize1,I
    nfoSummarize2,InfoSummarize3,InfoSummarize4). Gibt an, welche Variable in das Reading state kopiert werden soll.
  • @@ -1831,6 +1861,10 @@ Das Event, das geworfen wird, heißt ButtonEvent, der Wert ist die Z.B.: 2:MM
    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 ButtonEvent, und den Wert MM. +
  • saveSleeptimerInAction <int> +
    One of (0..1). Wenn gesetzt, wird ein etwaig gesetztes Attribut "stopSleeptimerInAction" ignoriert.
  • +
  • stopSleeptimerInAction <int> +
    One of (0..1). Wenn gesetzt, wird bei einem Wechsel des transportState auf "PAUSED_PLAYBACK" oder "STOPPED" ein etwaig definierter SleepTimer deaktiviert.
  • Beispiele / Hinweise