Sonos Modul

Wie ist die Planung diesbezüglich?

Bezugnehmend der Planung.

Da hätte ich sehr viele Funktionen die verwirklichen möchte.

Ich möchte die Songs in Szenen einbinden die ich über KNX Schalter aufrufe inkl Gruppen erstellen.
Bedienung über KNX Schalter Bsp. Play liste abspielen, alle Zonen auf stop bei Zentral aus,
Sprachausgabe über Ivona, Türglocke einbinden.

Von der Ferne möchte ich auch eine Sprach dabei abspielen, wenn mal meine Frau ihr Telefon auf lautlos hat und ich sie vergeblich anrufe :D.

Für hinweise wie ich mein Problem lösen könnte wäre ich sehr dankbar.

Ich komme leider nicht mehr weiter wegen den Problem.

Gruß und ein schönes Osterfest

Manfred

Ich denke, deine spezifische Problemstellung sollte in einem extra Thread behandelt werden. Dort kannst Du zeigen, wie weit Du bist und was nicht funktioniert.

Habe ich aber komme aber nicht wirklich weiter.

Kann wer was mit der Fehlermeldung anfangen die kommt, wenn ich eine Zone mit der Sonos App auf eine Gruppe zusammen füge und dann was abspiele.

Die Liste wird unendlich Lang. Lieg scheinbar daran, daß das Modul nicht die Zonen selber in die Gruppe hinzugefügt hat und jedes Mal, wenn das Sonos Script den Status der einzelnen Zonen Abruft kommt es zu Fehlermeldung und der Status wird auch nicht angezeigt was gerade läuft.

Was mach ich falsch oder darf man die Gruppen Schaltung nur mehr über den IPS verknüpfen?

Hallo,

wenn man den Play/Pause-Button eines Sonos-Lautsprechers, der gerade keine Musik wiedergibt, drückt, so wird dieser automatisch mit einem derzeit wiedergebenden Sonos-Lautsprecher gruppiert. Gibt es zum Zeitpunkt mehrere aktive Speaker-Gruppen , so verbindet sich der inaktive Lautsprecher mit derjenigen Gruppe, die als letzte mit der Wiedergabe begonnen hat.

Des Weiteren lassen sich die Lautsprecher auf demselben Weg wieder voneinander entkoppeln lassen. Außerdem wechselt der gewählte Lautsprecher durch das Button-Gedrückthalten nach und nach durch alle derzeit aktiven Gruppen. Diese müssen dann allerdings auch alle unterschiedliche Quellen wiedergeben.

Lässt sich dieser Tastendruck durch das Modul nachstellen?

Viele Grüße…

Hallo Zusamen

Ich bekomme Öfters mal eine Fehlermeldung.

27.04.2018 08:27:17 | ScriptEngine | Ergebnis für Ereignis 39409
<br />
<b>Fatal error</b>: Uncaught exception ‚Exception‘ with message ‚Error during Soap Call: UPnPError s:Client 701 (ERROR_AV_UPNP_AVT_INVALID_TRANSITION)‘ in C:\IP-Symcon\modules\SymconSonos\Sonos\sonosAccess.php:556
Stack trace:
#0 C:\IP-Symcon\modules\SymconSonos\Sonos\sonosAccess.php(286): SonosAccess->processSoapCall(’/MediaRenderer/…’, ‚urn:schemas-upn…‘, ‚Pause‘, Array)
#1 C:\IP-Symcon\modules\SymconSonos\Sonos\module.php(399): SonosAccess->Pause()
#2 C:\IP-Symcon\scripts__generated.inc.php(331): Sonos->Pause()
#3 C:\IP-Symcon\scripts\48924.ips.php(15): SNS_Pause(53384)
#4 {main}
thrown in <b>C:\IP-Symcon\modules\SymconSonos\Sonos\sonosAccess.php</b> on line <b>556</b><br />
Abort Processing during Fatal-Error: Uncaught exception ‚Exception‘ with message ‚Error during Soap Call: UPnPError s:Client 701 (ERROR_AV_UPNP_AVT_INVALID_TRANSITION)‘ in C:\IP-Symcon\modules\SymconSonos\Sonos\sonosAccess.php:556
Stack trace:
#0 C:\IP-Symcon\modules\SymconSonos\Sonos\sonosAccess.php(286): SonosAccess->processSoapCall(’/MediaRenderer/…’, ‚urn:schemas-upn…‘, ‚Pause‘, Array)
#1 C:\IP-Symcon\modules\SymconSonos\Sonos\module.php(399): SonosAccess->Pause()
#2 C:\IP-Symcon\scripts__generated.inc.php(331): Sonos->Pause()
#3 C:\IP-Symcon\scripts\48924.ips.php(15): SNS_Pause(53384)
#4 {main}
thrown
Error in Script C:\IP-Symcon\modules\SymconSonos\Sonos\sonosAccess.php on Line 556

Was hat diese Fehlermeldung zu bedeuten.

Mfg Stefan

Bin mit dem Problem leider noch nicht weiter - entgegen der Doku finde ich in dem _updateStatus Skript keinerlei Code zum Aktualisieren der PlayMode und Crossfade Variablen. Kann das jemand bestätigen ?

Mittlerweile konnte ich das ergänzen, ich glaube, Thorsten hat da nur die beiden relevanten Variablen übersehen:

<?
include_once("../modules/SymconSonos/Sonos/sonosAccess.php");
include_once("../modules/SymconSonos/Sonos/radio_stations.php");

$ip                    = IPS_GetProperty(IPS_GetParent($_IPS["SELF"]), "IPAddress");
$timeout               = IPS_GetProperty(IPS_GetParent($_IPS["SELF"]), "TimeOut");
$frequency             = IPS_GetProperty(IPS_GetParent($_IPS["SELF"]), "UpdateStatusFrequency");
$frequencyNotAvailable = IPS_GetProperty(IPS_GetParent($_IPS["SELF"]), "UpdateStatusFrequencyNA");

// Get all needed Variable IDs
$vidInstance      = IPS_GetParent($_IPS["SELF"]);
$vidVolume        = @IPS_GetObjectIDByName("Volume",        $vidInstance);
$vidMute          = @IPS_GetObjectIDByName("Mute",          $vidInstance);
$vidLoudness      = @IPS_GetObjectIDByName("Loudness",      $vidInstance);
$vidBass          = @IPS_GetObjectIDByName("Bass",          $vidInstance);
$vidTreble        = @IPS_GetObjectIDByName("Treble",        $vidInstance);
$vidBalance       = @IPS_GetObjectIDByName("Balance",       $vidInstance);
$vidMemberOfGroup = @IPS_GetObjectIDByName("MemberOfGroup", $vidInstance);
$vidStatus        = @IPS_GetObjectIDByName("Status",        $vidInstance);
$vidRadio         = @IPS_GetObjectIDByName("Radio",         $vidInstance);
$vidSleeptimer    = @IPS_GetObjectIDByName("Sleeptimer",    $vidInstance);
$vidNowPlaying    = @IPS_GetObjectIDByName("nowPlaying",    $vidInstance);
$vidGroupMembers  = @IPS_GetObjectIDByName("GroupMembers",  $vidInstance);
$vidDetails       = @IPS_GetObjectIDByName("Details",       $vidInstance);
$vidCoverURL      = @IPS_GetObjectIDByName("CoverURL",      $vidInstance);
$vidStationID     = @IPS_GetObjectIDByName("StationID",     $vidInstance);
$vidContentStream = @IPS_GetObjectIDByName("ContentStream", $vidInstance);
$vidArtist        = @IPS_GetObjectIDByName("Artist",        $vidInstance);
$vidTitle         = @IPS_GetObjectIDByName("Title",         $vidInstance);
$vidAlbum         = @IPS_GetObjectIDByName("Album",         $vidInstance);
$vidTrackDuration = @IPS_GetObjectIDByName("TrackDuration", $vidInstance);
$vidPosition      = @IPS_GetObjectIDByName("Position",      $vidInstance);
// *************************************
// Addet Crossfade and Transportsettings
// *************************************
$vidCrossfade     = @IPS_GetObjectIDByName("Crossfade",     $vidInstance);
$vidPlaymode      = @IPS_GetObjectIDByName("PlayMode",      $vidInstance);

// If the Sonos instance is not available update of grouping makes no sense
if ( $timeout && Sys_Ping($ip, $timeout) == false ){
  @IPS_SetScriptTimer($_IPS["SELF"], $frequencyNotAvailable );
  die('Sonos instance '.$ip.' is not available');
}

@IPS_SetScriptTimer($_IPS["SELF"], $frequency );

$sonos = new SonosAccess($ip);

$status = $sonos->GetTransportInfo();

SetValueInteger($vidVolume, $sonos->GetVolume());
if($vidMute)      SetValueInteger($vidMute,     $sonos->GetMute()     );
if($vidLoudness)  SetValueInteger($vidLoudness, $sonos->GetLoudness() );
if($vidBass)      SetValueInteger($vidBass,     $sonos->GetBass()     );
if($vidTreble)    SetValueInteger($vidTreble,   $sonos->GetTreble()   );
// *************************************
// Addet Crossfade and Transportsettings
// *************************************
if($vidCrossfade) SetValueInteger($vidCrossfade,$sonos->GetCrossfade());
if($vidPlaymode)  SetValueInteger($vidPlaymode, $sonos->GetTransportsettings());

if($vidBalance){
  $leftVolume  = $sonos->GetVolume("LF");
  $rightVolume = $sonos->GetVolume("RF");

  if ( $leftVolume == $rightVolume ){
    SetValueInteger($vidBalance, 0);
  }elseif ( $leftVolume > $rightVolume ){
    SetValueInteger($vidBalance, $rightVolume - 100 );
  }else{
    SetValueInteger($vidBalance, 100 - $leftVolume );
  }
}

$MemberOfGroup = 0;
if($vidMemberOfGroup) $MemberOfGroup = GetValueInteger($vidMemberOfGroup);

if ($MemberOfGroup){
  // If Sonos is member of a group, use values of Group Coordinator
  SetValueInteger($vidStatus, GetValueInteger(IPS_GetObjectIDByName("Status", $MemberOfGroup)));
  $actuallyPlaying = GetValueString(IPS_GetObjectIDByName("nowPlaying", $MemberOfGroup));
  SetValueInteger($vidRadio, GetValueInteger(IPS_GetObjectIDByName("Radio", $MemberOfGroup)));
  if($vidSleeptimer)    SetValueInteger($vidSleeptimer,   @GetValueInteger(IPS_GetObjectIDByName("Sleeptimer", $MemberOfGroup)));
  if($vidCoverURL)      SetValueString($vidCoverURL,      @GetValueString(IPS_GetObjectIDByName("CoverURL", $MemberOfGroup)));
  if($vidContentStream) SetValueString($vidContentStream, @GetValueString(IPS_GetObjectIDByName("ContentStream", $MemberOfGroup)));
  if($vidArtist)        SetValueString($vidArtist,        @GetValueString(IPS_GetObjectIDByName("Artist", $MemberOfGroup)));
  if($vidAlbum)         SetValueString($vidAlbum,         @GetValueString(IPS_GetObjectIDByName("Album", $MemberOfGroup)));
  if($vidTrackDuration) SetValueString($vidTrackDuration, @GetValueString(IPS_GetObjectIDByName("TrackDuration", $MemberOfGroup)));
  if($vidPosition)      SetValueString($vidPosition,      @GetValueString(IPS_GetObjectIDByName("Position", $MemberOfGroup)));
  if($vidTitle)         SetValueString($vidTitle,         @GetValueString(IPS_GetObjectIDByName("Title", $MemberOfGroup)));
  if($vidDetails)       SetValueString($vidDetails,       @GetValueString(IPS_GetObjectIDByName("Details", $MemberOfGroup)));
}else{
  SetValueInteger($vidStatus, $status);
  // Titelanzeige
  $currentStation = 0;

  if ( $status <> 1 ){
    // No title if not playing
    $actuallyPlaying = "";
  }else{
    $positionInfo = $sonos->GetPositionInfo();
    $mediaInfo    = $sonos->GetMediaInfo();

    if ($positionInfo["streamContent"]){
      $actuallyPlaying = $positionInfo["streamContent"];
    } else {
      $actuallyPlaying = $positionInfo["title"]." | ".$positionInfo["artist"];
    }

    // start find current Radio in VariableProfile
    $radioStations       = get_available_stations();
    $playingRadioStation = '';
    foreach ($radioStations as $radioStation) {
      if($radioStation["url"] == htmlspecialchars_decode($mediaInfo["CurrentURI"])){
        $playingRadioStation = $radioStation["name"];
        $image               = $radioStation["logo"];
        break;
      }
    }

    if( $playingRadioStation == ''){
      foreach ((new SimpleXMLElement($sonos->BrowseContentDirectory('R:0/0')['Result']))->item as $item) {
        if ($item->res == htmlspecialchars_decode($mediaInfo["CurrentURI"])){
          $playingRadioStation = (string)$item->xpath('dc:title')[0];
          break;
        }
      }
    }

    $Associations = IPS_GetVariableProfile("Radio.SONOS")["Associations"];
    if(isset($playingRadioStation)){
      foreach($Associations as $key=>$station) {
        if( $station["Name"] == $playingRadioStation ){
          $currentStation = $station["Value"];
          break;
        }
      }
    }
    // end find current Radio in VariableProfile
  }
  SetValueInteger($vidRadio, $currentStation);

  // detailed Information
  if($vidContentStream)   SetValueString($vidContentStream, @$positionInfo['streamContent']);
  if($vidArtist)          SetValueString($vidArtist,        @$positionInfo['artist']);
  if($vidAlbum)           SetValueString($vidAlbum,         @$positionInfo['album']);
  if($vidTrackDuration)   SetValueString($vidTrackDuration, @$positionInfo['TrackDuration'] );
  if($vidPosition)        SetValueString($vidPosition,      @$positionInfo['RelTime']);
  if($vidTitle){
    if(@$mediaInfo['title']){
      SetValueString($vidTitle, @$mediaInfo['title']);
    }else{
      SetValueString($vidTitle, @$positionInfo['title']);
    }
  }
  if($vidDetails){
    if (!isset($stationID)) $stationID = "";
    if(isset($positionInfo)){
      // SPDIF and analog
      if(preg_match('/^RINCON_/', $mediaInfo['title']) ){
        $detailHTML = "";
      // Radio or stream(?)
      }elseif($mediaInfo['title']){
        // get stationID if playing via TuneIn
       $stationID = preg_replace("#(.*)x-sonosapi-stream:(.*?)\?sid(.*)#is",'$2',$mediaInfo['CurrentURI']);
       if (!isset($image)) $image = "";
       if($stationID && $stationID[0]=="s"){
	 if(@GetValueString($vidStationID) == $stationID){
            $image = GetValueString($vidCoverURL);
	 }else{
            $serial = substr(IPS_GetProperty($vidInstance ,"RINCON"), 7,12);
            $image = preg_replace('#(.*)<LOGO>(.*?)\</LOGO>(.*)#is','$2',@file_get_contents("http://opml.radiotime.com/Describe.ashx?c=nowplaying&id=".$stationID."&partnerId=IAeIhU42&serial=".$serial));
	 }
       }else{
          $stationID = "";
       }
        $detailHTML =   "<table width=\"100%\">
                          <tr>
                            <td>
                              <div style=\"text-align: right;\">
                                <div><b>".$positionInfo['streamContent']."</b></div>
                                <div> </div>
                                <div>".$mediaInfo['title']."</div>
                              </div>
                            </td>";

			if(strlen($image) > 0) {
			   $detailHTML .= "<td width=\"170px\" valign=\"top\">
                              <div style=\"width: 170px; height: 170px; perspective: 170px; right: 0px; margin-bottom: 10px;\">
                              	<img src=\"".@$image."\" style=\"max-width: 170px; max-height: 170px; -webkit-box-reflect: below 0 -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0.88, transparent), to(rgba(255, 255, 255, 0.5))); transform: rotateY(-10deg) translateZ(-35px);\">
                              </div>
                            </td>";
			}

         $detailHTML .= "</tr>
                        </table>";

      // normal files
      }else{
        $durationSeconds        = 0;
        $currentPositionSeconds = 0;
        if($positionInfo['TrackDuration'] && preg_match('/\d+:\d+:\d+/', $positionInfo['TrackDuration']) ){
          $durationArray          = explode(":",$positionInfo['TrackDuration']);
          $currentPositionArray   = explode(":",$positionInfo['RelTime']);
          $durationSeconds        = $durationArray[0]*3600+$durationArray[1]*60+$durationArray[2];
          $currentPositionSeconds = $currentPositionArray[0]*3600+$currentPositionArray[1]*60+$currentPositionArray[2];
        }
        $detailHTML =   "<table width=\"100%\">
                          <tr>
                            <td>
                              <div style=\"text-align: right;\">
                                <div><b>".$positionInfo['title']."</b></div>
                                <div> </div>
                                <div>".$positionInfo['artist']."</div>
                                <div>".$positionInfo['album']."</div>
                                <div> </div>
                                <div>".$positionInfo['RelTime']." / ".$positionInfo['TrackDuration']."</div>
                              </div>
                            </td>";

         if(isset($positionInfo['albumArtURI'])) {
            $detailHTML .= "<td width=\"170px\" valign=\"top\">
                              <div style=\"width: 170px; height: 170px; perspective: 170px; right: 0px; margin-bottom: 10px;\">
                              	<img src=\"".@$positionInfo['albumArtURI']."\" style=\"max-width: 170px; max-height: 170px; -webkit-box-reflect: below 0 -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0.88, transparent), to(rgba(255, 255, 255, 0.5))); transform: rotateY(-10deg) translateZ(-35px);\">
                              </div>
                            </td>";
         }

         $detailHTML .= "</tr>
                        </table>";
      }
    }
    @SetValueString($vidDetails, $detailHTML);
    if($vidCoverURL){
		if((isset($image)) && (strlen($image) > 0)) {
		  SetValueString($vidCoverURL, $image);
		}else{
	     SetValueString($vidCoverURL, @$positionInfo['albumArtURI']);
		}
	 }
  SetValueString($vidStationID,$stationID);
  }

  // Sleeptimer
  if ($vidSleeptimer){
    $sleeptimer = $sonos->GetSleeptimer();
    if($sleeptimer){
      $SleeptimerArray = explode(":",$sonos->GetSleeptimer());

      $SleeptimerMinutes = $SleeptimerArray[0]*60+$SleeptimerArray[1];
      if($SleeptimerArray[2])
        $SleeptimerMinutes = $SleeptimerMinutes + 1;
    }else{
      $SleeptimerMinutes = 0;
    }

    SetValueInteger($vidSleeptimer, $SleeptimerMinutes);
  }
}

$nowPlaying   = GetValueString($vidNowPlaying);
if ($actuallyPlaying <> $nowPlaying)
    SetValueString($vidNowPlaying, $actuallyPlaying);

// Set Group Volume
$groupMembers        = GetValueString($vidGroupMembers);
$groupMembersArray   = Array();
if($groupMembers)
  $groupMembersArray = array_map("intval", explode(",",$groupMembers));
$groupMembersArray[] = $vidInstance;

$GroupVolume = 0;
foreach($groupMembersArray as $key=>$ID) {
  $GroupVolume += GetValueInteger(IPS_GetObjectIDByName("Volume", $ID));
}

SetValueInteger(IPS_GetObjectIDByName("GroupVolume", IPS_GetParent($_IPS["SELF"])), intval(round($GroupVolume / sizeof($groupMembersArray))));
?>

es sind nur die beiden einkommentierten Ergänzungen in der _updateStatus.php notwendig. Ich weiß, dass das dann wieder beim nächsten update verschwindet - daher die Bitte: Kannst du das im Modul übernehmen ?

Der Autor des Moduls scheint dieses nicht mehr zu pflegen … Ich glaube, es wird diesbezüglich keine Antwort geben.

Das wäre schade, da es ja schon ein „Grundmodul“ ist - ich glaube allerdings nicht, dass Kugelberg das Modul aufgibt, möglicherweise wartet er nur den Release von 5.0 Stable ab.

Anderenfalls hatte Fonzo schon einen prima Fork und könnte ggf. in die Bresche springen…

Abwarten,
ich vermute mal, Thorsten hat zu viel um die Ohren.
Und TomW hat sich erst mal selbst geholfem.
Ich nutze auch dieses Modul, aber nur für I-Net Radion auf meinen Sonos, daher fallen hier, solche Dinge nicht auf.

Hallo,

ich komme gerade nicht zu intensiven tests, daher wäre feedback gut:

Version 1.5.7 ist hochgeladen mit den Folgenden Änderungen:

  • es gibt jetzt SNS_SetTransportURI( )
  • Crossfade and Transportsettings sind im _updatStatus aufgenommen

Gruß,
Thorsten

Hi,

kann es sein, dass hier ein Stereo Paar oder 5.1 (also ein zusammenschluss mehrerer Boxen) involviert ist?
Ich glaube mich zu erinnern, dass es zu solchen Meldungen kommt, wenn man die Falsche Box als Instanz anlegt.

Gruß,
Thorsten

Geil, geil, geil.

Danke, Thorsten !

ich hoffe mal das Thorsten weitermacht,
Manchmal hat man halt nicht so viel Zeit, und das was bis jetzt gelaufen ist, ist schon Klasse !
Klar, könnte man heute noch mehr machen, aber ich würde da auch erst auf IPS 5 stable warten.

Danke, Thorsten !:slight_smile:

Jetzt macht Euch nicht gleich verrückt, man hat ja auch noch andere Dinge zu tun und Thorsten hat sich ja gemeldet. Entscheidender wird sein ob IP-Symcon Zugriff auf die Sonos API bekommt, das wäre ein wichtiger Schritt damit man Zukünftig sicher stellen kann das alles auch kompatibel läuft und dies auch so bleibt. Wobei das dann wahrscheinlich ja wieder ein ganz neues Modul wäre wenn man die API nutzt. Mit IP-Symcon 5 kommen auch viele Neuerungen die es dann durchaus z.B. auch noch einfacher machen Sonos automatisch einzurichten.

Hallo Thorsten es fehlen die Type Hints für


SNS_PlayFiles
SNS_PlayFilesGrouping

das führt dann ab IPS 5 zu vermehrten Meldungen im Meldungsfenster.

Moin,

hmmm…

Im coding steht:


public function PlayFiles(array $files, string $volumeChange) { ... }
public function PlayFilesGrouping(array $instances, array $files, string $volumeChange) { ... }

–> kann es an dem array liegen? Wird das in IPS nicht als Type Hint unterstützt?!
Würde auch für die Fehglermeldung sprechen:
Parameter files in function SNS_PlayFiles has no type hint. Please use either ‚bool‘, ‚int‘, ‚float‘ or ‚string‘.

Gruß,
Thorsten

IPS kann kein Array als Instanz-Funktion.
Alternativ einen String nutzen (Json encodiertes Array nutzen).
Michael

Hi,

ja, so ist es.
Ich bin hauptberuflich Software Entwickler, und wenn ich den Tag über 8-10 Stunden Coding vor den Augen habe, ist meine Motivation in meiner Freizeit eher gedämpft.

–> Meine Anforderungen sind abgedeckt und funktionieren sauber.
Und wenn was Sinnvolles gewünscht ist, baue ich es auch gerne ein. Nur halt nicht sofort.

Wenn jemand Kleinigkeiten einbauen will, gerne.
Nur ein Kompletter Umbau wie neulich mal geschehen ist für mich nicht abschätzbar. Das ist mir zu heikel, denn im Zweifel mache ich dann den Support.

Also:
Jeder ist eingeladen gute und überschaubare Änderungen einzuchecken.
Wenn es mehr ist, lieber in kleiner Häppchen, damit ich es in Vertretbarem Rahmen nachvollziehen kann.

Gruß,
Thorsten