Sonos Reading Radios in ein Array

Begonnen von TomLee, 20 Juli 2020, 09:09:46

Vorheriges Thema - Nächstes Thema

TomLee

Moin,

besteht die Möglichkeit aus diesem JSON ein Array zu erhalten welches kommasepariert nur die Titel ohne ' zurückgibt ?

{'R:0/0/1' => {'Title' => 'SWR4 Rheinland-Pfalz','Cover' => 'http://cdn-profiles.tunein.com/s1563/images/logog.png?t=153242','Ressource' => 'x-sonosapi-stream:s1563?sid=254&flags=8224&sn=0'},'R:0/0/3' => {'Title' => 'Radio Regenbogen','Cover' => 'http://cdn-radiotime-logos.tunein.com/s272334g.png','Ressource' => 'x-sonosapi-stream:s272334?sid=254&flags=8224&sn=0'},'R:0/0/5' => {'Title' => 'SWR4 Baden-Württemberg','Cover' => 'http://cdn-profiles.tunein.com/s20293/images/logog.png?t=1','Ressource' => 'x-sonosapi-stream:s20293?sid=254&flags=8224&sn=0'},'R:0/0/25' => {'Title' => 'PsyRadio Chillout','Cover' => 'http://cdn-radiotime-logos.tunein.com/s97066g.png','Ressource' => 'x-sonosapi-stream:s97066?sid=254&flags=8224&sn=0'},'R:0/0/23' => {'Title' => 'Nota Masria (Gemeinden)','Cover' => 'http://cdn-profiles.tunein.com/s290159/images/logog.png?t=158880','Ressource' => 'x-sonosapi-stream:s290159?sid=254&flags=8224&sn=0'},'R:0/0/33' => {'Title' => 'DASDING 92.5 (Euro-Hits)','Cover' => 'http://cdn-profiles.tunein.com/s20295/images/logog.png','Ressource' => 'x-sonosapi-stream:s76488?sid=254&flags=8224&sn=0'},'R:0/0/27' => {'Title' => '011.FM - Non Stop 60s','Cover' => 'http://cdn-radiotime-logos.tunein.com/s296020g.png','Ressource' => 'x-sonosapi-stream:s296020?sid=254&flags=8224&sn=0'}}

Gruß

Thomas

Beta-User

Vermutlich keine fertige Lösung, aber evtl. ein paar Bausteinchen:

- json2nameValue() kann zwischenzeitlich auch "filter": https://svn.fhem.de/trac/changeset/22407/. Kannst ja mal austesten, was rauskommt, wenn du da "Title" reinbastelst.
- Hier ging's um die Rückgabe einer komma-separierten Liste: https://forum.fhem.de/index.php/topic,112864.msg1072169.html#msg1072169. Mußt du aber umbauen, denn json2nameValue() liefert eine Referenz auf einen Hash zurück, und nicht direkt ein Array.
=> http://www.mathe2.uni-bayreuth.de/perl/EP/map.htm#sortHashValue könnte dabei helfen, dabei für den indirekten Zugriff sowas wie hier verwenden: https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/lib/AttrTemplate/mqtt2.template#L2582 

Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

juemuc

Hallo,

nutzt Du nicht das Sonos-Modul? Da sind doch alle Infos vorhanden.

Viele Grüße
Jürgen
3x Sonos Play 1, 1x Sonos Arc + Sub, 1 Sonos-One, 1x Sonos Playbar
FB6690 + FB7490 mit 4x Dect 200 und 3 Dect-ULE-Thermostate,  raspberry3B+, HM Funkmodul HM-MOD-RPI-PCB, HM Klingelsensor HM-Sen-DB-PCB, HM (IP) Fensterkontakte und  Amazon Echo Dot,  piVCCU, pi OS (bookworm).

TomLee

#3
Zitatnutzt Du nicht das Sonos-Modul? Da sind doch alle Infos vorhanden

Hab bisher keine andere Möglichkeit gesehen um

Zitatein Array zu erhalten welches kommasepariert nur die Titel ohne ' zurückgibt

zurückgeben könnte. Gibt es eine andere ?



Muss es denn mit json2nameValue sein, verstehen würd ich das gerne, ohne Hilfe aber zu hoch für mich.

Ist es so nicht ausreichend gelöst ?

{my @ar = (ReadingsVal("Button","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);;return join(',',@ar)}

Am Player noch nicht ausprobiert aber bei einem dummy

defmod Button dummy
attr Button readingList Radios
attr Button setList on off Radios

setstate Button on
setstate Button 2020-07-21 16:18:54 Radios {'R:0/0/1' => {'Title' => 'SWR4 Rheinland-Pfalz','Cover' => 'http://cdn-profiles.tunein.com/s1563/images/logog.png?t=153242','Ressource' => 'x-sonosapi-stream:s1563?sid=254&flags=8224&sn=0'},'R:0/0/3' => {'Title' => 'Radio Regenbogen','Cover' => 'http://cdn-radiotime-logos.tunein.com/s272334g.png','Ressource' => 'x-sonosapi-stream:s272334?sid=254&flags=8224&sn=0'},'R:0/0/5' => {'Title' => 'SWR4 Baden-Württemberg','Cover' => 'http://cdn-profiles.tunein.com/s20293/images/logog.png?t=1','Ressource' => 'x-sonosapi-stream:s20293?sid=254&flags=8224&sn=0'},'R:0/0/25' => {'Title' => 'PsyRadio Chillout','Cover' => 'http://cdn-radiotime-logos.tunein.com/s97066g.png','Ressource' => 'x-sonosapi-stream:s97066?sid=254&flags=8224&sn=0'},'R:0/0/23' => {'Title' => 'Nota Masria (Gemeinden)','Cover' => 'http://cdn-profiles.tunein.com/s290159/images/logog.png?t=158880','Ressource' => 'x-sonosapi-stream:s290159?sid=254&flags=8224&sn=0'},'R:0/0/33' => {'Title' => 'DASDING 92.5 (Euro-Hits)','Cover' => 'http://cdn-profiles.tunein.com/s20295/images/logog.png','Ressource' => 'x-sonosapi-stream:s76488?sid=254&flags=8224&sn=0'},'R:0/0/27' => {'Title' => '011.FM - Non Stop 60s','Cover' => 'http://cdn-radiotime-logos.tunein.com/s296020g.png','Ressource' => 'x-sonosapi-stream:s296020?sid=254&flags=8224&sn=0'}}
setstate Button 2020-07-21 17:37:20 state on


defmod Lamp dummy
attr Lamp setList SCHWACH AUS MITTEL AUS HELL AUS

setstate Lamp DASDING 92.5 (Euro-Hits)
setstate Lamp 2020-07-21 17:37:20 state DASDING 92.5 (Euro-Hits)



defmod not_Button notify Button:on {my @ar = (ReadingsVal("Button","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);;my $var =join(',',@ar);;\
fhem "set Lamp ".Each("Lamp", "$var") }

setstate not_Button 2020-07-21 17:37:20
setstate not_Button 2020-07-21 17:37:05 state active



Beta-User

Zitat von: TomLee am 21 Juli 2020, 17:41:44
Muss es denn mit json2nameValue sein, verstehen würd ich das gerne, ohne Hilfe aber zu hoch für mich.
Soweit ich das verstanden habe, brauchst du es im Kontext MQTT2_DEVICE. Da ist json2nameValue() mMn. das einfachste Werkzeug, um das Ziel zu erreichen. Von daher würde ich empfehlen, auf Klimmzüge rund um Dummy zu verzichten und den Stier direkt bei den Hörnern zu nehmen => json2nameValue() mit 4 Argumenten nutzen und einen eigenen readingList-Zweig dafür aufmachen. Das 4. Argument einfach in einem Testgerät mal ohne Präfix austesten:
json2nameValue($EVENT,'',$JSONMAP,'Title')
Als Ergebnis sollten nur "irgendwie nummerierte" Readings mit "Title" im Namen rauskommen.

Zitat
Ist es so nicht ausreichend gelöst ?
No, du brauchst sonst die for ... push-Variante aus dem Bausteinchen 2. (sowas ähnliches gab's mal mit DS18B20@Tasmota => template-file).

Das mag erst mal alles sehr abstrakt klingen, aber in Schritt 1 geht's erst mal nur darum, einen brauchbaren Hash mit json2nameValue() zu erzeugen. Erst wenn wir den haben, macht mMn. es Sinn, über die weitere Transformation nachzudenken.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

TomLee

#5
Nein, da hast du zu viel rein interpretiert, in meine kurze Frage, sry.

Es geht um diese Taste der TV-Fernbedienung aus diesem Thread ist aber auch nur Spielerei, nutz das nicht wirklich.

return fhem("set du_remote Sonos;saysonos Sonos") if $rcCode eq '0x20DF4EB1' && ReadingsVal('du_remote','state','') ne 'Sonos';

Wenn state des dummy schon Sonos ist, schalte durch die möglichen Radio-Favoriten , schwebt mir seit dem durch den Kopf und jetzt ist das über die o.g. Lösung realisierbar :P

Werd eh irgendwann auf s2m umstellen, dann ist das auch wieder hinfällig. ;D


TomLee

#6
Zitatjson2nameValue() liefert eine Referenz auf einen Hash zurück,

Es ist nicht so das ich mich damit nicht beschäftigt hätte und json2nameValue (meine Vorstellung) muss ja nicht ausschließlich im Kontext MQTT2_Device genutzt werden.

Wie dereferenziere ich, im ersten Schritt, den Inhalt den json2nameValue zurückgibt ?
Ich will einfach nur mal sehen was json2nameValue aus dem JSON macht.

Wenn ich hier unter 2.3  Und wie ruft man die Inhalte wieder ab? lese, dann versteh ich es bisher so dass das was json2nameValue zurückgibt praktisch $hash_ref = \%hash; ist.
my $hash = ReadingsVal("Button","test","unknown");; my $hash_ref = json2nameValue($hash,'','','Title');;
Verstehe ich das so richtig ?

Mit der Schleife foreach $key (keys%$hash_ref) würde durch die Hash-Elemente iteriert.

Mit $key bekäm ich erstmal das was json2nameValue macht zurück.

Bekomm ich aber nicht hin, $key muss doch erstmal deklariert werden ?

Kannst du mir bitte diesen ersten Schritt erläutern, erklären (wenn ich total daneben liege) ?

Beta-User

Vorab: Interessantes Dokument, das du da ausgegraben hast!

Klar, json2nameValue() kann man auch außerhalb nutzen, steht ja mit Absicht in fhem.pl und nicht im Code von MQTT2_DEVICE, und auch das mit my $hash_ref sieht soweit ok aus.
Was die Weiterverarbeitung angeht, ist das mit der for-Schleife (foreach=for, siehe Perl-Ecke) auch der richtige Ansatz. Im Prinzip sollte sich der Code aus https://svn.fhem.de/trac/browser/trunk/fhem/FHEM/10_MQTT2_DEVICE.pm#L168 ff. recyceln lassen, allerdings mit der Einschränkung, dass $hash ja noch nicht bekannt ist (aber über $defs{name} zu ermitteln sein sollte); da wird auch als notation "for my $key ..." verwendet. Damit sollten sich dann also in Schritt 1 Readings an einem beliebigen benannten Gerät erzeugen lassen (auch z.B. an einem Dummy oder dem Sonos-Gerät).

Leider war mein kleiner Test, einem dummy ("testdummy") via myUtils die Readings zu verpassen nicht  erfolgreich, ich habe nur noch keine Idee, warum. Hier jedenfalls mal der Code:
sub myj2nv_test {
my $hash = $defs{testdummy};
my $js = {'Title' => 'SWR4 Rheinland-Pfalz','Title2' => 'Radio Regenbogen'};

my $ret = json2nameValue($js,'','');
if($ret && ref $ret eq "HASH") {
  readingsBeginUpdate($hash);
  for my $k (keys %{$ret}) {
    readingsBulkUpdate($hash, makeReadingName($k), $ret->{$k});
  }
  readingsBulkUpdate($hash, "test", "lief");
  readingsEndUpdate($hash, 1);
  }
}

Da aber zumindest das Reading "test" aktualisiert wird, müßte das eigentlich passen...

Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

TomLee

{my @ar = (ReadingsVal("Button","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);;return join(',',@ar)}

Nochmal dazu weil ich es jetzt am Player umsetzen möchte und wieder mal (vergess es immer wieder, nutz das Teil fast nur für wenige Sprachausgaben) feststelle das die Radio-Titel ja URL-Encoded sein müssen.

Wie mach ich das mit der Schleife richtig das auch das letzte Element berücksichtigt wird:

{ my @ar = (ReadingsVal("Sonos_Wohnzimmer","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);; my $var =join(',',@ar);; foreach (@ar) {$var =~ s/ /\%20/;;} $var;;}

SWR4%20Rheinland-Pfalz,Radio%20Regenbogen,SWR4%20Baden-Württemberg,PsyRadio%20Chillout,Nota%20Masria%20(Gemeinden),DASDING%2092.5 (Euro-Hits),011.FM - Non Stop 60s

Beta-User

Wieso gehst du überhaupt nochmal zurück auf die array-Ebene und nutzt nicht den "global"-Modifier für eine einfache regex-Ersetzung?
{ my @ar = (ReadingsVal("Sonos_Wohnzimmer","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);; my $var =join(',',@ar);; $var =~ s/ /\%20/g;; $var;;}

(Und vergiß', dass es foreach gibt; for macht genau dasselbe...)
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

TomLee

Wieso gehst du überhaupt nochmal zurück auf die array-Ebene und nutzt nicht den "global"-Modifier für eine einfache regex-Ersetzung?

Jetzt weiß ichs. Danke.Danke.




Habs eben in der myUtils (ja ich weiß da kann man noch weiter optimieren) der FB eingebaut und klappt.  :)

Jetzt nochmal zu Each, das ich das auch richtig verstanden habe.
Es ist offensichtlich weil ich es getestet habe, frag aber trotzdem.
Der erste Parameter kann irgendein Device sein Hauptsache die Definition gibt es auch, richtig ?
Mit irgendeinem Fakenamen klappts nämlich nicht.


if (ReadingsVal('du_remote','state','') eq 'Sonos') {
my @ar = (ReadingsVal("Sonos_Wohnzimmer","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);
my $var =join(',',@ar);
$var =~ s/ /\%20/g;
return fhem("set Sonos_Wohnzimmer StartRadio ".Each("du_Testy2", "$var")) if $rcCode eq '0x20DF4EB1' && ReadingsVal('du_remote','state','') eq 'Sonos';
    return fhem("set Sonos_Wohnzimmer Play") if $rcCode eq '0x20DF22DD' && ReadingsVal('Sonos_Wohnzimmer','state','') eq 'STOPPED';
    return fhem("set Sonos_Wohnzimmer Pause") if $rcCode eq '0x20DF22DD' && ReadingsVal('Sonos_Wohnzimmer','state','') eq 'PLAYING';
    return fhem("set Sonos_Wohnzimmer VolumeD") if $rcCode eq '0x20DFE01F';
    return fhem("set Sonos_Wohnzimmer VolumeU") if $rcCode eq '0x20DF609F';
    #return fhem("set OG_Echo_Wohnzimmer:FILTER=playStatus!=playing tunein ".('s9014','s20293','s2485','s78261')[int(rand(4))]) if $rcCode eq '0x20DF4EB1';
    }

Beta-User

Zitat von: TomLee am 22 Juli 2020, 17:11:49
Der erste Parameter kann irgendein Device sein Hauptsache die Definition gibt es auch, richtig ?
Mit irgendeinem Fakenamen klappts nämlich nicht.
Korrekt; der aktuelle "Schaltzustand" wird in einem Internal an dem angegebenen Device festgehalten. Warum dafür aber den Dummy und nicht das jeweilige Sonos-Gerät?

Da bei dir das ganze aber dynamisch zu sein scheint (? Kann sich die Senderliste im Betrieb ändern?), stellt sich mir die Frage, ob es nicht besser wäre, irgendwie den aktuellen Sender abzufragen und dann mit einer Schleife über das (Leerzeichen-modifizierte?) Senderlisten-Array zu prüfen, welche Nummer der  aktuell eingestellte Sender grade hat und dann den nächsten aufzurufen? Das wäre dann auch "FHEM-restart-fest" (das Internal wird nirgends gespeichert, dann wird von vorne begonnen).


Was den Code-Schnippsel angeht, sehe ich auch noch Optimierungsmöglichkeiten (mal abgesehen von einer allgemeinen Generalisierung durch Übergabe des Zielgeräts):
if (ReadingsVal('du_remote','state','') eq 'Sonos') {
    if ($rcCode eq '0x20DF4EB1') {
      my @ar = (ReadingsVal("Sonos_Wohnzimmer","Radios","unknown") =~ m,Title'.=>.'(.[^']+.)',g);
      my $var =join(',',@ar);
      $var =~ s/ /\%20/g;
      return fhem("set Sonos_Wohnzimmer StartRadio ".Each("du_Testy2", "$var"));
    }
    if ($rcCode eq '0x20DF22DD') {
      return fhem("set Sonos_Wohnzimmer Play")  if ReadingsVal('Sonos_Wohnzimmer','state','') eq 'STOPPED';
      return fhem("set Sonos_Wohnzimmer Pause") if ReadingsVal('Sonos_Wohnzimmer','state','') eq 'PLAYING';
    }
    return fhem("set Sonos_Wohnzimmer VolumeD") if $rcCode eq '0x20DFE01F';
    return fhem("set Sonos_Wohnzimmer VolumeU") if $rcCode eq '0x20DF609F';
    #return fhem("set OG_Echo_Wohnzimmer:FILTER=playStatus!=playing tunein ".('s9014','s20293','s2485','s78261')[int(rand(4))]) if $rcCode eq '0x20DF4EB1';
}


Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

TomLee

Danke. Hätte schon noch umgestellt auf das Sonos-Gerät die Definition war einfach noch vom testen.




ZitatKann sich die Senderliste im Betrieb ändern?

Hier wird so selten der Player genutzt geschweige denn irgendwelche Radiosender hinzugefügt, da ändert sich nix.
Ich denke, weiß es nicht genau, die Liste kann sich nur ändern wenn man auch ein get ausführt.
Trotzdem, wenn ich es richtig verstanden habe ist es eine gute Idee zuvor abzufragen welcher Titel gerade läuft, sonst könnte es ja sein man ruft nochmal den gleichen Sender auf.

Zitatwelche Nummer der  aktuell eingestellte Sender grade hat
hab noch keine Idee wie, wenn mir bis morgen keine kommt frag ich nach einem Hinweis  :P



Zitat
... allgemeinen Generalisierung durch Übergabe des Zielgeräts

Du meinst doch das über ein userattr zu machen, hab ich das letzte mal auch ausprobiert, nur zum verstehen, aber nicht an dem jetzigen Beispiel, aber vorgenommen.

Wie ich es bis jetzt versteh, ergibt sich daraus das ich je Device (Sonos,Licht,Jupiter,Jalousie) ein notify definieren sollte, weil noch hab ich ja nur eins ?

Beta-User

#13
Zitat von: TomLee am 23 Juli 2020, 15:35:36
Wie ich es bis jetzt versteh, ergibt sich daraus das ich je Device (Sonos,Licht,Jupiter,Jalousie) ein notify definieren sollte, weil noch hab ich ja nur eins ?
Ja, nein, nicht unbedingt...

Es kommt insgesamt etwas darauf an, wie die Gesamtkonstruktion ist, Jupiter kann ich grade nicht einordnen, aber der Rest scheint ja völlig unabhängige Geräte abzubilden, von daher wären das 4 notify mit je einem anderen Funktionsaufruf (und dem entsprechenden Zieldevice darin als Aufrufparameter).
Um zu verhindern, dass du immer alle 4 subs durchläufst, könntest du den Trigger entsprechend beschränken und eben nur relevante Tastendrücke "durchlassen", dann ist der overhead sehr gering (bestimmte Tasten haben Doppelfunktionen, da kann man es wohl nicht ganz vermeiden, fliegt aber ja relativ schnell wieder aus der sub).

EDIT:
Also nicht über ein userattr, sondern einfach durch die Übergabe des Zielgeräts als Parameter im notify.
Ich mache das Teils auch über userattr oder readings, aber dann weiß der Trigger/das notify eigentlich noch gar nichts und der Code muß sich alles selbst zusammensuchen.
Beispiel: ein notify für alle Fenster-öffnen-Ereignisse. Das findet den zum Fenster gehörenden virtuellen Fensterkontakt (CUL_HM) und setzt dann den auf zu (gleich) bzw. offen (nach Ablauf einer via userattribut festlegbaren Zeit, die default (ohne Attribut) 90 Sekunden beträgt.
Ähnliches bei Fritzbox meldet WLAN-An-oder-Abmeldung (da kommen die Infos dann aber via getKeyValue).

Solche Konstruktionen machen mMn. dann Sinn, wenn man viele gleiche Ereignisse hat und dann nicht für jedes einen eigenen Handler bauen will... Hier ist das aber vermutlich was anderes.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: ZigBee2mqtt, MiLight@ESP-GW, BT@OpenMQTTGw | ZWave | SIGNALduino | MapleCUN | RHASSPY
svn: u.a Weekday-&RandomTimer, Twilight,  div. attrTemplate-files, MySensors

TomLee

Jupiter = MiLight Bulb in einer IKEA FADO an der Decke