Nachdem ich jetzt schon zwei Jahre im Haus wohne und der ständige Batteriewechsel der Funk-Klingel lästig war habe ich mich entschlossen endlich meine Vorstellung einer smarten Klingel zu realisieren.
Grundgedanke war es die vorhandenen Decken-Lautsprecher in das System mit einzubeziehen.
Hier nun zu meiner Anleitung wie ich es gemacht habe:
Material:
Gira Türstation (Klingel)
Gira Türkommunikation Audio oder Video
Gira Türkommunikation Schaltaktor
KNX Binäreingang
Wiregate
Sonos Connect AMP
Funktion Allgemein:
Ein Drücken an der Gira Türstation schaltet den Gira Türkommunikation Schaltaktor. Der Ausgang des Schaltaktors führt auf den Binäreingang und signalisiert dem KNX-Bus dass jemand geklingelt hat. Dieses Signal auf der Gruppenadresse überwacht das Wiregate und startet ein Pearl-Script welches den Sonos Connect AMP den Befehl gibt eine selbst definierte MP3 abzuspielen.
Funktion Sonos-Klingel:
Da sich die Sonos Player im StandBy Modus befinden wird das MP3 sofort abgespielt. Werden in verschiedenen Zonen Musiktitel aus der Playlist oder Webradio abgespielt merkt sich das Skript den aktuellen Titel, unterbricht diesen (inkl. Volume down), spielt den Klingelton ab und startet wieder mit der Playlist bzw. dem Webradio (inkl. Volume up).
Die Klingel via Sonos ist wenn die notwendige Ausstattung vorhanden ist relativ einfach zu realisieren und ist eine meiner Meinung nach sehr feine Sache.
Hier das Wiregate-Plugin welches die Gruppenadresse der Gira Türstation überwacht und das PHP-Skript startet:
1/4: Wiregate Plugin für Sonos-Klingel:
#!/usr/bin/perl
use strict;
use LWP::Simple;
my @GAs;
&readConf;
$plugin_info{$plugname.'_cycle'} = 86400;
foreach my $curr_ga (@GAs)
{
my $temp_value_ga = knx_read($curr_ga->{ga},0,1);
$plugin_subscribe{$curr_ga->{ga}}{$plugname} = 1; #An Gruppenadresse anmelden
if (($msg{'apci'} eq "A_GroupValue_Write") and ($msg{'dst'} eq ($curr_ga->{ga})) and ($temp_value_ga eq "1")) {
plugin_log($plugname,'Es wurde geklingelt.' );
knx_write($curr_ga->{ga},$curr_ga->{value_aus},1); # Dimmwert senden
plugin_log($plugname,'GA '.$curr_ga->{ga}. ' (Klingel) auf "'.$curr_ga->{value_aus}.'" gesetzt.' );
get( "http://".$curr_ga->{PATH}."/".$curr_ga->{URL});
plugin_log($plugname,'Aufrufen: ' . "http://".$curr_ga->{PATH}."/".$curr_ga->{URL} );
}
return("Status Plugin Klingel: OK");
}
### READ CONF ###
sub readConf
{
my $confFile = '/etc/wiregate/plugin/generic/conf.d/'.basename($plugname,'.pl').'.conf';
if (! -f $confFile) {
plugin_log($plugname, "no conf file [$confFile] found.");
} else {
#plugin_log($plugname, "reading conf file [$confFile].");
open(CONF, $confFile);
my @lines = ;
close($confFile);
my $result = eval("@lines");
#($result) and plugin_log($plugname, "conf file [$confFile] returned result[$result]");
if ($@) {
plugin_log($plugname, "ERR: conf file [$confFile] returned:");
my @parts = split(/\n/, $@);
plugin_log($plugname, "--> $_") foreach (@parts);
}
}
}
2/4: Wiregate Plugin Konfigurationsdatei:
push @GAs, { URL=> "sonos_2.php", ga => "7/7/0",
audio => "doorbell_01.mp3", value_ein => 1, value_aus =>0, PATH => "wiregate256/visu_svn_neu/plugins/klingel"};
3/4: Und hier der Link zum txt-File des PHP-Sourcecodes welcher den Sonos-Player die Befehle gibt:
4/4: PHPSonos.inc.php
'
;
*/
$header='POST /DeviceProperties/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:DeviceProperties:1#GetZoneAttributes"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->XMLsendPacket($content);
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
xml_parse_into_struct($xmlParser, $returnContent, $vals, $index);
xml_parser_free($xmlParser);
$ZoneAttributes = Array();
$key="CurrentZoneName"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneAttributes[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneAttributes[$key] = ""; }
$key="CurrentIcon"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneAttributes[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneAttributes[$key] = ""; }
return $ZoneAttributes; //Assoziatives Array
}
public function GetZoneInfo() // added br
{
/*
$content='POST /DeviceProperties/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:DeviceProperties:1#GetZoneInfo"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400
Content-Length: 295
'
;
*/
$header='POST /DeviceProperties/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:DeviceProperties:1#GetZoneInfo"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->XMLsendPacket($content);
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
xml_parse_into_struct($xmlParser, $returnContent, $vals, $index);
xml_parser_free($xmlParser);
$ZoneInfo = Array();
$key="SerialNumber"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="SoftwareVersion"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="SoftwareVersion"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="DisplaySoftwareVersion"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="HardwareVersion"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="IPAddress"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="MACAddress"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="CopyrightInfo"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
$key="ExtraInfo"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneInfo[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneInfo[$key] = ""; }
return $ZoneInfo; //Assoziatives Array
}
public function SetLEDState($state) // added br
{
if($state=="On") { $state = "On"; } else
{ if($state=="Off") { $state = "Off"; } else {
if($state) { $state = "On"; } else { $state = "Off"; }
}
}
$content='POST /DeviceProperties/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:DeviceProperties:1#SetLEDState"
' .$state. '';
return (bool)$this->sendPacket($content);
}
public function GetLEDState() // added br
{
$content='POST /DeviceProperties/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:DeviceProperties:1#GetLEDState"
0';
if ($this->sendPacket($content)=="On") { return(true); }else return(false);
}
/* urn:upnp-org:serviceId:GroupManagement */
public function AddMember($MemberID) // added br
// Joins a group 1st Arg is RINCON_MAC1400
// Returns assoziative Array with CurrentSettings and GroupUUIDJoined
{
$header='POST /GroupManagement/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:GroupManagement:1#AddMember"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
' . $MemberID . '
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->XMLsendPacket($content);
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
xml_parse_into_struct($xmlParser, $returnContent, $vals, $index);
xml_parser_free($xmlParser);
$ZoneAttributes = Array();
$key="CurrentTransportSettings"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneAttributes[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneAttributes[$key] = ""; }
$key="GroupUUIDJoined"; // Lookfor
if ( isset($index[strtoupper($key)][0]) and isset($vals[ $index[strtoupper($key)][0] ]['value'])) {$ZoneAttributes[$key] = $vals[ $index[strtoupper($key)][0] ]['value'];
} else { $ZoneAttributes[$key] = ""; }
return $ZoneAttributes; //Assoziatives Array
// set AVtransporturi ist notwendig
}
/* urn:upnp-org:serviceId:MusicServices */
// Not implemented
/* urn:upnp-org:serviceId:SystemProperties */
// Not implemented
/* urn:upnp-org:serviceId:ZoneGroupTopology */
// Not implemented
public function RemoveMember($MemberID) // added br
// Leaves a Group
// Returns None
{
$header='POST /GroupManagement/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:GroupManagement:1#RemoveMember"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
' . $MemberID . '
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
echo $this->sendPacket($content);
// set AVtransporturi ist für STOP notwendig
}
/* urn:upnp-org:serviceId:MusicServices */
// Not implemented
/* urn:upnp-org:serviceId:SystemProperties */
// Not implemented
/* urn:upnp-org:serviceId:ZoneGroupTopology */
// Not implemented
/******************* urn:schemas-upnp-org:device:MediaRenderer:1 ***********
***************************************************************************/
/* urn:upnp-org:serviceId:RenderingControl */
public function RampToVolume($ramp_type, $volume) //added br
/* Ramps Vol to $volume using the Method mentioned in $ramp_type as string:
"SLEEP_TIMER_RAMP_TYPE" - mutes and ups Volume per default within 17 seconds to desiredVolume
"ALARM_RAMP_TYPE" -Switches audio off and slowly goes to volume
"AUTOPLAY_RAMP_TYPE" - very fast and smooth; Implemented from Sonos for the unofficial autoplay feature.
When you switch on a Sonos Device it plays its autoplay Command/Playlist and Settings. It´s all there but currently no GUI setting is possible.
*Should return Rampseconds* but this is not implemented! */
{
$header='POST /MediaRenderer/RenderingControl/Control HTTP/1.1
HOST: '.$this->address.':1400
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:RenderingControl:1#RampToVolume"
';
$xml='
0Master'.$ramp_type.''.$volume.'
';
$content=$header . 'Content-Length: '. strlen($xml) .'
'. $xml;
return (int) $this->sendPacket($content);
}
/* urn:upnp-org:serviceId:AVTransport */
// If you don´t set $id you may get duplicate playlists!!!
public function SaveQueue($title,$id="") // added br
{
$header='POST /MediaRenderer/AVTransport/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SaveQueue"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='0'.$title.''.$id.'
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->sendPacket($content);
}
public function GetCrossfadeMode() // added br
{
$header='POST /MediaRenderer/AVTransport/Control HTTP/1.1
HOST: '.$this->address.':1400
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#GetCrossfadeMode"
';
$xml='
0
';
$content=$header . 'Content-Length: '. strlen($xml) .'
'. $xml;
return (bool)$this->sendPacket($content);
}
public function SetCrossfadeMode($mode) // added br
{
if($mode) { $mode = "1"; } else { $mode = "0"; }
$header='POST /MediaRenderer/AVTransport/Control HTTP/1.1
HOST: '.$this->address.':1400
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetCrossfadeMode"
';
$xml='
0'.$mode.'';
$content=$header . 'Content-Length: '. strlen($xml) .'
'. $xml;
$this->sendPacket($content);
}
public function Stop()
{
$content='POST /MediaRenderer/AVTransport/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:AVTransport:1#Stop"
0';
$this->sendPacket($content);
}
public function Pause()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 252
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Pause"
0';
$this->sendPacket($content);
}
public function Play()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 266
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Play"
01';
$this->sendPacket($content);
}
public function Next()
{
$content='POST /MediaRenderer/AVTransport/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:AVTransport:1#Next"
0';
$this->sendPacket($content);
}
public function Previous()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 258
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Previous"
0';
$this->sendPacket($content);
}
public function Seek($arg1,$arg2="NONE")
{
// Abwärtskompatibel zu Paresys Original sein
if ($arg2=="NONE"){
$Unit="REL_TIME"; $position=$arg1;
} else {$Unit=$arg1; $position=$arg2;}
$header='POST /MediaRenderer/AVTransport/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Seek"
CONTENT-TYPE: text/xml; charset="utf-8"
CONNECTION: close
HOST: '.$this->address.':1400';
$xml='
0'. $Unit .''.$position.'';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->sendPacket($content);
}
public function Rewind()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 296
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Seek"
0REL_TIME00:00:00';
$this->sendPacket($content);
}
public function SetVolume($volume)
{
$content='POST /MediaRenderer/RenderingControl/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 32'.strlen($volume).'
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:RenderingControl:1#SetVolume"
0Master'.$volume.'';
$this->sendPacket($content);
}
public function GetVolume()
{
$content='POST /MediaRenderer/RenderingControl/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 290
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:RenderingControl:1#GetVolume"
0Master';
return (int)$this->sendPacket($content);
}
public function SetMute($mute)
{
if($mute) { $mute = "1"; } else { $mute = "0"; }
$content='POST /MediaRenderer/RenderingControl/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 314
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:RenderingControl:1#SetMute"
0Master'.$mute.'';
$this->sendPacket($content);
}
public function GetMute()
{
$content='POST /MediaRenderer/RenderingControl/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 286
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:RenderingControl:1#GetMute"
0Master';
return (bool)$this->sendPacket($content);
}
public function SetPlayMode($mode)
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: '.(291+strlen($mode)).'
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetPlayMode"
0'.$mode.'';
$this->sendPacket($content);
}
public function GetTransportSettings()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 282
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#GetTransportSettings"
0';
$returnContent = $this->sendPacket($content);
// echo "\n===" . $this->address. "====\n" . $returnContent . "\n===\n";
if (strstr($returnContent, "NORMAL") !== false) {
return Array (
"repeat" => false,
"shuffle" => false
);
} elseif (strstr($returnContent, "REPEAT_ALL") !== false) {
return Array (
"repeat" => true,
"shuffle" => false
);
} elseif (strstr($returnContent, "SHUFFLE_NOREPEAT") !== false) {
return Array (
"repeat" => false,
"shuffle" => true
);
} elseif (strstr($returnContent, "SHUFFLE") !== false) {
return Array (
"repeat" => true,
"shuffle" => true
);
}
/* what is PLAYING??? br */
/* } elseif (strstr($returnContent, "PLAYING") !== false) {
return Array (
"repeat" => false,
"shuffle" => true
);
} */
}
public function GetTransportInfo()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 274
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#GetTransportInfo"
0';
$returnContent = $this->sendPacket($content);
if (strstr($returnContent, "PLAYING") !== false) {
return 1;
} elseif (strstr($returnContent, "PAUSED_PLAYBACK") !== false) {
return 2;
} elseif (strstr($returnContent, "STOPPED") !== false) {
return 3;
}
}
public function GetMediaInfo()
{
// NOTEBR: diese F. ist aktuell noch buggy
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 266
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#GetMediaInfo"
0';
$returnContent = $this->XMLsendPacket($content);
/* $returnContent = substr($returnContent, stripos($returnContent, '<'));
$returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
$returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
*/
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
xml_parse_into_struct($xmlParser, $returnContent, $vals, $index);
xml_parser_free($xmlParser);
$mediaInfo = Array();
if (isset($index["DC:TITLE"]) and isset($vals[$index["DC:TITLE"][0]]["value"])) {
$mediaInfo["title"] = $vals[$index["DC:TITLE"][0]]["value"];
} else {
$mediaInfo["title"] = "";
}
if (isset($vals[5]["value"])) {
$mediaInfo["CurrentURI"] = $vals[5]["value"];
} else {
$mediaInfo["CurrentURI"] = "";
}
if (isset($vals[6]["value"])) {
$mediaInfo["CurrentURIMetaData"] = $vals[6]["value"];
} else {
$mediaInfo["CurrentURIMetaData"] = "";
}
return $mediaInfo;
}
public function GetPositionInfo()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 272
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#GetPositionInfo"
0';
$returnContent = $this->sendPacket($content);
$position = substr($returnContent, stripos($returnContent, "NOT_IMPLEMENTED") - 7, 7);
$returnContent = substr($returnContent, stripos($returnContent, '<'));
$returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
$returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, "UTF-8");
xml_parse_into_struct($xmlParser, $returnContent, $vals, $index);
xml_parser_free($xmlParser);
$positionInfo = Array ();
$positionInfo["position"] = $position;
$positionInfo["RelTime"] = $position;
if (isset($index["RES"]) and isset($vals[$index["RES"][0]]["attributes"]["DURATION"])) {
$positionInfo["duration"] = $vals[$index["RES"][0]]["attributes"]["DURATION"];
$positionInfo["TrackDuration"] = $vals[$index["RES"][0]]["attributes"]["DURATION"];
} else {
$positionInfo["duration"] = "";
$positionInfo["TrackDuration"] = "";
}
if (isset($index["RES"]) and isset($vals[$index["RES"][0]]["value"])) {
$positionInfo["URI"] = $vals[$index["RES"][0]]["value"];
$positionInfo["TrackURI"] = $vals[$index["RES"][0]]["value"];
} else {
$positionInfo["URI"] = "";
$positionInfo["TrackURI"] = "";
}
if (isset($index["DC:CREATOR"]) and isset($vals[$index["DC:CREATOR"][0]]["value"])) {
$positionInfo["artist"] = $vals[$index["DC:CREATOR"][0]]["value"];
} else {
$positionInfo["artist"] = "";
}
if (isset($index["DC:TITLE"]) and isset($vals[$index["DC:TITLE"][0]]["value"])) {
$positionInfo["title"] = $vals[$index["DC:TITLE"][0]]["value"];
} else {
$positionInfo["title"] = "";
}
if (isset($index["UPNP:ALBUM"]) and isset($vals[$index["UPNP:ALBUM"][0]]["value"])) {
$positionInfo["album"] = $vals[$index["UPNP:ALBUM"][0]]["value"];
} else {
$positionInfo["album"] = "";
}
if (isset($index["UPNP:ALBUMARTURI"]) and isset($vals[$index["UPNP:ALBUMARTURI"][0]]["value"])) {
$positionInfo["albumArtURI"] = "http://" . $this->address . ":1400" . $vals[$index["UPNP:ALBUMARTURI"][0]]["value"];
} else {
$positionInfo["albumArtURI"] = "";
}
if (isset($index["R:ALBUMARTIST"]) and isset($vals[$index["R:ALBUMARTIST"][0]]["value"])) {
$positionInfo["albumArtist"] = $vals[$index["R:ALBUMARTIST"][0]]["value"];
} else {
$positionInfo["albumArtist"] = "";
}
if (isset($index["UPNP:ORIGINALTRACKNUMBER"]) and isset($vals[$index["UPNP:ORIGINALTRACKNUMBER"][0]]["value"])) {
$positionInfo["albumTrackNumber"] = $vals[$index["UPNP:ORIGINALTRACKNUMBER"][0]]["value"];
} else {
$positionInfo["albumTrackNumber"] = "";
}
if (isset($index["R:STREAMCONTENT"]) and isset($vals[$index["R:STREAMCONTENT"][0]]["value"])) {
$positionInfo["streamContent"] = $vals[$index["R:STREAMCONTENT"][0]]["value"];
} else {
$positionInfo["streamContent"] = "";
}
// added br if this contains "rincon" we are slave to a coordinator mentioned in this field (otherwise path to the file is provided)!
// implemented via second XMLsendpacket to not break michaels current code
/* if (isset($index["RES"][0]) and isset($vals[($index["RES"][0])]["value"])) {
$positionInfo["trackURI"] = $vals[($index["RES"][0])]["value"];
} else {
*/
$returnContent = $this->XMLsendPacket($content);
$xmlParser = xml_parser_create("UTF-8");
xml_parser_set_option($xmlParser, XML_OPTION_TARGET_ENCODING, "ISO-8859-1");
xml_parse_into_struct($xmlParser, $returnContent, $vals, $index);
xml_parser_free($xmlParser);
// }
if (isset($index["TRACKURI"][0]) and isset($vals[($index["TRACKURI"][0])]["value"])) {
$positionInfo["trackURI"] = $vals[($index["TRACKURI"][0])]["value"];
$positionInfo["TrackURI"] = $vals[($index["TRACKURI"][0])]["value"];
} else {
$positionInfo["trackURI"] = "";
}
// Track Number in Playlist
if (isset($index["TRACK"][0]) and isset($vals[($index["TRACK"][0])]["value"])) {
$positionInfo["Track"] = $vals[($index["TRACK"][0])]["value"];;
} else {
$positionInfo["Track"] = "";
}
return $positionInfo;
}
public function SetRadio($radio,$Name="IP-Symcon Radio")
{
$Metadata="<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns:r="urn:schemas-rinconnetworks-com:metadata-1-0/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"><item id="R:0/0/0" parentID="R:0/0" restricted="true"><dc:title>".$Name."</dc:title><upnp:class>object.item.audioItem.audioBroadcast</upnp:class><desc id="cdudn" nameSpace="urn:schemas-rinconnetworks-com:metadata-1-0/">SA_RINCON65031_</desc></item></DIDL-Lite>";
$this->SetAVTransportURI($radio,$MetaData);
}
public function SetAVTransportURI($tspuri,$MetaData="")
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: '.(342+strlen(htmlspecialchars($tspuri))+strlen(htmlspecialchars($MetaData))).'
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#SetAVTransportURI"
0'.htmlspecialchars($tspuri).''.htmlspecialchars($MetaData).'.';
$this->sendPacket($content);
}
public function SetQueue($queue,$MetaData="")
{
$this->SetAVTransportURI($queue,$MetaData);
}
public function ClearQueue()
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: 290
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#RemoveAllTracksFromQueue"
0';
$this->sendPacket($content);
}
public function AddToQueue($file)
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: '.(438+strlen(htmlspecialchars($file))).'
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#AddURIToQueue"
0'.htmlspecialchars($file).'01';
$this->sendPacket($content);
}
public function RemoveFromQueue($track)
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: '.(307+strlen($track)).'
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#RemoveTrackFromQueue"
0Q:0/'.$track.'';
$this->sendPacket($content);
}
public function SetTrack($track)
{
$content='POST /MediaRenderer/AVTransport/Control HTTP/1.1
CONNECTION: close
HOST: '.$this->address.':1400
CONTENT-LENGTH: '.(288+strlen($track)).'
CONTENT-TYPE: text/xml; charset="utf-8"
SOAPACTION: "urn:schemas-upnp-org:service:AVTransport:1#Seek"
0TRACK_NR'.$track.'';
$this->sendPacket($content);
}
/******************* // urn:schemas-upnp-org:device:MediaServer:1 ***********
***************************************************************************/
/* urn:upnp-org:serviceId:ContentDirectory */
// MERKERTITLE
//Gibt ein Array mit den Songs der aktuellen Playlist
public function GetCurrentPlaylist()
{
$header='POST /MediaServer/ContentDirectory/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
Q:0BrowseDirectChildren01000
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->sendPacket($content);
$returnContent = substr($returnContent, stripos($returnContent, '<'));
$returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
$returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
$xml = new SimpleXMLElement($returnContent);
$liste = array();
for($i=0,$size=count($xml);$i<$size;$i++)
{
$aktrow = $xml->item[$i];
$albumart = $aktrow->xpath("upnp:albumArtURI");
$title = $aktrow->xpath("dc:title");
$artist = $aktrow->xpath("dc:creator");
$album = $aktrow->xpath("upnp:album");
$liste[$i]['listid']=$i+1;
if(isset($albumart[0])){
$liste[$i]['albumArtURI']="http://" . $this->address . ":1400".(string)$albumart[0];
}else{
$liste[$i]['albumArtURI'] ="";
}
$liste[$i]['title']=(string)$title[0];
if(isset($artist[0])){
$liste[$i]['artist']=(string)$artist[0];
}else{
$liste[$i]['artist']="";
}
if(isset($album[0])){
$liste[$i]['album']=(string)$album[0];
}else{
$liste[$i]['album']="";
}
}
return $liste;
}
//Liefert ein Array mit allen Sonos Wiedergabelisten und deren Aufrufinformationen
public function GetSonosPlaylists()
{
$header='POST /MediaServer/ContentDirectory/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
SQ:BrowseDirectChildren0100
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->sendPacket($content);
$returnContent = substr($returnContent, stripos($returnContent, '<'));
$returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
$returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
$xml = new SimpleXMLElement($returnContent);
$liste = array();
for($i=0,$size=count($xml);$i<$size;$i++)
{
$attr = $xml->container[$i]->attributes();
$liste[$i]['id'] = (string)$attr['id'];
$title = $xml->container[$i];
$title = $title->xpath('dc:title');
$liste[$i]['title'] = (string)$title[0];
$liste[$i]['typ'] = "Sonos";
$liste[$i]['file'] = (string)$xml->container[$i]->res;
}
return $liste;
}
//Liefert ein Array mit allen "Importierte Playlisten" Wiedergabelisten und deren Aufrufinformationen
public function GetImportedPlaylists()
{
$header='POST /MediaServer/ContentDirectory/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
A:PLAYLISTSBrowseDirectChildren0100
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->sendPacket($content);
$returnContent = substr($returnContent, stripos($returnContent, '<'));
$returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
$returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
$xml = new SimpleXMLElement($returnContent);
$liste = array();
for($i=0,$size=count($xml);$i<$size;$i++)
{
$attr = $xml->container[$i]->attributes();
$liste[$i]['id'] = (string)$attr['id'];
$title = $xml->container[$i];
$title = $title->xpath('dc:title');
// br substring use cuts my playlist names at the 4th char
$liste[$i]['title'] = (string)$title[0];
$liste[$i]['title']=preg_replace("/^(.+)\.m3u$/i","\\1",$liste[$i]['title']);
$liste[$i]['typ'] = "Import";
$liste[$i]['file'] = (string)$xml->container[$i]->res;
}
return $liste;
}
//Gibt ein Array mit den einzelnen Songs der Playlist wieder
//ObjektID aus GetSonosPlaylists() oder GetImportetPlaylists()
public function GetPlaylist($value)
{
$header='POST /MediaServer/ContentDirectory/Control HTTP/1.1
SOAPACTION: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
CONTENT-TYPE: text/xml; charset="utf-8"
HOST: '.$this->address.':1400';
$xml='
'.$value.'BrowseDirectChildren01000
';
$content=$header . '
Content-Length: '. strlen($xml) .'
'. $xml;
$returnContent = $this->sendPacket($content);
$xmlParser = xml_parser_create();
$returnContent = substr($returnContent, stripos($returnContent, '<'));
$returnContent = substr($returnContent, 0, strrpos($returnContent, '>') + 4);
$returnContent = str_replace(array("<", ">", """, "&", "%3a", "%2f", "%25"), array("<", ">", "\"", "&", ":", "/", "%"), $returnContent);
$xml = new SimpleXMLElement($returnContent);
$liste = array();
for($i=0,$size=count($xml);$i<$size;$i++)
{
$aktrow = $xml->item[$i];
$albumart = $aktrow->xpath("upnp:albumArtURI");
$title = $aktrow->xpath("dc:title");
$artist = $aktrow->xpath("dc:creator");
$album = $aktrow->xpath("upnp:album");
$liste[$i]['listid']=$i+1;
if(isset($albumart[0])){
$liste[$i]['albumArtURI']="http://" . $this->address . ":1400".(string)$albumart[0];
}else{
$liste[$i]['albumArtURI'] ="";
}
$liste[$i]['title']=(string)$title[0];
if(isset($interpret[0])){
$liste[$i]['artist']=(string)$artist[0];
}else{
$liste[$i]['artist']="";
}
if(isset($album[0])){
$liste[$i]['album']=(string)$album[0];
}else{
$liste[$i]['album']="";
}
}
return $liste;
}
/***************************************************************************
Helper / sendPacket
***************************************************************************/
/*
Unter LocalUID findet sich der String, den man im $sonos->SetQueue Befehl braucht.
Den String kann man z.B. über folgendes Codeschnipsel bekommen:
$url = "http://"..":1400/status/zp";
$xml = simpleXML_load_file($url);
$result = $xml->ZPInfo->LocalUID;
Quelle: http://www.ip-symcon.de/forum/f53/php-sonos-klasse-ansteuern-einzelner-player-7676/#post87054
*/
// do not filter xml answer
private function XMLsendPacket( $content )
{
$fp = fsockopen($this->address, 1400 /* Port */, $errno, $errstr, 10);
if (!$fp)
throw new Exception("Error opening socket: ".$errstr." (".$errno.")");
fputs ($fp, $content);
$ret = "";
$buffer = "";
while (!feof($fp)) {
$buffer = fgets($fp,128);
// echo "\n;" . $buffer . ";\n"; //DEBUG
$ret.= $buffer;
}
// echo "\n\nReturn:" . $ret . "!!\n";
fclose($fp);
if(strpos($ret, "200 OK") === false)
throw new Exception("Error sending command: ".$ret);
$array = preg_split("/\n/", $ret);
return $array[count($array) - 1];
}
private function sendPacket( $content )
{
$fp = fsockopen($this->address, 1400 /* Port */, $errno, $errstr, 10);
if (!$fp)
throw new Exception("Error opening socket: ".$errstr." (".$errno.")");
fputs ($fp, $content);
$ret = "";
while (!feof($fp)) {
$ret.= fgetss($fp,128); // filter xml answer
}
fclose($fp);
if(strpos($ret, "200 OK") === false)
throw new Exception("Error sending command: ".$ret);
// echo "sendPacketDebug: "; //DEBUG
// print_r($ret);
$array = preg_split("/\n/", $ret);
return $array[count($array) - 1];
}
}
?>