Sonos im Tablet UI - Beispielkonfiguration

Begonnen von Sturi2011, 12 Dezember 2016, 23:23:19

Vorheriges Thema - Nächstes Thema

Sturi2011

Hallo,

ich wollte euch mal meine Sonos Integration vorstellen. Dazu habe ich die Scripte von Reinerlein und http://www.juergenstechnikwelt.de/ genommen und etwas angepasst. Ziel war, die Buttons im Tablet UI responsive zu machen und auch auf Aktionen mit der Sonos Anwendung zu reagieren.

Die Auswahl einer Playlist spielt diese ab. Der Play Button verwandelt sich in einen Pause Button. Das Select Element zeigt die aktuelle Playliste an.
Ein Klick auf Stop beendet die Wiedergabe. Das Select Element zeigt wieder seinen Titel Favoriten. Der Pause Button verwandelt sich wieder in einen Play Button.
Ein Klick auf Pause pausiert die Wiedergabe. Der Pause Button verwandelt sich in einen Play Button.

Ein Play in der Sonos App werwandelt den Play in einen Pause Button. Ein Pause in der Sonos App verwandelt den Pause in einen Play Button.

Gelöst habe ich das Ganze, indem ich alle Buttons den Status eines Dummys ändern lasse. Man benötigt für jedes Sonos Device einen Dummy mit dem Namen DM_Sonos_Raum

define DM_Sonos_Kueche dummy

Dieser Dummy wird mit Notifys überwacht. Die Notifys müssen ebenfalls für jeden Sonos Player angelegt werden.

define N_Sonos_Kueche_Play notify DM_Sonos_Kueche.PLAY {fhem "set Sonos_Kueche PLAY"}
define N_Sonos_Kueche_Pause notify DM_Sonos_Kueche.PAUSE {fhem "set Sonos_Kueche PAUSE"}
define N_Sonos_Kueche_Stop notify DM_Sonos_Kueche.STOP {fhem "set Sonos_Kueche STOP";;;; fhem "setreading DM_Sonos_Kueche default Favoriten"}


Für die Überwachung des Devices auf Änderungen, die nicht von Fhem kommen ist ein weiteres Notify pro device sowie ein event-on-change-reading am Device nötig.

define N_Sonos_Kueche_Device notify Sonos_Kueche:transportState.* { if (ReadingsVal("Sonos_Kueche","transportState","") eq "PAUSED_PLAYBACK") { fhem("set DM_Sonos_Kueche PAUSE")} elsif (ReadingsVal("Sonos_Kueche","transportState","") eq "STOPPED") { fhem("set DM_Sonos_Kueche STOP") } elsif (ReadingsVal("Sonos_Kueche","transportState","") eq "PLAYING") { fhem("set DM_Sonos_Kueche PLAY") }}

attr Sonos_Kueche event-on-change-reading .*


Für das Abspielen der Playlisten benötigen wir ein weiteres Notify

define N_SonosPlaylistAbspielen notify DM_Sonos_.* {SonosPlaylistStarten($EVENT)}

In der 99_MyUtils.pm müssen nun noch die folgenden Scripte eingebaut werden:

Playlisten erzeugen und in allen Fhem Dummys speichern
MeinePlayList()
{
   #Kueche ist der Master Player, daher Playlist hier ermitteln
   #Das Script muss manuell ausgeführt werden um die Playlisten einzulesen (nach jeder Änderung der Playlists)
   fhem("get Sonos_Kueche Playlists;");
   my $Playlist = ReadingsVal("Sonos_Kueche", "LastActionResult", "Keine");
   my @Playlists = split('GetPlaylists: ', $Playlist);
   my $playlistneu = 'Ostseewelle MV,'.$Playlists[1].',Favoriten';
   #zusätzllich noch unseren einzigen Radiosender dazu packen
   #playlist für TABUI select aufbereiten
   $playlistneu =~ s/,/:/g;
   $playlistneu =~ s/\"//g;

   #fhem("setreading duSonosPlaylists Playlist ".$playlistneu);
   fhem("setreading DM_Sonos_.* Playlist ".$playlistneu);
   fhem("setreading DM_Sonos_.* default Favoriten");
}


Abspielen der Playlisten
sub SonosPlaylistStarten($)
{
   my ($Playlist) = @_;
   my @player = split(' ',$Playlist);
   my @Liste = split($player[0]." ", $Playlist);

   $Liste[1] =~ s/ /%20/g;

   # falls der Radiosender gewählt wurde
   if($Playlist =~ /Ostseewelle/)
   {
      fhem("set $player[0] StartRadio $Liste[1]");
   }
   elsif($Playlist =~ /Favoriten/)
   {
   }
   else
   {
      fhem("set $player[0] StartPlaylist $Liste[1]");
   }
   my $SelPlayList = uri_unescape($Liste[1]);
   fhem("set DM_$player[0] PLAY");
   fhem("setreading DM_$player[0] default $SelPlayList");
}


Zu guter Letzt muss das Ganze noch in der Tablet UI definiert werden. Dies geschieht mit folgendem Code:

<li data-row="1" data-col="2" data-sizex="3" data-sizey="2">
   <header>Sonos Küche</header>
      <div class="container top-space">
         <div data-type="label" data-device="Sonos_Kueche" data-get="infoSummarize1"></div>
    <div data-type="select" data-device="DM_Sonos_Kueche" data-get="default" data-list="Playlist" data-set="Sonos_Kueche" class="wider large"></div>
      </div>
<div class="container inline">
  <div class="left">
   <div data-type="push" data-device="Sonos_Kueche" data-set-on="PREVIOUS" data-icon="fa-step-backward" class="cell"></div>
   <div data-type="switch" data-device="DM_Sonos_Kueche" data-set-on="PLAY" data-set-off="PAUSE" data-states='["PLAY","PAUSE","STOP"]' data-icons='["fa-pause","fa-play","fa-play"]' data-colors='["#555","#555","#555"]' data-background-colors='["#555","#555","#555"]' data-background-icon="fa-circle-thin" data-background-color="#555" class="cell"></div>
   <div data-type="push" data-device="Sonos_Kueche" data-set-on="NEXT" data-icon="fa-step-forward" class="cell"></div>
   <div data-type="push" data-device="DM_Sonos_Kueche" data-set-on="STOP" data-icon="fa-stop" class="cell"></div>
   <div data-type="multistatebutton" data-device="Sonos_Kueche" data-get="Shuffle" data-get-on='["0","1"]' data-set='["Shuffle 1","Shuffle 0"]' data-set-default="Shuffle 0" data-icons='["fa-random","fa-random"]' data-colors='["#2A2A2A","#00FF00"]' data-background-colors='["#555", "#555"]' background-color="#0000ff" off-color="#0000ff" class="cell"></div>
  </div>
</div>
<div class="container inline">
  <div class="left">
   <div data-type="spinner" data-device="Sonos_Kueche" data-get="Volume" data-set="Volume" data-max="50" data-step="4" data-icon-left="fa-volume-down" data-icon-right="fa-volume-up" class="top-space-2x spinner value" style="width: 200px; max-width: 200px; height: 50px; line-height: 45px; color: rgb(204, 204, 204); background-color: rgb(74, 74, 74);"><div class="lefticon fa fa-volume-down fa-lg fa-fw" style="color: rgb(170, 170, 170);"></div><div class="levelArea" style="width: 50%;"><div class="levelRange" style="left: 0px; width: 21.4286px; background: rgb(170, 105, 0) none repeat scroll 0% 0%;"></div></div><div class="righticon fa fa-volume-up fa-lg fa-fw" style="color: rgb(170, 170, 170);"></div></div>
  </div>
</div>
</li>


Sicher lassen sich die Notifys noch weiter zusammenfassen. Der Übersichtlichkeit halber sind sie hier getrennt ausgeführt.

Gruß Andreas

Noxus

Hallo Sturi,

wollte kurz Rückmeldung geben, dass alles relativ Problemlos funktioniert hat - nutze zwar nicht die Playlist, sondern Favoriten - aber vom Grundprinzip bleibt es ähnlich.
Vielen Dank für die Bereitstellung deiner Konfig.

Sturi2011

Hi,

danke für die positive Rückmeldung. Ich bastele gerade daran, das mit dem Gruppieren etwas eleganter zu lösen...

Gruß Andreas

Sturi2011

Hi,

Hier nun die Funktion für das Gruppieren der Sonos Player:

Es stehen 4 Gruppen oder ungruppiert zur Auswahl. An jedem Sonos Player Menu gibt es ein Circlemenu.
Mit diesem kann der Player einer der 4 Gruppen zugewiesen werden. Realisiert habe ich das ganze mit den
oben beschriebenen DM_Sonos_Raum Dummys und einem Zusätzlichen Attribut. Wenn Ihr den obigen Code
im Tablet UI eingesetzt habt benötigt Ihr nur ein zusätliches Notify pro Player und die Funktion GroupSonos
in der 99_MyUtils.pm. Zusätzlich ist eine Anpassung im FTUI HTML nötig.

GroupSonos für 99_MyUtils.pm
sub GroupSonos() {
my $search="DM_Sonos_";
my @GR1;
my @GR2;
my @GR3;
my @GR4;
my @GREmpty;
my ($devGroup,$CMDGR1,$CMDGR2,$CMDGR3,$CMDGR4,$CMDGREmpty);
foreach my $dev (sort keys %defs) {
    if($defs{$dev}{NAME} =~ m/$search/){
      $devGroup = ReadingsVal($defs{$dev}{NAME},"Gruppe","");
  if ($devGroup eq "1"){
  push @GR1, $defs{$dev}{NAME};
  }
  if ($devGroup eq "2"){
  push @GR2, $defs{$dev}{NAME};
  }
  if ($devGroup eq "3"){
  push @GR3, $defs{$dev}{NAME};
  }
  if ($devGroup eq "4"){
  push @GR4, $defs{$dev}{NAME};
  }
  if ($devGroup eq "-"){
  push @GREmpty, $defs{$dev}{NAME};
  }
    }
}
if (scalar @GR1 > 0){
$CMDGR1="set Sonos Groups [";
foreach my $n (@GR1) {
$n =~ s/^(DM_)//gi;
$CMDGR1 .="$n,";
}
chop($CMDGR1);
$CMDGR1 .="]";
Log 3, $CMDGR1;
fhem("$CMDGR1");
}
if (scalar @GR2 > 0){
$CMDGR2="set Sonos Groups [";
foreach my $n (@GR2) {
$n =~ s/^(DM_)//gi;
$CMDGR2 .="$n,";
}
chop($CMDGR2);
$CMDGR2 .="]";
Log 3, $CMDGR2;
fhem("$CMDGR2");
}
if (scalar @GR3 > 0){
$CMDGR3="set Sonos Groups [";
foreach my $n (@GR3) {
$n =~ s/^(DM_)//gi;
$CMDGR3 .="$n,";
}
chop($CMDGR3);
$CMDGR3 .="]";
Log 3, $CMDGR3;
fhem("$CMDGR3");
}
if (scalar @GR4 > 0){
$CMDGR4="set Sonos Groups [";
foreach my $n (@GR4) {
$n =~ s/^(DM_)//gi;
$CMDGR4 .="$n,";
}
chop($CMDGR4);
$CMDGR4 .="]";
Log 3, $CMDGR4;
fhem("$CMDGR4");
}
if (scalar @GREmpty > 0){
$CMDGREmpty="set Sonos Groups";
foreach my $n (@GREmpty) {
$n =~ s/^(DM_)//gi;
$CMDGREmpty .=" [$n],";
}
chop($CMDGREmpty);
Log 3, $CMDGREmpty;
fhem("$CMDGREmpty");
}
}



Zusätzliches Notify pro Sonos Player
define N_Sonos_Kueche_Group notify DM_Sonos_Kueche:Gruppe.* {GroupSonos()}

Der HTML Code erseztz den Abschnit ders Sliders und enthält den Slider sowie ein Circlemenu für die Tablet UI:
   <header>Sonos Flur</header>
      <div class="container top-space">
         <div data-type="label" data-device="Sonos_Flur" data-get="infoSummarize1"></div>
    <div data-type="select" data-device="DM_Sonos_Flur" data-get="default" data-list="Playlist" data-set="Sonos_Flur" class="wider large"></div>
      </div>
<div class="container inline">
  <div class="left">
   <div data-type="push" data-device="Sonos_Flur" data-set-on="PREVIOUS" data-icon="fa-step-backward" class="cell"></div>
   <div data-type="switch" data-device="DM_Sonos_Flur" data-set-on="PLAY" data-set-off="PAUSE" data-states='["PLAY","PAUSE","STOP"]' data-icons='["fa-pause","fa-play","fa-play"]' data-colors='["#555","#555","#555"]' data-background-colors='["#555","#555","#555"]' data-background-icon="fa-circle-thin" data-background-color="#555" class="cell"></div>
   <div data-type="push" data-device="Sonos_Flur" data-set-on="NEXT" data-icon="fa-step-forward" class="cell"></div>
   <div data-type="push" data-device="DM_Sonos_Flur" data-set-on="STOP" data-icon="fa-stop" class="cell"></div>
   <div data-type="multistatebutton" data-device="Sonos_Flur" data-get="Shuffle" data-get-on='["0","1"]' data-set='["Shuffle 1","Shuffle 0"]' data-set-default="Shuffle 0" data-icons='["fa-random","fa-random"]' data-colors='["#2A2A2A","#00FF00"]' data-background-colors='["#555", "#555"]' background-color="#0000ff" off-color="#0000ff" class="cell"></div>
  </div>
</div>
<div class="container inline narrow">
   <div data-type="spinner" data-device="Sonos_Flur" data-get="Volume" data-set="Volume" data-max="50" data-step="4" data-icon-left="fa-volume-down" data-icon-right="fa-volume-up" class="top-space-2x spinner value" style="width: 200px; max-width: 200px; height: 50px; line-height: 45px; color: rgb(204, 204, 204); background-color: rgb(74, 74, 74);"><div class="lefticon fa fa-volume-down fa-lg fa-fw" style="color: rgb(170, 170, 170);"></div><div class="levelArea" style="width: 50%;"><div class="levelRange" style="left: 0px; width: 21.4286px; background: rgb(170, 105, 0) none repeat scroll 0% 0%;"></div></div><div class="righticon fa fa-volume-up fa-lg fa-fw" style="color: rgb(170, 170, 170);"></div></div>
</div>
   <div data-type="circlemenu" class="cell circlemenu narrow">
    <ul class="menu">
      <li><div data-type="label" data-device="DM_Sonos_Flur" data-get="Gruppe" data-color="#555" class="circleborder big" style="Width:52px;Height=55px"></div></li>
      <li><div data-type="push" data-device="DM_Sonos_Flur"
   data-set="Gruppe"
               data-cmd="setreading"
   data-set-on="1"
               data-icon="">1</div></li>
      <li><div data-type="push" data-device="DM_Sonos_Flur"
   data-set="Gruppe"
               data-cmd="setreading"
   data-set-on="2"
               data-icon="">2</div></li>
      <li><div data-type="push" data-device="DM_Sonos_Flur"
   data-set="Gruppe"
               data-cmd="setreading"
   data-set-on="3"
               data-icon="">3</div></li>     
  <li><div data-type="push" data-device="DM_Sonos_Flur"
   data-set="Gruppe"
               data-cmd="setreading"
   data-set-on="4"
               data-icon="">4</div></li>
      <li><div data-type="push" data-device="DM_Sonos_Flur"
   data-set="Gruppe"
               data-cmd="setreading"
   data-set-on="-"
               data-icon="">-</div></li>    
    </ul>
   </div>


Viel Spaß beim Nachbasteln.

Gruß Andreas

Noxus

Eine gute Lösung was das Gruppieren betrifft Struri - danke für das teilen.
Habe zwar momentan nur 2 Sonos, aber es können sicher bald mehr werden  ;)

Nutzt Du eigentlich auch bei deinen Sonos die Sprachausgabe mit "speak"?
Mir ist aufgefallen, dass wenn eine Sprachausgabe erfolgt, der Dummy durch das skript "N_Sonos_Kueche_Device" irgendwie durcheinander kommt und ping-pong spielt. Das ging soweit, dass FHEM komplett abgestürzt ist, und die Sonos device nicht mehr funktioniert haben.
Habe erst einmal die skripte deaktiviert - nun tritt das Phänomen nicht mehr auf.
Muss mir das mal die Tage genauer anschauen.

Gruß

Sturi2011

Hallo,

nein Speak nutze ich nicht. Wie ist denn der Status des Sonos Devices bei transportState wenn die Sprachausgabe erfolgt?
Ich könnte mir vorstellen, das indem Reading etwas Steht womit der Notify nichts anfangen kann.
Ich schaue mir das Ganze morgen Abend mal an. Ich hatte sowieso vor Speak einzubinden.

Gruß Andreas

Noxus

gerade mal kurz geschaut -> transportState spring auf PLAYING und wieder auf STOPPED
Habe auch mal Musik abspielen lassen und dann ein längeren Satz per speak als Ausgabe - dabei ist der transportState während des Satzes einmal kurz wieder auf STOPPED und wieder PLAYING innerhalb einer Sekunde. Werde wie gesagt mal die Tage genauer mit loggen was passiert wenn der Fehler auftritt - momentan möchte ich aber kein Absturz verursachen, dann gibts ärger mit der Frau  ;D
Bin mal gespannt ob das Problem bei dir auch auftritt.

Gruß

Sturi2011

Hallo,

ja, der Fehler stellt sich bei mir auch so da. Die Lösung ist vermutlich das Command und das Anzeigeattribut am Dummy zu trennen. Ich setze das morgen mal um.

Gruß Andreas

Sturi2011

Hi,

bitte mal den Play Button umdefinieren:
<div data-type="switch" data-device="DM_Sonos_Flur" data-set-on="PLAY" data-set-off="PAUSE" data-get="tstate" data-states='["PLAY","PAUSE","STOP"]' data-icons='["fa-pause","fa-play","fa-play"]' data-colors='["#555","#555","#555"]' data-background-colors='["#555","#555","#555"]' data-background-icon="fa-circle-thin" data-background-color="#555" class="cell"></div>

und das entsprechende Notify N_Sonos_Name_Device bitt folgendermaßen definieren.
Sonos_Kueche:transportState.* { if (ReadingsVal("Sonos_Kueche","transportState","") eq "PAUSED_PLAYBACK") { fhem ("setreading DM_Sonos_Kueche tstate PAUSE")} elsif (ReadingsVal("Sonos_Kueche","transportState","") eq "STOPPED") { fhem ("setreading DM_Sonos_Kueche tstate STOP") } elsif (ReadingsVal("Sonos_Kueche","transportState","") eq "PLAYING") { fhem ("setreading DM_Sonos_Kueche tstate PLAY") }}

Dann funktioniert auch die Sprachausgabe. Gruppen muss ich heute Abend testen, da ich gerade das Sonos System nur remote im Zugrifff habe.

Bitte teste mal vorab. Das sollte es eigentlich schon gewesen sein.

Gruß Andreas

Noxus

Hallo Sturi,

es funktioniert nun perfekt - gute Idee dies über ein zweites reading am dummy zu lösen  8)
vielen Dank  :)

Sturi2011

Hi,

danke fürs gegentesten. Hier läuft es auch.

Gruß Andreas

Tobias

#11
Hmm, ich habe überhaupt keine Dummies, nur 2 notifies und 2 funktionen. (Cover dynamisch wechseln und die 5sekündliche Positionsanzeige)

Für die Auswahl bzw Erstellung einer Playlist aus meinem riesigen NAS Musikbibliothek habe ich mir ein eigenes Modul gebaut
Die Auswahl erscheint als Popup beim klick auf das Wettersymbol unten
https://forum.fhem.de/index.php/topic,63047.msg542990.html#msg542990
Maintainer: Text2Speech, TrashCal, MediaList

Meine Projekte: https://github.com/tobiasfaust
* PumpControl v2: allround Bewässerungssteuerung mit ESP und FHEM
* Ein Modbus RS485 zu MQTT Gateway für SolarWechselrichter

Sturi2011

Hallo,

dein Modul gefällt mir, ist für mich aber nicht nutzbar, da die Musikbibliotheken in der Cloud liegen.
Den Dummy habe ich eingebaut, um nicht direkt am Sonos Device Readings definieren zu müssen.
Die Notifys lassen sich wie oben geschrieben sicherlich auf 2 zusammenfassen. Wie löst du das Gruppieren?

Btw.: Die Bilder habe ich bewusst weggelassen um Platz für mehr anzuschaffende Sonos Devices zu schaffen.

Gruß Andreas

Tobias

Hi,
Ich habe fest definierte Gruppen pro Raum. Die verändere ich nicht - noch nicht.
Ja es Stimmt, auf einer Seite wird immer ein Raum (aka Sonos Gruppe) angezeigt. Aber wozu gibt es das Swiper Widget :)
Ich habe zwar aktuell nur ein Raum mit Sonos, aber wenn das nächste Kommt, wird nur noch das Swiper Widget dazwischen gelegt und gut ist. Dann kann man "wischen" um zwischen den Räumen zu wechseln. Alternativ kann man auch mehrere PageTabs einbauen. Diese "große" Seite mit Cover hat den WAF bei mir extrem gehoben :)

Aus MediaList Modul Sicht interessiert  mich, wie du die Playliste zusammenstellst.....
Ich habe gelernt, wenn in der Playliste http://<irgendwas> drin steht, interpretiert das SonosDevice es als Stream. Nur wenn eine UNC Freigabe drinsteht, wird es als echte MP3 Datei gesehen. Was steht bei dir drin?
Maintainer: Text2Speech, TrashCal, MediaList

Meine Projekte: https://github.com/tobiasfaust
* PumpControl v2: allround Bewässerungssteuerung mit ESP und FHEM
* Ein Modbus RS485 zu MQTT Gateway für SolarWechselrichter

Sturi2011

Hi,

die Playlisten sind die im Sonos definierten. Die erzeuge ich in der Sonos Controler APP.
Ausgelesen werden Sie mit reinerleins Script. Man bekommt allerdings (soweit ich weiß)
nur den Playlist Namen - nicht den Content zurück geliefert. Insofern läuft das Ganze
ohne Playlist Modul. Der Swiper würde bei mir den WAF senken, da es ein zusätzlicher
Bedienungsschritt ist.

Die Playlist würde ich super interessant finden, ich habe aber noch keine Idee, wie ich die
einzelnen Titel aus der internen Queue des Sonos bekomme. Sonst könnte man das Playlist
Widget gut als Popup integrieren. Vielleicht muss ich die Playlisten doch noch auf meinen
Server runter ziehen. Ich schaue es mir heute Abend mal an.

Wichtiger war bei mir die Möglichkeit einfach mal schnell zu gruppieren un die Musik mit in den
Keller oder das Badezimmer zu nehmen.

Gruß Andreas