mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-05-04 22:19:38 +00:00
Sonos: Fix some bugs and adds attribute disable to Sonos.
git-svn-id: https://svn.fhem.de/fhem/trunk@7575 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
d74e7ab2d8
commit
7722eb5a18
@ -7,9 +7,10 @@
|
||||
#
|
||||
# FHEM module to commmunicate with a Sonos-System via UPnP
|
||||
#
|
||||
# !WARNING!
|
||||
########################################################################################
|
||||
# !ATTENTION!
|
||||
# This Module needs additional Perl-Libraries.
|
||||
# Installation of:
|
||||
# Install:
|
||||
# * LWP::Simple
|
||||
# * LWP::UserAgent
|
||||
# * HTTP::Request
|
||||
@ -19,19 +20,42 @@
|
||||
# * LWP::Simple-Packagename (incl. LWP::UserAgent and HTTP::Request): libwww-perl
|
||||
# * SOAP::Lite-Packagename: libsoap-lite-perl
|
||||
#
|
||||
# e.g. as Windows ActivePerl (via Perl-Packagemanager)
|
||||
# * Install Package LWP (incl. LWP::UserAgent and HTTP::Request)
|
||||
# * Install Package SOAP::Lite
|
||||
# * SOAP::Lite-Special for Versions after 5.18:
|
||||
# * Add another Packagesource from suggestions or manual: Bribes de Perl (http://www.bribes.org/perl/ppm)
|
||||
# * Install Package: SOAP::Lite
|
||||
#
|
||||
# define <name> SONOS <host:port> [[[interval] waittime] delaytime]
|
||||
# Windows ActivePerl 64Bit is not functioning due to missing SOAP::Lite
|
||||
#
|
||||
# where <name> may be replaced by any name string
|
||||
########################################################################################
|
||||
# Configuration:
|
||||
# define <name> SONOS <host:port> [interval [waittime [delaytime]]]
|
||||
#
|
||||
# where <name> may be replaced by any fhem-devicename string
|
||||
# <host:port> is the connection identifier to the internal server. Normally "localhost" with a locally free port e.g. "localhost:4711".
|
||||
# interval is the interval in s, for checking the existence of a ZonePlayer after definition
|
||||
# interval is the interval in s, for checking the existence of a ZonePlayer
|
||||
# waittime is the time to wait for the subprocess. defaults to 8.
|
||||
# delaytime is the Time for delaying the network- and subprocess-part of this module. If the port is longer than neccessary blocked on the subprocess-side, it may be useful.
|
||||
#
|
||||
##############################################
|
||||
# Example:
|
||||
# define Sonos SONOS localhost:4711 30
|
||||
#
|
||||
########################################################################################
|
||||
# Changelog
|
||||
#
|
||||
# SVN-History:
|
||||
# 15.01.2015
|
||||
# Beim Anlegen der neuen Devices werden die Aliasnamen nun mit der Funktion im Team erweitert
|
||||
# Der Mechanismus zum Starten des SubProzesses wurde angepasst, um auf Synology-Begebenheiten Rücksicht zu nehmen
|
||||
# Die Coverdarstellung für einige Spotify-Titel wurde korrigiert, indem eine andere Spotify-API verwendet wird
|
||||
# Bei Playlist-Covern wird nun das Cover des ersten Titels mit AlbumArt angezeigt
|
||||
# Bei Favourite-Covern werden nun Album-Favoriten auch mit Cover dargestellt (das Cover des ersten Titels mit AlbumArt)
|
||||
# Ein Album aus der lokalen Bibliothek konnte mittels "StartFavourite" nicht korrekt gestartet werden (es wurde nicht als Liste übertragen, sondern als Titel gestartet)
|
||||
# LogLevel für die "Connection accepted"-Meldungen auf 3 hochgesetzt
|
||||
# Es gibt jetzt ein Attribut "disable" am Sonos-Device. Wird es auf 1 gesetzt, wird der SubProzess beendet und verarbeitet somit keine Sonos-Nachrichten mehr. Wird es auf 0 gesetzt (oder gelöscht), wird der SubProzess wieder gestartet.
|
||||
# 08.01.2015
|
||||
# Bei der Wiedergabeanweisung "PlayURI" gab es einen Fehler
|
||||
# 05.01.2105
|
||||
@ -443,7 +467,7 @@ sub SONOS_Initialize ($) {
|
||||
$hash->{GetFn} = 'SONOS_Get';
|
||||
$hash->{SetFn} = 'SONOS_Set';
|
||||
$hash->{AttrFn} = 'SONOS_Attribute';
|
||||
$hash->{NotifyFn} = 'SONOS_Notify';
|
||||
# $hash->{NotifyFn} = 'SONOS_Notify';
|
||||
|
||||
# CGI
|
||||
my $name = "sonos";
|
||||
@ -455,7 +479,7 @@ sub SONOS_Initialize ($) {
|
||||
eval {
|
||||
no strict;
|
||||
no warnings;
|
||||
$hash->{AttrList}= 'pingType:'.join(',', @SONOS_PINGTYPELIST).' targetSpeakDir targetSpeakURL targetSpeakFileTimestamp:0,1 targetSpeakFileHashCache:0,1 Speak1 Speak2 Speak3 Speak4 SpeakCover Speak1Cover Speak2Cover Speak3Cover Speak4Cover generateProxyAlbumArtURLs:0,1 proxyCacheTime proxyCacheDir characterDecoding '.$readingFnAttributes;
|
||||
$hash->{AttrList}= 'disable:1,0 pingType:'.join(',', @SONOS_PINGTYPELIST).' targetSpeakDir targetSpeakURL targetSpeakFileTimestamp:1,0 targetSpeakFileHashCache:1,0 Speak1 Speak2 Speak3 Speak4 SpeakCover Speak1Cover Speak2Cover Speak3Cover Speak4Cover generateProxyAlbumArtURLs:1,0 proxyCacheTime proxyCacheDir characterDecoding '.$readingFnAttributes;
|
||||
use strict;
|
||||
use warnings;
|
||||
};
|
||||
@ -707,13 +731,15 @@ sub SONOS_getGroupsRG() {
|
||||
sub SONOS_FhemWebCallback($) {
|
||||
my ($URL) = @_;
|
||||
|
||||
SONOS_Log undef, 5, 'FhemWebCallback: '.$URL;
|
||||
|
||||
# Einfache Grundprüfungen
|
||||
return ("text/html; charset=UTF8", 'Fehlerhafte Anfrage: '.$URL) if ($URL !~ m/^\/sonos\//i);
|
||||
return ("text/html; charset=UTF8", 'Forbidden call: '.$URL) if ($URL !~ m/^\/sonos\//i);
|
||||
$URL =~ s/^\/sonos//i;
|
||||
|
||||
# Proxy-Features...
|
||||
if ($URL =~ m/^\/proxy\//i) {
|
||||
return ("text/html; charset=UTF8", 'Proxybetrieb nicht aktiviert: '.$URL) if (!AttrVal(SONOS_getDeviceDefHash(undef)->{NAME}, 'generateProxyAlbumArtURLs', 0));
|
||||
return ("text/html; charset=UTF8", 'No Proxy configured: '.$URL) if (!AttrVal(SONOS_getDeviceDefHash(undef)->{NAME}, 'generateProxyAlbumArtURLs', 0));
|
||||
|
||||
my $proxyCacheTime = AttrVal(SONOS_getDeviceDefHash(undef)->{NAME}, 'proxyCacheTime', 0);
|
||||
my $proxyCacheDir = AttrVal(SONOS_getDeviceDefHash(undef)->{NAME}, 'proxyCacheDir', '/tmp');
|
||||
@ -721,6 +747,7 @@ sub SONOS_FhemWebCallback($) {
|
||||
|
||||
# Zurückzugebende Adresse ermitteln...
|
||||
my $albumurl = uri_unescape($1) if ($URL =~ m/^\/proxy\/aa\?url=(.*)/i);
|
||||
$albumurl =~ s/'/'/ig;
|
||||
|
||||
# Nur für Sonos-Player den Proxy spielen (und für Spotify-Links)
|
||||
my $ip = '';
|
||||
@ -731,7 +758,7 @@ sub SONOS_FhemWebCallback($) {
|
||||
last;
|
||||
}
|
||||
}
|
||||
return ("text/html; charset=UTF8", 'Anfrage für Nicht-Sonos-Player: '.$URL) if (defined($ip) && $albumurl !~ /\/original\//i && $albumurl !~ /\/music\/image\?/i);
|
||||
return ("text/html; charset=UTF8", 'Call for No-Sonos-Player: '.$URL) if (defined($ip) && $albumurl !~ /\.cloudfront.net\//i && $albumurl !~ /\.scdn.co\/image\//i && $albumurl !~ /\/music\/image\?/i);
|
||||
|
||||
# Generierter Dateiname für die Cache-Funktionalitaet
|
||||
my $albumHash;
|
||||
@ -778,6 +805,11 @@ sub SONOS_FhemWebCallback($) {
|
||||
$albumHash =~ m/(.*)\/(.*)\.(.*)/;
|
||||
FW_serveSpecial($2, $3, $1, 1);
|
||||
|
||||
return (undef, undef);
|
||||
} else {
|
||||
SONOS_Log undef, 1, 'Cover couldn\'t be loaded: '.$albumurl;
|
||||
|
||||
FW_serveSpecial('sonos_empty', 'jpg', $attr{global}{modpath}.'/FHEM/lib/UPnP', 1);
|
||||
return (undef, undef);
|
||||
}
|
||||
}
|
||||
@ -786,6 +818,8 @@ sub SONOS_FhemWebCallback($) {
|
||||
if ($URL =~ m/^\/cover\//i) {
|
||||
$URL =~ s/^\/cover//i;
|
||||
|
||||
SONOS_Log undef, 5, 'Cover: '.$URL;
|
||||
|
||||
if ($URL =~ m/^\/empty.jpg/i) {
|
||||
FW_serveSpecial('sonos_empty', 'jpg', $attr{global}{modpath}.'/FHEM/lib/UPnP', 1);
|
||||
return (undef, undef);
|
||||
@ -813,7 +847,7 @@ sub SONOS_FhemWebCallback($) {
|
||||
}
|
||||
|
||||
# Wenn wir hier ankommen, dann konnte nichts verarbeitet werden...
|
||||
return ("text/html; charset=UTF8", 'Fehlerhafte Anfrage: '.$URL);
|
||||
return ("text/html; charset=UTF8", 'Call failure: '.$URL);
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
@ -871,10 +905,12 @@ sub SONOS_Define($$) {
|
||||
$hash->{DELAYTIME} = $delaytime;
|
||||
$hash->{STATE} = 'waiting for subprocess...';
|
||||
|
||||
if (AttrVal($hash->{NAME}, 'disable', 0) == 0) {
|
||||
if ($hash->{DELAYTIME}) {
|
||||
InternalTimer(gettimeofday() + $hash->{DELAYTIME}, 'SONOS_DelayStart', $hash, 0);
|
||||
} else {
|
||||
SONOS_DelayStart($hash);
|
||||
InternalTimer(gettimeofday() + 1, 'SONOS_DelayStart', $hash, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
@ -888,6 +924,8 @@ sub SONOS_Define($$) {
|
||||
sub SONOS_DelayStart($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
return undef if (AttrVal($hash->{NAME}, 'disable', 0));
|
||||
|
||||
# Prüfen, ob ein Server erreichbar wäre, und wenn nicht, einen Server starten
|
||||
SONOS_StartClientProcessIfNeccessary($hash->{DeviceName});
|
||||
|
||||
@ -914,15 +952,76 @@ sub SONOS_DelayOpenDev($) {
|
||||
sub SONOS_Attribute($$$@) {
|
||||
my ($mode, $devName, $attrName, $attrValue) = @_;
|
||||
|
||||
my $disableChange = 0;
|
||||
|
||||
if ($mode eq 'set') {
|
||||
if ($attrName eq 'verbose') {
|
||||
SONOS_DoWork('undef', 'setVerbose', $attrValue);
|
||||
} elsif ($attrName eq 'disable') {
|
||||
if ($attrValue && AttrVal($devName, $attrName, 0) != 1) {
|
||||
SONOS_Log(undef, 5, 'Neu-Disabled');
|
||||
$disableChange = 1;
|
||||
}
|
||||
|
||||
if (!$attrValue && AttrVal($devName, $attrName, 0) != 0) {
|
||||
SONOS_Log(undef, 5, 'Neu-Enabled');
|
||||
$disableChange = 1;
|
||||
}
|
||||
}
|
||||
} elsif ($mode eq 'del') {
|
||||
if ($attrName eq 'disable') {
|
||||
if (AttrVal($devName, $attrName, 0) != 0) {
|
||||
SONOS_Log(undef, 5, 'Deleted-Disabled');
|
||||
$disableChange = 1;
|
||||
$attrValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($disableChange) {
|
||||
my $hash = SONOS_getDeviceDefHash(undef);
|
||||
|
||||
# Wenn der Prozess beendet werden muss...
|
||||
if ($attrValue) {
|
||||
SONOS_Log undef, 5, 'Call AttributeFn: Stop SubProcess...';
|
||||
|
||||
InternalTimer(gettimeofday() + 1, 'SONOS_StopSubProcess', $hash, 0);
|
||||
}
|
||||
|
||||
# Wenn der Prozess gestartet werden muss...
|
||||
if (!$attrValue) {
|
||||
SONOS_Log undef, 5, 'Call AttributeFn: Start SubProcess...';
|
||||
|
||||
InternalTimer(gettimeofday() + 1, 'SONOS_DelayStart', $hash, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_StopSubProcess - Tries to stop the subprocess
|
||||
#
|
||||
########################################################################################
|
||||
sub SONOS_StopSubProcess($) {
|
||||
my ($hash) = @_;
|
||||
|
||||
# Den SubProzess beenden, wenn wir ihn selber gestartet haben
|
||||
if ($SONOS_StartedOwnUPnPServer) {
|
||||
# DevIo_OpenDev($hash, 1, undef);
|
||||
DevIo_SimpleWrite($hash, "shutdown\n", 0);
|
||||
DevIo_CloseDev($hash);
|
||||
setReadingsVal($hash, "state", 'disabled', TimeNow());
|
||||
$hash->{STATE} = 'disabled';
|
||||
|
||||
# Alle SonosPlayer-Devices disappearen
|
||||
for my $player (SONOS_getAllSonosplayerDevices()) {
|
||||
SONOS_readingsSingleUpdateIfChanged($player, 'presence', 'disappeared', 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_Notify - Implements NotifyFn function
|
||||
@ -932,24 +1031,6 @@ sub SONOS_Notify() {
|
||||
my ($hash, $notifyhash) = @_;
|
||||
|
||||
return undef;
|
||||
|
||||
#if (($notifyhash->{NAME} eq 'global') && ($notifyhash->{CHANGED}[0] eq 'REREADCFG')) {
|
||||
# SONOS_Log undef, 0, 'Detecting rereadcfg. Restart Sonos-Subprocess...';
|
||||
#
|
||||
# # Vorab warten, da im Hintergrund der Thread noch läuft und auch erstmal fertig werden muss...
|
||||
# select(undef, undef, undef, $hash->{WAITTIME});
|
||||
#
|
||||
# # Verbindung beenden, damit der SubProzess die Chance hat neu initialisiert zu werden...
|
||||
# RemoveInternalTimer($hash);
|
||||
# DevIo_SimpleWrite($hash, "disconnect\n", 0);
|
||||
# DevIo_CloseDev($hash);
|
||||
#
|
||||
# # Neu anstarten...
|
||||
# SONOS_StartClientProcessIfNeccessary($hash->{DeviceName}) if ($SONOS_StartedOwnUPnPServer);
|
||||
# InternalTimer(gettimeofday() + $hash->{WAITTIME}, 'SONOS_DelayOpenDev', $hash, 0);
|
||||
#}
|
||||
#
|
||||
#return undef;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
@ -980,6 +1061,7 @@ sub SONOS_Read($) {
|
||||
|
||||
# Wenn hier gar nichts gekommen ist, dann diesen Aufruf beenden...
|
||||
if (!defined($buf) || ($buf eq '')) {
|
||||
if (!AttrVal($hash->{NAME}, 'disable', 0)) {
|
||||
SONOS_Log undef, 1, 'Nothing could be read from TCP-Channel (the first level) even though the Read-Function was called.';
|
||||
|
||||
# Verbindung beenden, damit der SubProzess die Chance hat neu initialisiert zu werden...
|
||||
@ -990,6 +1072,7 @@ sub SONOS_Read($) {
|
||||
# Neu anstarten...
|
||||
SONOS_StartClientProcessIfNeccessary($hash->{DeviceName}) if ($SONOS_StartedOwnUPnPServer);
|
||||
InternalTimer(gettimeofday() + $hash->{WAITTIME}, 'SONOS_DelayOpenDev', $hash, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1000,6 +1083,7 @@ sub SONOS_Read($) {
|
||||
|
||||
# Wenn hier gar nichts gekommen ist, dann diesen Aufruf beenden...
|
||||
if (!defined($newRead) || ($newRead eq '')) {
|
||||
if (!AttrVal($hash->{NAME}, 'disable', 0)) {
|
||||
SONOS_Log undef, 1, 'Nothing could be read from TCP-Channel (the second level) even though the Read-Function was called. The client is now directed to shutdown and the connection should be re-initialized...';
|
||||
|
||||
# Verbindung beenden, damit der SubProzess die Chance hat neu initialisiert zu werden...
|
||||
@ -1010,6 +1094,7 @@ sub SONOS_Read($) {
|
||||
# Neu anstarten...
|
||||
SONOS_StartClientProcessIfNeccessary($hash->{DeviceName}) if ($SONOS_StartedOwnUPnPServer);
|
||||
InternalTimer(gettimeofday() + $hash->{WAITTIME}, 'SONOS_DelayOpenDev', $hash, 0);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1292,12 +1377,10 @@ sub SONOS_Read($) {
|
||||
|
||||
my $srcURI = '';
|
||||
if (defined($tempURI) && $tempURI ne '') {
|
||||
if ($tempURI =~ m/getaa.*?x-sonos-spotify.*?(spotify.*)%3f/i) {
|
||||
my $infos = get('https://embed.spotify.com/oembed/?url='.$1);
|
||||
|
||||
if ($infos =~ m/"thumbnail_url":"(.*?)cover(.*?)"/i) {
|
||||
$srcURI = $1.'original'.$2;
|
||||
$srcURI =~ s/\\//g;
|
||||
if ($tempURI =~ m/getaa.*?x-sonos-spotify%3aspotify%3atrack%3a(.*)%3f/i) {
|
||||
my $infos = SONOS_getSpotifyCoverURL($1);
|
||||
if ($infos ne '') {
|
||||
$srcURI = $infos;
|
||||
$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 {
|
||||
@ -1473,7 +1556,8 @@ sub SONOS_StartClientProcessIfNeccessary($) {
|
||||
my $verboselevel = AttrVal(SONOS_getDeviceDefHash(undef)->{NAME}, 'verbose', $attr{global}{verbose});
|
||||
|
||||
# Prozess anstarten...
|
||||
exec('perl '.substr($0, 0, -7).'FHEM/00_SONOS.pm '.$port.' '.$verboselevel.' '.(($attr{global}{mseclog}) ? '1' : '0'));
|
||||
# exec('perl '.substr($0, 0, -7).'FHEM/00_SONOS.pm '.$port.' '.$verboselevel.' '.(($attr{global}{mseclog}) ? '1' : '0'));
|
||||
exec("$^X $attr{global}{modpath}/FHEM/00_SONOS.pm $port $verboselevel ".(($attr{global}{mseclog}) ? '1' : '0'));
|
||||
exit(0);
|
||||
}
|
||||
} else {
|
||||
@ -1603,6 +1687,8 @@ sub SONOS_InitClientProcess($) {
|
||||
sub SONOS_IsSubprocessAliveChecker() {
|
||||
my ($hash) = @_;
|
||||
|
||||
return undef if (AttrVal($hash->{NAME}, 'disable', 0));
|
||||
|
||||
my $answer;
|
||||
my $socket = new IO::Socket::INET(PeerAddr => $hash->{DeviceName}, Proto => 'tcp');
|
||||
if ($socket) {
|
||||
@ -2454,7 +2540,7 @@ sub SONOS_Discover() {
|
||||
|
||||
if (SONOS_CheckProxyObject($udn, $SONOS_AVTransportControlProxy{$udn})) {
|
||||
# Entscheiden, ob eine Abspielliste geladen und gestartet werden soll, oder etwas direkt abgespielt werden kann
|
||||
if ($resultHash{$favouriteName}{METADATA} =~ m/<upnp:class>object.container.playlistContainer<\/upnp:class>/i) {
|
||||
if ($resultHash{$favouriteName}{METADATA} =~ m/<upnp:class>object.container.(playlistContainer|album.musicAlbum)<\/upnp:class>/i) {
|
||||
|
||||
SONOS_Log $udn, 5, 'StartFavourite AddToQueue-Res: "'.$resultHash{$favouriteName}{RES}.'", -Meta: "'.$resultHash{$favouriteName}{METADATA}.'"';
|
||||
|
||||
@ -3069,15 +3155,31 @@ sub SONOS_Discover() {
|
||||
sub SONOS_MakeCoverURL($$) {
|
||||
my ($udn, $resURL) = @_;
|
||||
|
||||
if ($resURL =~ m/^(x-rincon-cpcontainer|x-sonos-spotify).*?(spotify.*?)(\?|$)/i) {
|
||||
my $infos = get('https://embed.spotify.com/oembed/?url='.$2);
|
||||
SONOS_Log $udn, 5, 'MakeCoverURL-Before: '.$resURL;
|
||||
|
||||
if ($infos =~ m/"thumbnail_url":"(.*?)cover(.*?)"/i) {
|
||||
$resURL = $1.'original'.$2;
|
||||
$resURL =~ s/\\//g;
|
||||
}
|
||||
} elsif($resURL =~ m/savedqueues/i) {
|
||||
if ($resURL =~ m/^x-rincon-cpcontainer.*?(spotify.*?)(\?|$)/i) {
|
||||
$resURL = SONOS_getSpotifyCoverURL($1, 1);
|
||||
} elsif ($resURL =~ m/^x-sonos-spotify:spotify%3atrack%3a(.*?)(\?|$)/i) {
|
||||
$resURL = SONOS_getSpotifyCoverURL($1);
|
||||
} elsif (($resURL =~ m/x-rincon-playlist:.*?#(.*)/i) || ($resURL =~ m/savedqueues.rsq(#\d+)/i)) {
|
||||
my $search = $1;
|
||||
$search = 'SQ:'.$1 if ($search =~ m/#(\d+)/i);
|
||||
|
||||
# Default, if nothing could be retreived...
|
||||
$resURL = '/fhem/sonos/cover/playlist.jpg';
|
||||
|
||||
if (SONOS_CheckProxyObject($udn, $SONOS_ContentDirectoryControlProxy{$udn})) {
|
||||
my $result = $SONOS_ContentDirectoryControlProxy{$udn}->Browse($search, 'BrowseDirectChildren', '', 0, 5, '');
|
||||
if ($result) {
|
||||
my $tmp = $result->getValue('Result');
|
||||
|
||||
if (defined($tmp) && $tmp =~ m/<item id=".+?".*?>.*?<upnp:albumArtURI>(.*?)<\/upnp:albumArtURI>.*?<\/item>/i) {
|
||||
$resURL = $1;
|
||||
|
||||
$resURL = $1.$resURL if (SONOS_Client_Data_Retreive($udn, 'reading', 'location', '') =~ m/^(http:\/\/.*?:.*?)\//i);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
my $stream = 0;
|
||||
$stream = 1 if ($resURL =~ /x-sonosapi-stream/);
|
||||
@ -3087,9 +3189,40 @@ sub SONOS_MakeCoverURL($$) {
|
||||
# Alles über Fhem als Proxy laufen lassen?
|
||||
$resURL = '/fhem/sonos/proxy/aa?url='.uri_escape($resURL) if (($resURL !~ m/^\//) && SONOS_Client_Data_Retreive('undef', 'attr', 'generateProxyAlbumArtURLs', 0));
|
||||
|
||||
SONOS_Log $udn, 5, 'MakeCoverURL-After: '.$resURL;
|
||||
|
||||
return $resURL;
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_getSpotifyCoverURL - Generates the approbriate cover-url for Spotify-Cover
|
||||
#
|
||||
########################################################################################
|
||||
sub SONOS_getSpotifyCoverURL($;$) {
|
||||
my ($trackID, $oldStyle) = @_;
|
||||
$oldStyle = 0 if (!defined($oldStyle));
|
||||
|
||||
my $infos = '';
|
||||
if ($oldStyle) {
|
||||
$infos = $1 if (get('https://embed.spotify.com/oembed/?url='.$trackID) =~ m/"thumbnail_url":"(.*?)"/i);
|
||||
} else {
|
||||
$infos = $1 if (get('https://api.spotify.com/v1/tracks/'.$trackID) =~ m/"images".*?:.*?\[.*?{.*?"height".*?:.*?640,.*?"url".*?:.*?"(.*?)",.*?"width"/is);
|
||||
}
|
||||
|
||||
$infos =~ s/\\//g;
|
||||
$infos = $1.'original'.$3 if ($infos =~ m/(.*?\/)(cover|default)(\/.*)/i);
|
||||
|
||||
# Falls es ein Standardcover von Spotify geben soll, lieber das Thumbnail von Sonos verwenden...
|
||||
return '' if ($infos =~ m/\/static\/img\/defaultCoverL.png/i);
|
||||
|
||||
if ($infos ne '') {
|
||||
return $infos;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
########################################################################################
|
||||
#
|
||||
# SONOS_GetSpeakFileExtension - Retrieves the desired fileextension
|
||||
@ -4065,13 +4198,24 @@ sub SONOS_Discover_Callback($$$) {
|
||||
|
||||
$master = !$invisible || $isZoneBridge;
|
||||
}
|
||||
}
|
||||
|
||||
# Für den Aliasnamen schöne Bezeichnungen ermitteln...
|
||||
my $aliasSuffix = '';
|
||||
$aliasSuffix = ' - Hinten Links' if ($topoType eq '_LR');
|
||||
$aliasSuffix = ' - Hinten Rechts' if ($topoType eq '_RR');
|
||||
$aliasSuffix = ' - Links' if ($topoType eq '_LF');
|
||||
$aliasSuffix = ' - Rechts' if ($topoType eq '_RF');
|
||||
$aliasSuffix = ' - Subwoofer' if ($topoType eq '_SW');
|
||||
$aliasSuffix = ' - Mitte' if ($topoType eq '_LF_RF');
|
||||
|
||||
# Wenn der aktuelle Player der Master ist, dann kein Kürzel anhängen,
|
||||
# damit gibt es immer einen Player, der den Raumnamen trägt, und die anderen enthalten Kürzel
|
||||
if ($master) {
|
||||
$topoType = '';
|
||||
}
|
||||
}
|
||||
|
||||
# Raumnamen erweitern
|
||||
$name .= $topoType;
|
||||
$saveRoomName .= $topoType;
|
||||
|
||||
@ -4124,7 +4268,7 @@ sub SONOS_Discover_Callback($$$) {
|
||||
# Define SonosPlayer-Device with attributes
|
||||
SONOS_Client_Notifier('CommandDefine:'.$name.' SONOSPLAYER '.$udn);
|
||||
SONOS_Client_Notifier('CommandAttr:'.$name.' room '.$SONOS_Client_Data{SonosDeviceName});
|
||||
SONOS_Client_Notifier('CommandAttr:'.$name.' alias '.$roomName);
|
||||
SONOS_Client_Notifier('CommandAttr:'.$name.' alias '.$roomName.$aliasSuffix);
|
||||
SONOS_Client_Notifier('CommandAttr:'.$name.' group '.$groupName);
|
||||
SONOS_Client_Notifier('CommandAttr:'.$name.' icon '.$iconPath);
|
||||
SONOS_Client_Notifier('CommandAttr:'.$name.' sortby 1');
|
||||
@ -6174,7 +6318,7 @@ if (defined($SONOS_ListenPort)) {
|
||||
my ($port, $iaddr) = sockaddr_in($addrinfo);
|
||||
my $name = gethostbyaddr($iaddr, AF_INET);
|
||||
|
||||
SONOS_Log undef, 1, "Connection accepted from $name:$port";
|
||||
SONOS_Log undef, 3, "Connection accepted from $name:$port";
|
||||
|
||||
# Von dort kommt die Anfrage, dort finde ich den Telnet-Port von Fhem :-)
|
||||
$SONOS_UseTelnetForQuestions_Host = $name;
|
||||
@ -6663,7 +6807,7 @@ You can start this client on your own (to let it run instantly and independent f
|
||||
<br />
|
||||
<a name="SONOSdefine"></a>
|
||||
<h4>Define</h4>
|
||||
<code>define <name> SONOS [upnplistener] [[[interval] waittime] delaytime]</code>
|
||||
<code>define <name> SONOS [upnplistener [interval [waittime [delaytime]]]]</code>
|
||||
<br /><br /> Define a Sonos interface to communicate with a Sonos-System.<br />
|
||||
<p>
|
||||
<code>[upnplistener]</code><br />The name and port of the external upnp-listener. If not given, defaults to <code>localhost:4711</code>. The port has to be a free portnumber on your system. If you don't start a server on your own, the script does itself.<br />If you start it yourself write down the correct informations to connect.</p>
|
||||
@ -6712,10 +6856,13 @@ The order in the sublists are important, because the first entry defines the so-
|
||||
<br />
|
||||
<a name="SONOSattr"></a>
|
||||
<h4>Attributes</h4>
|
||||
'''Attention'''<br />The most of the attributes can only be used after a restart of fhem, because it must be initially transfered to the subprocess.
|
||||
<ul>
|
||||
<li><b>Common</b><ul>
|
||||
<li><a name="SONOS_attribut_characterDecoding"><code>attr <name> characterDecoding <codingname></code>
|
||||
</a><br />With this attribute you can define a character-decoding-class. E.g. <UTF-8>. Default is <CP-1252>.</li>
|
||||
<li><a name="SONOS_attribut_disable"><code>attr <name> disable <value></code>
|
||||
</a><br />One of (0,1). With this value you can disable the whole module. Works immediatly. If set to 1 the subprocess will be terminated and no message will be transmitted. If set to 0 the subprocess is again startet.<br />It is useful when you install new Sonos-Components and don't want any disgusting devices during the Sonos setup.</li>
|
||||
<li><a name="SONOS_attribut_pingType"><code>attr <name> pingType <string></code>
|
||||
</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>
|
||||
</ul></li>
|
||||
@ -6788,7 +6935,7 @@ Man kann den Server unabhängig von FHEM selbst starten (um ihn dauerhaft und un
|
||||
<br />
|
||||
<a name="SONOSdefine"></a>
|
||||
<h4>Definition</h4>
|
||||
<code>define <name> SONOS [upnplistener] [[[interval] waittime] delaytime]</code>
|
||||
<code>define <name> SONOS [upnplistener [interval [waittime [delaytime]]]]</code>
|
||||
<br /><br /> Definiert das Sonos interface für die Kommunikation mit dem Sonos-System.<br />
|
||||
<p>
|
||||
<code>[upnplistener]</code><br />Name und Port eines externen UPnP-Client. Wenn nicht angegebenen wird <code>localhost:4711</code> festgelegt. Der Port muss eine freie Portnummer ihres Systems sein. <br />Wenn sie keinen externen Client gestartet haben, startet das Skript einen eigenen.<br />Wenn sie einen eigenen Dienst gestartet haben, dann geben sie hier die entsprechenden Informationen an.</p>
|
||||
@ -6837,12 +6984,15 @@ Dabei ist die Reihenfolge innerhalb der Unterlisten wichtig, da der erste Eintra
|
||||
<br />
|
||||
<a name="SONOSattr"></a>
|
||||
<h4>Attribute</h4>
|
||||
'''Hinweis'''<br />Die Attribute werden erst bei einem Neustart von Fhem verwendet, da diese dem SubProzess initial zur Verfügung gestellt werden müssen.
|
||||
<ul>
|
||||
<li><b>Grundsätzliches</b><ul>
|
||||
<li><a name="SONOS_attribut_characterDecoding"><code>attr <name> characterDecoding <codingname></code>
|
||||
</a><br />Hiermit kann die Zeichendekodierung eingestellt werden. Z.b. <UTF-8>. Standardmäßig wird <CP-1252> verwendet.</li>
|
||||
<li><a name="SONOS_attribut_disable"><code>attr <name> disable <value></code>
|
||||
</a><br />Eines von (0,1). Hiermit kann das Modul abgeschaltet werden. Wirkt sofort. Bei 1 wird der SubProzess beendet, und somit keine weitere Verarbeitung durchgeführt. Bei 0 wird der Prozess wieder gestartet.<br />Damit kann das Modul temporär abgeschaltet werden, um bei der Neueinrichtung von Sonos-Komponenten keine halben Zustände mitzubekommen.</li>
|
||||
<li><a name="SONOS_attribut_pingType"><code>attr <name> pingType <string></code>
|
||||
</a><br /> One of (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>
|
||||
</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>
|
||||
</ul></li>
|
||||
<li><b>Proxy-Einstellungen</b><ul>
|
||||
<li><a name="SONOS_attribut_generateProxyAlbumArtURLs"><code>attr <name> generateProxyAlbumArtURLs <int></code>
|
||||
|
@ -181,7 +181,7 @@ sub SONOSPLAYER_Initialize ($) {
|
||||
$hash->{StateFn} = "SONOSPLAYER_State";
|
||||
$hash->{NotifyFn} = 'SONOSPLAYER_Notify';
|
||||
|
||||
$hash->{AttrList}= "disable:0,1 generateVolumeSlider:0,1 generateVolumeEvent:0,1 generateSomethingChangedEvent:0,1 generateInfoSummarize1 generateInfoSummarize2 generateInfoSummarize3 generateInfoSummarize4 stateVariable: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,CrossfadeMode,Balance,HeadphoneConnected,SleepTimer,Presence,RoomName,SaveRoomName,PlayerType,Location,SoftwareRevision,SerialNum,InfoSummarize1,InfoSummarize2,InfoSummarize3,InfoSummarize4 model minVolume maxVolume minVolumeHeadphone maxVolumeHeadphone VolumeStep getAlarms:0,1 buttonEvents ".$readingFnAttributes;
|
||||
$hash->{AttrList}= "disable:1,0 generateVolumeSlider:1,0 generateVolumeEvent:1,0 generateSomethingChangedEvent:1,0 generateInfoSummarize1 generateInfoSummarize2 generateInfoSummarize3 generateInfoSummarize4 stateVariable: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,CrossfadeMode,Balance,HeadphoneConnected,SleepTimer,Presence,RoomName,SaveRoomName,PlayerType,Location,SoftwareRevision,SerialNum,InfoSummarize1,InfoSummarize2,InfoSummarize3,InfoSummarize4 model minVolume maxVolume minVolumeHeadphone maxVolumeHeadphone VolumeStep getAlarms:1,0 buttonEvents ".$readingFnAttributes;
|
||||
|
||||
return undef;
|
||||
}
|
||||
@ -648,7 +648,13 @@ sub SONOSPLAYER_Set($@) {
|
||||
for(my $i = 4; $i < @a; $i++) {
|
||||
$text .= ' '.$a[$i];
|
||||
}
|
||||
|
||||
my $sonosName = SONOS_getDeviceDefHash(undef)->{NAME};
|
||||
if ((ReadingsVal($sonosName, 'targetSpeakDir', '') eq '') || (ReadingsVal($sonosName, 'targetSpeakURL', '') eq '')) {
|
||||
return $key.' not possible. Please define valid "targetSpeakDir" and "targetSpeakURL" for Device "'.$sonosName.'" first.';
|
||||
} else {
|
||||
SONOS_DoWork($udn, lc($key), $value, $value2, $text);
|
||||
}
|
||||
} elsif (lc($key) eq 'alarm') {
|
||||
# Hier die komplette restliche Zeile in den zweiten Parameter packen, da damit auch Leerzeichen möglich sind
|
||||
my $text = '';
|
||||
@ -1082,6 +1088,7 @@ sub SONOSPLAYER_Log($$$) {
|
||||
<br />
|
||||
<a name="SONOSPLAYERattr"></a>
|
||||
<h4>Attributes</h4>
|
||||
'''Attention'''<br />The attributes can only be used after a restart of fhem, because it must be initially transfered to the subprocess.
|
||||
<ul>
|
||||
<li><b>Common</b><ul>
|
||||
<li><a name="SONOSPLAYER_attribut_disable"><code>attr <name> disable <int></code>
|
||||
@ -1361,6 +1368,7 @@ Here an event is defined, where in time of 2 seconds the Mute-Button has to be p
|
||||
<br />
|
||||
<a name="SONOSPLAYERattr"></a>
|
||||
<h4>Attribute</h4>
|
||||
'''Hinweis'''<br />Die Attribute werden erst bei einem Neustart von Fhem verwendet, da diese dem SubProzess initial zur Verfügung gestellt werden müssen.
|
||||
<ul>
|
||||
<li><b>Grundsätzliches</b><ul>
|
||||
<li><a name="SONOSPLAYER_attribut_disable"><code>attr <name> disable <int></code>
|
||||
|
Loading…
x
Reference in New Issue
Block a user