Sonos2mqtt: Unterschied zwischen den Versionen

Aus FHEMWiki
(→‎Favoriten starten: Favoriten Liste Auswahl ergänzt)
K (Formatierung)
 
(32 dazwischenliegende Versionen von 5 Benutzern werden nicht angezeigt)
Zeile 7: Zeile 7:


== Tipps zur Verwendung ==
== Tipps zur Verwendung ==
Die automatische Konfiguration setzt den alias entsprechend dem Namen im Sonos vergeben. Die langen MQTT2_RINCON_ Namen sind unhandlich und schlecht lesbar. Man kann Player mit einem devspec ansprechen, das simpelste ist:
Die automatische Konfiguration setzt den alias entsprechend dem im Sonos vergeben Namen . Die langen MQTT2_RINCON_ Namen sind unhandlich und schlecht lesbar. Man kann Player mit einem devspec ansprechen, das simpelste ist:
:<code>set alias=Büro play</code>
Oder alle Player
:<code>set model=sonos2mqtt_speaker leaveGroup</code>
 
== Konfiguration der Player ==
Mit dem Template sonos_bridge_comfort wird aus dem contrib Ordner eine Datei 99_sonos2mqttUtils.pm nachgeladen und aktiviert. Darin befindet sich der wesentliche Code, die sonos2mqtt Geräte selbst enthalten nur rudimentäre Aufrufe.
 
Selbstverständlich kann man diese Datei jederzeit selbst gestalten (FHEM Menu:Edit Files) aber man sollte beachten, dass eine erneute Anwendung des Templates die Datei überschreibt.


set alias=Büro play
Man kann auch jederzeit diese Datei aus dem SVN neu laden:
<syntaxhighlight lang="perl">
{ Svn_GetFile("contrib/AttrTemplate/99_sonos2mqttUtils.pm", "FHEM/99_sonos2mqttUtils.pm", sub(){ CommandReload(undef, "99_sonos2mqttUtils") }) }
</syntaxhighlight>Hauptroutine ist sonos2mqtt die mit zwei Parametern aufgerufen wird. <code>{sonos2mqtt($NAME,$EVENT)}</code>


Oder alle Player
Die Optik des Players wird mit der Routine <code>sonos2mqtt_devStateIcon</code> bestimmt.


set model=sonos2mqtt_speaker leaveGroup
Man kann die Player mittels <code>attr a:model=sonos2mqtt_speaker webCmd volume</code> mit einem Slider in der Übersicht ausstatten.


== Ansicht der Player ==
Ändert man die Sonoslandschaft, kann man auch alles einreißen und neu erzeugen lassen. Zunächst alles löschen (für die FHEM Kommandozeile):<syntaxhighlight lang="perl">
Im Template wird nur ein simples devStateIcon ausgeliefert. Dort kann man sehr viel mehr reinpacken. Hier mal die aktuelle Arbeitsvariante zum nachrüsten in der [[Import von Code Snippets|Raw Definition]]:<syntaxhighlight lang="perl">
{fhem("delete a:model=sonos2mqtt_speaker;;delete FileLog_MQTT2_RINCON.*");;qx(rm ./log/MQTT2_RINCON_*);;return ""}
attr MQTT2_RINCON_.* devStateIcon {\
</syntaxhighlight>Danach muss man sonos2mqtt einfach neu starten: pm2 start ... bzw den docker container neu starten.
my $wpix = '250px';;\
my $groupname = ReadingsVal($name,'groupName','0');;\
my $sgroupname = (split(' ',ReadingsVal($name,'groupName','')))[0];;\
my $uuidtoname = (devspec2array('DEF='.ReadingsVal($name,'coordinatorUuid','0')))[0];;\
my $vol = ReadingsVal($name,'volume','');;\
my $img = ReadingsVal($name,'currentTrack_AlbumArtUri','');;\
my $mystate = $name eq $uuidtoname \
  ? ReadingsVal($name,'state','FEHLER') : ReadingsVal($uuidtoname,'state','');;\
my $playpic = $mystate eq 'PLAYING'\
  ? 'rc_PAUSE@red'    : $mystate eq 'PAUSED_PLAYBACK'\
  ? 'rc_PLAY@green'  : $mystate eq 'STOPPED'\
  ? 'rc_PLAY@green'  : $mystate eq 'TRANSITIONING'\
  ? 'rc_PLAY@blue'    : $mystate eq 'set_next'\
  ? 'rc_NEXT@blue'    : $mystate eq 'set_previous'\
  ? 'rc_PREVIOUS@blue': $mystate eq 'set_volumeUp'\
  ? 'rc_VOLUP@blue'  : $mystate eq 'set_volumeDown'\
  ? 'rc_VOLDOWN@blue' : $mystate eq 'set_mute'\
  ? 'rc_MUTE@blue'    : 'rc_PLAY@yellow';;\
my $mutecmd = ReadingsVal($name,'mute','0') eq 'false'?'on':'off';;\
my $mutepic = $mutecmd eq 'on'?'rc_MUTE':'rc_VOLUP';;\
my $show = 'FEHLER';;\
my $currentTrack_Artist = ReadingsVal($name,'currentTrack_Artist','FEHLER');;\
my $currentTrack_Title = ReadingsVal($name,'currentTrack_Title','FEHLER');;\
if ($currentTrack_Title =~ 'x-sonosapi-stream:'){$currentTrack_Title=''};;\
my $currentTrack = $mystate eq 'TRANSITIONING'\
  ? 'Puffern...' : $currentTrack_Artist.' - '.$currentTrack_Title;;\
my $nextTrack_Artist = ReadingsVal($name,'nextTrack_Artist','FEHLER');;\
my $nextTrack_Title = ReadingsVal($name,'nextTrack_Title','FEHLER');;\
my $nextTrack = $nextTrack_Artist.' - '.$nextTrack_Title;;\
my $previouspic = 'rc_PREVIOUS';;\
my $nextpic = 'rc_NEXT';;\
my $voldownpic = 'rc_VOLDOWN';;\
my $voluppic = 'rc_VOLUP';;\
my $leavegrouppic = 'rc_LEFT';;\
my $showlg = ReadingsVal($name,"name","0") ne $groupname ? "<a href=\"/fhem?cmd.dummy=set $name leaveGroup&XHR=1\">".FW_makeImage($leavegrouppic)."</a>" : "";;\
if (($mystate eq 'PLAYING')\
  || ($mystate eq 'TRANSITIONING' )\
  || ($mystate eq 'set_next' )\
  || ($mystate eq 'set_previous' )\
  || ($mystate eq 'set_volumeUp' )\
  || ($mystate eq 'set_volumeDown' )\
  || ($mystate eq 'set_mute' )) { \
    my $shownp = ReadingsVal($name,'name','') eq $sgroupname \
    ? "<a href=\"/fhem?cmd.dummy=set $name previous&XHR=1\">".FW_makeImage($previouspic)."</a>\
      <a href=\"/fhem?cmd.dummy=set $name next&XHR=1\">".FW_makeImage($nextpic)."</a>" : "";; \
    $show = "$showlg <a href=\"/fhem?cmd.dummy=set $name toggle&XHR=1\">".FW_makeImage($playpic)."</a>\
    <a href=\"/fhem?cmd.dummy=set $name volumeDown&XHR=1\">".FW_makeImage($voldownpic)."</a>\
    $shownp\
    <a href=\"/fhem?cmd.dummy=set $name volumeUp&XHR=1\">".FW_makeImage($voluppic)."</a>\
    &nbsp;;&nbsp;;&nbsp;;&nbsp;;\
    <a href=\"/fhem?cmd.dummy=set $name mute $mutecmd&XHR=1\">".FW_makeImage($mutepic)."</a><br>";;\
  \
    if (ReadingsVal($name,'name','') eq $sgroupname) {\
      $show = ReadingsVal($name,'currentTrack_TrackUri','') =~ 'spdif'\
      ? 'TV': ReadingsVal($name,'enqueuedMetadata_UpnpClass','FEHLER') ne 'object.item.audioItem.audioBroadcast'\
      ? "$show<marquee style='width: $wpix'>Aktueller Track: $currentTrack<br>Nächster Track: $nextTrack</marquee>"\
      : "$show<marquee style='width: $wpix'>Radio: $currentTrack</marquee>"\
    }\
    elsif (ReadingsVal($name,'name','') ne $groupname) {\
      $show = "$show Master: $sgroupname"}\
    }\
    else {\
      $show = $name eq $uuidtoname\
      ? "$showlg <a href=\"/fhem?cmd.dummy=set $name toggle&XHR=1\">".FW_makeImage($playpic)."</a>"\
      : "$showlg <a href=\"/fhem?cmd.dummy=set $name toggle&XHR=1\">".FW_makeImage($playpic)."</a><br>Master: $sgroupname"\
    }\
  "<div>\
  <table>\
    <tr>\
      <td><div style='display: inline-block;; margin-right: 5px;; border: 1px solid lightgray;;\
              height: 4.00em;; width: 4.00em;; background-image: url($img);; background-size: contain;;'></div></td>\
      <td>$show</td>\
    </tr>\
  </table>\
  </div>"\
}
</syntaxhighlight>


== Befehle nachrüsten ==
== Befehle nachrüsten ==
Um Befehle nicht manuell in die setList / readingList Einträge jedes Players machen zu müssen, wird diese Routine verwendet.
Um Befehle in der setList / getList / readingList nachzurüsten gibt es die Routine <code>sonos2mqtt_mod_list(devspec,attrName,line)</code>
 
Der Code ist für die Raw Definition gedacht. Die ersten drei Zeilen sind jeweils anzupassen!


Hier im Beispiel wird der setter für den folgenden Speak Befehl eingefügt bzw. ersetzt. <syntaxhighlight lang="perl">
Vorhandenen Zeilen werden ersetzt. Identifiziert wird nur der erste Teil bis zum ":". Beispiel für die Komandozeile:
{my @devlist = devspec2array('MQTT2_RINCON_.*');;\
my $attr = 'setList';;\
my $item = q(  speak:textField { my $tts="SonosTTS";;my ($cmd,$vol,$text)=split(' ', $EVENT,3);;fhem("set $tts tts $text;;sleep $tts:playing:.0 ;;set $NAME notify $vol [$tts:httpName]")});;\
my ($first,$sec)=split(' ',$item,2);;\
$first=~s/ //g;;\
foreach (@devlist) {\
  my @arr = grep {$_ !~ $first} split("\n",AttrVal($_,$attr,''));;\
  push @arr,$item;;\
  my $val = join "\n",@arr;;\
  $val =~ s/;;/;;;;/g;;\
  fhem("attr $_ $attr $val")}\
return "$attr in ".scalar(@devlist)." Definitionen modifiziert"\
}
</syntaxhighlight>Vorhandenen Zeilen werden ersetzt. Identifiziert wird nur der erste Teil.  


=== Speak Befehl ===
<code>{sonos2mqtt_mod_list('a:model=sonos2mqtt_bridge','readingList','sonos/RINCON_([0-9A-Z]+)/Reply:.* Reply')}</code>
Verwendet wird ein Befehl ähnlich wie in der Sonos Umgebung:


set Player speak <volume> text
== Sprachausgabe ==
Man hat zwei Möglichkeiten, dies umzusetzen:
# Text2Speech FHEM intern
# Mit dem sonos text to speech Server https://svrooij.io/sonos2mqtt/


=== Text2Speech Variante einrichten ===
Zwei zusätzliche Geräte sind notwendig:
Zwei zusätzliche Geräte sind notwendig:
* [[Text2Speech]] im Servermodus, erzeugt mp3 Dateien im cache Verzeichnis und legt einen Link im Reading httpName ab.
* [[Text2Speech]] im Servermodus, erzeugt mp3 Dateien im cache Verzeichnis und legt einen Link im Reading httpName ab.
* Ein HTTP Server stellt die Dateien im gleichen Verzeichnis bereit
* Ein HTTP Server stellt die Dateien im gleichen Verzeichnis bereit
Der speak Befehl im Player Device läuft in 3 Schritten:
'''Hinweis''': Für die vollständige Funktion und Installation von Text2Speech ist die Commandref zu beachten! Für das unten im Beispiel verwendete Feature UseMP3Wrap muss das Tool mp3wrap installiert werden (z.B. <code>apt install mp3wrap</code>). Dies ist bei längeren Texten und bei der Verwendung von eingebetteten festen Sounds existenziell!
# mit dem TTS Gerät wird die mp3 Datei erzeugt,
 
# mit dem sleep wird auf die Fertigstellung gewartet,
Für die Funktion ist wichtig, dass das Sonos System den Host im Link zur Datei richtig auflösen kann. Deshalb wird der Hostname und Port des Servers im Reading host des TTS Device als Name oder IP Adresse abgelegt! Dies kann entweder mit dem hier gezeigten Befehl oder vollständig manuell erfolgen. Für FHEM innerhalb Docker muss man die Adresse des Docker Host angeben.
# die Datei wird mit set Player notify volume uri abgespielt.
<syntaxhighlight lang="perl">
Für die Funktion ist wichtig, dass das Sonos System den Host im Link zur Datei richtig auflösen kann. Deshalb wird der Hostname und Port des Servers im Reading host des TTS Device als Name oder IP Adresse abgelegt! Dies kann entweder mit dem hier gezeigten Befehl oder vollständig manuell erfolgen.<syntaxhighlight lang="perl">
defmod SonosTTS Text2Speech none
defmod SonosTTS Text2Speech none
attr SonosTTS TTS_UseMP3Wrap 1
attr SonosTTS TTS_UseMP3Wrap 1
attr SonosTTS TTS_Language Deutsch
attr SonosTTS userReadings httpName:lastFilename.* {'http://'.ReadingsVal($name,'host','set host:port first').'/fhem/'.ReadingsVal($name,'lastFilename','')}
attr SonosTTS userReadings httpName:lastFilename.* {'http://'.ReadingsVal($name,'host','set host:port first').'/fhem/'.ReadingsVal($name,'lastFilename','')}
attr SonosTTS TTS_CacheFileDir cache
attr SonosTTS TTS_CacheFileDir cache
setreading SonosTTS host {(qx(hostname -s|tr -d '\n').':'.InternalVal('WEB','PORT','8083'))}
setreading SonosTTS host {(qx(hostname -s|tr -d '\n').':'.InternalVal('WEB','PORT','8083'))}
#setreading SonosTTS host {((split(' ', qx(hostname -I)))[0].':'.InternalVal('WEB','PORT','8083'))}
#setreading SonosTTS host {((split(' ', qx(hostname -I)))[0].':'.InternalVal('WEB','PORT','8083'))}
#setreading SonosTTS host <hostname>:<port>


defmod SonosSpeakWeb HTTPSRV cache cache SonosSpeakWeb
defmod SonosSpeakWeb HTTPSRV cache cache SonosSpeakWeb
</syntaxhighlight>Durch den "sonos2mqtt notify" Befehl wird die laufende Umgebung wiederhergestellt.  
</syntaxhighlight>
* Wird der speak Befehl an den Gruppenmaster gesendet wird die mp3 Datei in der gesamten Gruppe gespielt.  
Der Sprachausgabe Befehle im Player Device läuft in 3 Schritten:
* Wird der speak Befehl an ein Mitglied einer Gruppe gesendet (nicht den Master) wird die Gruppe aufgetrennt und später wieder hergestellt.
# mit dem TTS Gerät wird die mp3 Datei erzeugt,
Die Erweiterung der setList im MQTT2 Player Device sieht wie folgt aus. Man kann den Befehl einfach bei allen Playern mit dem obigen [[Sonos2mqtt#Befehle nachr.C3.BCsten|Codeschnipsel]] nachrüsten.<syntaxhighlight lang="perl">
# mit dem sleep wird auf die Fertigstellung gewartet,
speak:textField { my $tts="SonosTTS";my ($cmd,$vol,$text)=split(' ', $EVENT,3);fhem("set $tts tts $text;sleep $tts:playing:.0 ;set $NAME notify $vol [$tts:httpName]")}
# die Datei wird mit <code>set Player notify volume uri</code> abgespielt.
</syntaxhighlight>Will man keine laufenden Sendung unterbrechen sondern einfach eine Ansage machen und danach etwas starten, kann man so vorgehen:<syntaxhighlight lang="perl">
Durch den "sonos2mqtt notify" Befehl wird die laufende Umgebung wiederhergestellt.
* Wird der Sprachbefehl an den Gruppenmaster gesendet wird die mp3 Datei in der gesamten Gruppe gespielt.  
* Wird der Sprachbefehl an ein Mitglied einer Gruppe gesendet (nicht den Master) wird die Gruppe aufgetrennt und später wieder hergestellt.
Es sind zwei Befehle zur Sprachausgabe eingebaut:
 
=== sayText Befehl ===
Dieser Befehl orientiert sich an den FHEM [[DevelopmentGuidelinesAV|DevelopmentGuidelines]] und sammelt "gleichzeitig" eintreffende Sprachnachrichten, damit nichts verloren geht. Informationen im Forum dazu in {{Link2Forum|Topic=111711|Message=1100112|LinkText=diesem Beitrag}}.
 
Die Lautstärke wird separat in SonosTTS gesetzt: <code>setreading SonosTTS vol 15</code>, ebenso die Sprache (einmalig bei der Einrichtung oder bei Bedarf zwischendurch) <code>attr SonosTTS TTS_Language Deutsch</code>
 
''Hinweis: Der Befehl <code>set SonosTTS volume xx</code> hat im Servermodus des Devices keine Wirkung!''
=== Speak Befehl ===
Dieser Befehl ist ähnlich wie der in der FHEM-Sonos Umgebung:
:<code>set Player speak <volume> text</code>
 
Verwendet man diesen Syntax (mit Sprache und Stimme am Anfang), wird automatisch der speak Befehl an die sonos-tts abgesetzt. Die sonos-tts muss man separat installieren/integrieren!
 
<code>set EG.KU.Sonos speak de-DE Vicki 25 Test</code>
 
Will man keine laufenden Sendung unterbrechen sondern einfach eine Ansage machen und danach etwas starten, kann man so vorgehen:
<syntaxhighlight lang="perl">
set SonosTTS tts Hier steht die Ansage;sleep SonosTTS:playing:.0 ; set alias=PlayerAlias playUri [SonosTTS:httpName]
set SonosTTS tts Hier steht die Ansage;sleep SonosTTS:playing:.0 ; set alias=PlayerAlias playUri [SonosTTS:httpName]
</syntaxhighlight>Generell kann man feste mp3 Dateien (Klingeltöne, Klänge usw.) auch im cache Verzeichnis ablegen und direkt mit dem Link starten. Mit set magic kann man dabei einfach Teile aus anderen Readings holen.<syntaxhighlight lang="perl">
</syntaxhighlight>
 
=== Volume Befehl ===
Der Befehl akzeptiert einen zweiten Wert als Startwert d.h. set volume 25 10 setzt sofort auf 10 und startet dann fading auf 25. Ein zweiter Wert -1 erzeugt ein fading vom aktuellen Wert auf den angegebenen.
 
==== Spiele feste Sounds ====
Generell kann man feste mp3 Dateien (Klingeltöne, Klänge usw.) auch im cache Verzeichnis ablegen und direkt mit dem Link starten.  
 
Es gibt zahlreiche Soundquellen im Internet, ist der gewünschte Sound dabei, kann man ihn innerhalb FHEM herunterladen und an Ort und Stelle platzieren. (Beispiel ohne und mit Umbennung)
<syntaxhighlight lang="perl">
"wget -qP ./cache https://cdn.smartersoft-group.com/various/pull-bell-short.mp3"
"wget -qO ./cache/KlingelTon.mp3 https://cdn.smartersoft-group.com/various/pull-bell-short.mp3"
</syntaxhighlight>
Vergewissern ob der gewünschte Sound auch da ist: {qx(ls -lha ./cache)}
 
Mit set magic kann man dabei einfach Teile aus anderen Readings holen.
<syntaxhighlight lang="perl">
set alias=Büro notify 25 {('http://[SonosTTS:host]/fhem/cache/KlingelTon.mp3')}
set alias=Büro notify 25 {('http://[SonosTTS:host]/fhem/cache/KlingelTon.mp3')}
</syntaxhighlight>
Man kann auch den setter im Gerät erweitern <code>{sonos2mqtt_mod_list('a:model=sonos2mqtt_speaker','setList',q(playSound:textField ....))}</code> :
<syntaxhighlight lang="perl">
playSound:textField {my $tts="SonosTTS";my ($cmd,$vol,$file)=split(' ', $EVENT,3);$file=($file=~m/.*\.mp3$/)?"$file":"$file.mp3";fhem("set $NAME notify $vol http://[$tts:host]/fhem/[a:$tts:TTS_CacheFileDir]/$file")}
</syntaxhighlight>
Damit sind dann dieser Syntax möglich:
<syntaxhighlight lang="perl">
set alias=Büro playSound 40 KlingelTon.mp3
</syntaxhighlight>
oder ohne Dateiendung (wird auf mp3 gesetzt)
<syntaxhighlight lang="perl">
set alias=Büro playSound 40 KlingelTon
</syntaxhighlight>
</syntaxhighlight>


=== Favoriten starten ===
=== SonosBridge ===
Die Favoritenliste kann einmal zentral in der SonosBridge abgelegt werden. Dazu müssen wird dort die readingList ergänzen.<syntaxhighlight lang="perl">
Die SonsoBridge enthält bereits ein paar zentrale Funktionen und Readings, z.B. wird beim Laden der Favoritenliste ein Reading favlist und grouplist erzeugt, welches zur Erweiterung der Player mit Auswahllisten dienen kann.
  sonos/RINCON_([0-9A-Z]+)/Favorites:.* Favorites
</syntaxhighlight>Wer das nicht per Hand machen will hier der Code für die Raw Definition:<syntaxhighlight lang="perl">
{my @devlist = devspec2array('model=sonos2mqtt_bridge');;\
my $attr = 'readingList';;\
my $item = q(  sonos/RINCON_([0-9A-Z]+)/Favorites:.* Favorites);;\
my ($first,$sec)=split(' ',$item,2);;\
$first=~s/ //g;;\
foreach (@devlist) {\
  my @arr = grep {$_ !~ $first} split("\n",AttrVal($_,$attr,''));;\
  push @arr,$item;;\
  my $val = join "\n",@arr;;\
  $val =~ s/;;/;;;;/g;;\
  fhem("attr $_ $attr $val")}\
return "$attr in ".scalar(@devlist)." Definitionen modifiziert"\
}
</syntaxhighlight>Einmalig kann bei einen Player die Favoritenliste geholt werden. (Wir fragen zwar einen Player, das Reading entsteht aber in der SonosBridge!)<syntaxhighlight lang="perl">
set alias=Büro x_raw_payload {"command": "adv-command","input": {"cmd": "GetFavorites","reply": "Favorites"}}
</syntaxhighlight>Für eine regelmäßige Verwendung kann der SonosBridge eine getList spendiert werden, man muss dazu einen Player eintragen (RINCON_).<syntaxhighlight lang="perl">
Favorites:noArg Favorites sonos/RINCON_XXXXXXXXXXXXX/control {"command": "adv-command","input": {"cmd": "GetFavorites","reply": "Favorites"}}
</syntaxhighlight>Nun kann man noch die Favoritenliste zum Auswählen eintragen. Dieser Code ist etwas umfangreicher und wieder ein "Script" für die Raw Def.<syntaxhighlight lang="perl">
{use JSON;;use HTML::Entities;;use Encode qw(encode decode);;\
my $enc = 'UTF8';;\
my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];;\
my $read = 'Favorites';;\
my $decoded = decode_json(ReadingsVal($dev,$read,''));;\
my @arr  = @{$decoded->{'Result'}};;\
my @out;;\
foreach (@arr) {\
  my $dec=encode($enc, decode_entities($_->{'Title'}));;\
  $dec =~ s/\s/./g;;\
  push @out,$dec}\
my $favliste= join ',', sort @out;;\
\
my @devlist = devspec2array('MQTT2_RINCON_.*');;\
my $attr = 'setList';;\
my $item = '  playFav:'.$favliste.q( {use JSON;;use HTML::Entities;;use Encode qw(encode decode);;my $enc = 'UTF8';;my $uri='';;my $search = (split(' ', $EVENT,2))[1];;my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];;my $read = 'Favorites';;my $decoded = decode_json(ReadingsVal($dev,$read,''));;my @arr  = @{$decoded->{'Result'}};;foreach (@arr) {if (encode($enc, decode_entities($_->{'Title'})) =~ /$search/i){$uri = $_->{'TrackUri'} }};;fhem("set $NAME playUri $uri") if ($uri ne '')});;\
my ($first,$sec)=split(':',$item,2);;\
  $first=~s/ //g;;\
foreach (@devlist) {\
  my @arr = grep {$_ !~ $first} split("\n",AttrVal($_,$attr,''));;\
  push @arr,$item;;\
  my $val = join "\n",@arr;;\
  $val =~ s/;;/;;;;/g;;\
  fhem("attr $_ $attr $val")}\
return "$attr in ".scalar(@devlist)." Definitionen modifiziert"\
}
</syntaxhighlight>Der erste Teil des Codes erzeugt aus den Favoriten ein Komma separierte, sortierte Liste der Titel und ersetzt die Leerzeichen durch Punkte. Damit eignet sich diese Liste für die Auswahlbox. Dies wird als Ergänzung der setList im zweiten Teil des Codes erzeugt und bei allen Playern eingetragen.


Man kann den playFav Befehl auch mit im set Befehl mit einem Teil des Favoriten Namen verwenden. Enthält die Favoritenliste z.B. Radio Leipzig würde der auch mit diesem Befehl angesteuert werden:<syntaxhighlight lang="perl">
=== Player mit Favoritenliste und Gruppenliste ausstatten ===
Wenn nicht schon geschehen muss man jetzt die Favoriten zum ersten Mal einlesen: <syntaxhighlight lang="perl">
get SonosBridge Favorites
</syntaxhighlight>Nachdem die SonosBridge "aufgerüstet" ist, kann man allen Playern die Favoritenliste zum Auswählen eintragen. Beide Zeile sind für die FHEM Kommandozeile und verwenden die Routine aus der 99_sonos2mqttUtils.
<syntaxhighlight lang="perl">
{sonos2mqtt_mod_list('a:model=sonos2mqtt_speaker','setList','joinGroup:'.ReadingsVal((devspec2array('a:model=sonos2mqtt_bridge'))[0],'grouplist','').q( {sonos2mqtt($NAME,$EVENT)}))}
{sonos2mqtt_mod_list('a:model=sonos2mqtt_speaker','setList','playFav:'.ReadingsVal((devspec2array('a:model=sonos2mqtt_bridge'))[0],'favlist','').q( {sonos2mqtt($NAME,$EVENT)}))}
</syntaxhighlight>
Man kann den playFav Befehl auch im set Befehl mit einem Teil des Favoriten Namen verwenden. Enthält die Favoritenliste z.B. Radio Leipzig würde der auch mit diesem Befehl angesteuert werden:<syntaxhighlight lang="perl">
set alias=Bad playFav leipzig
set alias=Bad playFav leipzig
</syntaxhighlight>
</syntaxhighlight>
=== Listen der Favoriten Radios und Playlist erstellen ===
Im Sonos System kann man an (mindestens) drei Stellen Favoriten hinterlegen: Sonos-Favoriten, Sonos-Playlisten, TuneIn: Meine Radiosender
Diese drei Listen kann man einlesen und für die einfache Suche und Auswahl verwenden. Der play Befehl akzeptiert dazu zwei weitere Parameter und sucht in den entsprechenden Listen.
Diese play Befehle starten nicht sofort, sondern erst nach einem folgenden set ... play. Damit kann man etwas vorbereiten und gezielt starten.
<code>set alias=Büro play Favorite Deutschlandfunk</code>
<code>set alias=Büro play Radio HitRadio</code>
<code>set alias=Büro play Playlist Meine Hits</code>
Die Sonos-Favoriten-, TuneIn: Meine Radiosender- und Sonos-Playlisten dazu vom Sonos System einmalig (bzw. bei Veränderungen) einlesen:
<pre>
get SonosBridge Reply Playlists;sleep SonosBridge:Reply.*;setreading SonosBridge Playlists [SonosBridge:Reply]
get SonosBridge Reply Favorites;sleep SonosBridge:Reply.*;setreading SonosBridge Favorites [SonosBridge:Reply]
get SonosBridge Reply Radios;sleep SonosBridge:Reply.*;setreading SonosBridge Radios [SonosBridge:Reply]
</pre>
Beispiel für ein Guten Morgen Radio:
<pre>
set alias=BadWanne,alias=Kueche joinGroup Bad;
set alias=BadWanne,alias=Bad volume 13 6;
set alias=Bad play Favorite Deutschlandfunk.Kultur.RP;
set alias=Kueche volume 18 8;
sleep 0.2;set alias=Bad play
</pre>


=== Radioliste durchtasten ===
=== Radioliste durchtasten ===
Will man eine Liste von bestimmten Radiostation mit einem Taster "durchtasten" kann man das wie folgt tun:
Das oben erwähnte <code>99_sonos2mqttUtils.pm</code> (ab 6.1.2023) schreibt die Radios aus dem Reading <code>Favoriten</code> in das userReading <code>favlist</code> im SonosBridge Device und aktualisiert das auch.
==== Der Befehl zum weiterschalten ====
Jedes Mal wenn dieser Befehl ausgeführt wird, wird der nächste "Radio-Favorit" (aus dem userReading <code>favlist</code> des SonosBridge Devices) gestartet.
Wird eine Radiosender gespielt wird bei einem <code>set ''sonos2mqtt_speaker'' next</code> auf den nächsten Sender in dieser Liste geschaltet und der <code>transportState</code> wird beibehalten.
==== Name der Radiostation vor dem Umschalten ansagen ====
Will man vor dem Radiostart noch die Ansage des Senders haben, geht das zwar auch mit dem <code>speak</code> Befehl, die direkte Ausgabe ohne Restore der Umgebung (sonos2mqtt notify) ist aber effektiver.
Damit das funktioniert müssen wir die Events einschränken:
<syntaxhighlight lang="perl">
attr model=sonos2mqtt_speaker event-on-change-reading .*
{my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];;my $r=Each($dev,ReadingsVal($dev,'favlist',''));;my $play = (devspec2array('alias=Büro'))[0];;my $tts="SonosTTS";;fhem("set $tts tts Es folgt $r;;sleep $tts:playing:.0;;set $play playUri [$tts:httpName];;sleep $play:play;;sleep $play:PLAYING;;sleep $play:STOPPED;;set $play playFav $r")}
</syntaxhighlight>
Kurze Erklärung zum Code
* ermittelt den nächsten Radiosender in der Liste: <code>my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];;my $r=Each($dev,ReadingsVal($dev,'favlist',<nowiki>''</nowiki>))</code>
* ermitteln des Namens des <code>''sonos2mqtt_speaker''</code> Devices anhand des <code>alias</code> Attributes: <code>my $play = (devspec2array('alias=Büro'))[0]</code>
* erzeugt die Ansage "Es folgt SenderXY": <code>my $tts="SonosTTS";;fhem("set $tts tts Es folgt $r</code>
* wenn die mp3 Datei fertig erzeugt ist: <code>sleep $tts:playing:.0</code>, wird sie mit dem Befehl playUri an den oben ermittelten <code>''sonos2mqtt_speaker''</code> gesendet: <code>set $play playUri [$tts:httpName]</code>
* es wird eine Eventfolge abgewartet -> play / PLAYING / STOPPED: <code>sleep $play:play;;sleep $play:PLAYING;;sleep $play:STOPPED</code>
* danach wird der Radiosender gestartet: <code>set $play playFav $r</code>
Der Code ist so einfach und relativ "steif" für die Kommandozeile. Man kann das auch in einen Setter packen (<code>set ''sonos2mqtt_speaker'' toggleRadio</code>). Dazu den Text unten in das Attribut <code>setList</code> des <code>''sonos2mqtt_speaker''</code> Devices hinzufügen:
<syntaxhighlight lang="perl">
toggleRadio:noArg {my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];my $r=Each($dev,ReadingsVal($dev,'favlist',''));my $tts="SonosTTS";fhem("set $tts tts Es folgt $r;sleep $tts:playing:.0;set $NAME playUri [$tts:httpName];sleep $NAME:play;sleep $NAME:PLAYING;sleep $NAME:STOPPED;set $NAME playFav $r")}
</syntaxhighlight>
== Dokumentationen und weitere Entwicklungen ==
ToDo
ToDo


== Dokumentationen und weiter Entwicklungen ==
Erweiterung um Alarmhandling siehe [https://forum.fhem.de/index.php/topic,111711.msg1162031.html#msg1162031 Forum]
ToDo
 
[[Kategorie:MQTT]]

Aktuelle Version vom 6. Januar 2023, 14:03 Uhr

Grundeinrichtung

Die Grundeinrichtung ist bereits im Artikel MQTT2-Module - Praxisbeispiele beschrieben. Hier soll es um die praktische Verwendung und Erweiterung gehen.

Für alle Erweiterungen wird versucht vorhandene Devices in FHEM zu verwenden.

Viele Dinge werden derzeit noch entwickelt und können frei gestaltet werden - der Vorteil von generischen FHEM Devices.

Tipps zur Verwendung

Die automatische Konfiguration setzt den alias entsprechend dem im Sonos vergeben Namen . Die langen MQTT2_RINCON_ Namen sind unhandlich und schlecht lesbar. Man kann Player mit einem devspec ansprechen, das simpelste ist:

set alias=Büro play

Oder alle Player

set model=sonos2mqtt_speaker leaveGroup

Konfiguration der Player

Mit dem Template sonos_bridge_comfort wird aus dem contrib Ordner eine Datei 99_sonos2mqttUtils.pm nachgeladen und aktiviert. Darin befindet sich der wesentliche Code, die sonos2mqtt Geräte selbst enthalten nur rudimentäre Aufrufe.

Selbstverständlich kann man diese Datei jederzeit selbst gestalten (FHEM Menu:Edit Files) aber man sollte beachten, dass eine erneute Anwendung des Templates die Datei überschreibt.

Man kann auch jederzeit diese Datei aus dem SVN neu laden:

{ Svn_GetFile("contrib/AttrTemplate/99_sonos2mqttUtils.pm", "FHEM/99_sonos2mqttUtils.pm", sub(){ CommandReload(undef, "99_sonos2mqttUtils") }) }

Hauptroutine ist sonos2mqtt die mit zwei Parametern aufgerufen wird. {sonos2mqtt($NAME,$EVENT)}

Die Optik des Players wird mit der Routine sonos2mqtt_devStateIcon bestimmt.

Man kann die Player mittels attr a:model=sonos2mqtt_speaker webCmd volume mit einem Slider in der Übersicht ausstatten.

Ändert man die Sonoslandschaft, kann man auch alles einreißen und neu erzeugen lassen. Zunächst alles löschen (für die FHEM Kommandozeile):

{fhem("delete a:model=sonos2mqtt_speaker;;delete FileLog_MQTT2_RINCON.*");;qx(rm ./log/MQTT2_RINCON_*);;return ""}

Danach muss man sonos2mqtt einfach neu starten: pm2 start ... bzw den docker container neu starten.

Befehle nachrüsten

Um Befehle in der setList / getList / readingList nachzurüsten gibt es die Routine sonos2mqtt_mod_list(devspec,attrName,line)

Vorhandenen Zeilen werden ersetzt. Identifiziert wird nur der erste Teil bis zum ":". Beispiel für die Komandozeile:

{sonos2mqtt_mod_list('a:model=sonos2mqtt_bridge','readingList','sonos/RINCON_([0-9A-Z]+)/Reply:.* Reply')}

Sprachausgabe

Man hat zwei Möglichkeiten, dies umzusetzen:

  1. Text2Speech FHEM intern
  2. Mit dem sonos text to speech Server https://svrooij.io/sonos2mqtt/

Text2Speech Variante einrichten

Zwei zusätzliche Geräte sind notwendig:

  • Text2Speech im Servermodus, erzeugt mp3 Dateien im cache Verzeichnis und legt einen Link im Reading httpName ab.
  • Ein HTTP Server stellt die Dateien im gleichen Verzeichnis bereit

Hinweis: Für die vollständige Funktion und Installation von Text2Speech ist die Commandref zu beachten! Für das unten im Beispiel verwendete Feature UseMP3Wrap muss das Tool mp3wrap installiert werden (z.B. apt install mp3wrap). Dies ist bei längeren Texten und bei der Verwendung von eingebetteten festen Sounds existenziell!

Für die Funktion ist wichtig, dass das Sonos System den Host im Link zur Datei richtig auflösen kann. Deshalb wird der Hostname und Port des Servers im Reading host des TTS Device als Name oder IP Adresse abgelegt! Dies kann entweder mit dem hier gezeigten Befehl oder vollständig manuell erfolgen. Für FHEM innerhalb Docker muss man die Adresse des Docker Host angeben.

defmod SonosTTS Text2Speech none
attr SonosTTS TTS_UseMP3Wrap 1
attr SonosTTS TTS_Language Deutsch
attr SonosTTS userReadings httpName:lastFilename.* {'http://'.ReadingsVal($name,'host','set host:port first').'/fhem/'.ReadingsVal($name,'lastFilename','')}
attr SonosTTS TTS_CacheFileDir cache
setreading SonosTTS host {(qx(hostname -s|tr -d '\n').':'.InternalVal('WEB','PORT','8083'))}
#setreading SonosTTS host {((split(' ', qx(hostname -I)))[0].':'.InternalVal('WEB','PORT','8083'))}
#setreading SonosTTS host <hostname>:<port>

defmod SonosSpeakWeb HTTPSRV cache cache SonosSpeakWeb

Der Sprachausgabe Befehle im Player Device läuft in 3 Schritten:

  1. mit dem TTS Gerät wird die mp3 Datei erzeugt,
  2. mit dem sleep wird auf die Fertigstellung gewartet,
  3. die Datei wird mit set Player notify volume uri abgespielt.

Durch den "sonos2mqtt notify" Befehl wird die laufende Umgebung wiederhergestellt.

  • Wird der Sprachbefehl an den Gruppenmaster gesendet wird die mp3 Datei in der gesamten Gruppe gespielt.
  • Wird der Sprachbefehl an ein Mitglied einer Gruppe gesendet (nicht den Master) wird die Gruppe aufgetrennt und später wieder hergestellt.

Es sind zwei Befehle zur Sprachausgabe eingebaut:

sayText Befehl

Dieser Befehl orientiert sich an den FHEM DevelopmentGuidelines und sammelt "gleichzeitig" eintreffende Sprachnachrichten, damit nichts verloren geht. Informationen im Forum dazu in diesem Beitrag.

Die Lautstärke wird separat in SonosTTS gesetzt: setreading SonosTTS vol 15, ebenso die Sprache (einmalig bei der Einrichtung oder bei Bedarf zwischendurch) attr SonosTTS TTS_Language Deutsch

Hinweis: Der Befehl set SonosTTS volume xx hat im Servermodus des Devices keine Wirkung!

Speak Befehl

Dieser Befehl ist ähnlich wie der in der FHEM-Sonos Umgebung:

set Player speak <volume> text

Verwendet man diesen Syntax (mit Sprache und Stimme am Anfang), wird automatisch der speak Befehl an die sonos-tts abgesetzt. Die sonos-tts muss man separat installieren/integrieren!

set EG.KU.Sonos speak de-DE Vicki 25 Test

Will man keine laufenden Sendung unterbrechen sondern einfach eine Ansage machen und danach etwas starten, kann man so vorgehen:

set SonosTTS tts Hier steht die Ansage;sleep SonosTTS:playing:.0 ; set alias=PlayerAlias playUri [SonosTTS:httpName]

Volume Befehl

Der Befehl akzeptiert einen zweiten Wert als Startwert d.h. set volume 25 10 setzt sofort auf 10 und startet dann fading auf 25. Ein zweiter Wert -1 erzeugt ein fading vom aktuellen Wert auf den angegebenen.

Spiele feste Sounds

Generell kann man feste mp3 Dateien (Klingeltöne, Klänge usw.) auch im cache Verzeichnis ablegen und direkt mit dem Link starten.

Es gibt zahlreiche Soundquellen im Internet, ist der gewünschte Sound dabei, kann man ihn innerhalb FHEM herunterladen und an Ort und Stelle platzieren. (Beispiel ohne und mit Umbennung)

"wget -qP ./cache https://cdn.smartersoft-group.com/various/pull-bell-short.mp3"
"wget -qO ./cache/KlingelTon.mp3 https://cdn.smartersoft-group.com/various/pull-bell-short.mp3"

Vergewissern ob der gewünschte Sound auch da ist: {qx(ls -lha ./cache)}

Mit set magic kann man dabei einfach Teile aus anderen Readings holen.

set alias=Büro notify 25 {('http://[SonosTTS:host]/fhem/cache/KlingelTon.mp3')}

Man kann auch den setter im Gerät erweitern {sonos2mqtt_mod_list('a:model=sonos2mqtt_speaker','setList',q(playSound:textField ....))} :

playSound:textField {my $tts="SonosTTS";my ($cmd,$vol,$file)=split(' ', $EVENT,3);$file=($file=~m/.*\.mp3$/)?"$file":"$file.mp3";fhem("set $NAME notify $vol http://[$tts:host]/fhem/[a:$tts:TTS_CacheFileDir]/$file")}

Damit sind dann dieser Syntax möglich:

set alias=Büro playSound 40 KlingelTon.mp3

oder ohne Dateiendung (wird auf mp3 gesetzt)

set alias=Büro playSound 40 KlingelTon

SonosBridge

Die SonsoBridge enthält bereits ein paar zentrale Funktionen und Readings, z.B. wird beim Laden der Favoritenliste ein Reading favlist und grouplist erzeugt, welches zur Erweiterung der Player mit Auswahllisten dienen kann.

Player mit Favoritenliste und Gruppenliste ausstatten

Wenn nicht schon geschehen muss man jetzt die Favoriten zum ersten Mal einlesen:

get SonosBridge Favorites

Nachdem die SonosBridge "aufgerüstet" ist, kann man allen Playern die Favoritenliste zum Auswählen eintragen. Beide Zeile sind für die FHEM Kommandozeile und verwenden die Routine aus der 99_sonos2mqttUtils.

{sonos2mqtt_mod_list('a:model=sonos2mqtt_speaker','setList','joinGroup:'.ReadingsVal((devspec2array('a:model=sonos2mqtt_bridge'))[0],'grouplist','').q( {sonos2mqtt($NAME,$EVENT)}))}
{sonos2mqtt_mod_list('a:model=sonos2mqtt_speaker','setList','playFav:'.ReadingsVal((devspec2array('a:model=sonos2mqtt_bridge'))[0],'favlist','').q( {sonos2mqtt($NAME,$EVENT)}))}

Man kann den playFav Befehl auch im set Befehl mit einem Teil des Favoriten Namen verwenden. Enthält die Favoritenliste z.B. Radio Leipzig würde der auch mit diesem Befehl angesteuert werden:

set alias=Bad playFav leipzig

Listen der Favoriten Radios und Playlist erstellen

Im Sonos System kann man an (mindestens) drei Stellen Favoriten hinterlegen: Sonos-Favoriten, Sonos-Playlisten, TuneIn: Meine Radiosender

Diese drei Listen kann man einlesen und für die einfache Suche und Auswahl verwenden. Der play Befehl akzeptiert dazu zwei weitere Parameter und sucht in den entsprechenden Listen.

Diese play Befehle starten nicht sofort, sondern erst nach einem folgenden set ... play. Damit kann man etwas vorbereiten und gezielt starten.

set alias=Büro play Favorite Deutschlandfunk

set alias=Büro play Radio HitRadio

set alias=Büro play Playlist Meine Hits

Die Sonos-Favoriten-, TuneIn: Meine Radiosender- und Sonos-Playlisten dazu vom Sonos System einmalig (bzw. bei Veränderungen) einlesen:

get SonosBridge Reply Playlists;sleep SonosBridge:Reply.*;setreading SonosBridge Playlists [SonosBridge:Reply]
get SonosBridge Reply Favorites;sleep SonosBridge:Reply.*;setreading SonosBridge Favorites [SonosBridge:Reply]
get SonosBridge Reply Radios;sleep SonosBridge:Reply.*;setreading SonosBridge Radios [SonosBridge:Reply]

Beispiel für ein Guten Morgen Radio:

set alias=BadWanne,alias=Kueche joinGroup Bad;
set alias=BadWanne,alias=Bad volume 13 6;
set alias=Bad play Favorite Deutschlandfunk.Kultur.RP;
set alias=Kueche volume 18 8;
sleep 0.2;set alias=Bad play

Radioliste durchtasten

Will man eine Liste von bestimmten Radiostation mit einem Taster "durchtasten" kann man das wie folgt tun:

Das oben erwähnte 99_sonos2mqttUtils.pm (ab 6.1.2023) schreibt die Radios aus dem Reading Favoriten in das userReading favlist im SonosBridge Device und aktualisiert das auch.

Der Befehl zum weiterschalten

Jedes Mal wenn dieser Befehl ausgeführt wird, wird der nächste "Radio-Favorit" (aus dem userReading favlist des SonosBridge Devices) gestartet.

Wird eine Radiosender gespielt wird bei einem set sonos2mqtt_speaker next auf den nächsten Sender in dieser Liste geschaltet und der transportState wird beibehalten.

Name der Radiostation vor dem Umschalten ansagen

Will man vor dem Radiostart noch die Ansage des Senders haben, geht das zwar auch mit dem speak Befehl, die direkte Ausgabe ohne Restore der Umgebung (sonos2mqtt notify) ist aber effektiver.

Damit das funktioniert müssen wir die Events einschränken:

attr model=sonos2mqtt_speaker event-on-change-reading .*
{my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];;my $r=Each($dev,ReadingsVal($dev,'favlist',''));;my $play = (devspec2array('alias=Büro'))[0];;my $tts="SonosTTS";;fhem("set $tts tts Es folgt $r;;sleep $tts:playing:.0;;set $play playUri [$tts:httpName];;sleep $play:play;;sleep $play:PLAYING;;sleep $play:STOPPED;;set $play playFav $r")}

Kurze Erklärung zum Code

  • ermittelt den nächsten Radiosender in der Liste: my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];;my $r=Each($dev,ReadingsVal($dev,'favlist',''))
  • ermitteln des Namens des sonos2mqtt_speaker Devices anhand des alias Attributes: my $play = (devspec2array('alias=Büro'))[0]
  • erzeugt die Ansage "Es folgt SenderXY": my $tts="SonosTTS";;fhem("set $tts tts Es folgt $r
  • wenn die mp3 Datei fertig erzeugt ist: sleep $tts:playing:.0, wird sie mit dem Befehl playUri an den oben ermittelten sonos2mqtt_speaker gesendet: set $play playUri [$tts:httpName]
  • es wird eine Eventfolge abgewartet -> play / PLAYING / STOPPED: sleep $play:play;;sleep $play:PLAYING;;sleep $play:STOPPED
  • danach wird der Radiosender gestartet: set $play playFav $r

Der Code ist so einfach und relativ "steif" für die Kommandozeile. Man kann das auch in einen Setter packen (set sonos2mqtt_speaker toggleRadio). Dazu den Text unten in das Attribut setList des sonos2mqtt_speaker Devices hinzufügen:

toggleRadio:noArg {my $dev = (devspec2array('model=sonos2mqtt_bridge'))[0];my $r=Each($dev,ReadingsVal($dev,'favlist',''));my $tts="SonosTTS";fhem("set $tts tts Es folgt $r;sleep $tts:playing:.0;set $NAME playUri [$tts:httpName];sleep $NAME:play;sleep $NAME:PLAYING;sleep $NAME:STOPPED;set $NAME playFav $r")}

Dokumentationen und weitere Entwicklungen

ToDo

Erweiterung um Alarmhandling siehe Forum