PHP Sonos (Klasse zum Ansteuern einzelner Player)

Hallo zusammen,

ich habe mir die PHPSonos-Klasse geschnappt, um über meine Hausautomation (FHEM) meinen Sonos-Player steuern zu können.
Dabei habe ich einige Funktionen drumherum gebaut, die ich hiermit der Allgemeinheit zum Ausschlachten und als Ideengrundlage zur Verfügung stellen will.

Mein System besteht im Wesentlichen aus einem PHP-Skript, welches die PHPSonos-Klasse kapselt, damit ich einfach Kommandozeilenparameter übergeben kann.
Dabei übergebe ich den Parameter „Zone“, welcher bei mir der Name der Zone ist. Um das dann in eine IP-Adresse umwandeln zu können, habe ich zur Beschleunigung des Zugriffs eine kleine Textdatei erzeugt, in der diese Informationen stehen.
Diese Textdatei kann mit demselben Skript generiert werden, Dabei werden alle Zoneplayer des Netzes erkannt und in die Datei geschrieben. Damit kann man also in Polling-Intervallen die Informationen aktualisieren lassen (wie oft ändern sich die Player und ihre IP-Adresse schon wirklich?)

Desweiteren habe ich ein Skript geschrieben, welches exemplarisch (an meinem Exemplar :slight_smile: zeigt, wie man sich für die Aktualisierung von Titelinformationen (wie Abspielzustand und Titel usw.) registrieren kann, und die Informationen verarbeiten kann.
Das ist aber wirklich nur exemplarisch zu verstehen, da das dann sehr speziell auf mich zugeschnitten ist. Ich triggere bei mir FHEM an, damit dieser mir meinen Verstärker für diesen ZonePlayer anmacht (ich habe einen ZP90 da stehen).

Für diesen ganzen UPnP-Kram verwende ich die PHP-Erweiterung GUPNP (http://php.net/manual/en/book.gupnp.php), für die man vielleicht ein bißchen Mühe investieren muss, um sie installiert zu bekommen :slight_smile:
Dann hat man aber in seinen PHP-Skripten die Möglichkeit sauber auf UPnP zuzugreifen, und die Player finden zu lassen bzw. benachrichtigt zu werden, wenn sich was ändert.

Desweiteren verwende ich in meinen Code eine Funktion namens xml2array (xml2array() - XML Parser for PHP < PHP < Bin-Co), die aber im Code enthalten ist, und nicht extra installiert werden muss.

Der Rest ist auf meinem Mist gewachsen, etwas kommentiert, aber es ist schon eine Menge :slight_smile:

Nun zu einer kurzen Übersicht des ZIP:

  • PHPSonos.inc.php -> Original-Sonos-Klasse, wie man sie hier im Forum finden kann
  • sonos.php -> Kommandozeilentool zum vereinfachten Steuern von Sonos. Aufruf ohne Parameter gibt die Hilfe aus
  • sonos_event.php -> Kommandozeilentool als Event-Listener auf Abspielzustandsänderungen, momentan hardcoded auf „Wohnzimmer“ (in Zeile 83), damit mein FHEM getriggert werden kann.
  • zones.txt -> Wird von beiden Skripten verwendet, um Zonennamen in IP-Adressen o.ä. umzuwandeln. Kann mit dem Kommando „php sonos.php action=getzonelist“ erzeugt werden.

Sooo… Viel Text

Viel Spaß damit, und ich hoffe, ich konnte dem einen oder anderen etwas Arbeit abnehmen.

Grüße Reiner

sonos.zip (22.4 KB)

Hallo zusammen,

gestern Abend war ich doch einen kleinen Schritt zu voreilig.

Dank der Aufräumstrategie der Zoneplayer bzgl. der Subscriptions muss ein Timeout eingestellt werden (in sonos_event.php in Zeile 46), damit der Automatismus von GUPnP das Auffrischen der Subscription übernehmen kann.

Desweiteren kam ein Fehler im Log der Titel-Anzeige, wenn man einen Radiosender anhört oder dahin wechselt. Die Unterstützung für die Anzeige von Streams habe ich dann auch gleich mit eingebaut.

Ich habe das Skript jetzt mal eine ganze Weile beobachtet, und momentan nichts feststellen können.

Hier also das Update nochmal als komplettes ZIP-File…

Grüße Reiner

sonos_20121120.zip (22.9 KB)

Moin…

… mal ne Frage an die Sonos-Cracks hier. Besteht die Möglichkeit das SONOS-Skript um die Funktion zu erweitern, das aus IPS heraus der Wecker gestellt werden kann?!
Also AN/AUS und die die Weckzeit verändert werden kann?

Gruß,
Peter

Kurz und knapp: Ja, die Möglichkeit besteht.

Ich bin selbst erst seit 3 Tagen stolzer Besitzer einer Sonos Anlage, von daher habe ich noch nicht so viel Erfahrung.
Nach dem ich sehr viel Zeit mit der Suche nach der SleepTimer Einstellung per PHP erfolglos vergeudet habe, habe ich mich selbst in die Materie eingelesen. Das System habe ich nun verstanden und auch die Möglichkeit den SleepTimer einzubinden bewältigt.

Hier der Code dazu:

zum Einfügen in die PHPSonos.inc.php


public function sleeptimer($time)
	{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 335
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#ConfigureSleepTimer"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:ConfigureSleepTimer xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><NewSleepTimerDuration>'.$time.'</NewSleepTimerDuration></u:ConfigureSleepTimer></s:Body></s:Envelope>';
		$this->sendPacket($content);
	}
	


public function sleeptimer_off()
	{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 327
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#ConfigureSleepTimer"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><s:Body><u:ConfigureSleepTimer xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><NewSleepTimerDuration></NewSleepTimerDuration></u:ConfigureSleepTimer></s:Body></s:Envelope>';
		$this->sendPacket($content);
	}

Zum setzen des Timers: (15 min)

$sonos->sleeptimer("00:15:00");

Sleeptimer löschen:

$sonos->sleeptimer_off();

Alarme bzw. Wecker folgen sicher die nächsten Tage, jedoch ist das ein vielfaches der Arbeit…

Das mit dem WEcker ist schonmal sehr interessant, wenn das Kommt. Besonders interessieren tut mich die Übergabe einer eigenen Abspiel Quelle, wie eine m3u oder Radiosender.

@Reiner:
Was meinst Du mit dem Satz:

Kommandozeilentool als Event-Listener auf Abspielzustandsänderungen, momentan hardcoded auf „Wohnzimmer“ (in Zeile 83), damit mein FHEM getriggert werden kann.

Bekommst Du da mit, ob ein Player von Pause auf Play geht oder wie?

Hi Ta Lun,

genau. Als Listener wird man über die Änderungen beim Abspielen informiert. Also so Ereignisse wie Start/Stop oder ein Liedwechsel (auch während z.B. ein Radiostream läuft). Des Weiteren wird natürlich auch das Umschalten auf Radio o.ä. gemeldet.
Man kann sich also auch irgendwohin melden lassen, wenn irgendwo ein neuer Liedtitel angezeigt werden muss (und natürlich wie dieser lautet).
Das Programm kann man einfach starten (nachdem man sich eine Zones-Datei erzeugt hat, die wird gebraucht). Dann rauscht auf der Konsole die Meldung bzgl. Titelanzeige durch
u.U. müsste man in Zeile 140 die Prüfung auf die Zone „Wohnzimmer“ anpassen, und in den Zeilen 116 und 119 den Trigger ausmarkieren oder anpassen.

Man könnte sich natürlich auch noch für die anderen Events wie Lautstärke ändern o.ä. anmelden (alles was der Sonos-Controller halt kann :slight_smile:
Das war aber für meine „Verstärker-Anschalt-Funktion-durch-Haussteuerung“ nicht nötig, läßt sich aber relativ einfach nachbauen.

Grüße Reiner

Hallo zusammen,

ich habe für mich einen Weg gesucht in meinem Sonos eine Playlist zu löschen.
Die Playlist habe ich zuvor anlegen lassen als Auslagerungsdatei, falls IPS eine Meldung macht, oder meine Hausklingel betätigt wird. Wenn die MEldung/ Klingel fertig ist, holt er sich die Playlist wieder ab, und löscht diese dann wieder in den Sonosplaylisten.

Was kann die Funktion:

Ich nutze Sie im eine Sonosplaylist zu löschen.

Hierzu der Befehl:

$sonos->DestroyObject(SQ:50)

Das ‚SQ:50‘ ist dabei die id der Playlist die ich löschen möchte.
Ich glaube ich dass man damit z.B. einen Radiofavoriten, Alarm etc. löschen kann, hierzu bräuchte man nur die Radio-ID, Alarm-ID etc…

Die ID der Playlist bekommt man hier im feld: „id“:

$sonos->GetSonosPlaylists();

Hier also die Funktion

public function DestroyObject($id)
	{
$content='POST /MediaServer/ContentDirectory/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 250
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#DestroyObject"

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body><u:DestroyObject xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">

<ObjectID>'.$id.'</ObjectID>

</u:DestroyObject></s:Body></s:Envelope>';

		$this->sendPacket($content);
	}

@bengie:
Vielleicht kann man die ja beim nächsten PHPsonos.ins.php wieder mit einbauen.
Du hast inzw. so eine schöne Doku erstellt, falls du hier noch etwas separat geschrieben brauchst, dann sag bescheid.

greets chris

Hallo zusammen

Ich habe mich etwas mit der Klasse beschäftigt. Einzelne Funktionen konnte ich bereits problemlos einbauen. Gescheitert bin ich beim laden und danach abspielen einer bereits vorhandenen Sonos-Playlist. Wie muss ich da vorgehen?

Danke und Gruss
Jean-Paul

Hi Jean-Paul,

du musst nach dem Laden der Playlist in die Queue dessen Resource-ID an SetAVTransportURI übergeben.

Also zum Beispiel:
x-rincon-queue:RINCON_000E5828D0F401400#0

Die unterscheidet sich bei dir natürlich :slight_smile:

Rausfinden kannst du diese ID mittels einer Browse-Anfrage mit der Kennung ‚Q:‘, die Antwort steht im XML-Tag ‚res‘.

Grüße Reiner

Hallo Rainer

Besten Dank für Deine Antwort. Und wie lade ich die Playlist in die Queue? Bis jetzt habe ich mit
$sonos->GetSonosPlaylists();
$sonos->GetPlaylist(‚SQ:3‘);
Die Playlists und den Inhalt einer Playlist geladen, aber so sind diese ja noch nicht in der Queue.

Danke und Gruss
Jean-Paul

Hi Jean-Paul,

momentan gibt es nur die Möglichkeit in einer Schleife jeden Titel einzeln per AddToQueue hinzuzufügen.

Sonos bietet auch noch eine Methode AddMultipleURIsToQueue, die noch nicht in der PHP-Klasse gekapselt enthalten ist. Leider habe ich noch nicht das Datenformat herausgefunden, wie man alle Titel als Parameter angeben kann. Das muss irgendeine Art von XML-Struktur sein, nur finde ich irgendwie nicht raus, welche akzeptiert wird.

Grüße Reiner

Hi Rainer

Besten Dank, lag ich doch richtig mit dem Ansatz die Playlist durchzugehen und dann einfach jeden Titel der Queue hinzuzufügen. Das läuft nun einwandfrei.

Aber das mit dem Abspielen der Queue hat irgendwie nicht hingehauen. Wenn ich den Radio am laufen habe und dann auf die Queue wechseln will geht das irgendwie nicht.

print_r($sonos->Browse(‚Q:‘)); liefert mir folgendes Ergebnis:
Array
(
[0] => Array
(
[typ] => container
[res] => x-rincon-queue%3ARINCON_000E5830B7AE01400%230
[duration] => leer
[albumArtURI] => leer
[title] => AVT Instance 0
[artist] => leer
[id] => Q%3A0
[parentid] => Q%3A
[album] => leer
)
)

Habe nun versucht mit folgenden Befehlen von Radio auf die Queue umzuschalten und diese abzuspielen:
SetAVTransportURI(‚x-rincon-queue:RINCON_000E5830B7AE01400#0‘);
$sonos->play();

Bei mir läuft aber einfach der Radio weiter :slight_smile:

Was mache ich falsch.

Besten Dank und Gruss
Jean-Paul

Ich glaub ich habs:
$sonos->SetQueue(‚x-rincon-queue:RINCON_000E5830B7AE01400#0‘);
führt zum gewünschten Ergebnis…

Danke nochmals für Deine Hilfe, Rainer.

Gruss Jean-Paul

Hi Jean-Paul,

schön, das es jetzt läuft.
Sorry, ich hatte vergessen zu schreiben, dass man dazu noch Metadaten angeben muss. Das ist genau das, was SetQueue kapselt :slight_smile:

Ich bin momentan dabei ein Perl Modul für Sonos zu schreiben, wo ich nicht auf die PHP-Klasse zurückgreifen kann, sodass meine Gedanken eher im „Raw-UPnP-Modus“ laufen :slight_smile:

Grüße Reiner

Hi Jean-Paul,

ich habe doch noch einen deutlich schnelleren Weg zum Laden einer Playlist gefunden.

Man kann an AddURIToQueue einfach die ID einer Playlist übergeben.

Zum Beispiel:
file:///jffs/settings/savedqueues.rsq#3
in das Feld EnqueuedURI, den Rest leer lassen, bzw. Standard. Diese ID erhält man durch eine Browse-Anfrage mittels ‚SQ:‘. Dann im Ergebnis-XML das Feld ‚res‘.

Damit wird die komplette Playlist in einem Rutsch geladen. Und das deutlich schneller als auf manuellem Wege. Allerdings hat man dann keinen Enfluss mehr auf jedes Lied einzeln, was man aber ja meistens sowieso nicht möchte.

Wenn man die Playlist in einem Rutsch laden und gleich starten möchte, kann man diese Kennung auch an StartAutoPlay im Feld ProgramURI übergeben. Hier muss man aber gleich eine Lautstärke mitgeben, was manchmal eher unpraktisch ist.

Ich weiß jetzt auf die Schnelle nicht, was davon bereits in der phpsonos-Klasse umgesetzt ist, da ich die momentan, wie beschrieben, nicht verwende… Aber das wirst du bestimmt rausfinden können :slight_smile:

Grüße Reiner

Gestern hat hat mir Sonos mein IPS zum erliegen gebracht.

Kaum noch Reaktion von IPS, enorm verzögert. Warteschlange zog sich ins unermessliche. Die 3 sekünndige Abfrage des Volumes und der Funktion gab immer wieder Fehlermeldung aus.

Grund war ein Sonos ZP100 der sich anscheinend „aufgehängt“ hatte.

Einmal Stecker raus-rein und lief sofort wieder einwandfrei. Hab aber gebraucht bis ich auf die Idee kam.

Hat jemand schonmal ähnliches gehabt und kenn evtl ne Möglichkeit das in Zukunft abzustellen ?

Mit einem Semaphore könntest Du zumindest verhindern, dass das Script mehrfach ausgeführt wird und sich die Queue füllt.

Hallo zusammen,

bezüglich Sonos bin ich leider kompletter Neueinsteiger und benötige mal einen Klaps auf den Hinterkopf. :slight_smile:

Ich möchte Sonos als Klingel nutzen und spiele mit dem beigefügten Script.

Wenn jemand klingelt, möchte ich eine eventuell laufende Playlist speichern, einen speziellen Klingelton ablaufen lassen und dannach die Playlist weiterführen.

Gibt es keine Playlist soll nur der Klingelton abgespielt werden.

Momentan fehlt das Playlisthandling noch ganz und das nachstehende Script läuft nur, wenn der Klingelton mindestens einmal abgespielt wurde. War der Player aber vorher vom Strom getrennt (und die Playlist damit leer) spielt das Script nichts ab.

Wie kann ich realisieren das ich mit dem Klingeln den Player notfalls einschalten kann und nach der Initialisierung der Klingelton erklingt?

Danke für jeden Tip,
Stromer

<?

 include("PHPSonos.inc.php");

$sonos = new PHPSonos("10.3.2.122"); //Sonos ZP IPAdresse - S5

//Grundfunktionen
//$sonos->Pause();
//$sonos->Play();
//$sonos->Next();
//$sonos->Previous();
//$sonos->Rewind();
//$sonos->SetVolume(25); //0-100 in %
//$sonos->SetPlayMode("NORMAL"); //REPEAT_ALL, SHUFFLE, NORMAL
//$sonos->SetMute(false); //True = MUTE, False = KEIN MUTE

//Klassik Radio abspielen
//$sonos->SetRadio("x-rincon-mp3radio://players.creacast.com/creacast/klassik/playlist.pls");
//$sonos->Play();

//Neue MP3 abspielen
$sonos->ClearQueue(); //Playlist löschen
$sonos->AddToQueue("x-file-cifs://QN1/01m/12-klingel/Klingelton-GranVals.mp3"); //Datei hinzufügen
//$sonos->SetQueue("x-rincon-queue:RINCON_"."000E5851D6D8"."01400#0"); //Playlist auswählen (Nötig, wenn Radio vorher ausgewählt war)
$sonos->Play();

//$sonos->SetTrack(1); //1-n
//$sonos->RemoveFromQueue(1); //1-n

?>

Hallo Reiner,

würdest Du bitte noch eine Anmerkung zur Installation von PECL und dem Rest machen. Ich versuche mich gerade daran, scheitere aber leider immer wieder.

Danke, Stromer

Hi Stromer,

hmmm… viel gibt es da nicht zu sagen. Auf meinem Raspberry Pi unter Debian war das nur die Zeile:

pecl install gupnp

Wenn ich das jetzt ausführe, erhalte ich natürlich nur die Meldung, dass es bereits installiert ist:

pecl/gupnp is already installed and is the same as the released version 1.0.0

Was für Probleme hast du denn?

Grüße Reiner

P.S.: Sorry für die späte Rückmeldung, aber ich habe irgendwie keine Info über deinen Beitrag erhalten…