mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
SONOS: Bugfixes and new features, see changelog in file.
git-svn-id: https://svn.fhem.de/fhem/trunk@14650 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
4912e959fb
commit
9f203de0ac
@ -1,6 +1,6 @@
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS.pm (c) by Reiner Leins, Juni 2017
|
||||
# SONOS.pm (c) by Reiner Leins, July 2017
|
||||
# rleins at lmsoft dot de
|
||||
#
|
||||
# $Id$
|
||||
@ -51,6 +51,18 @@
|
||||
# Changelog (last 4 entries only, see Wiki for complete changelog)
|
||||
#
|
||||
# SVN-History:
|
||||
# 05.07.2017
|
||||
# Veralteten Mechanismus für das Unterbrechen der Sendeschleife aufgeräumt.
|
||||
# SONOS_ConvertNumToWord kann nun mit undef-Übergaben umgehen.
|
||||
# Andere Methodik zum Ermitteln von FavouriteName, RadioName und PlaylistName eingebaut.
|
||||
# Es gibt ein neues Attribut "SubProcessLogfileName". Damit kann die Logausgabe des SubProzesses in eine eigene Datei umgeleitet werden. Unter Windows z.B. gibt es sonst keine saubere Darstellungsmöglichkeit für die Logausgabe, da die beiden Prozesse sich gegenseitig die Ausgaben im Fhem-Log überschreiben. Bei Angabe von '-' wird wie bisher auf STDOUT (und damit im Fhem-Log) geloggt.
|
||||
# Kleinere Fehler bei einzelnen Reading-Aktualisierungen behoben.
|
||||
# Beim Zerlegen der MusicServicesList gab es einen Fehler, der z.B. dafür gesorgt hatte, dass Apple Music nicht erkannt wurde.
|
||||
# Umbau der Verarbeitungslogik in Richtung SubProzess. Das sollte nun schneller und sicher sequentiell verarbeitet werden.
|
||||
# Die Aktualisierung des Readings "LastProcessAnswer" wird jetzt während eines laufenden Bulkupdates auch als solches durchgeführt, und zerstört damit nicht mehr dieses laufende Update.
|
||||
# Alle noch vorhandenen ReadingsSingleUpdate-Aufrufe wurden BulkUpdate-Sicher gemacht.
|
||||
# Die prozentuale Fortschrittsanzeige hat bei Streams negative, hohe Werte angezeigt. Steht jetzt wieder auf 0.
|
||||
# Beim Löschen blieb noch die automatisch angelegte ReadingsGroup für die aktuelle Abspielliste bestehen.
|
||||
# 21.06.2017
|
||||
# Bei der ersten Verbindung war das Reading für die letzte SubProzess-Antwort eventuell bereits veraltet. Das wird nun durch ein Datum in der Zukunft verhindert.
|
||||
# 20.06.2017
|
||||
@ -72,29 +84,6 @@
|
||||
# Bei der Ermittlung der Masterplayer werden "unsichtbare" Player (wie Bridge o.ä.) nun unterdrückt.
|
||||
# An einer Stelle wurde ein fehlerhafter Default-Wert für 'currentTrackPositionSec' eingesetzt, was zu Folgefehlern führte.
|
||||
# Die Wiederverwendung von Ports für die UPnP-Erkennung muss nun mittels dem Attribut 'reusePorts' aktiviert werden.
|
||||
# 14.05.2017
|
||||
# FHEMWEB-Anzeige der Player auf die neu möglichen, nicht quadratischen, Radiocover angepasst.
|
||||
# In der Datei ControlPoint.pm wurde beim Öffnen des SSDP-Ports das Attribut ReusePort hinzugefügt (sofern das Betriebssystem das unterstützt).
|
||||
# Es gibt ein neues Reading "currentEnqueuedTransportHandle", welches zur Weitergabe der aktuellen Quelle des aktuellen Titels verwendet werden kann.
|
||||
# Es gibt zwei neue Readings "currentTrackHandle" und "nextTrackHandle", welche zur Weitergabe der aktuellen bzw. nächsten Wiedergabe geeignet sind.
|
||||
# Es gibt ein neues Reading "currentSource", welches (wenn beliefert) den Namen der Quelle des Titels angibt. Bei Spotify z.B. die gewählte Playliste oder das Album, oder die gewählte Sonos-Playliste.
|
||||
# Die Trackprovider werden jetzt über die verfügbaren, von Sonos bereitgestellten, MusicServices ermittelt.
|
||||
# (Fehlende) Radiocover werden nun über den offiziellen Webservice von Sonos ermittelt, und werden wieder geladen.
|
||||
# Etwaige, immer noch, fehlende Radiocover werden als Fallback wie bisher geladen. Das sollte aber nicht mehr vorkommen.
|
||||
# Spotify-Playlisten-Cover werden wieder geladen.
|
||||
# Teilweise wurden Bibliothekscover nicht geladen, das sollte wieder gehen.
|
||||
# Die Verbindungsprüfung zwischen Fhem-Modul und SubProzess erfolgt nur noch, wenn nicht sowieso schon Daten übertragen werden, und sollte dementsprechend nicht mehr dazwischenfunken.
|
||||
# Die Verbindungsprüfung und der grundsätzliche Verbindungsaufbau zum SubProzess wurden umgestellt.
|
||||
# Fehlermeldungen bei Fhem-Player-Such-Prozeduren verbessert.
|
||||
# Es gibt zwei (bzw. vier) neue Readings "TrackProviderIconRoundURL" und "TrackProviderIconQuadraticURL" (jeweils für "current" und für "next"). Mit diesen lassen sich die entsprechenden Provider-Icons anzeigen.
|
||||
# Die Titelanzeige in FhemWeb enthält nun auch die neuen Provider-Icons.
|
||||
# Detaildarstellung der SonosPlayer-Devices enthält jetzt auch die Coverdarstellung.
|
||||
# Bug in "SONOS_GetTimeFromString" behoben.
|
||||
# Es gibt zwei neue Attribute "simulateCurrentTrackPosition" und "simulateCurrentTrackPositionPercentFormat". Mit diesen kann man eine Simulation des Fortschritts von currentTrackPosition im gewählten Intervall aktivieren. Dabei werden die Readings "currentTrackPositionSimulated", "currentTrackPositionSimulatedSec" und "currentTrackPositionSimulatedPercent" (nur bei gelieferter "currentTrackDuration") aktualisiert.
|
||||
# Es gibt zwei neue Readings "currentTrackDurationSec" und "nextTrackDurationSec", welche die jeweilige Tracklänge in Sekunden angeben.
|
||||
# Durch ein mittlerweile verändertes Notify-Event-Handling in Fhem wurden die Bookmarks nicht immer zusammen mit dem globalen Save-Befehl gespeichert.
|
||||
# Die Cover-/Titelanzeige wird nun intern vom Modul durchgeführt. Deshalb ist keine zusätzliche ReadingsGroup für die Anzeige und Aktualisierung mehr notwendig. In der Raumansicht kann man das Verhalten für alle Sonosplayer einheitlich mit dem Attribut "deviceRoomView" beeinflussen. Es kann die Zustände "Both" und "DeviceLineOnly" annehmen.
|
||||
# Die Steuermöglichkeiten werden nun intern vom Modul dargestellt. Dazu muss die Cover-/Titelanzeige aktiviert sein. Will man die Steuerung ausblenden, kann man das Attribut "suppressControlButtons" für Sonosplayer einzeln setzen setzen.
|
||||
#
|
||||
########################################################################################
|
||||
#
|
||||
@ -240,7 +229,7 @@ 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);
|
||||
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_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);
|
||||
|
||||
# Communication between the two "levels" of threads
|
||||
@ -254,7 +243,6 @@ my $SONOS_Thread :shared = -1;
|
||||
my $SONOS_Thread_IsAlive :shared = -1;
|
||||
my $SONOS_Thread_PlayerRestore :shared = -1;
|
||||
|
||||
my $SONOS_Thread_IsAlive_CheckerActive = 0;
|
||||
my %SONOS_Thread_IsAlive_Counter;
|
||||
my $SONOS_Thread_IsAlive_Counter_MaxMerci = 2;
|
||||
|
||||
@ -331,10 +319,10 @@ my $SONOS_mseclog = 0;
|
||||
if ($ARGV[2]) {
|
||||
$SONOS_mseclog = $ARGV[2];
|
||||
}
|
||||
my $SONOS_Client_LogfileName :shared = '-';
|
||||
my $SONOS_StartedOwnUPnPServer = 0;
|
||||
my $SONOS_Client_Selector;
|
||||
my %SONOS_Client_Data :shared = ();
|
||||
my $SONOS_Client_NormalQueueWorking :shared = 1;
|
||||
my $SONOS_Client_SendQueue = Thread::Queue->new();
|
||||
my $SONOS_Client_SendQueue_Suspend :shared = 0;
|
||||
|
||||
@ -374,7 +362,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 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 '.$readingFnAttributes;
|
||||
use strict;
|
||||
use warnings;
|
||||
};
|
||||
@ -997,11 +985,14 @@ sub SONOS_Attribute($$$@) {
|
||||
my ($mode, $devName, $attrName, $attrValue) = @_;
|
||||
|
||||
my $disableChange = 0;
|
||||
my @attrListToSubProcess = qw(getListsDirectlyToReadings);
|
||||
|
||||
if ($mode eq 'set') {
|
||||
if ($attrName eq 'verbose') {
|
||||
SONOS_DoWork('undef', 'setVerbose', $attrValue);
|
||||
} elsif ($attrName eq 'getListsDirectlyToReadings') {
|
||||
} elsif ($attrName eq 'SubProcessLogfileName') {
|
||||
SONOS_DoWork('undef', 'setLogfileName', $attrValue);
|
||||
} elsif (SONOS_isInList($attrName, @attrListToSubProcess)) {
|
||||
SONOS_DoWork('undef', 'setAttribute', $attrName, $attrValue);
|
||||
} elsif ($attrName eq 'disable') {
|
||||
if ($attrValue && AttrVal($devName, $attrName, 0) != 1) {
|
||||
@ -1060,7 +1051,7 @@ sub SONOS_StopSubProcess($) {
|
||||
# Den SubProzess beenden, wenn wir ihn selber gestartet haben
|
||||
if ($SONOS_StartedOwnUPnPServer) {
|
||||
# DevIo_OpenDev($hash, 1, undef);
|
||||
DevIo_SimpleWrite($hash, "shutdown\n", 0);
|
||||
DevIo_SimpleWrite($hash, "shutdown\n", 2);
|
||||
DevIo_CloseDev($hash);
|
||||
setReadingsVal($hash, "state", 'disabled', TimeNow());
|
||||
$hash->{STATE} = 'disabled';
|
||||
@ -1202,12 +1193,12 @@ sub SONOS_Read($) {
|
||||
}
|
||||
} elsif ($line =~ m/^ReadingsSingleUpdate:(.*?):(.*?):(.*)/) {
|
||||
if (lc($1) eq 'undef') {
|
||||
readingsSingleUpdate(SONOS_getSonosPlayerByName(), $2, $3, 1);
|
||||
SONOS_readingsSingleUpdate(SONOS_getSonosPlayerByName(), $2, $3, 1);
|
||||
} else {
|
||||
my $hash = SONOS_getSonosPlayerByUDN($1);
|
||||
|
||||
if ($hash) {
|
||||
readingsSingleUpdate($hash, $2, $3, 1);
|
||||
SONOS_readingsSingleUpdate($hash, $2, $3, 1);
|
||||
} else {
|
||||
SONOS_Log undef, 0, "Fehlerhafter Aufruf von ReadingsSingleUpdate: $1:$2:$3";
|
||||
}
|
||||
@ -1338,7 +1329,9 @@ sub SONOS_Read($) {
|
||||
# Wurden für das Device bereits Favoriten geladen? Dann raussuchen, ob gerade ein solcher abgespielt wird...
|
||||
$current{FavouriteName} = '';
|
||||
eval {
|
||||
my %favourites = %{eval(ReadingsVal($hash->{NAME}, 'Favourites', '{}'))};
|
||||
my $readingsValue = ReadingsVal($hash->{NAME}, 'Favourites', '');
|
||||
$readingsValue = '()' if (trim($readingsValue) eq '');
|
||||
my %favourites = %{eval($readingsValue)};
|
||||
while (my ($key, $value) = each (%favourites)) {
|
||||
if (defined($current{EnqueuedTransportURI}) && defined($value->{Ressource})) {
|
||||
if ($value->{Ressource} eq $current{EnqueuedTransportURI}) {
|
||||
@ -1354,7 +1347,9 @@ sub SONOS_Read($) {
|
||||
# Wurden für das Device bereits Playlisten geladen? Dann raussuchen, ob gerade eine solche abgespielt wird...
|
||||
$current{PlaylistName} = '';
|
||||
eval {
|
||||
my %playlists = %{eval(ReadingsVal($hash->{NAME}, 'Playlists', '{}'))};
|
||||
my $readingsValue = ReadingsVal($hash->{NAME}, 'Playlists', '');
|
||||
$readingsValue = '()' if (trim($readingsValue) eq '');
|
||||
my %playlists = %{eval($readingsValue)};
|
||||
while (my ($key, $value) = each (%playlists)) {
|
||||
if (defined($current{EnqueuedTransportURI}) && defined($value->{Ressource})) {
|
||||
if ($value->{Ressource} eq $current{EnqueuedTransportURI}) {
|
||||
@ -1370,7 +1365,9 @@ sub SONOS_Read($) {
|
||||
# Wurden für das Device bereits Radios geladen? Dann raussuchen, ob gerade ein solches abgespielt wird...
|
||||
$current{RadioName} = '';
|
||||
eval {
|
||||
my %radios = %{eval(ReadingsVal($hash->{NAME}, 'Radios', '{}'))};
|
||||
my $readingsValue = ReadingsVal($hash->{NAME}, 'Radios', '');
|
||||
$readingsValue = '()' if (trim($readingsValue) eq '');
|
||||
my %radios = %{eval($readingsValue)};
|
||||
while (my ($key, $value) = each (%radios)) {
|
||||
if (defined($current{EnqueuedTransportURI}) && defined($value->{Ressource})) {
|
||||
if ($value->{Ressource} eq $current{EnqueuedTransportURI}) {
|
||||
@ -1589,18 +1586,25 @@ sub SONOS_Read($) {
|
||||
my $currentValue;
|
||||
|
||||
my $srcURI = '';
|
||||
my $getLocalCoverArt = AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'getLocalCoverArt', 0);
|
||||
if (defined($tempURI) && $tempURI ne '') {
|
||||
if ($tempURI =~ m/getaa.*?x-sonos-spotify%3aspotify%3atrack%3a(.*)%3f/i) {
|
||||
my $infos = SONOS_getSpotifyCoverURL($1);
|
||||
if ($infos ne '') {
|
||||
$srcURI = $infos;
|
||||
|
||||
if ($getLocalCoverArt) {
|
||||
$currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.jpg';
|
||||
SONOS_Log undef, 4, "Transport-Event: Spotify-Bilder-Download: SONOS_DownloadReplaceIfChanged('$srcURI', '".$currentValue."');";
|
||||
}
|
||||
} else {
|
||||
$srcURI = $groundURL.$tempURI;
|
||||
|
||||
if ($getLocalCoverArt) {
|
||||
$currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.'.SONOS_ImageDownloadTypeExtension($groundURL.$tempURI);
|
||||
SONOS_Log undef, 4, "Transport-Event: Spotify-Bilder-Download failed. Use normal thumbnail: SONOS_DownloadReplaceIfChanged('$srcURI', '".$currentValue."');";
|
||||
}#http://192.168.0.47:1400/getaa?s=1&u=x-sonosapi-stream%3as84483%3fsid%3d254%26flags%3d32
|
||||
}
|
||||
}
|
||||
} elsif ($tempURI =~ m/getaa.*?x-sonosapi-stream%3a(.+?)%3f/i) {
|
||||
$srcURI = SONOS_GetRadioMediaMetadata($hash->{UDN}, $1);
|
||||
eval {
|
||||
@ -1609,25 +1613,37 @@ sub SONOS_Read($) {
|
||||
$srcURI = $groundURL.$tempURI;
|
||||
}
|
||||
};
|
||||
|
||||
if ($getLocalCoverArt) {
|
||||
$currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.png';
|
||||
SONOS_Log undef, 4, "Transport-Event: Radiocover-Download: SONOS_DownloadReplaceIfChanged('$srcURI', '".$currentValue."');";
|
||||
}
|
||||
} elsif ($tempURI =~ m/^\/fhem\/sonos\/cover\/(.*)/i) {
|
||||
$srcURI = $attr{global}{modpath}.'/FHEM/lib/UPnP/sonos_'.$1;
|
||||
|
||||
if ($getLocalCoverArt) {
|
||||
$currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.jpg';
|
||||
SONOS_Log undef, 4, "Transport-Event: Cover-Copy: SONOS_DownloadReplaceIfChanged('$srcURI', '".$currentValue."');";
|
||||
}
|
||||
} else {
|
||||
$srcURI = $groundURL.$tempURI;
|
||||
|
||||
if ($getLocalCoverArt) {
|
||||
$currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.'.SONOS_ImageDownloadTypeExtension($groundURL.$tempURI);
|
||||
SONOS_Log undef, 4, "Transport-Event: Bilder-Download: SONOS_DownloadReplaceIfChanged('$srcURI', '".$currentValue."');";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$srcURI = $attr{global}{modpath}.'/FHEM/lib/UPnP/sonos_empty.jpg';
|
||||
|
||||
if ($getLocalCoverArt) {
|
||||
$currentValue = $attr{global}{modpath}.'/www/images/default/SONOSPLAYER/'.$name.'_'.$nextName.'AlbumArt.png';
|
||||
SONOS_Log undef, 4, "Transport-Event: CoverArt konnte nicht gefunden werden. Verwende FHEM-Logo. Bilder-Download: SONOS_DownloadReplaceIfChanged('$srcURI', '".$currentValue."');";
|
||||
}
|
||||
}
|
||||
|
||||
my $filechanged = 0;
|
||||
if (AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'getLocalCoverArt', 0)) {
|
||||
if ($getLocalCoverArt) {
|
||||
mkpath($attr{global}{modpath}.'/www/images/default/SONOSPLAYER/');
|
||||
$filechanged = SONOS_DownloadReplaceIfChanged($srcURI, $currentValue);
|
||||
# Icons neu einlesen lassen, falls die Datei neu ist
|
||||
@ -1653,9 +1669,9 @@ sub SONOS_Read($) {
|
||||
}
|
||||
|
||||
# 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
|
||||
if (AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'getLocalCoverArt', 0)) {
|
||||
if ($getLocalCoverArt) {
|
||||
if ($filechanged) {
|
||||
readingsSingleUpdate($hash, $nextReading.'AlbumArtURI', $currentValue, 1);
|
||||
SONOS_readingsSingleUpdate($hash, $nextReading.'AlbumArtURI', $currentValue, 1);
|
||||
} else {
|
||||
SONOS_readingsSingleUpdateIfChanged($hash, $nextReading.'AlbumArtURI', $currentValue, 1);
|
||||
}
|
||||
@ -1669,9 +1685,9 @@ sub SONOS_Read($) {
|
||||
my @alarmIDs = split(/,/, $3);
|
||||
|
||||
if ($4) {
|
||||
readingsSingleUpdate($hash, 'AlarmList', $4, 0);
|
||||
SONOS_readingsSingleUpdate($hash, 'AlarmList', $4, 0);
|
||||
} else {
|
||||
readingsSingleUpdate($hash, 'AlarmList', '{}', 0);
|
||||
SONOS_readingsSingleUpdate($hash, 'AlarmList', '{}', 0);
|
||||
}
|
||||
SONOS_readingsSingleUpdateIfChanged($hash, 'AlarmListIDs', join(',', sort {$a <=> $b} @alarmIDs), 0);
|
||||
SONOS_readingsSingleUpdateIfChanged($hash, 'AlarmListVersion', $2, 1);
|
||||
@ -1685,7 +1701,7 @@ sub SONOS_Read($) {
|
||||
|
||||
if ($chash) {
|
||||
SONOS_Log undef, 4, "DoWorkAnswer arrived for ".$chash->{NAME}."->$2: '$3'";
|
||||
readingsSingleUpdate($chash, $2, $3, 1);
|
||||
SONOS_readingsSingleUpdate($chash, $2, $3, 1);
|
||||
} else {
|
||||
SONOS_Log undef, 0, "Fehlerhafter Aufruf von DoWorkAnswer: $1:$2:$3";
|
||||
}
|
||||
@ -1697,7 +1713,7 @@ sub SONOS_Read($) {
|
||||
}
|
||||
|
||||
# LastAnswer aktualisieren...
|
||||
readingsSingleUpdate(SONOS_getSonosPlayerByName(), 'LastProcessAnswer', SONOS_TimeNow(), 1);
|
||||
SONOS_readingsSingleUpdate(SONOS_getSonosPlayerByName(), 'LastProcessAnswer', SONOS_TimeNow(), 1);
|
||||
|
||||
# Checker aktivieren...
|
||||
InternalTimer(gettimeofday() + $hash->{INTERVAL}, 'SONOS_IsSubprocessAliveChecker', $hash, 0);
|
||||
@ -1816,7 +1832,7 @@ sub SONOS_InitClientProcessLater($) {
|
||||
|
||||
# Begrüßung weglesen...
|
||||
my $answer = DevIo_SimpleRead($hash);
|
||||
DevIo_SimpleWrite($hash, "Establish connection\r\n", 0);
|
||||
DevIo_SimpleWrite($hash, "Establish connection\r\n", 2);
|
||||
|
||||
# Verbindung aufbauen...
|
||||
InternalTimer(gettimeofday() + 1, 'SONOS_InitClientProcess', $hash, 0);
|
||||
@ -1844,9 +1860,9 @@ sub SONOS_InitClientProcess($) {
|
||||
}
|
||||
|
||||
# Grundsätzliche Informationen bzgl. der konfigurierten Player übertragen...
|
||||
my $setDataString = 'SetData:'.$hash->{NAME}.':'.AttrVal($hash->{NAME}, 'verbose', '3').':'.AttrVal($hash->{NAME}, 'pingType', $SONOS_DEFAULTPINGTYPE).':'.AttrVal($hash->{NAME}, 'usedonlyIPs', '').':'.AttrVal($hash->{NAME}, 'ignoredIPs', '').':'.AttrVal($hash->{NAME}, 'reusePort', 0).':'.join(',', @playername).':'.join(',', @playerudn);
|
||||
my $setDataString = 'SetData:'.$hash->{NAME}.':'.AttrVal($hash->{NAME}, 'verbose', '3').':'.AttrVal($hash->{NAME}, 'SubProcessLogfileName', '-').':'.AttrVal($hash->{NAME}, 'pingType', $SONOS_DEFAULTPINGTYPE).':'.AttrVal($hash->{NAME}, 'usedonlyIPs', '').':'.AttrVal($hash->{NAME}, 'ignoredIPs', '').':'.AttrVal($hash->{NAME}, 'reusePort', 0).':'.join(',', @playername).':'.join(',', @playerudn);
|
||||
SONOS_Log undef, 5, $setDataString;
|
||||
DevIo_SimpleWrite($hash, $setDataString."\n", 0);
|
||||
DevIo_SimpleWrite($hash, $setDataString."\n", 2);
|
||||
|
||||
# Gemeldete Attribute, Definitionen und Readings übertragen...
|
||||
foreach my $fhem_dev (sort keys %main::defs) {
|
||||
@ -1893,15 +1909,15 @@ sub SONOS_InitClientProcess($) {
|
||||
|
||||
# Übertragen...
|
||||
SONOS_Log undef, 5, 'SetValues:'.$dataName.':'.join('|', @values);
|
||||
DevIo_SimpleWrite($hash, 'SetValues:'.$dataName.':'.join('|', @values)."\n", 0);
|
||||
DevIo_SimpleWrite($hash, 'SetValues:'.$dataName.':'.join('|', @values)."\n", 2);
|
||||
}
|
||||
}
|
||||
|
||||
# Alle Informationen sind drüben, dann Threads dort drüben starten
|
||||
DevIo_SimpleWrite($hash, "StartThread\n", 0);
|
||||
DevIo_SimpleWrite($hash, "StartThread\n", 2);
|
||||
|
||||
# Interner Timer für die Überprüfung der Verbindung zum Client (nicht verwechseln mit dem IsAlive-Timer, der die Existenz eines Sonosplayers überprüft)
|
||||
readingsSingleUpdate($hash, 'LastProcessAnswer', '2100-01-01 00:00:00', 0);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastProcessAnswer', '2100-01-01 00:00:00', 0);
|
||||
InternalTimer(gettimeofday() + ($hash->{INTERVAL} * 2), 'SONOS_IsSubprocessAliveChecker', $hash, 0);
|
||||
|
||||
return undef;
|
||||
@ -1923,7 +1939,7 @@ sub SONOS_IsSubprocessAliveChecker() {
|
||||
SONOS_DoWork('undef', 'refreshProcessAnswer') if ($lastProcessAnswer < gettimeofday() - $hash->{INTERVAL});
|
||||
|
||||
# Wenn die letzte Antwort zu lange her ist, dann den SubProzess neustarten...
|
||||
if ($lastProcessAnswer < gettimeofday() - (3 * $hash->{INTERVAL})) {
|
||||
if ($lastProcessAnswer < gettimeofday() - (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: '.SONOS_GetTimeString($lastProcessAnswer).')... try to restart the process and connection...';
|
||||
|
||||
@ -1970,7 +1986,7 @@ sub SONOS_IsSubprocessAliveChecker() {
|
||||
#
|
||||
# # Verbindung beenden, damit der SubProzess die Chance hat neu initialisiert zu werden...
|
||||
# RemoveInternalTimer($hash);
|
||||
# DevIo_SimpleWrite($hash, "disconnect\n", 0);
|
||||
# DevIo_SimpleWrite($hash, "disconnect\n", 2);
|
||||
# DevIo_CloseDev($hash);
|
||||
#
|
||||
# # Neu anstarten...
|
||||
@ -2367,13 +2383,11 @@ sub SONOS_DoWork($$;@) {
|
||||
}
|
||||
}
|
||||
|
||||
my $hash = SONOS_getSonosPlayerByName();
|
||||
|
||||
while ($SONOS_Thread_IsAlive_CheckerActive) {
|
||||
select(undef, undef, undef, 0.1);
|
||||
}
|
||||
eval {
|
||||
DevIo_SimpleWrite($hash, 'DoWork:'.$udn.':'.$method.':'.encode_utf8(join('--#--', @params))."\r\n", 0);
|
||||
my $hash = SONOS_getSonosPlayerByName();
|
||||
if (defined($hash->{TCPDev}) && ($hash->{TCPDev}->connected())) {
|
||||
DevIo_SimpleWrite($hash, 'DoWork:'.$udn.':'.$method.':'.encode_utf8(join('--#--', @params))."\r\n", 2);
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 0, 'ERROR in DoWork: '.$@;
|
||||
@ -2414,9 +2428,73 @@ sub SONOS_Discover() {
|
||||
};
|
||||
|
||||
# Thread Signal Handler for doing some work in this thread 'environment'
|
||||
$SIG{'HUP'} = sub {
|
||||
#$SIG{'HUP'} = sub {
|
||||
# while ($SONOS_ComObjectTransportQueue->pending()) {
|
||||
# SONOS_Discover_DoQueue($SONOS_ComObjectTransportQueue->peek());
|
||||
# $SONOS_ComObjectTransportQueue->dequeue();
|
||||
# }
|
||||
#
|
||||
# return 1;
|
||||
#};
|
||||
|
||||
SONOS_LoadBookmarkValues();
|
||||
|
||||
my $error;
|
||||
do {
|
||||
$SONOS_RestartControlPoint = 0;
|
||||
|
||||
eval {
|
||||
$SONOS_Controlpoint = UPnP::ControlPoint->new(SearchPort => 0, SubscriptionPort => 0, SubscriptionURL => '/fhemmodule', MaxWait => 30, UsedOnlyIP => \%usedonlyIPs, IgnoreIP => \%ignoredIPs, ReusePort => $reusePort);
|
||||
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
|
||||
|
||||
#$SONOS_Controlpoint->handle;
|
||||
my @mysockets = $SONOS_Controlpoint->sockets();
|
||||
my $select = IO::Select->new(@mysockets);
|
||||
|
||||
while (!$SONOS_RestartControlPoint) {
|
||||
# UPnP-Sockets abfragen...
|
||||
my @sockets = $select->can_read(0.01);
|
||||
for my $sock (@sockets) {
|
||||
$SONOS_Controlpoint->handleOnce($sock);
|
||||
}
|
||||
|
||||
# Befehlsqueue abfragen...
|
||||
while ($SONOS_ComObjectTransportQueue->pending()) {
|
||||
my $data = $SONOS_ComObjectTransportQueue->peek();
|
||||
SONOS_Discover_DoQueue($SONOS_ComObjectTransportQueue->peek());
|
||||
$SONOS_ComObjectTransportQueue->dequeue();
|
||||
}
|
||||
}
|
||||
};
|
||||
$error = $@;
|
||||
|
||||
# Nur wenn es der Fehler mit der XML-Struktur ist, dann den UPnP-Handler nochmal anstarten...
|
||||
if (($error =~ m/multiple roots, wrong element '.*?'/si) || ($error =~ m/junk '.*?' after XML element/si) || ($error =~ m/mismatched tag '.*?'/si) || ($error =~ m/no element found/si) || ($error =~ m/500 Can't connect to/si) || ($error =~ m/not properly closed tag '.*?'/si) || ($error =~ m/Bad arg length for Socket::unpack_sockaddr_in/si)) {
|
||||
SONOS_Log undef, 2, "Error during UPnP-Handling, restarting handling: $error";
|
||||
SONOS_StopControlPoint();
|
||||
} else {
|
||||
SONOS_Log undef, 2, "Error during UPnP-Handling: $error";
|
||||
SONOS_StopControlPoint();
|
||||
|
||||
undef($error);
|
||||
}
|
||||
} while ($error || $SONOS_RestartControlPoint);
|
||||
|
||||
SONOS_SaveBookmarkValues();
|
||||
|
||||
SONOS_Log undef, 3, 'UPnP-Thread wurde beendet.';
|
||||
$SONOS_Thread = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_Discover_DoQueue - Do the working job (command from Fhem -> Sonosplayer)
|
||||
#
|
||||
########################################################################################
|
||||
sub SONOS_Discover_DoQueue($) {
|
||||
my ($data) = @_;
|
||||
|
||||
my $workType = $data->{WorkType};
|
||||
my $udn = $data->{UDN};
|
||||
my @params = ();
|
||||
@ -2426,6 +2504,8 @@ sub SONOS_Discover() {
|
||||
if ($workType eq 'setVerbose') {
|
||||
$SONOS_Client_LogLevel = $params[0];
|
||||
SONOS_Log undef, $SONOS_Client_LogLevel, "Setting LogLevel to new value: $SONOS_Client_LogLevel";
|
||||
} elsif ($workType eq 'setLogfileName') {
|
||||
$SONOS_Client_LogfileName = $params[0]
|
||||
} elsif ($workType eq 'refreshProcessAnswer') {
|
||||
SONOS_Client_Notifier('rePing:undef::');
|
||||
} elsif ($workType eq 'setAttribute') {
|
||||
@ -4369,44 +4449,6 @@ sub SONOS_Discover() {
|
||||
if ($@) {
|
||||
SONOS_MakeSigHandlerReturnValue($udn, 'LastActionResult', 'DoWork-Exception ERROR: '.$@);
|
||||
}
|
||||
|
||||
$SONOS_ComObjectTransportQueue->dequeue();
|
||||
}
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
SONOS_LoadBookmarkValues();
|
||||
|
||||
my $error;
|
||||
do {
|
||||
$SONOS_RestartControlPoint = 0;
|
||||
|
||||
eval {
|
||||
$SONOS_Controlpoint = UPnP::ControlPoint->new(SearchPort => 0, SubscriptionPort => 0, SubscriptionURL => '/fhemmodule', MaxWait => 30, UsedOnlyIP => \%usedonlyIPs, IgnoreIP => \%ignoredIPs, ReusePort => $reusePort);
|
||||
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
|
||||
$SONOS_Controlpoint->handle;
|
||||
};
|
||||
$error = $@;
|
||||
|
||||
# Nur wenn es der Fehler mit der XML-Struktur ist, dann den UPnP-Handler nochmal anstarten...
|
||||
if (($error =~ m/multiple roots, wrong element '.*?'/si) || ($error =~ m/junk '.*?' after XML element/si) || ($error =~ m/mismatched tag '.*?'/si) || ($error =~ m/no element found/si) || ($error =~ m/500 Can't connect to/si) || ($error =~ m/not properly closed tag '.*?'/si) || ($error =~ m/Bad arg length for Socket::unpack_sockaddr_in/si)) {
|
||||
SONOS_Log undef, 2, "Error during UPnP-Handling, restarting handling: $error";
|
||||
SONOS_StopControlPoint();
|
||||
} else {
|
||||
SONOS_Log undef, 2, "Error during UPnP-Handling: $error";
|
||||
SONOS_StopControlPoint();
|
||||
|
||||
undef($error);
|
||||
}
|
||||
} while ($error || $SONOS_RestartControlPoint);
|
||||
|
||||
SONOS_SaveBookmarkValues();
|
||||
|
||||
SONOS_Log undef, 3, 'UPnP-Thread wurde beendet.';
|
||||
$SONOS_Thread = -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
@ -5847,6 +5889,8 @@ sub SONOS_Utf8ToLatin1($) {
|
||||
sub SONOS_ConvertNumToWord($) {
|
||||
my ($var) = @_;
|
||||
|
||||
return 'off' if (!defined($var));
|
||||
|
||||
if (!looks_like_number($var)) {
|
||||
return 'on' if (lc($var) ne 'off');
|
||||
return 'off';
|
||||
@ -6292,54 +6336,75 @@ sub SONOS_Discover_Callback($$$) {
|
||||
|
||||
# ContentDirectory-Subscription
|
||||
if ($contentDirectoryService) {
|
||||
eval {
|
||||
$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;
|
||||
} else {
|
||||
SONOS_Log undef, 1, 'ContentDirectory-Service-subscribing NOT successful';
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 1, 'ContentDirectory-Service-subscribing NOT successful: '.$@;
|
||||
}
|
||||
} else {
|
||||
undef($SONOS_ContentDirectorySubscriptions{$udn});
|
||||
}
|
||||
|
||||
# Alarm-Subscription
|
||||
if ($alarmService && (SONOS_Client_Data_Retreive($udn, 'attr', 'getAlarms', 0) != 0)) {
|
||||
eval {
|
||||
$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;
|
||||
} else {
|
||||
SONOS_Log undef, 1, 'Alarm-Service-subscribing NOT successful';
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 1, 'Alarm-Service-Service-subscribing NOT successful: '.$@;
|
||||
}
|
||||
} else {
|
||||
undef($SONOS_AlarmSubscriptions{$udn});
|
||||
}
|
||||
|
||||
# ZoneGroupTopology-Subscription
|
||||
if ($zoneGroupTopologyService) {
|
||||
eval {
|
||||
$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;
|
||||
} else {
|
||||
SONOS_Log undef, 1, 'ZoneGroupTopology-Service-subscribing NOT successful';
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 1, 'ZoneGroupTopology-Service-subscribing NOT successful: '.$@;
|
||||
}
|
||||
} else {
|
||||
undef($SONOS_ZoneGroupTopologySubscriptions{$udn});
|
||||
}
|
||||
|
||||
# DeviceProperties-Subscription
|
||||
if ($devicePropertiesService) {
|
||||
eval {
|
||||
$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;
|
||||
} else {
|
||||
SONOS_Log undef, 1, 'DeviceProperties-Service-subscribing NOT successful';
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 1, 'DeviceProperties-Service-subscribing NOT successful: '.$@;
|
||||
}
|
||||
} else {
|
||||
undef($SONOS_DevicePropertiesSubscriptions{$udn});
|
||||
}
|
||||
|
||||
# AudioIn-Subscription
|
||||
if ($audioInService) {
|
||||
eval {
|
||||
$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;
|
||||
@ -6347,12 +6412,17 @@ sub SONOS_Discover_Callback($$$) {
|
||||
SONOS_Log undef, 1, 'AudioIn-Service-subscribing NOT successful';
|
||||
delete($SONOS_AudioInSubscriptions{$udn});
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 1, 'AudioIn-Service-Service-subscribing NOT successful: '.$@;
|
||||
}
|
||||
} else {
|
||||
undef($SONOS_AudioInSubscriptions{$udn});
|
||||
}
|
||||
|
||||
# MusicServices-Subscription
|
||||
if ($musicServicesService) {
|
||||
eval {
|
||||
$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;
|
||||
@ -6360,6 +6430,10 @@ sub SONOS_Discover_Callback($$$) {
|
||||
SONOS_Log undef, 1, 'MusicServices-Service-subscribing NOT successful';
|
||||
delete($SONOS_MusicServicesSubscriptions{$udn});
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
SONOS_Log undef, 1, 'MusicServices-Service-Service-subscribing NOT successful: '.$@;
|
||||
}
|
||||
} else {
|
||||
undef($SONOS_MusicServicesSubscriptions{$udn});
|
||||
}
|
||||
@ -6605,9 +6679,9 @@ sub SONOS_IsAlive($) {
|
||||
$SONOS_ComObjectTransportQueue->enqueue(\%data);
|
||||
|
||||
# Signalhandler aufrufen, wenn er nicht sowieso noch läuft...
|
||||
if (defined(threads->object($SONOS_Thread))) {
|
||||
threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
}
|
||||
#if (defined(threads->object($SONOS_Thread))) {
|
||||
# threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
#}
|
||||
}
|
||||
|
||||
return $result;
|
||||
@ -8860,13 +8934,12 @@ sub SONOS_AudioInCallback($$) {
|
||||
SONOS_Client_Notifier('ReadingsBeginUpdate:undef');
|
||||
|
||||
# LineInPlayer-Listen aktualisieren...
|
||||
my @lineInPlayer = grep { SONOS_Client_Data_Retreive(SONOS_Client_Data_Retreive('udn', 'udn', $_, $_), 'reading', 'LineInConnected', 0) } @{eval(SONOS_Client_Data_Retreive(undef, 'reading', 'AllPlayer', '[]'))};
|
||||
my @lineInPlayer = grep { SONOS_Client_Data_Retreive(SONOS_Client_Data_Retreive('udn', 'udn', $_, $_), 'reading', 'LineInConnected', 0) } @{eval(SONOS_Client_Data_Retreive('undef', 'reading', 'AllPlayer', '[]'))};
|
||||
SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', 'undef', 'LineInPlayer', SONOS_Dumper(\@lineInPlayer));
|
||||
|
||||
if (SONOS_Client_Data_Retreive('undef', 'attr', 'getListsDirectlyToReadings', 0)) {
|
||||
SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', 'undef', 'LineInPlayerList', (scalar(@lineInPlayer) ? '-|' : '').join('|', @lineInPlayer));
|
||||
SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', 'undef', 'LineInPlayerListAlias', (scalar(@lineInPlayer) ? '-
|
||||
Auswahl|' : '').join('|', map { my $udn = SONOS_Client_Data_Retreive('udn', 'udn', $_, $_); $_ = SONOS_Client_Data_Retreive($udn, 'reading', 'roomName', $udn).' ~ '.SONOS_Client_Data_Retreive($udn, 'reading', 'LineInName', $udn); $_ } @lineInPlayer));
|
||||
SONOS_Client_Data_Refresh('ReadingsBulkUpdateIfChanged', 'undef', 'LineInPlayerListAlias', (scalar(@lineInPlayer) ? 'Auswahl|' : '').join('|', map { my $udn = SONOS_Client_Data_Retreive('udn', 'udn', $_, $_); $_ = SONOS_Client_Data_Retreive($udn, 'reading', 'roomName', $udn).' ~ '.SONOS_Client_Data_Retreive($udn, 'reading', 'LineInName', $udn); $_ } @lineInPlayer));
|
||||
}
|
||||
|
||||
SONOS_Client_Notifier('ReadingsEndUpdate:undef');
|
||||
@ -8940,14 +9013,14 @@ sub SONOS_MusicServicesCallback($$) {
|
||||
my %musicServices = ();
|
||||
my $result = $response->getValue('AvailableServiceDescriptorList');
|
||||
SONOS_Log undef, 5, 'MusicService-Call: '.$result;
|
||||
while ($result =~ m/<Service.*?Id="(\d+)".*?Name="(.+?)".*?SecureUri="(.+?)".*?Capabilities="(\d+)".*?>.*?<Strings.*?Uri="(.+?)".*?\/>.*?<PresentationMap.*?Uri="(.+?)".*?\/>.*?<\/Service>/sgi) {
|
||||
while ($result =~ m/<Service.*?Id="(\d+)".*?Name="(.+?)".*?SecureUri="(.+?)".*?Capabilities="(\d+)".*?>.*?(<Strings.*?Uri="(.+?)".*?\/>|).*?<PresentationMap.*?Uri="(.+?)".*?\/>.*?<\/Service>/sgi) {
|
||||
my $id = $1;
|
||||
my $name = $2;
|
||||
my $smapi = $3;
|
||||
my $capabilities = $4;
|
||||
my $stringsURL = $5;
|
||||
my $stringsURL = $6;
|
||||
|
||||
my $presentationMap = $6;
|
||||
my $presentationMap = $7;
|
||||
|
||||
my $promoString = '';
|
||||
if (defined($stringsURL) && ($stringsURL ne '')) {
|
||||
@ -9532,13 +9605,34 @@ sub SONOS_readingsEndUpdate($$) {
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_readingsSingleUpdateIfChanged - Wrapper for readingsSingleUpdate. Do only things if value has changed.
|
||||
# SONOS_readingsSingleUpdate - Wrapper for readingsSingleUpdate.
|
||||
#
|
||||
########################################################################################
|
||||
sub SONOS_readingsSingleUpdate($$$$) {
|
||||
my ($hash, $readingName, $readingValue, $doTrigger) = @_;
|
||||
|
||||
if (defined($hash->{".updateTimestamp"})) {
|
||||
readingsBulkUpdate($hash, $readingName, $readingValue);
|
||||
} else {
|
||||
readingsSingleUpdate($hash, $readingName, $readingValue, $doTrigger);
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_readingsSingleUpdateIfChanged - Wrapper for readingsSingleUpdate. Do things only if value has changed.
|
||||
#
|
||||
########################################################################################
|
||||
sub SONOS_readingsSingleUpdateIfChanged($$$$) {
|
||||
my ($hash, $readingName, $readingValue, $doTrigger) = @_;
|
||||
|
||||
readingsSingleUpdate($hash, $readingName, $readingValue, $doTrigger) if ReadingsVal($hash->{NAME}, $readingName, '~~ReAlLyNoTeQuAlSmArKeR~~') ne $readingValue;
|
||||
if (ReadingsVal($hash->{NAME}, $readingName, '~~ReAlLyNoTeQuAlSmArKeR~~') ne $readingValue) {
|
||||
if (defined($hash->{".updateTimestamp"})) {
|
||||
readingsBulkUpdate($hash, $readingName, $readingValue);
|
||||
} else {
|
||||
readingsSingleUpdate($hash, $readingName, $readingValue, $doTrigger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
@ -9648,10 +9742,11 @@ sub SONOS_getSonosPlayerByRoomName($) {
|
||||
sub SONOS_Undef ($$) {
|
||||
my ($hash, $name) = @_;
|
||||
|
||||
# Alle Timer entfernen...
|
||||
RemoveInternalTimer($hash);
|
||||
|
||||
DevIo_SimpleWrite($hash, "disconnect\n", 0);
|
||||
DevIo_CloseDev($hash);
|
||||
# SubProzess beenden, und Verbindung kappen...
|
||||
SONOS_StopSubProcess($hash);
|
||||
|
||||
return undef;
|
||||
}
|
||||
@ -9671,15 +9766,6 @@ sub SONOS_Delete($$) {
|
||||
CommandDelete(undef, $player->{NAME});
|
||||
}
|
||||
|
||||
# Den SubProzess beenden
|
||||
# Wenn wir einen eigenen UPnP-Server gestartet haben, diesen hier auch wieder beenden, ansonsten nichts tun...
|
||||
if ($SONOS_StartedOwnUPnPServer) {
|
||||
# Da die Verbindung bereits durch UndefFn beendet wurde, muss sie hier neu aufgebaut werden, damit ich den Subprozess selbst beenden kann (vorher wurde nur die Verbindung beendet)...
|
||||
DevIo_OpenDev($hash, 1, undef);
|
||||
DevIo_SimpleWrite($hash, "shutdown\n", 0);
|
||||
DevIo_CloseDev($hash);
|
||||
}
|
||||
|
||||
# Etwas warten...
|
||||
select(undef, undef, undef, 1);
|
||||
|
||||
@ -9702,9 +9788,9 @@ sub SONOS_Shutdown ($$) {
|
||||
# Wenn wir einen eigenen UPnP-Server gestartet haben, diesen hier auch wieder beenden,
|
||||
# ansonsten nur die Verbindung kappen
|
||||
if ($SONOS_StartedOwnUPnPServer) {
|
||||
DevIo_SimpleWrite($hash, "shutdown\n", 0);
|
||||
DevIo_SimpleWrite($hash, "shutdown\n", 2);
|
||||
} else {
|
||||
DevIo_SimpleWrite($hash, "disconnect\n", 0);
|
||||
DevIo_SimpleWrite($hash, "disconnect\n", 2);
|
||||
}
|
||||
DevIo_CloseDev($hash);
|
||||
|
||||
@ -9864,7 +9950,13 @@ sub SONOS_Log($$$) {
|
||||
$tim .= sprintf(".%03d", $microseconds / 1000);
|
||||
}
|
||||
|
||||
if ($SONOS_Client_LogfileName eq '-') {
|
||||
print "$tim $level: SONOS".threads->tid().": $text\n";
|
||||
} else {
|
||||
open(my $fh, '>>', $SONOS_Client_LogfileName);
|
||||
print $fh "$tim $level: SONOS".threads->tid().": $text\n";
|
||||
close $fh;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $hash = SONOS_getSonosPlayerByUDN($udn);
|
||||
@ -9900,7 +9992,6 @@ if (defined($SONOS_ListenPort)) {
|
||||
|
||||
$SIG{'INT'} = sub {
|
||||
# Hauptschleife beenden
|
||||
$SONOS_Client_NormalQueueWorking = 0;
|
||||
$runEndlessLoop = 0;
|
||||
|
||||
# Sub-Threads beenden, sofern vorhanden
|
||||
@ -9941,8 +10032,6 @@ if (defined($SONOS_ListenPort)) {
|
||||
$SONOS_Client_Selector = IO::Select->new($sock);
|
||||
|
||||
while ($runEndlessLoop) {
|
||||
# NormalQueueWorking wird für die Dauer einer Direkt-Wert-Anfrage deaktiviert, damit hier nicht blockiert und/oder zuviel weggelesen wird.
|
||||
if ($SONOS_Client_NormalQueueWorking) {
|
||||
# Nachschauen, ob Subscriptions erneuert werden müssen
|
||||
if (time() - $lastRenewSubscriptionCheckTime > 1800) {
|
||||
$lastRenewSubscriptionCheckTime = time ();
|
||||
@ -9957,7 +10046,7 @@ if (defined($SONOS_ListenPort)) {
|
||||
$SONOS_ComObjectTransportQueue->enqueue(\%data);
|
||||
|
||||
# Signalhandler aufrufen, wenn er nicht sowieso noch läuft...
|
||||
threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
#threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -10020,10 +10109,6 @@ if (defined($SONOS_ListenPort)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
# Wenn die Verarbeitung gerade unterbrochen sein soll, dann hier etwas warten, um keine 100% CPU-Last zu erzeugen
|
||||
select(undef, undef, undef, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
SONOS_Log undef, 0, 'Das Lauschen auf der Schnittstelle wurde beendet. Prozess endet nun auch...';
|
||||
@ -10068,14 +10153,17 @@ sub SONOS_Client_Notifier($) {
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
# SONOS_Client_Data_Retreive: Retrieves stored data, and calls AskXX if necessary
|
||||
# SONOS_Client_Data_Retreive: Retrieves stored data.
|
||||
########################################################################################
|
||||
sub SONOS_Client_Data_Retreive($$$$) {
|
||||
my ($udn, $reading, $name, $default) = @_;
|
||||
sub SONOS_Client_Data_Retreive($$$$;$) {
|
||||
my ($udn, $reading, $name, $default, $nologging) = @_;
|
||||
$udn = '' if (!defined($udn));
|
||||
$nologging = 0 if (!defined($nologging));
|
||||
|
||||
my $udnBuffer = ($udn eq 'undef') ? 'SONOS' : $udn;
|
||||
|
||||
return $default if (!defined($SONOS_Client_Data{Buffer}));
|
||||
|
||||
# Prüfen, ob die Anforderung überhaupt bedient werden darf
|
||||
if ($reading eq 'attr') {
|
||||
if (SONOS_posInList($name, @SONOS_PossibleAttributes) == -1) {
|
||||
@ -10097,10 +10185,10 @@ sub SONOS_Client_Data_Retreive($$$$) {
|
||||
|
||||
# Anfrage zulässig, also ausliefern...
|
||||
if (defined($SONOS_Client_Data{Buffer}->{$udnBuffer}) && defined($SONOS_Client_Data{Buffer}->{$udnBuffer}->{$name})) {
|
||||
SONOS_Log undef, 4, "SONOS_Client_Data_Retreive($udnBuffer, $reading, $name, $default) -> ".$SONOS_Client_Data{Buffer}->{$udnBuffer}->{$name};
|
||||
SONOS_Log undef, 4, "SONOS_Client_Data_Retreive($udnBuffer, $reading, $name, $default) -> ".$SONOS_Client_Data{Buffer}->{$udnBuffer}->{$name} if (!$nologging);
|
||||
return $SONOS_Client_Data{Buffer}->{$udnBuffer}->{$name};
|
||||
} else {
|
||||
SONOS_Log undef, 4, "SONOS_Client_Data_Retreive($udnBuffer, $reading, $name, $default) -> DEFAULT";
|
||||
SONOS_Log undef, 4, "SONOS_Client_Data_Retreive($udnBuffer, $reading, $name, $default) -> DEFAULT" if (!$nologging);
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
@ -10174,29 +10262,30 @@ sub SONOS_Client_ConsumeMessage($$) {
|
||||
$SONOS_Client_Selector->remove($client);
|
||||
shutdown($client, 2);
|
||||
close($client);
|
||||
} elsif ($msg =~ m/SetData:(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*)/i) {
|
||||
} elsif ($msg =~ m/SetData:(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*?):(.*)/i) {
|
||||
$SONOS_Client_Data{SonosDeviceName} = $1;
|
||||
$SONOS_Client_LogLevel = $2;
|
||||
$SONOS_Client_Data{pingType} = $3;
|
||||
$SONOS_Client_LogfileName = $3;
|
||||
$SONOS_Client_Data{pingType} = $4;
|
||||
|
||||
my @usedonlyIPs = split(/,/, $4);
|
||||
my @usedonlyIPs = split(/,/, $5);
|
||||
$SONOS_Client_Data{usedonlyIPs} = shared_clone(\@usedonlyIPs);
|
||||
for my $elem (@usedonlyIPs) {
|
||||
$usedonlyIPs{SONOS_Trim($elem)} = 1;
|
||||
}
|
||||
|
||||
my @ignoredIPs = split(/,/, $5);
|
||||
my @ignoredIPs = split(/,/, $6);
|
||||
$SONOS_Client_Data{ignoredIPs} = shared_clone(\@ignoredIPs);
|
||||
for my $elem (@ignoredIPs) {
|
||||
$ignoredIPs{SONOS_Trim($elem)} = 1;
|
||||
}
|
||||
|
||||
$reusePort = $6;
|
||||
$reusePort = $7;
|
||||
|
||||
my @names = split(/,/, $7);
|
||||
my @names = split(/,/, $8);
|
||||
$SONOS_Client_Data{PlayerNames} = shared_clone(\@names);
|
||||
|
||||
my @udns = split(/,/, $8);
|
||||
my @udns = split(/,/, $9);
|
||||
$SONOS_Client_Data{PlayerUDNs} = shared_clone(\@udns);
|
||||
|
||||
my @playeralive = ();
|
||||
@ -10333,7 +10422,7 @@ sub SONOS_Client_ConsumeMessage($$) {
|
||||
# Auf die Queue legen wenn Thread läuft und Signalhandler aufrufen, wenn er nicht sowieso noch läuft...
|
||||
if ($SONOS_Thread != -1) {
|
||||
$SONOS_ComObjectTransportQueue->enqueue(\%data);
|
||||
threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
#threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
}
|
||||
} elsif (lc($msg) eq 'startthread') {
|
||||
# Discover-Thread
|
||||
@ -10440,7 +10529,7 @@ sub SONOS_Client_IsAlive() {
|
||||
$SONOS_ComObjectTransportQueue->enqueue(\%data);
|
||||
|
||||
# Signalhandler aufrufen, wenn er nicht sowieso noch läuft...
|
||||
threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
#threads->object($SONOS_Thread)->kill('HUP') if ($SONOS_ComObjectTransportQueue->pending() == 1);
|
||||
|
||||
# Da ich das nur an den ersten verfügbaren Player senden muss, kann hier die Schleife direkt beendet werden
|
||||
last;
|
||||
@ -10602,6 +10691,8 @@ The order in the sublists are important, because the first entry defines the so-
|
||||
</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>
|
||||
</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>
|
||||
</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.</li>
|
||||
<li><a name="SONOS_attribut_usedonlyIPs"><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>
|
||||
@ -10798,6 +10889,8 @@ Dabei ist die Reihenfolge innerhalb der Unterlisten wichtig, da der erste Eintra
|
||||
</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>
|
||||
</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>
|
||||
</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.</li>
|
||||
<li><a name="SONOS_attribut_usedonlyIPs"><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>
|
||||
|
@ -1,6 +1,6 @@
|
||||
########################################################################################
|
||||
#
|
||||
# SONOSPLAYER.pm (c) by Reiner Leins, Juni 2017
|
||||
# SONOSPLAYER.pm (c) by Reiner Leins, July 2017
|
||||
# rleins at lmsoft dot de
|
||||
#
|
||||
# $Id$
|
||||
@ -409,7 +409,7 @@ sub SONOSPLAYER_SimulateCurrentTrackPosition() {
|
||||
|
||||
readingsBeginUpdate($hash);
|
||||
|
||||
my $trackDurationSec = SONOS_GetTimeSeconds(ReadingsVal($hash->{NAME}, 'currentTrackDuration', 0)) - 1;
|
||||
my $trackDurationSec = SONOS_GetTimeSeconds(ReadingsVal($hash->{NAME}, 'currentTrackDuration', 0));
|
||||
|
||||
my $trackPositionSec = 0;
|
||||
if (ReadingsVal($hash->{NAME}, 'transportState', 'STOPPED') eq 'PLAYING') {
|
||||
@ -500,7 +500,7 @@ sub SONOSPLAYER_Get($@) {
|
||||
} elsif (lc($reading) eq 'ethernetportstatus') {
|
||||
my $portNum = $a[2];
|
||||
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'Portstatus properly returned', 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Portstatus properly returned', 1);
|
||||
|
||||
my $url = ReadingsVal($name, 'location', '');
|
||||
$url =~ s/(^http:\/\/.*?)\/.*/$1\/status\/enetports/;
|
||||
@ -516,7 +516,7 @@ sub SONOSPLAYER_Get($@) {
|
||||
} elsif (lc($reading) eq 'alarm') {
|
||||
my $id = $a[2];
|
||||
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'Alarm-Hash properly returned', 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Alarm-Hash properly returned', 1);
|
||||
|
||||
my @idList = split(',', ReadingsVal($name, 'AlarmListIDs', ''));
|
||||
if (!SONOS_isInList($id, @idList)) {
|
||||
@ -1011,7 +1011,7 @@ sub SONOSPLAYER_Set($@) {
|
||||
foreach my $dev (SONOS_getAllSonosplayerDevices()) {
|
||||
push(@sonosDevs, $dev->{NAME}) if ($dev->{NAME} ne $hash->{NAME});
|
||||
}
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'AddMember: Wrong Sonos-Devicename "'.$value.'". Use one of "'.join('", "', @sonosDevs).'"', 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'AddMember: Wrong Sonos-Devicename "'.$value.'". Use one of "'.join('", "', @sonosDevs).'"', 1);
|
||||
|
||||
return undef;
|
||||
}
|
||||
@ -1027,7 +1027,7 @@ sub SONOSPLAYER_Set($@) {
|
||||
foreach my $dev (SONOS_getAllSonosplayerDevices()) {
|
||||
push(@sonosDevs, $dev->{NAME}) if ($dev->{NAME} ne $hash->{NAME});
|
||||
}
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'RemoveMember: Wrong Sonos-Devicename "'.$value.'". Use one of "'.join('", "', @sonosDevs).'"', 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'RemoveMember: Wrong Sonos-Devicename "'.$value.'". Use one of "'.join('", "', @sonosDevs).'"', 1);
|
||||
|
||||
return undef;
|
||||
}
|
||||
@ -1063,7 +1063,7 @@ sub SONOSPLAYER_Set($@) {
|
||||
# Anweisung an den alten linken Lautsprecher absetzen
|
||||
SONOS_DoWork($udn, 'separateStereoPair', uri_escape($leftPlayerShort.':LF,LF;'.$rightPlayerShort.':RF,RF'));
|
||||
} elsif (lc($key) eq 'reboot') {
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'Reboot properly initiated', 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Reboot properly initiated', 1);
|
||||
|
||||
my $url = ReadingsVal($name, 'location', '');
|
||||
$url =~ s/(^http:\/\/.*?)\/.*/$1\/reboot/;
|
||||
@ -1072,12 +1072,12 @@ sub SONOSPLAYER_Set($@) {
|
||||
} elsif (lc($key) eq 'wifi') {
|
||||
$value = lc($value);
|
||||
if ($value ne 'on' && $value ne 'off' && $value ne 'persist-off') {
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'Wrong parameter "'.$value.'". Use one of "off", "persist-off" or "on".', 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'Wrong parameter "'.$value.'". Use one of "off", "persist-off" or "on".', 1);
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
readingsSingleUpdate($hash, 'LastActionResult', 'WiFi properly set to '.$value, 1);
|
||||
SONOS_readingsSingleUpdate($hash, 'LastActionResult', 'WiFi properly set to '.$value, 1);
|
||||
|
||||
my $url = ReadingsVal($name, 'location', '');
|
||||
$url =~ s/(^http:\/\/.*?)\/.*/$1\/wifictrl?wifi=$value/;
|
||||
@ -1239,6 +1239,7 @@ sub SONOSPLAYER_Delete($$) {
|
||||
SONOSPLAYER_DeleteIfExists($hash->{NAME}.'RG_Favourites');
|
||||
SONOSPLAYER_DeleteIfExists($hash->{NAME}.'RG_Playlists');
|
||||
SONOSPLAYER_DeleteIfExists($hash->{NAME}.'RG_Radios');
|
||||
SONOSPLAYER_DeleteIfExists($hash->{NAME}.'RG_Queue');
|
||||
|
||||
# Das Entfernen des Sonos-Devices selbst übernimmt Fhem
|
||||
return undef;
|
||||
|
@ -723,8 +723,8 @@ sub subscribe {
|
||||
my $ua = LWP::UserAgent->new(timeout => 20);
|
||||
my $response = $ua->request($request);
|
||||
|
||||
if ($response->is_success &&
|
||||
$response->code == 200) {
|
||||
if ($response->is_success) {
|
||||
if ($response->code == 200) {
|
||||
my $sid = $response->header('SID');
|
||||
$timeout = $response->header('Timeout');
|
||||
if ($timeout =~ /^Second-(\d+)$/) {
|
||||
@ -739,10 +739,11 @@ sub subscribe {
|
||||
EventSubURL => "$url");
|
||||
$cp->addSubscription($subscription);
|
||||
return $subscription;
|
||||
} else {
|
||||
carp("Subscription request successful but answered with error: " . $response->code . " " . $response->message);
|
||||
}
|
||||
else {
|
||||
carp("Subscription request failed with error: " .
|
||||
$response->code . " " . $response->message);
|
||||
} else {
|
||||
carp("Subscription request failed with error: " . $response->code . " " . $response->message);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user