Radio-/Playlisten aus FHEM laden für "Sonos Widget for smartVISU / FHEM"

Begonnen von raman, 19 Juni 2016, 13:44:30

Vorheriges Thema - Nächstes Thema

raman

Ich hab mir mal erlaubt das sonst echt tolle Widget (Dank an den Entwickler!) so anzupassen, dass es möglich ist die Radio- und Playlisten aus den Readings von FHEM auszulesen.
Ich fand es immer etwas umständlich, die Listen händisch in smartVISU anzugeben.

Hier mal der die Anleitung (Bausatz!) für die Änderungen, die im Code nötig sind.

In widget_ddtlabs_sonos.js (oder in der eigene visu.js) muss folgendes eingefügt werden:


/* -----------------------------------------------
ddtlabs_sonos.selectmenu
----------------------------------------------- */

$(document).delegate('select[data-widget="ddtlabs_sonos.selectmenu"]', {
'update': function (event, response) {
if ($(this).val() == undefined) {
$(this).find('option').remove().end();
var newOption = "<option value='" + $(this).attr('data-label') + "'>" + $(this).attr('data-label') + "</option>";
$(this).append(newOption).selectmenu('refresh');
var playlists = response[0].split(';');
                        playlists.sort();
for (var i = 0; i < playlists.length; i++) {
var newOption = "<option value='" + playlists[i] + "'>" + playlists[i] + "</option>";
$(this).append(newOption).selectmenu('refresh');
}
}
},
'change': function (event) {
io.write($(this).attr('data-item'), $(this).val());
$(this).val($(this).attr('data-label')).selectmenu('refresh',true);
}
});


$(document).delegate('input[data-widget="ddtlabs_pos_sonos.slider"]', {
'update': function (event, response) {
$('#' + this.id).val(response).slider('refresh').attr('mem', $(this).val());
},
});



In widget_ddtlabs_sonos.html müssen folgende Zeilen ersetzt werden:

Zeile 24

{% macro player(id, gad, neighbors, radiolist_header, radiolist_items, playlist_header, playlist_items) %}

durch

{% macro player(id, gad, neighbors, radiolist_header, playlist_header) %}

ersetzen.

Zeile 134

{{ ddtlabs_int_sonos.selectmenu(id~'playlist', gad~playlist,  playlist_items, '', playlist_header) }}

durch

{{ ddtlabs_int_sonos.selectmenu(id~'playlist', gad~playlist, playlist_header) }}

ersetzen.

Zeile 136

{{ ddtlabs_int_sonos.selectmenu(id~'radiolist', gad~radiolist, radiolist_items, '', radiolist_header, 'urlencode') }}

durch

{{ ddtlabs_int_sonos.selectmenu(id~'radiolist', gad~radiolist, radiolist_header) }}

ersetzen.

Zeile 281 - 293

{% macro selectmenu(id, gad, items, label, selected, urlencode) %}
<a class="ui-btn-inline ui-mini selectmenu">
<select name="{{ uid(page, id) }}" id="{{ uid(page, id) }}" data-widget="ddtlabs_sonos.selectmenu" data-item="{{ gad }}" data-native-menu="false" data-placeholder="false" data-mini="true" data-native-menu="false" class="selectmenu" />
{% for item in items %}
{% if urlencode %}
<option value="{{ item[1]|url_encode }}">{{ item[0] }}</option>
{% else %}
<option value="{{ item[1] }}">{{ item[0] }}</option>
{% endif %}
{% endfor %}
<option selected>{{ selected }}</option>
</select>
</a>

durch

{% macro selectmenu(id, gad, label) %}
<a class="ui-btn-inline ui-mini select">
<select name="{{ uid(page, id) }}" id="{{ uid(page, id) }}" data-widget="ddtlabs_sonos.selectmenu" data-item="{{ gad }}" data-label="{{ label }}" data-native-menu="false" data-placeholder="false" data-mini="true" data-native-menu="false" class="selectmenu" />
</select>
</a>

ersetzen.

In 99_fronthemSonosUtils.pm im FHEM-Ordner folgendes zwischen Zeile 847 und 850 eingügen:


sub SonosLists(@)
{
my ($param) = @_;
my $cmd = $param->{cmd};
my $gad = $param->{gad};
my $gadval = $param->{gadval};

my $device = $param->{device};
my $reading = $param->{reading};
my $event = $param->{event};

my @args = @{$param->{args}};
my $cache = $param->{cache};

my $cName = "fronthem converter (SonosLists): ";

if ($param->{cmd} eq 'get')
{
if ($reading eq "Playlists")
{
main::fhem("get $device PlaylistsWithCovers $gadval");
}
elsif ($reading eq "Radios")
{
main::fhem("get $device RadiosWithCovers $gadval");
}
$event = ($reading eq 'state')?main::Value($device):main::ReadingsVal($device, $reading, '');
    $param->{cmd} = 'send';
}
if ($param->{cmd} eq 'send')
{
my $playlist = '';
my @data = split(/,'/,$event);

for(my $i = 0; $i < @data; $i++) {
my $templist = $data[$i];
if($templist =~ /Title/){
$templist =~ /'Title'\s=>\s'(.*)'/;
$playlist .= $1;
$playlist .= ';';
}
}
chop($playlist);
$param->{gad} = $gad;
$param->{gadval} = $playlist;
$param->{gads} = [];
return undef;
}
elsif ($param->{cmd} eq 'rcv')
{
if ($reading eq "Playlists")
{
$gadval =~ s/ /%20/g;
main::fhem("set $device LoadPlaylist $gadval");
#main::fhem("set $device Play"); # directly start playing
$param->{results} = [];
return 'done';
}
elsif ($reading eq "Radios")
{
$gadval =~ s/ /%20/g;
main::fhem("set $device LoadRadio $gadval");
#main::fhem("set $device Play"); # directly start playing
$param->{results} = [];
return 'done';
}
else {
# other readings...
main::Log3 undef, 1, $cName . "SonosLists converter should only be used for reading Sonos ". $reading;
main::Log3 undef, 1, $cName . "but was used for: set " . $device . " " . $reading . " " . $gadval;
$param->{results} = [];
return undef;
}
}
elsif ($param->{cmd} eq '?')
{
return 'usage: Sonos';
}
return undef;
}


Danach sollte man einen Neustart von FHEM durchführen und den Cache von smartVISU löschen!

Es sollte dann für fronthem ein neuer Converter "SonosLists" zur Verfügung stehen.

GADs in fronthem sind:


"mm_mySonos_Playlist" : {
    "type" : "item",
    "device" : "mySonos",
    "reading" : "Playlists",
    "set" : "Playlists",
    "converter" : "SonosLists"
},

"mm_mySonos_Radiolist" : {
    "type" : "item",
    "device" : "mySonos",
    "reading" : "Radios",
    "set" : "Radios",
    "converter" : "SonosLists"
},


Definition des Players in smartVISU z.B. mit:


{% import "widget_ddtlabs_sonos.html" as ddtlabs_sonos %}
{{ ddtlabs_sonos.player('mySonos', 'MySonos', '', 'Radio:', 'Playlisten:')}}


Viel Spaß damit, ich hoffe es funktioniert auch bei euch!


dev0

Herzlich willkommen im Forum raman!

Tolle Arbeit. Das Selectmenu ansich kann man bestimmt auch für andere Widget wiederverwenden.
Zwei Kleinigkeiten musste ich allerdings anpassen, damit es (bei mir?) funktioniert: Im Konverter passte das Substituieren von $templist nicht ganz (ein single quote störte) und das angegebene GAD Setting für Radiolist/reading muss "Radios" lauten und nicht "Playlists".

Wenn Du nichts dagegen hast, dann würde ich das bei Gelegenheit in das Sonos Widget auf github einpflegen.

raman

Hallo dev0,

kannst du gerne mit ins Widget einpflegen.
Das mit Radios ist ein klassische copy&paste-Fehler. 
Was mir selbst noch aufgefallen ist, dass das splitten der Playlist bzw. Radiolist mit dem Seperator ',', wie ich es hier urprünglich gepostet habe, nicht zuverlässig funktioniert, da manche Radiosender ein Komma im Namen haben. Deshalb ist ein Semikolon besser (nicht zwei, da sonst chop im Konverterteil nicht alle Seperatoren am Ende entfernt).

Das Selectmenü kann man sicher als eigenes (Basic)Widget einpflegen.

Ach ja, wenn die Radio-/Playlisten automatisch nach Anwahl abgespielt werden sollen, kann man folgende Zeilen im Konverter aktivieren.


#main::fhem("set $device Play"); # directly start playing


Aber das ist Geschmackssache,  das kannst du machen wie du willst. Oder man überlässt es jedem User selbst!

Gruß
raman

dev0

Zitat von: raman am 20 Juni 2016, 14:12:06
das splitten der Playlist bzw. Radiolist mit dem Seperator ',', wie ich es hier urprünglich gepostet habe, nicht zuverlässig funktioniert
Besser noch ganz auf das Splitten verzichten:

if ($param->{cmd} eq 'send')
{
  my @playlist;
  my %evt = %{eval( $event )};
  foreach (keys %evt) { push(@playlist, $evt{$_}{"Title"}) }
  $param->{gadval} = join(";;",sort @playlist);
  $param->{gad} = $gad;
  $param->{gads} = [];
  return undef;
}


Beim genaueren Hinsehen ist mir noch aufgefallen, dass folgende Zeilen im Konverter überflüssig sind und ein unnötiges Delay verursachen. Oder erkenne ich den Sinn nur nicht?

if ($reading eq "Playlists")
{
  main::fhem("get $device PlaylistsWithCovers $gadval");
}
elsif ($reading eq "Radios")
{
  main::fhem("get $device RadiosWithCovers $gadval");
}

raman

Deine Lösung ist eleganter! (In perl bin ich noch nicht so fit.)

Dass die Listen neu geladen werden, hatte ich für Testzwecke eingebaut, da ja fhem und sv nicht "mitbekommen", wenn man die Listen über die Sonos-App ändert. Kann man aber auch weglassen.

Evtl. kann man das z.B. über nen Pageevent in sv getrennt anstoßen.

dev0

Ich bin in Perl auch nicht so fit, ist auch nur ein Hobby für mich. Javascript/JQuery macht mir aber noch mehr Schierigkeiten, daher fande ich es toll, dass Du es übernommen hast, die Readings aus Fhem für ein Selectmenü verfügbar zu machen. Hatte schon 2x im Forum danach gefragt aber bisher keine Unterstützung bekommen.

Wenn Playlisten in der Sonos App geändert werden, dann sollte das Backend (Sonos Modul) das eigentlich mitbekommen. Aber man könnte ja noch einen administrativen Button im Widget für ein Popup einbauen, dass solche Dinge wie Listen neu einlesen, Sonos Modul restart, ... zur Verfügung stellt.

Huntercover

genau was ich gesucht habe !
Bevor ich den Umbau wage (Perl ist nicht meins..): könnte der Code auch mit MPD funktionieren, bzw. hat das irgendwer portiert?

Vielen Dank!