yamahayxc
Ein Plugin zur Steuerung von Yamaha MusicCast-kompatiblen Geräten (Receiver, Streaminglautsprecher u.ä.), z.B. An/Aus, Eingangswahl, Lautstärke und Mute, Play/Pause, aktuellen Status lesen
Die Grundlage für das Plugin wurde schamlos bei Raoul Thill geklaut, dem Autor des yamaha-Plugins für ältere Yamaha-Receiver in SmartHomeNG.
Anmerkungen
Das Plugin wird nach Bedarf weiterentwickelt und unterstützt noch längst nicht alle möglichen Funktionen, die im Yamaha Extended Control (YXC)-Standard definiert sind. Ich nutze es aber selbst täglich. Die genutzten und getesteten Geräte sind RX-V483, ISX-18D und WX-010. Das Plugin nutzt die YXC API, welche auf dem Austausch von JSON-formatierten Daten basiert. Es abonniert die Daten der konfigurierten Geräte, um laufend mit Statusmeldungen versorgt zu werden. Diese werden per UDP verteilt, solange eine Kommunikation zwischen Client (Plugin) und Gerät erfolgt, und 10 Minuten nach der letzten Kommunikation. Das Plugin kann mit mehreren Verbindungen gleichzeitig umgehen. Bisher habe ich keine Fehler feststellen können, aber ich habe nur 5 Geräte zum Testen.
Items können nach eigenen Vorstellungen eingerichtet werden; sie werden vom Plugin durch das Item-Attribut yamahayxc_cmd identifiziert. yamahayxc_host und yamahayxc_zone müssen nicht direkt im Steuerungs-/Statusitems stehen - das Plugin sucht sie in den Vorfahren-Items nach oben. Dadurch können Items beliebig tief verschachtelt werden, z.B. um Fähigkeits-Items als Unteritems ihres jeweiligen Werte-Items abzulegen.
Das update-Item (in Struct yamahayxc.amp) hat keine eigene Funktion, sondern fragt beim Setzen aktiv den kompletten Status des Geräts ab (alle Zonen + netusb + Tuner + Link + Alarm). Nach jedem Schreiben eines beliebigen anderen Items aktualisiert das Plugin ohnehin automatisch den betroffenen Bereich (z.B. nur die geänderte Zone bei einem Zonen-Item, nur netusb bei einem netusb-Item, usw.) - ein komplettes update ist also i.d.R. nicht nötig, außer beim Timeout des 10-Minuten-Abonnements. Für einen gezielten, aber nicht vollständigen Refresh gibt es zusätzlich update_dsp (in Struct dsp/dsp_content), update_netusb (in media.netusb/netusb) sowie update_tuner/update_link.
Änderungen, Hinweise und Erweiterungen werden jederzeit angenommen, Wünsche können etwas dauern…
Neben main unterstützt das Plugin jetzt auch eigene Structs für zone2, zone3 und zone4 (yamahayxc.zone2/yamahayxc.zone3/yamahayxc.zone4, jeweils mit power/volume/mute/sleep/input/wakeup/update sowie den zonenspezifischen Link-Reglern link_control/link_audio_delay/link_audio_quality - inhaltlich dasselbe wie amp, nur für die jeweils andere Zone). Jedes dieser drei Structs bringt zusätzlich ein present-Item (r/o, bool) mit: erst True, wenn das Gerät die Zone laut getFeatures tatsächlich meldet - so kann eine Visu Zonen-Regler ein-/ausblenden, ohne raten zu müssen. amp (main) hat kein present, da main immer existiert. netusb-Funktionen (Wiedergabe, Presets, Titel/Interpret) sind weiterhin geräteweit (nicht zonenspezifisch), wie es der YXC-Standard vorsieht. Da mir keine Mehrzonen-Hardware zum Testen zur Verfügung steht, ist dieser Teil nur gegen die offizielle YXC-Spezifikation entwickelt, nicht gegen echte Geräte getestet - Rückmeldungen sind willkommen.
Der Tuner (AM/FM) wird jetzt ebenfalls unterstützt: Band/Frequenz einstellen, Sendersuchlauf, Presets abrufen/speichern/löschen/durchschalten (siehe Struct yamahayxc.tuner). DAB und das Umsortieren von Presets (movePreset) sind bewusst nicht implementiert - DAB, weil ich keine Testgeräte habe, movePreset weil es zwei Parameter (Quell- und Zielposition) braucht und sich nicht in das Ein-Item-ein-Wert-Schema des Plugins einfügt; über passthru bleibt es trotzdem erreichbar.
Klangfeld/DSP-Funktionen (Sound Program, 3D Surround, Direct, Pure Direct, Enhancer, Klangregelung, Equalizer, Balance) sind jetzt ebenfalls verfügbar (siehe Struct yamahayxc.dsp), zonenspezifisch und je nach Gerät unterschiedlich verfügbar. Für die Bool-Werte (surround_3d/direct/pure_direct/enhancer) folgt das Plugin dem in der YXC-Spezifikation dokumentierten Typ (JSON-Bool); konnte ich für diese neuen Items nicht an echter Hardware verifizieren - Rückmeldungen sind willkommen. mute/die Alarm-Items akzeptieren seit einem Bugfix beide Repräsentationen (natives JSON-Bool und String "true"/"false") - ein Gerät, das ein natives Bool sendet, ließ das Item nach jedem Schreiben durch den synchronen Zonen-Refresh sofort wieder auf False zurückfallen, da bis dahin nur der String-Vergleich unterstützt wurde. Im Struct sind Bass/Höhen/Balance unter tone gruppiert (tone.bass/tone.treble/tone.balance) und Equalizer-Bänder unter equalizer (equalizer.low/equalizer.mid/equalizer.high); tone_control_mode/equalizer_mode bleiben eigenständige Geschwister-Items, da sie für die jeweilige Gruppe insgesamt gelten. Zusätzlich gibt es dsp.available (r/o, bool): true, sobald die Zone laut getFeatures irgendeine DSP-Fähigkeit meldet (Sound Program oder einen der Klangfeld-Funktionsnamen) - praktisch z.B. um in einer Visu ein DSP-Popup nur anzuzeigen, wenn es auch etwas zu bedienen gibt.
Breaking Change / Migration: Vor dieser Umstellung lagen tone_bass/tone_treble/eq_low/eq_mid/eq_high/balance als flache Items direkt unter dem Geräteitem. Wer das Struct yamahayxc.dsp nutzt, muss vorhandene Item-Referenzen (Visu-Widgets, Logiken) auf die neuen Pfade umstellen. Für einen schrittweisen Umzug ohne Ausfallzeit gibt es das Struct yamahayxc.dsp_bridge: es bindet yamahayxc.dsp (neue Pfade) ein und ergänzt zusätzlich die sechs alten flachen Items - beide Pfad-Sätze sind dann gleichzeitig live (dasselbe yamahayxc_cmd kann jetzt auf mehrere Items gleichzeitig registriert werden, alle erhalten Updates). Nach dem Umzug der Referenzen die sechs alten Items aus der eigenen Konfiguration entfernen bzw. zurück auf yamahayxc.dsp wechseln. volume selbst ist nicht betroffen - der Pfad ist unverändert, es kamen nur Unteritems hinzu.
Weitere Struct-Umstellung - ``basic`` wurde zu ``amp`` + ``netusb`` aufgeteilt: Das bisherige Struct basic vermischte zonenspezifische Verstärkersteuerung (power/volume/mute/sleep/input) mit geräteweiter netusb-Wiedergabe (track/artist/albumart/playback/preset/curtime/totaltime/passthru/playing/standby) - bei Mehrzonen-Geräten führte das dazu, dass die netusb-Items bei jeder zusätzlich mit basic ausgestatteten Zone dupliziert wurden. Neu:
yamahayxc.amp- Verstärker-/Zonenkern (power/volume/mute/sleep/input) plus die zonenspezifischen Link-Regler (link_control/link_audio_delay/link_audio_quality), Ersatz fürbasic. Zusätzlichinput.sources(Liste der vom Gerät gemeldeten gültigen Eingänge, pergetFeatures, analog zuvolume.min/max). Für zone2/zone3/zone4 gibt es die inhaltlich identischen Structsyamahayxc.zone2/zone3/zone4(siehe oben, inkl.present).yamahayxc.netusb- die bisherigen netusb-Items, jetzt eigenständig; kombinieren mitampwie gehabt:struct: [yamahayxc.amp, yamahayxc.netusb](playing/standbybenötigenpowerausamp, um korrekt aufzulösen).yamahayxc.alarmist jetzt ebenfalls eigenständig (nur die drei Alarm-Items) statt automatischbasiceinzubinden - kombinieren wiedsp/tuner/link:struct: [yamahayxc.amp, yamahayxc.alarm].yamahayxc.tuner/link/netusbsind jetzt alle Aliase auf Unterbäume eines gemeinsamen, selbst nicht direkt zu verwendenden Structsyamahayxc.media(media.tuner/media.netusb/media.link) - inhaltlich unverändert (bis auf die drei Link-Regler, siehe unten), nur intern anders organisiert.mediaheißt so (vormalszone), weil es inzwischen wirklich rein geräteweit ist und beliebig oft/einmal pro Host kombiniert werden kann, ohne dass etwas dupliziert.yamahayxc.dspist jetzt ebenfalls ein eigenständiger Alias (aufdsp_content) statt Teil des altenzone-Structs - DSP ist wieampzonenspezifisch, gehörte also nie wirklich zu den geräteweiten netusb/tuner/link-Funktionen.
Weitere Breaking Change: struct: yamahayxc.link allein (ohne amp kombiniert) enthält link_control/link_audio_delay/link_audio_quality nicht mehr - die kommen jetzt aus amp/zone2/zone3/zone4, da sie laut Spezifikation zonenspezifisch sind (anders als der Rest von Link). Jedes dokumentierte Beispiel kombiniert ohnehin bereits [amp, ..., link], wo die Items am selben Pfad landen wie vorher (nur aus einem anderen Struct stammend) - betroffen ist also nur eine alleinstehende link-Nutzung, nicht der übliche Fall.
Migration wie beim DSP-Umzug: yamahayxc.legacy_basic ist ein eingefrorener Schnappschuss des alten basic-Structs (identische Item-Pfade, keine Anpassung nötig), yamahayxc.legacy_alarm entsprechend für das alte alarm. Wer struct: yamahayxc.basic bzw. yamahayxc.alarm verwendet, kann einfach auf yamahayxc.legacy_basic/yamahayxc.legacy_alarm umbenennen und später in Ruhe auf [yamahayxc.amp, yamahayxc.netusb] umstellen.
Neu sind außerdem reachable und last_seen (in Struct yamahayxc.amp/zone_amp, r/o, geräteweit statt zonenspezifisch - siehe oben zu media, warum das hier trotzdem im „amp“-Struct sitzt statt in einem eigenen: dasselbe Prinzip wie beim update-Item, das ebenfalls dort liegt, obwohl es geräteweit wirkt). Bisher blieb ein Gerät, das die Verbindung verliert (z.B. ein Außenlautsprecher ohne Strom), für das Plugin unsichtbar: Push-Benachrichtigungen laufen nach spätestens 10 Minuten ohne Kommunikation aus (siehe oben), und ohne aktive Anfrage bemerkte das Plugin den Ausfall gar nicht - alle Items (power, playback, track, artist usw.) blieben für immer auf ihrem letzten Wert stehen, ohne jede Kennzeichnung. Das Plugin fragt jetzt zusätzlich in einem festen Intervall (Parameter cycle, Standard 60s) den Gerätestatus ab, unabhängig davon, ob gerade Push-Daten eintreffen. Nach zwei aufeinanderfolgenden Fehlversuchen wird das Gerät als nicht erreichbar markiert (reachable = false, eine Warnung im Log), nach dem ersten wieder erfolgreichen Kontakt sofort wieder als erreichbar (reachable = true, ein kompletter Refresh aller Items). last_seen (Unix-Timestamp) wird bei jedem erfolgreichen Kontakt aktualisiert. Alle anderen Items werden bei Nichterreichbarkeit bewusst nicht zurückgesetzt - sie behalten ihren letzten bekannten Wert, eine Visu kann anhand von reachable/last_seen selbst entscheiden, wie sie das darstellt (z.B. abblenden). Schreibzugriffe (z.B. power setzen) werden unabhängig vom reachable-Status immer versucht - ein veralteter „nicht erreichbar“-Status ist kein Beweis, dass ein Gerät gerade wirklich noch offline ist.
MusicCast Link (Multiroom) - inzwischen an echter Mehrgeräte-Hardware getestet (Grundfunktionen), Details siehe unten. Struct yamahayxc.link bietet eine vereinfachte Sicht auf die rohen YXC dist/*-Primitiven:
linked(r/o, bool) ist das primäre „bin ich überhaupt gruppiert?“-Signal, abgeleitet ausgroup_id. Laut YXC-Spezifikation (Advanced) wirdgroup_idbeim Trennen auf eine 32-stellige Null-Hex-ID („000…“) zurückgesetzt - das ist offiziell dokumentiertes Verhalten, kein Gerätefehler.role(r/o, ‚server‘/‘client‘/‘none‘) wird auflinkedabgestimmt: sobaldlinkedfalse ist, liefert das Item immer ‚none‘, unabhängig davon, was das Gerät selbst gerade meldet. Grund: An echter Hardware (bestätigt sowohl über einenleavedieses Plugins als auch unabhängig über die MusicCast-App) wurde beobachtet, dass ein Gerät nach dem Trennen weiterhinrole: clientmeldet, währendgroup_idbereits korrekt auf Null steht -roleallein ist also kein verlässliches Signal,group_id/linkedsind es.available_devices(r/o, Liste) listet alle Hosts, für die dieses Plugin mindestens ein Item konfiguriert hat (reines Plugin-Bookkeeping, kein Gerätestatus) - gedacht als Grundlage für eine Visu-Dropdown-Auswahl fürclient.join/server.add_device. Steht bereits direkt nach dem Plugin-Start zur Verfügung, unabhängig davon, ob das Gerät erreichbar ist, und bleibt auch danach aktuell: Wird zur Laufzeit ein Item (Admin-UI) mityamahayxc_hostangelegt, bearbeitet (z.B. Hostname geändert) oder gelöscht, aktualisiert sich die Liste auf allen betroffenen Geräten sofort, ohne auf die nächste Geräteabfrage zu warten - sobald das letzte Item für einen Host verschwindet, wird auch dessen Eintrag entfernt.Server- und Client-Aktionen/-Infos sind in
server/clientunterverschachtelt - beide Gruppen existieren auf jedem Gerät gleichzeitig (welche gerade zutrifft, zeigtrole):server.linked_devices(r/o, Liste der Client-IPs) ist nur gefüllt, währendrole‚server‘ ist - ein Client kennt laut Spezifikation seine Mitspieler nicht, nur der Server führt die Liste.server.add_device/server.remove_device(als/werde Server, Ziel-Host als Client hinzufügen/entfernen),server.disband(eigene Rolle komplett aufgeben, Verteilung endet damit).client.join(Wert = Ziel-Host, wird Client davon),client.leave(die Gruppe verlassen).
Alle fünf sind bool- oder String-Trigger-Items:
enforce_updatesist gesetzt, und die reinen bool-Trigger (server.disband/client.leave/update) setzen sich nach getaner Aktion selbst aufFalsezurück (kein manuelles Aus-/Wieder-Einschalten nötig, um sie erneut auszulösen) -client.join/server.add_device/server.remove_devicebehalten ihren zuletzt geschriebenen Zielwert. Ein reines „Verteilung pausieren, Gruppe behalten“ (YXCstopDistribution) gibt es bewusst nicht als eigenes Item - dafür fehlt ein klarer Wiederaufnahme-Weg und ein praktischer Anwendungsfall; bei Bedarf später nachrüstbar.Selten benötigte/rohe Felder (
group_id,group_name,server_zone,audio_dropout,control,audio_delay,audio_quality) liegen unteroptions-options.group_idinsbesondere bleibt bewusst sichtbar, dalinkedgenau davon abgeleitet wird.
Diese Orchestrierungs-Funktionen (client.join/server.add_device/server.remove_device/client.leave/server.disband) rufen mehrere API-Aufrufe über zwei Geräte hinweg auf (Client + Master), wie es die YXC-Spezifikation (Advanced) in Abschnitt 9.1 beschreibt. Zwei konkrete Unsicherheiten bleiben (unabhängig von den o.g. Grundfunktionen, die inzwischen getestet sind):
Der
num-Parameter vonstartDistribution(„Link distribution number“) ist in der Spezifikation nicht eindeutig definiert; das Plugin verwendet einen einfachen, pro Plugin-Instanz hochzählenden Zähler als bestmögliche Interpretation.Antwortcode 200/201 bedeutet laut Spezifikation „Linking/Unlinking in progress“ - diese Vorgänge laufen also asynchron auf dem Gerät. Das Plugin wartet den Abschluss nicht ab, sondern verlässt sich auf die nächste Statusabfrage bzw. Push-Benachrichtigung.
Bitte weiterhin Rückmeldungen zu ungewöhnlichem Verhalten geben, insbesondere bei mehr als zwei Geräten oder verschachtelten Gruppen-Operationen - bisher nur mit zwei Geräten (1 Server, 1 Client) getestet.
Für Lautstärke und die Klangfeld-Wertebereiche (Bass/Höhen, Equalizer, Balance) liest das Plugin per getFeatures den vom Gerät gemeldeten min/max/step-Bereich aus und stellt ihn als read-only Unteritems bereit (z.B. volume.min/volume.max/volume.step, tone.bass.min/…, equalizer.low.min/…). Da diese Werte beim Start einmalig abgefragt werden und sich zur Laufzeit nicht ändern, aktualisieren sie sich nicht laufend. volume selbst ist standardmäßig auf 0..100 Prozent skaliert (beschreibbar, Umrechnung anhand von volume.min/volume.max, im Plugin selbst berechnet, nicht per eval-Struct) - die Lautstärke in reinen Gerätewerten liegt unter volume.raw. Damit kann eine Visu volume immer mit einer festen 0..100-Skala verwenden, unabhängig vom tatsächlichen Gerätebereich.
Genauso liest das Plugin für jedes Item mit einer festen Werteliste (statt eines Zahlenbereichs) die vom Gerät tatsächlich unterstützten Werte per getFeatures aus und stellt sie als read-only Listen-Unteritem <item>.values bereit: input.sources (bereits länger vorhanden), sowie neu sound_program.values, tone_control_mode.values, equalizer_mode.values, link_control.values, link_audio_delay.values und link_audio_quality.values. Meldet ein Gerät eine bestimmte Liste nicht (modellabhängig - z.B. tone_control_mode_list/equalizer_mode_list fehlen auf manchen Geräten ganz), bleibt das jeweilige .values-Item einfach leer ([]), statt einen Platzhalter oder Fehler zu erzeugen.
Beim Start fragt das Plugin pro Gerät getFeatures ab und merkt sich, welche Befehle (power/volume/mute) und Eingänge in der jeweiligen Zone tatsächlich unterstützt werden. Nicht unterstützte Befehle/Eingänge werden abgelehnt (mit Logmeldung), Lautstärkewerte werden auf den vom Gerät gemeldeten Bereich (min/max/step) begrenzt. Ist das Gerät beim Start nicht erreichbar, wird diese Prüfung übersprungen und wie bisher ungeprüft gesendet.
Inhalte durchsuchen (UPnP/USB/DLNA, z.B. um wie in der MusicCast-App durch USB-Ordner oder einen Medienserver zu navigieren) ist jetzt ebenfalls möglich, über getListInfo/setListControl (siehe Struct media.netusb/netusb, Unteritem browse): browse.list liefert die aktuelle Seite (bis zu 8 Einträge je getListInfo-Aufruf, .attribute je Eintrag zeigt u.a. an, ob er auswählbar (Ordner) und/oder abspielbar (Datei) ist), browse.page fordert eine bestimmte Seite an (wird intern auf ein Vielfaches von 8 abgerundet), browse.select/browse.play nehmen den absoluten Index eines Eintrags (browse.index + Position in browse.list), browse.return wechselt eine Ebene nach oben (bool-Trigger, setzt sich selbst zurück). Zwei Dinge sind dabei laut Spezifikation zu beachten, nicht nur Implementierungsdetails dieses Plugins: Die Navigationsposition ist geräteweit eine geteilte Cursor-Position, nicht pro Client/Zone getrennt - parallele Nutzung über die MusicCast-App oder eine zweite Visu-Sitzung überschneidet sich also. Und getListInfo kann das Gerät laut Spezifikation bis zu 30 Sekunden lang blockieren (keine andere Anfrage wird währenddessen angenommen) - deutlich länger als der sonstige Standard-Timeout des Plugins, browse.busy zeigt an, während ein Abruf läuft. browse.play spielt vorerst immer in Zone „main“ ab (Suche über setSearchString ist noch nicht implementiert). Gegen die offizielle YXC-Spezifikation entwickelt, noch nicht an echter Hardware getestet - Rückmeldungen sind willkommen.
Zusätzlich zu select/play/return unterstützt browse jetzt auch die vier Aktionen aus dem „…“-Kontextmenü der echten MusicCast-App: browse.play_now (sofort abspielen, unterbricht laufende Wiedergabe), browse.play_next (nach aktuellem Titel einreihen), browse.queue_add (ans Ende der Warteschlange anhängen) und browse.playlist_add (einer der bis zu 5 benannten MusicCast-Playlisten hinzufügen - welche, wird vorher über browse.playlist_bank gewählt, 1-5, gerätespezifisch, muss vor dem ersten playlist_add gesetzt sein). Alle vier nehmen wie browse.select/browse.play den absoluten Index eines Eintrags. Wichtig: Diese vier sind nicht Teil der offiziellen YXC-Spezifikation - getListInfo``s ``list_info[].attribute-Bitmaske zeigt zwar pro Eintrag an, ob er dafür geeignet ist (Bits 23-26: Play Now / Play Next / Add Play Queue / Add MusicCast Playlist), aber weder setListControl noch managePlay/manageList dokumentieren dafür einen type-Wert - die Spezifikation kennt bei manageList nur Streaming-Dienst-spezifisches Bookmarking (add_bookmark/add_track/add_to_playlist/… für Napster/Pandora/JUKE/Qobuz/TIDAL/Deezer). Die tatsächlich verwendeten Werte (play_now/play_next/add_to_queue/add_to_mc_playlist, alle über manageList) stammen aus einem Netzwerkmitschnitt der echten App - siehe _build_cmd_manage_list() im Plugincode. Entsprechend unklar/unbestätigt: ob diese Werte über alle netusb-Quellen (USB/Server/Streaming-Dienste) hinweg gleich funktionieren, oder Geräte-/Firmware-abhängig variieren. Rückmeldungen zu abweichendem Verhalten sind besonders hier willkommen. browse.play_now funktioniert dabei nicht nur auf einzelnen Titeln, sondern genauso auf Ordner-/Album-Einträgen (per Mitschnitt bestätigt) - spielt dann offenbar das ganze Album/den ganzen Ordner ab dem ersten Titel.
Ebenfalls per Mitschnitt gefunden und implementiert: die eigentliche Wiedergabe-Warteschlange, komplett unter media.netusb/netusb, Unteritem queue - taucht in der Spezifikation nicht einmal als „Reserved“ auf (nur getStatus.play_queue und getPlayInfo.play_queue_type deuten überhaupt an, dass es sowas gibt). queue.list/queue.max_line/queue.playing_index (per getPlayQueue) zeigen die aktuelle Warteschlange - eigener Index-Raum, nicht derselbe wie browse.index/browse.list: queue.play/queue.delete beziehen sich auf die Position in der Warteschlange, nicht auf die Position in der gerade durchsuchten Liste (delete statt remove, da letzteres mit Item.remove() kollidiert). queue.save_playlist (bool-Trigger) sichert die komplette Warteschlange als über browse.playlist_bank gewählte Playliste - dieselbe Bank-Auswahl wie browse.playlist_add, aber ganze Warteschlange statt einzelnem Eintrag. queue.clear leert sie, queue.update fragt sie manuell neu ab. Anders als browse.list (feste 8er-Seiten laut Spezifikation) lieferte ein einzelner Abruf im Mitschnitt alle 21 Einträge einer Warteschlange auf einmal zurück - das Plugin implementiert deshalb (noch) keine Seitenweise-Abfrage für queue.list, Verhalten bei sehr langen Warteschlangen ist unbestätigt. queue_type (auf netusb-Ebene, neben repeat/shuffle) zeigt die Art der aktuellen Warteschlange (beobachteter Wert: "user").
Ergänzend zu playlist_bank/playlist_add (Playlisten-Verwaltung, ebenfalls per Mitschnitt gefunden, nicht in der Spezifikation - nicht mal als „Reserved“): browse.playlist_names liefert die Namen aller 5 Playlisten (getMcPlaylistName, Index 0 = Bank 1, per Mitschnitt bestätigt - Umbenennen von Bank 1 änderte name_list[0]), browse.playlist_rename benennt die über playlist_bank gewählte Playliste um (setMcPlaylistName, aktualisiert playlist_names danach automatisch), browse.playlist_clear leert sie (clearMcPlaylist, Name bleibt erhalten). playlist_names wird nicht laufend abgefragt (ändert sich selten) - neben der automatischen Aktualisierung nach playlist_rename lässt sich das gemeinsame media.netusb.update (bzw. update_netusb) jederzeit manuell dafür nutzen, es aktualisiert jetzt beides (Wiedergabestatus und Playlistennamen).
Wiederhol- und Zufallswiedergabemodus sind ebenfalls verfügbar: media.netusb/netusb bekommt neu repeat (Werte off/one/all) und shuffle (Werte off/on/songs/albums, geräteabhängig), beide beschreibbar über setRepeat/setShuffle. Die jeweils gültigen Werte liefert das Gerät ohnehin bei jeder getPlayInfo-Abfrage mit, verfügbar als read-only repeat_available/shuffle_available - keine zusätzliche Abfrage nötig. Anders als bei input/sound_program etc. werden geschriebene Werte hier nicht gegen die Werteliste geprüft (die stammt aus getPlayInfo, nicht aus dem sonst für list_check genutzten zonenbezogenen getFeatures) - ein ungültiger Wert wird einfach ans Gerät weitergereicht und von dort ggf. mit einem Fehler-response_code abgelehnt (im Log als Warnung sichtbar).
Seit SmartHomeNG v1.7 werden Item-Structs bereitgestellt, mit denen die Funktionalitäten eines einfachen Players und zusätzlich Weckerfunktionen genutzt werden können.
Anforderungen
Das folgende Python-Modul muss vorhanden sein:
requests
Dies wird normalerweise durch SmartHomeNG automatisch installiert. Wer es manuell installieren möchte, kann das über das PIP-Tool oder über die Distribution tun:
# Python
pip3 install requests
# Debian based
sudo apt-get install python3-requests
# Arch Linux
sudo pacman -S python-requests
# Fedora
sudo dnf install python3-requests
Konfiguration
plugin.yaml
yamahayxc:
plugin_name = YamahaYXC
items.yaml
media:
wx010:
yamahayxc_host: 192.168.2.211
yamahayxc_zone: main
struct: [yamahayxc.amp, yamahayxc.netusb]
Zweite Zone desselben Geräts (z.B. Receiver mit main + zone2): einfach ein zweites Geräte-Item mit derselben yamahayxc_host, aber anderer yamahayxc_zone anlegen, und dafür yamahayxc.zone2/zone3/zone4 statt yamahayxc.amp verwenden (inhaltlich identisch, inklusive present). netusb nur bei einer der Zonen einbinden (netusb ist geräteweit, nicht zonenspezifisch - würde sonst pro Zone dupliziert):
media:
avr_main:
yamahayxc_host: 192.168.2.212
yamahayxc_zone: main
struct: [yamahayxc.amp, yamahayxc.netusb]
avr_zone2:
yamahayxc_host: 192.168.2.212
yamahayxc_zone: zone2
struct: yamahayxc.zone2
oder ohne Item-Structs (mit identischem Resultat zu [yamahayxc.amp, yamahayxc.netusb] kombiniert auf einem Item - die amp-Items direkt unter dem Geräteitem, die netusb-Items ab dem Track-Abschnitt weiter unten):
media:
wx010:
yamahayxc_host: 192.168.2.211
yamahayxc_zone: main
# writable items to control device/playback
# True = power on, False = standby
power:
type: bool
yamahayxc_cmd: power
enforce_updates: 'True'
# numeric volume. Range is 0..60 on my devices. May vary
volume:
type: num
yamahayxc_cmd: volume
enforce_updates: 'True'
# optional: device-reported range, fetched once via getFeatures
min:
type: num
yamahayxc_cmd: volume_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: volume_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: volume_step
visu_acl: ro
# optional: r/w volume scaled to 0..100 using min/max above
percent:
type: num
yamahayxc_cmd: volume_percent
visu_acl: rw
enforce_updates: 'True'
# True = mute enable, False = mute disable
mute:
type: bool
yamahayxc_cmd: mute
enforce_updates: 'True'
# input source as string. Heavily dependent on device.
input:
type: str
yamahayxc_cmd: input
enforce_updates: 'True'
# optional: device-reported list of valid input values,
# fetched once via getFeatures
sources:
type: list
yamahayxc_cmd: input_sources
visu_acl: ro
# possible values are 'play', 'stop', 'pause', 'previous', 'next'...
# (netusb from here on - device-global, not zone-specific)
playback:
type: str
yamahayxc_cmd: playback
enforce_updates: 'True'
# repeat mode, valid values in repeat_available below ('off'/'one'/'all')
repeat:
type: str
yamahayxc_cmd: repeat
enforce_updates: 'True'
# shuffle mode, valid values in shuffle_available below
# ('off'/'on'/'songs'/'albums')
shuffle:
type: str
yamahayxc_cmd: shuffle
enforce_updates: 'True'
# values are numeric and can (as of now) not be queried by the plugin
preset:
type: num
yamahayxc_cmd: preset
enforce_updates: 'True'
# values are numeric and can be 0 / 30 / 60 / 90 / 120 [minutes]
sleep:
type: num
yamahayxc_cmd: sleep
enforce_updates: 'True'
# read-only items to monitor device/playback status
# name of current track, if available
track:
type: str
yamahayxc_cmd: track
# name of current artist, if available. Radio station name for net_radio
artist:
type: str
yamahayxc_cmd: artist
# this is the URL of current album art image, if supported / supplied
# it is hosted on the respective yamaha device
albumart:
type: str
yamahayxc_cmd: albumart
# current time of playback in percent of total_time
# -1 if total_time is not available
play_pos:
type: num
yamahayxc_cmd: play_time
# total time of playback in seconds. 0 if not applicable / available
totaltime:
type: num
yamahayxc_cmd: total_time
# currently valid values for repeat/shuffle above, straight off
# getPlayInfo (not a separate query)
repeat_available:
type: list
yamahayxc_cmd: repeat_available
shuffle_available:
type: list
yamahayxc_cmd: shuffle_available
# write-only item to pass arbitrary command. Use at own discretion
passthru:
type: str
yamahayxc_cmd: passthru
enforce_updates: 'True'
# write-only item to force a full refresh (all zones + netusb + tuner +
# link + alarm). See notes above - update_dsp/update_netusb/update_tuner/
# update_link give a narrower, category-scoped refresh instead.
update:
type: bool
yamahayxc_cmd: state
enforce_updates: 'True'
# the following items are only valid for devices with alarm clock functions
# these are included in addition to the others from the 'alarm' struct:
# enable / disable alarm function
alarm_on:
type: bool
yamahayxc_cmd: alarm_on
enforce_updates: 'True'
# enable / disable alarm beep (solo or in addition to music)
alarm_beep:
type: bool
yamahayxc_cmd: alarm_beep
enforce_updates: 'True'
# get/set alarm time. Formatted as 4 digit 24 hour string
alarm_time:
type: str
yamahayxc_cmd: alarm_time
enforce_updates: 'True'
# the following items are only valid for devices with a tuner (AM/FM);
# also available as struct 'yamahayxc.tuner' (alias for media.tuner),
# device-global (nicht zonenspezifisch). Item-Namen ohne 'tuner_'-Präfix,
# der Pfad (tuner.band statt tuner_band) gibt den Kontext bereits vor.
tuner:
# band auswählen, 'am' oder 'fm'
band:
type: str
yamahayxc_cmd: tuner_band
enforce_updates: 'True'
# Frequenz direkt einstellen, in kHz (z.B. 87500 = 87,5 MHz)
freq:
type: num
yamahayxc_cmd: tuner_freq
enforce_updates: 'True'
# Sendersuchlauf: 'up' / 'down' / 'cancel' / 'auto_up' / 'auto_down' / 'tp_up' / 'tp_down'
seek:
type: str
yamahayxc_cmd: tuner_seek
enforce_updates: 'True'
# aktuell empfangen? (read-only)
tuned:
type: bool
yamahayxc_cmd: tuner_tuned
# Sendername/RDS-Text, falls verfügbar (read-only)
station:
type: str
yamahayxc_cmd: tuner_station
# Preset abrufen (Nummer je nach Gerät)
preset:
type: num
yamahayxc_cmd: tuner_preset
enforce_updates: 'True'
# aktuellen Sender auf Preset-Nummer speichern
preset_store:
type: num
yamahayxc_cmd: tuner_preset_store
enforce_updates: 'True'
# Preset-Nummer löschen
preset_clear:
type: num
yamahayxc_cmd: tuner_preset_clear
enforce_updates: 'True'
# zum nächsten/vorherigen Preset wechseln: 'next' / 'previous'
preset_switch:
type: str
yamahayxc_cmd: tuner_preset_switch
enforce_updates: 'True'
# die folgenden Items steuern Klangfeld/DSP-Funktionen (geräteabhängig);
# auch als Struct 'yamahayxc.dsp' verfügbar, zonenspezifisch
# Sound Program auswählen (z.B. 'munich', 'straight', ... - geräteabhängig)
sound_program:
type: str
yamahayxc_cmd: sound_program
enforce_updates: 'True'
# 3D Surround an/aus
surround_3d:
type: bool
yamahayxc_cmd: surround_3d
enforce_updates: 'True'
# Direct-Modus an/aus
direct:
type: bool
yamahayxc_cmd: direct
enforce_updates: 'True'
# Pure-Direct-Modus an/aus
pure_direct:
type: bool
yamahayxc_cmd: pure_direct
enforce_updates: 'True'
# Enhancer an/aus
enhancer:
type: bool
yamahayxc_cmd: enhancer
enforce_updates: 'True'
# Klangregelungs-Modus, z.B. 'manual' / 'auto' / 'bypass' (geräteabhängig)
tone_control_mode:
type: str
yamahayxc_cmd: tone_control_mode
enforce_updates: 'True'
# tone/bass/höhen/balance gruppiert unter 'tone', jeweils mit
# optionalem min/max/step-Unteritem (gerätespezifischer Bereich,
# via getFeatures)
tone:
# Bass-Wert (nur wirksam bei tone_control_mode=manual)
bass:
type: num
yamahayxc_cmd: tone_bass
enforce_updates: 'True'
min:
type: num
yamahayxc_cmd: tone_bass_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: tone_bass_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: tone_bass_step
visu_acl: ro
# Höhen-Wert (nur wirksam bei tone_control_mode=manual)
treble:
type: num
yamahayxc_cmd: tone_treble
enforce_updates: 'True'
min:
type: num
yamahayxc_cmd: tone_treble_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: tone_treble_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: tone_treble_step
visu_acl: ro
# L/R-Balance, negativ=links, positiv=rechts
balance:
type: num
yamahayxc_cmd: balance
enforce_updates: 'True'
min:
type: num
yamahayxc_cmd: balance_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: balance_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: balance_step
visu_acl: ro
# Equalizer-Modus, z.B. 'manual' / 'auto' / 'bypass' (geräteabhängig)
equalizer_mode:
type: str
yamahayxc_cmd: equalizer_mode
enforce_updates: 'True'
# Equalizer-Bänder gruppiert unter 'equalizer', jeweils mit
# optionalem min/max/step-Unteritem
equalizer:
# Equalizer tiefe Frequenzen (nur wirksam bei equalizer_mode=manual)
low:
type: num
yamahayxc_cmd: eq_low
enforce_updates: 'True'
min:
type: num
yamahayxc_cmd: eq_low_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: eq_low_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: eq_low_step
visu_acl: ro
# Equalizer mittlere Frequenzen (nur wirksam bei equalizer_mode=manual)
mid:
type: num
yamahayxc_cmd: eq_mid
enforce_updates: 'True'
min:
type: num
yamahayxc_cmd: eq_mid_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: eq_mid_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: eq_mid_step
visu_acl: ro
# Equalizer hohe Frequenzen (nur wirksam bei equalizer_mode=manual)
high:
type: num
yamahayxc_cmd: eq_high
enforce_updates: 'True'
min:
type: num
yamahayxc_cmd: eq_high_min
visu_acl: ro
max:
type: num
yamahayxc_cmd: eq_high_max
visu_acl: ro
step:
type: num
yamahayxc_cmd: eq_high_step
visu_acl: ro
# die folgenden Items steuern MusicCast Link (Multiroom), geräteabhängig;
# auch als Struct 'yamahayxc.link' (Alias für media.link) verfügbar.
# Grundfunktionen an echter Mehrgeräte-Hardware getestet, siehe Hinweise
# weiter oben in diesem Dokument. Vereinfachte UI über die rohen YXC
# dist/*-Primitiven - 'linked'/'role' sind die primären Signale, Rohdaten
# liegen unter 'options'. link_control/link_audio_delay/
# link_audio_quality liegen NICHT hier, sondern in 'amp'/'zone2'/'zone3'/
# 'zone4' (zonenspezifisch, siehe oben).
link:
# primäres "bin ich gruppiert?"-Signal, aus group_id
# abgeleitet (read-only)
linked:
type: bool
yamahayxc_cmd: link_linked
# 'server' / 'client' / 'none' - wird auf 'none' erzwungen,
# sobald 'linked' false ist, auch wenn das Gerät selbst noch
# einen älteren Wert meldet (read-only)
role:
type: str
yamahayxc_cmd: link_role
# alle Hosts, für die dieses Plugin mindestens ein Item
# konfiguriert hat (Plugin-Bookkeeping, kein Gerätestatus) -
# z.B. für eine Visu-Dropdown-Auswahl (read-only). Steht
# bereits direkt nach dem Start zur Verfügung.
available_devices:
type: list
yamahayxc_cmd: link_hosts
# Aktionen/Infos, die nur als Gruppen-Server Sinn ergeben
server:
# IP-Adressen der Gruppen-Clients, nur gefüllt während
# role=server (read-only)
linked_devices:
type: list
yamahayxc_cmd: link_devices
# (als/werde Server) den angegebenen Host/IP als Client
# hinzufügen
add_device:
type: str
yamahayxc_cmd: link_add_client
enforce_updates: 'True'
# den angegebenen Host/IP aus der Gruppe entfernen
remove_device:
type: str
yamahayxc_cmd: link_remove_client
enforce_updates: 'True'
# eigene Rolle komplett aufgeben (kein separates
# "pausieren, Gruppe behalten" - kein klarer
# Wiederaufnahme-Weg, kein praktischer Anwendungsfall)
disband:
type: bool
yamahayxc_cmd: link_disband
enforce_updates: 'True'
# Aktionen, die nur als Gruppen-Client Sinn ergeben
client:
# als Client des angegebenen Host/IP einer Gruppe
# beitreten
join:
type: str
yamahayxc_cmd: link_join
enforce_updates: 'True'
# die Gruppe verlassen
leave:
type: bool
yamahayxc_cmd: link_leave
enforce_updates: 'True'
# selten benötigte/rohe Felder
options:
# Gruppen-ID, 32-stelliger Hex-String (read-only) - das
# ist, wovon 'linked' abgeleitet wird
group_id:
type: str
yamahayxc_cmd: link_group_id
# Gruppenname setzen/lesen (nur im flüchtigen Speicher
# des Geräts)
group_name:
type: str
yamahayxc_cmd: link_group_name
enforce_updates: 'True'
# Zone, die als Master dient, falls role=server (read-only)
server_zone:
type: str
yamahayxc_cmd: link_server_zone
# Tonaussetzer während Distribution erkannt? (read-only)
audio_dropout:
type: bool
yamahayxc_cmd: link_audio_dropout
# link_control ('normal'/'stability_boost'), link_audio_delay
# ('lip_sync'/'audio_sync_on'/'audio_sync_off'/'balanced') und
# link_audio_quality ('compressed'/'uncompressed') sind zonenspezifisch
# (Spezifikations-Ausnahme innerhalb von Link) und liegen deshalb in
# 'amp'/'zone2'/'zone3'/'zone4', nicht hier:
link_control:
type: str
yamahayxc_cmd: link_control
enforce_updates: 'True'
link_audio_delay:
type: str
yamahayxc_cmd: link_audio_delay
enforce_updates: 'True'
link_audio_quality:
type: str
yamahayxc_cmd: link_audio_quality
enforce_updates: 'True'
Beispiel der Nutzung per CLI-Plugin
> up media.wx010.power=True
> up media.wx010.input=net_radio
> up media.wx010.volume=15
> up media.wx010.mute=True
> up media.wx010.mute=False
> up media.wx010.playback=play
> up media.wx010.power=False
> up media.wx010.passthru='v1/Main/setPower?power=off'
> up media.wx010.alarm_time='1430'
> up media.avr.tuner.band=fm
> up media.avr.tuner.freq=87500
> up media.avr.tuner.preset=5
> up media.avr.sound_program=munich
> up media.avr.tone.bass=3
> up media.avr.tone.balance=-5
> up media.avr.volume=50
> up media.avr.volume.raw=35
> up media.roomA.link.client.join=192.168.2.212
> up media.roomA.link.client.leave=True
- PS:
Das war gelogen. Der WX-010 hat gar keine Wecker-Funktionen…