2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-22 20:24:36 +00:00

SONOS: Change Subscriptions-Refresh, change Timestamp in LastProcessAnswer to epoch time

git-svn-id: https://svn.fhem.de/fhem/trunk@15672 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
Reinerlein 2017-12-23 12:38:40 +00:00
parent 66fac12975
commit ad6571f017
2 changed files with 88 additions and 299 deletions

View File

@ -1,6 +1,6 @@
########################################################################################
#
# SONOS.pm (c) by Reiner Leins, July 2017
# SONOS.pm (c) by Reiner Leins, December 2017
# rleins at lmsoft dot de
#
# $Id$
@ -51,6 +51,14 @@
# Changelog (last 4 entries only, see Wiki for complete changelog)
#
# SVN-History:
# 23.12.2017
# Subscriptions-Refresh umgebaut.
# Devicenamen mit Punkt (.) funktionieren nun.
# Fehler mit "undefined value $value" behoben.
# GetTrackProvider liefert bei Nichtfinden in der MusicServicesList nun eine leere Angabe, und keine undefined.
# Die Angabe in LastProcessAnswer ist nicht Zeitumstellungsfest. Der Wert wurde nun umgestellt auf epoch-Zeit.
# Der Verweis auf %intAt wurde entfernt. Die Variable wurde sowieso nie verwendet.
# Warnungsunterdrückung von 'mumpitzstuff' eingebaut.
# 28.09.2017
# Provider-Icons werden wieder korrekt ermittelt
# Das Verarbeiten der Arbeitsschlange im SubProcess wurde optimiert
@ -60,8 +68,6 @@
# Bei einem Modify wird von Fhem nur die DefFn aufgerufen (und nicht vorher UndefFn). Dadurch blieben Reste, die aber vor einer Definition aufgeräumt werden müssen. Resultat war eine 100%-CPU-Last.
# 09.07.2017
# BulkUpdate: Beginn und Ende sind nun sicher davor einen vom SubProzess gestarteten BulkUpdate vorzeitig zu beenden.
# 05.07.2017
# Neue Variante für das Ermitteln der laufenden Favoriten, Radios oder Playlists.
#
########################################################################################
#
@ -135,7 +141,7 @@ my $reusePort = 0;
########################################################
# Standards aus FHEM einbinden
########################################################
use vars qw{%attr %modules %defs %intAt %data};
use vars qw{%attr %modules %defs %data};
########################################################
@ -429,6 +435,9 @@ sub SONOS_getCoverTitleRG($;$$) {
my ($device, $width, $space) = @_;
$width = 500 if (!defined($width));
my $saveDevice = $device;
$saveDevice =~ s/[^a-zA-Z0-9]//g;
my $transportState = ReadingsVal($device, 'transportState', '');
my $presence = ReadingsVal($device, 'presence', 'disappeared');
$presence = 'disappeared' if ($presence =~ m/~~NotLoadedMarker~~/i);
@ -454,44 +463,44 @@ sub SONOS_getCoverTitleRG($;$$) {
$transportState = FW_makeImage('audio_pause', 'Paused', 'SONOS_Transportstate') if ($transportState eq 'PAUSED_PLAYBACK');
$transportState = FW_makeImage('audio_stop', 'Stopped', 'SONOS_Transportstate') if ($transportState eq 'STOPPED');
my $fullscreenDiv = '<style type="text/css">.SONOS_Transportstate { height: 0.8em; margin-top: -6px; margin-left: 2px; }</style><div id="cover_current'.$device.'" style="position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 10000; background-color: rgb(20,20,20);" onclick="document.getElementById(\'cover_current'.$device.'\').style.display = \'none\'; document.getElementById(\'global_fulldiv_'.$device.'\').innerHTML = \'\';"><div style="position: absolute; top: 10px; left: 5px; display: inline-block; height: 35px; width: 35px; background-image: url('.ReadingsVal($device, 'currentTrackProviderIconRoundURL', '').'); background-repeat: no-repeat; background-size: contain; background-position: center center;"></div><div style="width: 100%; top 5px; text-align: center; font-weight: bold; color: lightgray; font-size: 200%;">'.ReadingsVal($device, 'roomName', $device).$transportState.'</div><div style="position: relative; top: 8px; height: 86%; max-width: 100%; text-align: center;"><div style="display: inline-block; height: calc(100% - 70px); width: 100%; background-image: url('.((lc($presence) eq 'disappeared') ? '/'.AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'webname', 'fhem').'/sonos/cover/empty.jpg' : ReadingsVal($device, 'currentAlbumArtURL', '')).'); background-repeat: no-repeat; background-size: contain; background-position: center center;"/></div><div style="position: absolute; width: 100%; bottom: 8px; padding: 5px; text-align: center; font-weight: bold; color: lightgray; background-color: rgb(20,20,20); font-size: 120%;">'.((lc($presence) eq 'disappeared') ? 'Player disappeared' : ReadingsVal($device, 'infoSummarize1', '')).'</div><div id="hash_'.$device.'" style="display: none; color: white;">'.md5_hex(ReadingsVal($device, 'roomName', $device).ReadingsVal($device, 'infoSummarize2', '').ReadingsVal($device, 'currentTrackPosition', '').ReadingsVal($device, 'currentAlbumArtURL', '')).'</div>'.(($normalAudio) ? '<div id="prog_runtime_'.$device.'" style="display: none; color: white;">'.$currentRuntime.'</div><div id="prog_starttime_'.$device.'" style="display: none; color: white;">'.$currentStarttime.'</div><div id="prog_playing_'.$device.'" style="display: none; color: white;">'.$playing.'</div><div id="progress'.$device.'" style="position: absolute; bottom: 0px; width: 100%; height: 2px; border: 1px solid #000; overflow: hidden;"><div id="progressbar'.$device.'" style="width: '.(($currentPosition * 100) / $currentRuntime).'%; height: 2px; border-right: 1px solid #000000; background: #d65946;"></div></div>' : '').'</div>';
my $fullscreenDiv = '<style type="text/css">.SONOS_Transportstate { height: 0.8em; margin-top: -6px; margin-left: 2px; }</style><div id="cover_current'.$saveDevice.'" style="position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 10000; background-color: rgb(20,20,20);" onclick="document.getElementById(\'cover_current'.$saveDevice.'\').style.display = \'none\'; document.getElementById(\'global_fulldiv_'.$saveDevice.'\').innerHTML = \'\';"><div style="position: absolute; top: 10px; left: 5px; display: inline-block; height: 35px; width: 35px; background-image: url('.ReadingsVal($device, 'currentTrackProviderIconRoundURL', '').'); background-repeat: no-repeat; background-size: contain; background-position: center center;"></div><div style="width: 100%; top 5px; text-align: center; font-weight: bold; color: lightgray; font-size: 200%;">'.ReadingsVal($device, 'roomName', $device).$transportState.'</div><div style="position: relative; top: 8px; height: 86%; max-width: 100%; text-align: center;"><div style="display: inline-block; height: calc(100% - 70px); width: 100%; background-image: url('.((lc($presence) eq 'disappeared') ? '/'.AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'webname', 'fhem').'/sonos/cover/empty.jpg' : ReadingsVal($device, 'currentAlbumArtURL', '')).'); background-repeat: no-repeat; background-size: contain; background-position: center center;"/></div><div style="position: absolute; width: 100%; bottom: 8px; padding: 5px; text-align: center; font-weight: bold; color: lightgray; background-color: rgb(20,20,20); font-size: 120%;">'.((lc($presence) eq 'disappeared') ? 'Player disappeared' : ReadingsVal($device, 'infoSummarize1', '')).'</div><div id="hash_'.$saveDevice.'" style="display: none; color: white;">'.md5_hex(ReadingsVal($device, 'roomName', $device).ReadingsVal($device, 'infoSummarize2', '').ReadingsVal($device, 'currentTrackPosition', '').ReadingsVal($device, 'currentAlbumArtURL', '')).'</div>'.(($normalAudio) ? '<div id="prog_runtime_'.$saveDevice.'" style="display: none; color: white;">'.$currentRuntime.'</div><div id="prog_starttime_'.$saveDevice.'" style="display: none; color: white;">'.$currentStarttime.'</div><div id="prog_playing_'.$saveDevice.'" style="display: none; color: white;">'.$playing.'</div><div id="progress'.$saveDevice.'" style="position: absolute; bottom: 0px; width: 100%; height: 2px; border: 1px solid #000; overflow: hidden;"><div id="progressbar'.$saveDevice.'" style="width: '.(($currentPosition * 100) / $currentRuntime).'%; height: 2px; border-right: 1px solid #000000; background: #d65946;"></div></div>' : '').'</div>';
my $javascriptTimer = 'function refreshTime'.$device.'() {
var playing = document.getElementById("prog_playing_'.$device.'");
my $javascriptTimer = 'function refreshTime'.$saveDevice.'() {
var playing = document.getElementById("prog_playing_'.$saveDevice.'");
if (!playing || (playing && (playing.innerHTML == "0"))) {
return;
}
var runtime = document.getElementById("prog_runtime_'.$device.'");
var starttime = document.getElementById("prog_starttime_'.$device.'");
var runtime = document.getElementById("prog_runtime_'.$saveDevice.'");
var starttime = document.getElementById("prog_starttime_'.$saveDevice.'");
if (runtime && starttime) {
var now = new Date().getTime();
var percent = (Math.round(now / 10.0) - Math.round(starttime.innerHTML * 100.0)) / runtime.innerHTML;
document.getElementById("progressbar'.$device.'").style.width = percent + "%";
document.getElementById("progressbar'.$saveDevice.'").style.width = percent + "%";
setTimeout(refreshTime'.$device.', 100);
setTimeout(refreshTime'.$saveDevice.', 100);
}
}';
my $javascriptText = '<script type="text/javascript">
if (!document.getElementById("global_fulldiv_'.$device.'")) {
if (!document.getElementById("global_fulldiv_'.$saveDevice.'")) {
var newDiv = document.createElement("div");
newDiv.setAttribute("id", "global_fulldiv_'.$device.'");
newDiv.setAttribute("id", "global_fulldiv_'.$saveDevice.'");
document.body.appendChild(newDiv);
var newScript = document.createElement("script");
newScript.setAttribute("type", "text/javascript");
newScript.appendChild(document.createTextNode(\'function refreshFull'.$device.'() {
var fullDiv = document.getElementById("element_fulldiv_'.$device.'");
newScript.appendChild(document.createTextNode(\'function refreshFull'.$saveDevice.'() {
var fullDiv = document.getElementById("element_fulldiv_'.$saveDevice.'");
if (!fullDiv) {
return;
}
var elementHTML = decodeURIComponent(fullDiv.innerHTML);
var global = document.getElementById("global_fulldiv_'.$device.'");
var global = document.getElementById("global_fulldiv_'.$saveDevice.'");
var oldGlobal = global.innerHTML;
var hash = document.getElementById("hash_'.$device.'");
var hashMatch = /<div id="hash_'.$device.'".*?>(.+?)<.div>/i;
var hash = document.getElementById("hash_'.$saveDevice.'");
var hashMatch = /<div id="hash_'.$saveDevice.'".*?>(.+?)<.div>/i;
hashMatch.exec(elementHTML);
if ((oldGlobal != "") && (!hash || (hash.innerHTML != RegExp.$1))) {
@ -499,10 +508,10 @@ sub SONOS_getCoverTitleRG($;$$) {
}
if (oldGlobal != "") {
setTimeout(refreshFull'.$device.', 1000);
var playing = document.getElementById("prog_playing_'.$device.'");
setTimeout(refreshFull'.$saveDevice.', 1000);
var playing = document.getElementById("prog_playing_'.$saveDevice.'");
if (playing && playing.innerHTML == "1") {
setTimeout(refreshTime'.$device.', 100);
setTimeout(refreshTime'.$saveDevice.', 100);
}
}
} '.$javascriptTimer.'\'));
@ -512,7 +521,7 @@ sub SONOS_getCoverTitleRG($;$$) {
</script>';
$javascriptText =~ s/\n/ /g;
return $javascriptText.'<table cellpadding="0" cellspacing="0" style="padding: 0px; margin: 0px;"><tr><td valign="top" style="padding: 0px; margin: 0px;"><div style="" onclick="document.getElementById(\'global_fulldiv_'.$device.'\').innerHTML = \'&nbsp;\'; refreshFull'.$device.'(); '.($playing ? 'refreshTime'.$device.'();' : '').'">'.SONOS_getCoverRG($device).'</div><div style="display: none;" id="element_fulldiv_'.$device.'">'.SONOS_URI_Escape($fullscreenDiv).'</div></td><td valign="top" style="padding: 0px; margin: 0px;"><div style="margin-left: 0px; min-width: '.$width.'px;">'.SONOS_getTitleRG($device, $space).'</div></td></tr></table>';
return $javascriptText.'<table cellpadding="0" cellspacing="0" style="padding: 0px; margin: 0px;"><tr><td valign="top" style="padding: 0px; margin: 0px;"><div style="" onclick="document.getElementById(\'global_fulldiv_'.$saveDevice.'\').innerHTML = \'&nbsp;\'; refreshFull'.$saveDevice.'(); '.($playing ? 'refreshTime'.$saveDevice.'();' : '').'">'.SONOS_getCoverRG($device).'</div><div style="display: none;" id="element_fulldiv_'.$saveDevice.'">'.SONOS_URI_Escape($fullscreenDiv).'</div></td><td valign="top" style="padding: 0px; margin: 0px;"><div style="margin-left: 0px; min-width: '.$width.'px;">'.SONOS_getTitleRG($device, $space).'</div></td></tr></table>';
}
########################################################################################
@ -527,7 +536,7 @@ sub SONOS_getCoverRG($;$) {
my $presence = ReadingsVal($device, 'presence', 'disappeared');
$presence = 'disappeared' if ($presence =~ m/~~NotLoadedMarker~~/i);
return '<div informid="'.$device.'-display_coverrg"><div style="display: inline-block; margin-right: 5px; border: 1px solid lightgray; height: '.$height.'; width: '.$height.'; background-image: url('.((lc($presence) eq 'disappeared') ? '/'.AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'webname', 'fhem').'/sonos/cover/empty.jpg' : ReadingsVal($device, 'currentAlbumArtURL', '')).'); background-repeat: no-repeat; background-size: contain; background-position: center center;"><div style="position: relative; top: 0px; left: 2px; display: inline-block; height: 15px; width: 15px; background-image: url('.ReadingsVal($device, 'currentTrackProviderIconRoundURL', '').'); background-repeat: no-repeat; background-size: contain; background-position: center center;"></div></div></div>';
return '<div informid="'.$device.'-display_covertitle"><div style="display: inline-block; margin-right: 5px; border: 1px solid lightgray; height: '.$height.'; width: '.$height.'; background-image: url('.((lc($presence) eq 'disappeared') ? '/'.AttrVal(SONOS_getSonosPlayerByName()->{NAME}, 'webname', 'fhem').'/sonos/cover/empty.jpg' : ReadingsVal($device, 'currentAlbumArtURL', '')).'); background-repeat: no-repeat; background-size: contain; background-position: center center;"><div style="position: relative; top: 0px; left: 2px; display: inline-block; height: 15px; width: 15px; background-image: url('.ReadingsVal($device, 'currentTrackProviderIconRoundURL', '').'); background-repeat: no-repeat; background-size: contain; background-position: center center;"></div></div></div>';
}
########################################################################################
@ -1708,7 +1717,7 @@ sub SONOS_Read($) {
}
# LastAnswer aktualisieren...
SONOS_readingsSingleUpdate(SONOS_getSonosPlayerByName(), 'LastProcessAnswer', SONOS_TimeNow(), 1);
SONOS_readingsSingleUpdate(SONOS_getSonosPlayerByName(), 'LastProcessAnswer', time(), 1);
# Checker aktivieren...
InternalTimer(gettimeofday() + $hash->{INTERVAL}, 'SONOS_IsSubprocessAliveChecker', $hash, 0);
@ -1912,7 +1921,7 @@ sub SONOS_InitClientProcess($) {
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)
SONOS_readingsSingleUpdate($hash, 'LastProcessAnswer', '2100-01-01 00:00:00', 0);
SONOS_readingsSingleUpdate($hash, 'LastProcessAnswer', '0', 0);
InternalTimer(gettimeofday() + ($hash->{INTERVAL} * 2), 'SONOS_IsSubprocessAliveChecker', $hash, 0);
return undef;
@ -1928,15 +1937,15 @@ sub SONOS_IsSubprocessAliveChecker() {
return undef if (AttrVal($hash->{NAME}, 'disable', 0));
my $lastProcessAnswer = SONOS_GetTimeFromString(ReadingsVal(SONOS_getSonosPlayerByName()->{NAME}, 'LastProcessAnswer', '2100-01-01 00:00:00'));
my $lastProcessAnswer = ReadingsVal(SONOS_getSonosPlayerByName()->{NAME}, 'LastProcessAnswer', '0');
# Wenn länger nichts passiert ist, dann eine Aktualisierung anfordern...
SONOS_DoWork('undef', 'refreshProcessAnswer') if ($lastProcessAnswer < gettimeofday() - $hash->{INTERVAL});
SONOS_DoWork('undef', 'refreshProcessAnswer') if ($lastProcessAnswer < time() - $hash->{INTERVAL});
# Wenn die letzte Antwort zu lange her ist, dann den SubProzess neustarten...
if ($lastProcessAnswer < gettimeofday() - (4 * $hash->{INTERVAL})) {
if ($lastProcessAnswer < time() - (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...';
SONOS_Log $hash->{UDN}, 2, 'LastProcessAnswer way too old (Lastanswer: '.$lastProcessAnswer.' ~ '.SONOS_GetTimeString($lastProcessAnswer).')... try to restart the process and connection...';
# Letzten Zeitpunkt und Anzahl der Neustarts merken...
my $sHash = SONOS_getSonosPlayerByName();
@ -1956,42 +1965,6 @@ sub SONOS_IsSubprocessAliveChecker() {
RemoveInternalTimer($hash, 'SONOS_IsSubprocessAliveChecker');
InternalTimer(gettimeofday() + $hash->{INTERVAL}, 'SONOS_IsSubprocessAliveChecker', $hash, 0);
}
#my $answer;
## Neue Verbindung parallel zur bestehenden Kommunikationsleitung.
## Nur zum Prüfen, ob der SubProzess noch lebt und antworten kann.
#my $socket = new IO::Socket::INET(PeerAddr => $hash->{DeviceName}, Proto => 'tcp');
#if ($socket) {
# $socket->sockopt(SO_LINGER, pack("ii", 1, 0));
#
# $socket->recv($answer, 500);
# $socket->send("Test\r\n", 0);
#
# $socket->shutdown(2);
# $socket->close();
#}
## Ab hier keine Parallelverbindung mehr offen...
#
#if (defined($answer)) {
# $answer =~ s/[\r\n]//g;
#}
#
#if (!defined($answer) || ($answer !~ m/^This is UPnP-Server listening for commands/)) {
# SONOS_Log undef, 0, 'No (or incorrect) answer from Subprocess. Restart Sonos-Subprocess...';
#
# # Verbindung beenden, damit der SubProzess die Chance hat neu initialisiert zu werden...
# RemoveInternalTimer($hash);
# DevIo_SimpleWrite($hash, "disconnect\n", 2);
# DevIo_CloseDev($hash);
#
# # Neu anstarten...
# SONOS_StartClientProcessIfNeccessary($hash->{DeviceName}) if ($SONOS_StartedOwnUPnPServer);
# InternalTimer(gettimeofday() + $hash->{WAITTIME}, 'SONOS_DelayOpenDev', $hash, 0);
#} elsif (defined($answer) && ($answer =~ m/^This is UPnP-Server listening for commands/)) {
# SONOS_Log undef, 4, 'Got correct answer from Subprocess...';
# RemoveInternalTimer($hash, 'SONOS_IsSubprocessAliveChecker');
# InternalTimer(gettimeofday() + $hash->{INTERVAL}, 'SONOS_IsSubprocessAliveChecker', $hash, 0);
#}
}
########################################################################################
@ -2285,18 +2258,6 @@ sub SONOS_Set($@) {
}
}
}
# Jetzt noch die Mengen sicherstellen
# Dazu aktuellen Zustand nochmal holen
#@current = ();
#$current = SONOS_Get($hash, qw($hash->{NAME} Groups));
#$current =~ s/ //g;
#while ($current =~ m/(\[.*?\])/ig) {
# my @tmp = split(/,/, substr($1, 1, -1));
# push @current, \@tmp;
#}
#SONOS_Log undef, 5, "Current after List: ".Dumper(\@current);
} elsif (lc($key) =~ m/^(Stop|Pause|Mute|MuteOn|MuteOff)(All|)$/i) {
my $commandType = lc($1);
my $commandValue = $value;
@ -4043,222 +4004,15 @@ sub SONOS_Discover_DoQueue($) {
SONOS_MakeSigHandlerReturnValue($udn, 'LastActionResult', ucfirst($workType).': '.SONOS_AnswerMessage(1));
} elsif ($workType eq 'renewSubscription') {
if (defined($SONOS_TransportSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_TransportSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_TransportSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'Transport-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! Transport-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_RenderingSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_RenderingSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_RenderingSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'Rendering-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! Rendering-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_GroupRenderingSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_GroupRenderingSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_GroupRenderingSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'GroupRendering-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! GroupRendering-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_ContentDirectorySubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_ContentDirectorySubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_ContentDirectorySubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'ContentDirectory-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! ContentDirectory-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_AlarmSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_AlarmSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_AlarmSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'Alarm-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! Alarm-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_ZoneGroupTopologySubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_ZoneGroupTopologySubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_ZoneGroupTopologySubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'ZoneGroupTopology-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! ZoneGroupTopology-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_DevicePropertiesSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_DevicePropertiesSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_DevicePropertiesSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'DeviceProperties-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! DeviceProperties-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_AudioInSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_AudioInSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_AudioInSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'AudioIn-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! AudioIn-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
if (defined($SONOS_MusicServicesSubscriptions{$udn}) && (Time::HiRes::time() - $SONOS_MusicServicesSubscriptions{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SONOS_MusicServicesSubscriptions{$udn}->renew();
SONOS_Log $udn, 3, 'MusicServices-Subscription for ZonePlayer "'.$udn.'" has expired and is now renewed.';
};
if ($@) {
SONOS_Log $udn, 3, 'Error! MusicServices-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...
if ($@ =~ m/Can.t connect to/) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
SONOS_ProcessRenew($udn, 'Transport', \%SONOS_TransportSubscriptions);
SONOS_ProcessRenew($udn, 'Rendering', \%SONOS_RenderingSubscriptions);
SONOS_ProcessRenew($udn, 'GroupRendering', \%SONOS_GroupRenderingSubscriptions);
SONOS_ProcessRenew($udn, 'ContentDirectory', \%SONOS_ContentDirectorySubscriptions);
SONOS_ProcessRenew($udn, 'Alarm', \%SONOS_AlarmSubscriptions);
SONOS_ProcessRenew($udn, 'ZoneGroupTopology', \%SONOS_ZoneGroupTopologySubscriptions);
SONOS_ProcessRenew($udn, 'DeviceProperties', \%SONOS_DevicePropertiesSubscriptions);
SONOS_ProcessRenew($udn, 'AudioIn', \%SONOS_AudioInSubscriptions);
SONOS_ProcessRenew($udn, 'MusicServices', \%SONOS_MusicServicesSubscriptions);
} elsif ($workType eq 'startHandle') {
if ($params[0] =~ m/^(.+)\|(.+)$/) {
my $songURI = $1;
@ -4417,6 +4171,41 @@ sub SONOS_Discover_DoQueue($) {
}
}
########################################################################################
#
# SONOS_ProcessRenew - Process the renewal of sbscriptions
#
########################################################################################
sub SONOS_ProcessRenew($$$) {
my ($udn, $subscriptionName, $subscriptionHash) = @_;
if (defined($subscriptionHash->{$udn}) && (Time::HiRes::time() - $subscriptionHash->{$udn}->{_startTime} > $SONOS_SUBSCRIPTIONSRENEWAL)) {
eval {
$SIG{__WARN__} = sub { $_ = shift; };
$subscriptionHash->{$udn}->renew();
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: '.$@;
# Wenn der Player nicht erreichbar war, dann entsprechend entfernen...
# Hier aber nur eine kleine Lösung, da es nur ein Notbehelf sein soll...
if ($@ =~ m/Can.t connect to/i) {
SONOS_DeleteProxyObjects($udn);
# Player-Informationen aktualisieren...
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
# Discovery neu anstarten, falls der Player irgendwie doch noch erreichbar sein sollte...
$SONOS_Search = $SONOS_Controlpoint->searchByType('urn:schemas-upnp-org:device:ZonePlayer:1', \&SONOS_Discover_Callback);
}
}
}
}
########################################################################################
#
# SONOS_GetBrowseStructuredResult - Browse and give the result back
@ -5467,6 +5256,8 @@ sub SONOS_GetTrackProvider($;$) {
return ($result, $roundIcon, $quadraticIcon);
}
}
return ('', '', '');
};
if ($@) {
SONOS_Log undef, 2, 'Unable to identify TrackProvider for "'.$songURI.'". Revert to empty default! Errormessage: '.$@;
@ -6623,8 +6414,6 @@ sub SONOS_IsAlive($) {
$result = 0;
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'presence', 'disappeared');
# Brauchen wir das wirklich? Dabei werden die lokalen Infos nicht aktualisiert...
#SONOS_Client_Notifier('deleteCurrentNextTitleInformationAndDisappear:'.$udn);
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'state', 'disappeared');
SONOS_Client_Data_Refresh('ReadingsSingleUpdateIfChanged', $udn, 'transportState', 'STOPPED');
$doDeleteProxyObjects = 1;
@ -10211,11 +10000,11 @@ sub SONOS_Client_Data_Refresh($$$$) {
my $udnBuffer = ($udn eq 'undef') ? 'SONOS' : $udn;
SONOS_Log undef, 4, "SONOS_Client_Data_Refresh(".(defined($sendCommand) ? $sendCommand : 'undef').", $udn, $name, $value)";
SONOS_Log undef, 4, 'SONOS_Client_Data_Refresh('.(defined($sendCommand) ? $sendCommand : 'undef').", $udn, $name, ".(defined($value) ? $value : 'undef').')';
$SONOS_Client_Data{Buffer}->{$udnBuffer}->{$name} = $value;
if (defined($sendCommand) && ($sendCommand ne '')) {
SONOS_Client_Notifier($sendCommand.':'.$udn.':'.$name.':'.$value);
SONOS_Client_Notifier($sendCommand.':'.$udn.':'.$name.':'.(defined($value) ? $value : 'undef'));
}
}

View File

@ -1,6 +1,6 @@
########################################################################################
#
# SONOSPLAYER.pm (c) by Reiner Leins, July 2017
# SONOSPLAYER.pm (c) by Reiner Leins, December 2017
# rleins at lmsoft dot de
#
# $Id$
@ -258,7 +258,7 @@ sub SONOSPLAYER_Detail($$$;$) {
return '' if (!ReadingsVal($d, 'IsMaster', 0) || (ReadingsVal($d, 'playerType', '') eq 'ZB100'));
# Open incl. Inform-Div
my $html .= '<html><div informId="'.$d.'-display_covertitle">';
my $html .= '<html><div informid="'.$d.'-display_covertitle">';
# Cover-/TitleView
$html .= '<div style="border: 1px solid gray; border-radius: 10px; padding: 5px;">';