NEWS
[Vorlage] Denon HEOS Script
-
Hi!
Ich habe seit Wochen das Heos Skript laufen, ohne Probleme!
Seit gestern ist es so, wenn das Heos Skript aktiv ist, dass sich mein Denon Verstärker permanent ein und ausschaltet, immer wieder, ein-aus,ein-aus.
Erst wenn ich das Heos Skript stoppe, dann gehts wieder.
Hat irgendwer eine ähnliche Erfahrung?
Liebe Grüße
Jürgen -
Hi
Generiert das Script in dem Zusammenhang irgendwelche auffälligen Logeinträge?
Wenn nicht, dann könnte es am Denon selbst liegen.
Dann würd ich mal ein Netzwerkreset am Denon versuchen. Wie das geht, steht in der Anleitung.
Danach musst du allerdings die Netzwerkeinstellungen neu einstellen.Gruß Thomas
-
@Desastro
Hi!
Ja, danke Dir!
Habe alles wieder resettet, jetzt gehts wieder.
Habe aber das Problem immer wieder, muss mal forschen woran das liegt!
Liebe Grüße
Jürgen -
Hast du das Script in einer eigenen Javascript Instanz laufen, oder alles zusammen mit weiteren Scripts?
Wäre evtl. dann auch noch ein Test wert.Gruß Thomas
-
@Desastro
alles klar -
@Desastro
Hi!
Bin jetzt draufgekommen, dass wirklich der Javascript Adapter Schuld an diesem Problem ist, bei einem Reset hat es wieder funktioniert.
Du meinst eine 2te Instanz wäre eine Option?
Liebe Grüße
Jürgen -
-
@Desastro
Hi!
So, ich habe die 2. Instanz aktiviert.
Heute in der Früh hat GSD alles funktioniert bis auf eine Kleinigkeit:Früher war es bei meinem Radiosender so, dass wenn Musik gespielt worden ist, dann habe ich das Cover und den Songtitel gesehen.
Jetzt sehe ich nur mehr das Logo des Radiosenders!
Sprich "Aktuelles Lied" und "Aktuelles Coverbild" werden in den Objekten nicht aktualisiert.
Hat wer eine Idee warum das so ist? -
das passiert, wenn ich die Objekte lösche und das Skript wieder neu starte, um die Objekte zu erzeugen
web.0 2020-02-28 09:28:07.539 info (26785) ==>Connected system.user.admin from ::ffff:192.168.1.159 web.0 2020-02-28 09:28:05.988 info (26785) ==>Connected system.user.admin from ::ffff:192.168.1.159 javascript.1 2020-02-28 09:26:58.908 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] signed in: success javascript.1 2020-02-28 09:26:58.030 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] signed in: success javascript.1 2020-02-28 09:26:58.021 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [HeosPlayer 192.168.1.177] starting HEOS player for IP 192.168.1.177 javascript.1 2020-02-28 09:26:58.013 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] connected to 192.168.1.177 javascript.1 2020-02-28 09:26:58.012 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] connected to HEOS javascript.1 2020-02-28 09:26:58.010 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] connecting to 192.168.1.177 ... javascript.1 2020-02-28 09:26:57.269 info (16326) script.js.Weldscripts.Javascript.Heos_neu: registered 1 subscription and 0 schedules javascript.1 2020-02-28 09:26:57.267 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] connecting to HEOS ... javascript.1 2020-02-28 09:26:57.263 info (16326) Start javascript script.js.Weldscripts.Javascript.Heos_neu javascript.1 2020-02-28 09:26:54.113 error (16326) error in onStop callback: TypeError: this.nodessdp_client.destroy is not a function javascript.1 2020-02-28 09:26:54.112 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] disconnecting from HEOS ... javascript.1 2020-02-28 09:26:54.110 info (16326) Stop script script.js.Weldscripts.Javascript.Heos_neu javascript.1 2020-02-28 09:26:46.169 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] signed in: success javascript.1 2020-02-28 09:26:45.882 warn (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] Command_not_recognized javascript.1 2020-02-28 09:26:45.879 warn (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] Command_not_recognized javascript.1 2020-02-28 09:26:45.496 warn (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] Command_not_recognized javascript.1 2020-02-28 09:26:45.494 warn (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] Command_not_recognized javascript.1 2020-02-28 09:26:45.476 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] command: javascript.1 2020-02-28 09:26:45.474 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] command: javascript.1 2020-02-28 09:26:45.415 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.415 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.415 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.415 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.415 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.415 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.414 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.414 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.414 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:726:26) javascript.1 2020-02-28 09:26:45.414 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.413 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.412 warn (16326) State "javascript.1.heos.192_168_1_177.duration_MMSS" not found javascript.1 2020-02-28 09:26:45.412 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.412 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.412 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.412 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.411 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.411 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.411 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.411 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.410 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:725:26) javascript.1 2020-02-28 09:26:45.410 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.410 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.409 warn (16326) State "javascript.1.heos.192_168_1_177.duration" not found javascript.1 2020-02-28 09:26:45.409 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.409 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.409 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.408 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.408 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.408 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.408 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.408 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.407 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:724:26) javascript.1 2020-02-28 09:26:45.407 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.407 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.406 warn (16326) State "javascript.1.heos.192_168_1_177.cur_pos_MMSS" not found javascript.1 2020-02-28 09:26:45.406 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.406 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.405 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.405 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.405 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.405 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.404 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.404 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.404 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:723:26) javascript.1 2020-02-28 09:26:45.403 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.403 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.402 warn (16326) State "javascript.1.heos.192_168_1_177.cur_pos" not found javascript.1 2020-02-28 09:26:45.401 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.401 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.400 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.400 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.400 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.399 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.399 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.399 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.398 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:726:26) javascript.1 2020-02-28 09:26:45.398 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.398 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.397 warn (16326) State "javascript.1.heos.192_168_1_177.duration_MMSS" not found javascript.1 2020-02-28 09:26:45.397 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.396 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.396 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.396 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.396 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.395 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.395 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.395 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.395 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:725:26) javascript.1 2020-02-28 09:26:45.393 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.393 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.391 warn (16326) State "javascript.1.heos.192_168_1_177.duration" not found javascript.1 2020-02-28 09:26:45.391 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.391 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.390 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.390 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.390 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.390 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.390 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.389 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.389 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:724:26) javascript.1 2020-02-28 09:26:45.389 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.388 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.388 warn (16326) State "javascript.1.heos.192_168_1_177.cur_pos_MMSS" not found javascript.1 2020-02-28 09:26:45.385 warn (16326) at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17) javascript.1 2020-02-28 09:26:45.385 warn (16326) at Socket.Readable.push (_stream_readable.js:224:10) javascript.1 2020-02-28 09:26:45.384 warn (16326) at readableAddChunk (_stream_readable.js:269:11) javascript.1 2020-02-28 09:26:45.384 warn (16326) at addChunk (_stream_readable.js:288:12) javascript.1 2020-02-28 09:26:45.383 warn (16326) at Socket.emit (events.js:198:13) javascript.1 2020-02-28 09:26:45.383 warn (16326) at Socket.net_client.on (script.js.Weldscripts.Javascript.Heos_neu:341:51) javascript.1 2020-02-28 09:26:45.382 warn (16326) at Heos.onData (script.js.Weldscripts.Javascript.Heos_neu:358:14) javascript.1 2020-02-28 09:26:45.382 warn (16326) at Heos.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:478:44) javascript.1 2020-02-28 09:26:45.381 warn (16326) at HeosPlayer.parseResponse (script.js.Weldscripts.Javascript.Heos_neu:723:26) javascript.1 2020-02-28 09:26:45.381 warn (16326) at HeosPlayer.setState (script.js.Weldscripts.Javascript.Heos_neu:691:4) javascript.1 2020-02-28 09:26:45.380 warn (16326) at setState (/opt/iobroker/node_modules/iobroker.javascript/lib/sandbox.js:1425:20) javascript.1 2020-02-28 09:26:45.379 warn (16326) State "javascript.1.heos.192_168_1_177.cur_pos" not found javascript.1 2020-02-28 09:26:45.348 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] signed in: success javascript.1 2020-02-28 09:26:45.333 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [HeosPlayer 192.168.1.177] starting HEOS player for IP 192.168.1.177 javascript.1 2020-02-28 09:26:45.280 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] connected to 192.168.1.177 javascript.1 2020-02-28 09:26:45.279 info (16326) script.js.Weldscripts.Javascript.Heos_neu: [Heos] connected to HEOS
-
Hi
Hast du bei der neuen Instanz in den Einstellungen "node-ssdp" bei den zusätzlichen Modulen mit eingetragen?
Mehr fällt mir da grad auch nicht ein.Gruß Thomas
-
@Desastro ja, habe ich, danke
-
Hallo Uhula,
ich bin immer noch total begeistert von deinem Skript und der Heos-Steuerung über den Broker.
@Uhula sagte in [Vorlage] Denon HEOS Script:
@Meister-Mopper Den Fehler ignorieren, ist ein überflüssiger Aufruf im destroy, ist in meiner neuesten Version schon behoben. Ich versuche demnächst mal aus dem Script einen Adapter zu machen.
Ist die neuste Version von deinem Skript schon erhältlich, oder ist das die vom 15.11.?
Ich bin auch schon sehr gespannt auf deinen Adapter. Wenn Du Tester brauchst, bin ich gerne dabei.Bei mir bekomme ich nach wie vor von den mp3s über DLNA oder Heos Netzwerkfreigabe keine Infos/Tags/Titel angezeigt.
Viele Grüße, Christian
-
@chrisblu Bei mir läuft bereits das neue Script - recht stabil, unterstützt auch Raumgruppen; muss es aber noch an den neuen "0_userdata.0" State-Ordner anpassen, dann ist es javascript-Instanz unabhängig. Btw. die States werden dann nicht mehr unter der Player-IP-Adresse, sondern unter der Player-ID abgelegt (kam bei mir hin und wieder vor, das Player einen neue IP bekamen ..., die ID bleibt konstant).
Zum Adapter dauert es noch, habe erst einmal das MD CSS v2 Projekt priorisiert.
-
@Uhula, das klingt gut. Bin schon gespannt.
Danke für die Info, viele Grüße, Christian -
@Uhula erstmal vielen Dank für deine Arbeit. Ich habe einige Bugs in deinem ersten Script gefixt (@jwedenig es werden auch wieder alle Song Infos aktualisiert). Auch wenn Version 2 bereits unterwegs ist, will ich euch meine Änderungen nicht vorenthalten. Unter anderem ist mir aufgefallen, dass das Filtern und Verarbeiten der Pakete vom node-ssdp nicht korrekt funktioniert, wenn eine Hue Bridge im Netzwerk funkt oder Pakete unvollständig sind. Zudem habe ich noch ein connected Flag für jeden Player hinzugefügt. Dieses muss leider extern über ein Script resettet werden z.B. in Kombination mit dem Ping Adapter. Für diesen Fall habe ich mir ein Blockly Script gebaut, welches das connected Flag resettet und bei Player Updates das Heos Script neu startet. Bei Bedarf kann ich das auch noch teilen:
/**************************** * HEOS Script for ioBroker **************************** * 23.01.2018 Uhula, MIT License, no warranty, use on your own risc * * Wichtig! **************************** * (a) Im Javascript-Adapter muss in der Instanz-Konfiguration "node-ssdp" mit angegeben werden! * Alternativ kann die Bibliothek auch komplett über "npm install node-ssdp" installiert * werden. * (b) Wenn mit Favoriten (Presets) gearbeitet werden soll, müssen die HEOS-Konto Logindaten in * den beiden Konstanten HEOS_USERNAME (EmailAdr) und HEOS_PASSWORD hier im Script in der * Konfiguration angegeben werden (so, wie in der HEOS App)! * (c) Eine Konfiguration von IP-Adressen und Player-IDs ist nicht notwendig! * (d) U.U. kann es notwendig sein das Script nach dem 1.Start zu beenden und nach 30 Sek erneut zu starten, da * die Neuanlage der ioBroker States etwas Zeit benötigt. Warnungen sind dabei zu ignorieren. ;-) * * * Funktion **************************** * Das Script stellt zwei JS Klassen zur Steuerung der Denon HEOS Geräte zur Verfügung. Die * class Heos dient dabei dem Erkennen und Steuern der HEOS Geräte. Die class HeosPlayer dient der * Interpretation der Antworten der Heos Geräte. * * Das Script muss lediglich gestartet werden, es sucht dann im Netzwerk nach HEOS Playern und * erzeugt in der aktuellen Javascript-Instanz einen Subeintrag für die Favoriten und je einen Subeintrag * für jeden gefundenen Player. Dort wiederum werden State-Variablen zur Aufnahme der Player-Daten angelegt. * * Das Script erzeugt auch on-Handler um auf Steuerungen über vis und andere Scripte an den State-Variablen * reagieren zu können. * * Beim Beenden des Scripts werden alle Verbindungen und on-Handler geschlossen. * * Das Script ermöglicht die HEOS Player zu steuern, es soll aber nicht die HEOS App ersetzen. Dazu fehlen * etliche Funktionen wie Playlisten-Handhabung, Gruppensteuerung usw. * * * class Heos **************************** * (a) Erkennen von HEOS Geräten (Playern) * Das Erkennen der HEOS Geräte findet via UPD unter Nutzung von node-ssdp statt. node-ssdp muss dazu im * Javascript-Adapter in der Konfiguration mit angegeben werden! * (b) der Kommunikation über TelNet * Es wird genau eine TelNet Verbindung zu einem HEOS Gerät aufgebaut, dieses reicht die Sendungen * und Antworten zentral weiter * (c) der Ermittlung von Favoriten (Presets) und * Für jeden Favorit wird ein ioBroker State heos.presets.n mit entsprechenden Sub-States erzeugt. * Zur Nutzung der Presets ist ein SignIn notwendig, hierzu müssen HEOS_USERNAME und HEOS_PASSWORD * gesetzt werden. * (d) dem Instanziieren der class HeosPlayer je HEOS Gerät. * Je HEOS Gerät wird ein ioBroker-State mit der IP-Adresse des Players erzeugt, dieser State erhält * diverse Sub-States * * State-Variable * -------------- * .heos.command(cmd) // write * Hierüber können der Heos-Klasse Befehle übergeben werden, für cmd gilt: * * connect * Verbindung zu HEOS aufbauen bzw. erneut aufbauen (praktisch ein reset) * disconnect * Verbindung zu HEOS beenden * load_presets * lädt die Favoriten neu * * alle anderen cmd-Werte werden "as is" versucht an HEOS zu senden, damit ist z.B. auch das * Gruppieren von HEOS Speakern möglich. * * group/set_group?pid=<pid1>,<pid2>,... * setzen einer Gruppe, die pids sind die der Player wie sie unter den Objekten * für jeden Player abgelegt sind. Statt diese direkt zu verwenden, kann man auch * die ioBroker binding-Funktionalität nutzen und Platzhalter verwenden. * Bsp: group/set_group?pid={javascript.1.heos.192_168_2_46.pid},{javascript.1.heos.192_168_2_43.pid} * * group/set_group?pid=<pid1> * hebt die Gruppierung wieder auf * Bsp: group/set_group?pid={javascript.1.heos.192_168_2_46.pid} * * * Favoriten (je Favorit ein Unterordner n=1 bis Anzahl) * * .heos.presets.<n>.image_url (read) * Bild des Favoriten * * .heos.presets.<n>.name (read) * Name des Favoriten * * .heos.presets.<n>.playable (read) * Spielbar? true/false * * .heos.presets.<n>.type (read) * Typ des Favoriten (station, ...) * * * class HeosPlayer **************************** * (a) der Steuerung genau eines HEOS Gerätes. Hierzu wird die zentrale Telnet Verbindung der class Heos genutzt. * (b) dem Erzeugen der ioBroker-States zum Speichern der Geräte-Werte * (b) der Auswertung von Antworten der HEOS Geräte und Zuweisung an die entsprechenden ioBroker-States * * State-Variable * -------------- * .heos.<player_ip>.command (write) * Hierüber können dem Heos-Player Befehle übergeben werden. Diese werden im Klartext in die State-Variable * geschrieben. Getrennt durch das | Zeichen können mehrere Befehle hintereinander eingetragen werden. * Bsp: Setzen der Lautstärke auf 20 und Abspielen des 1.Favoriten * set_volume&level=20|play_preset&preset=1 * * set_volume&level=0|1|..|100 : Setzt die gewünschte Lautstärke * set_play_state&state=play|pause|stop : Startet und stoppt die Wiedergabe * set_play_mode&repeat=on_all|on_one|off&shuffle=on|off: Setzt Wiederholung und Zufallsweidergabe * set_mute&state=on|off : Stumm schalten oder nicht * volume_down&step=1..10 : Lautstärke verringern um * volume_up&step=1..10 : Lauststäre erhöhen um * play_next : Nächsten Titel spielen * play_previous : Vorherigen Titel spielen * play_preset&preset=1|2|..|n : Favorit Nr n abspielen * play_stream&url=url_path : URL-Stream abspielen * * Befehle, die intern genutzt werden und nicht aufgerufen werden müssen: * * get_volume : Füllt den .heos.<player_ip>.volume State * get_play_state : Füllt den .heos.<player_ip>.play_state State * get_play_mode : Füllt den .heos.<player_ip>.play_mode State * get_now_playing_media : Füllt die .heos.<player_ip>.now_playing_media_... States * * * .heos.<player_ip>.cur_pos (read) * lfd. Position der Wiedergabe in [Sek] * * .heos.<player_ip>.cur_pos_MMSS (read) * lfd. Position der Wiedergabe im Format MM:SS * * .heos.<player_ip>.duration (read) * Länge des aktuellen Titels in [Sek] * * .heos.<player_ip>.duration_MMSS (read) * Länge des aktuellen Titels im Format MM:SS * * .heos.<player_ip>.ip (read) * IP-Adresse des HEOS Players * * .heos.<player_ip>.last_error (read) * Text des letzten Fehlers, der vom Player gesendet wurde. Wird bei jedem neuen Befehl zurückgesetzt. * Wenn man bspw. versucht eine Wiedergabe über Amazon o.ä. zu starten und es gibt aber keine Verbindung * dahin, steht hier der Fehlertext drin * * .heos.<player_ip>.model (read) * Modell des HEOS Players * * .heos.<player_ip>.mute (read write) * Boolsche Variable, die anzeigt ob der Player gemutet (Stumm geschaltet) wurde. Hierüber kann auch ein * mute gesetzt werden * * .heos.<player_ip>.name (read) * Name des HEOS Players * * .heos.<player_ip>.now_playing_media_... (read) * Eine Gruppe von States, in welchen Infos über den aktuellen Titel gespeichert werden, wird automatisch * aktualisiert. * * .heos.<player_ip>.pid (read) * Player-ID des HEOS Players * * .heos.<player_ip>.play_mode_repeat (read write) * Repeat-Modus der Wiedergabe. Mögliche Werte: on_all|on_one|off * * .heos.<player_ip>.play_mode_shuffle (read write) * Zufallswiedergabe true/false * * .heos.<player_ip>.play_state (read write) * Zustand des Players. Mögliche Werte play|pause|stop * * .heos.<player_ip>.serial (read) * Seriennummmer des HEOS Players * * .heos.<player_ip>.volume (read write) * Lautstärke im Bereich 0 - 100. * * * * Weiterführende Links **************************** * HEOS CLI Protokoll: http://rn.dmglobal.com/euheos/HEOS_CLI_ProtocolSpecification.pdf * http://forum.iobroker.net/viewtopic.php?f=30&t=5693&p=115554#p115554 * **/ /**************************** * Konfiguration ****************************/ const HEOS_USERNAME = ''; const HEOS_PASSWORD = ''; /**************************** * ab hier nichts mehr ändern ;-) ****************************/ var net = require('net'); const stateDISCONNECTED = 0; const stateCONNECTING = 1; const stateCONNECTED = 2; /******************** * class Heos ********************/ class Heos { constructor() { this.init(); createState( this.statePath+'command', '', {name: 'Kommando für Heos-Script' }); createState( this.statePath+'connected', false, {name: 'Verbunden?' }); createState( this.statePath+'last_error', '', {name: 'Letzter Fehler' }); on({id: this.statePath+'command', change: "any"}, (obj) => { this.executeCommand( obj.state.val ); }); } logDebug(msg) { console.debug('[Heos] '+msg) }; log(msg) { console.log('[Heos] '+msg); } logWarn(msg) { console.warn('[Heos] '+msg); } logError(msg) { console.error('[Heos] '+msg); } init() { this.statePath = 'javascript.'+instance+'.heos.'; this.players = []; this.net_client = undefined; this.nodessdp_client = undefined; this.ip=''; this.msgs = []; this.lastResponse = ''; this.state = stateDISCONNECTED; this.unfinishedResponses = ''; this.ssdpSeartTargetName = 'urn:schemas-denon-com:device:ACT-Denon:1'; } connect() { try { this.log('connecting to HEOS ...'); setState( this.statePath+"connected", false ); const NodeSSDP = require('node-ssdp').Client; this.nodessdp_client = new NodeSSDP(); this.nodessdp_client.explicitSocketBind = true; this.nodessdp_client.on('response', (headers, statusCode, rinfo) => this.onNodeSSDPResponse(headers, statusCode, rinfo) ); this.nodessdp_client.on('error', error => { client.close(); this.logError(error); }); this.nodessdp_client.search(this.ssdpSeartTargetName); } catch(err) { this.logError( 'connect: '+err.message ); } } /** Alle Player stoppen und die TelNet Verbindung schließen **/ disconnect() { this.log('disconnecting from HEOS ...'); unsubscribe('javascript.'+instance+'.heos'); var i=0; for (i=0; i<this.players.length; i++) { var playerStatePath = this.players[i].heosPlayer.statePath; setState( playerStatePath+"connected", false ); } if (typeof this.net_client!=='undefined') { this.registerChangeEvents( false ); this.net_client.destroy(); this.net_client.unref(); } if (typeof this.nodessdp_client!=='undefined') { this.nodessdp_client.stop(); } setState( this.statePath+"connected", false ); this.log('disconnected from HEOS'); } executeCommand(cmd) { this.log('command: '+cmd); switch (cmd) { case 'load_presets' : this.getMusicSources(); break; case 'connect' : this.disconnect(); this.init(); this.connect(); break; case 'disconnect' : this.disconnect(); break; default: if (this.state == stateCONNECTED) { this.msgs.push( 'heos://'+cmd+'\n' ); this.sendNextMsg(); } } } sleep(milliseconds) { return new Promise(resolve => setTimeout(resolve, milliseconds)); } /** es wurde mindestens ein Player erkannt, nun über dessen IP alle bekannten HEOS Player * durch senden von "player/get_players" ermitteln */ onNodeSSDPResponse(headers, statusCode, rinfo) { try { // rinfo {"address":"192.168.2.225","family":"IPv4","port":53871,"size":430} if (typeof this.net_client=='undefined') { if(headers.ST === this.ssdpSeartTargetName){ this.ip = rinfo.address; this.log('connecting to '+this.ip+' ...'); this.net_client = net.connect({host:this.ip, port:1255}); this.net_client.setKeepAlive(true, 5000); this.state = stateCONNECTING; this.net_client.on('error',(error) => { this.logError(error); this.disconnect(); }); this.net_client.on('connect', () => { setState( this.statePath+"connected", true ); this.log('connected to HEOS'); this.state = stateCONNECTED; this.log('connected to '+this.ip); this.getPlayers(); this.registerChangeEvents( true ); this.signIn(); this.getMusicSources(); }); // Gegenseite hat die Verbindung geschlossen this.net_client.on('end', () => { this.logWarn('HEOS closed the connection to '+this.ip); this.disconnect(); }); // timeout this.net_client.on('timeout', () => { this.logWarn('Timeout trying connect to '+this.ip); this.disconnect(); }); // Datenempfang this.net_client.on('data', (data) => this.onData(data) ); } else { this.log('Getting wrong ssdp entry. Keep trying...'); } } } catch(err) { this.logError( 'onNodeSSDPResponse: '+err.message ); } } removeFirstOccurrence(str, searchstr) { var index = str.indexOf(searchstr); if (index === -1) { return str; } return str.slice(0, index) + str.slice(index + searchstr.length); } /** es liegen Antwort(en) vor **/ onData(data) { try { data = data.toString(); this.logDebug("received data:" + data); data=data.replace(/[\n\r]/g, ''); // Steuerzeichen "CR" entfernen // es können auch mehrere Antworten vorhanden sein! {"heos": ... } {"heos": ... } // diese nun in einzelne Antworten zerlegen data = this.unfinishedResponses + data; this.unfinishedResponses = ''; data=data.replace(/{\s*"heos"\s*:/g, '|{"heos":'); var responses = data.split('|'); for (var r=0; r<responses.length; r++ ) { if(responses[r].trim().length > 0){ try { JSON.parse(responses[r]); this.parseResponse(responses[r]); } catch(e) { this.logDebug("invalid json (error: " + e.message + "): " + responses[r]); this.unfinishedResponses += responses[r]; } } } // wenn weitere Msg zum Senden vorhanden sind, die nächste senden if (this.msgs.length>0) this.sendNextMsg(); } catch(err) { this.logError( 'onData: '+err.message ); } } /** Antwort(en) verarbeiten. Sich wiederholende Antworten ignorieren **/ parseResponse (response) { try { if (response == this.lastResponse || response.indexOf("command under process") > 0 ) return this.lastResponse = response; var jmsg; var i; var jdata = JSON.parse(response); if ( !jdata.hasOwnProperty('heos') || !jdata.heos.hasOwnProperty('command') || !jdata.heos.hasOwnProperty('message')) return; // msg auswerten try { jmsg = '{"' + decodeURI(jdata.heos.message).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"').replace(/\s/g,'_') + '"}'; jmsg = JSON.parse(jmsg); } catch(err) { jmsg = {}; } this.logDebug('parse response: '+response); this.logDebug('parse message: '+JSON.stringify(jmsg)); // cmd auswerten var cmd = jdata.heos.command.split('/'); var cmd_group = cmd[0]; cmd = cmd[1]; // result ? var result = 'success'; if (jdata.heos.hasOwnProperty('result') ) result = jdata.heos.result; if ( result!='success' ) { setState(this.statePath+'last_error', cmd + ' | ' + jmsg.text); this.logWarn(jmsg.text); } switch (cmd_group) { case 'player': switch (cmd) { // {"heos": {"command": "player/get_players", "result": "success", "message": ""}, // "payload": [{"name": "HEOS Bar", "pid": 1262037998, "model": "HEOS Bar", "version": "1.430.160", "ip": "192.168.2.225", "network": "wifi", "lineout": 0, "serial": "ADAG9170202780"}, // {"name": "HEOS 1 rechts", "pid": -1746612370, "model": "HEOS 1", "version": "1.430.160", "ip": "192.168.2.201", "network": "wifi", "lineout": 0, "serial": "AMWG9170934429"}, // {"name": "HEOS 1 links", "pid": 68572158, "model": "HEOS 1", "version": "1.430.160", "ip": "192.168.2.219", "network": "wifi", "lineout": 0, "serial": "AMWG9170934433"} // ]} case 'get_players' : if ( (jdata.hasOwnProperty('payload')) && (this.players.length===0) ) { for (i=0; i<jdata.payload.length; i++) { var player = jdata.payload[i]; this.players.push(player); } this.startPlayers(); } else if(jdata.playload.length != this.players.length){ this.log("New Player detected. Restart.") this.disconnect(); } break; } break; // {"heos": {"command": "browse/get_music_sources", "result": "success", "message": ""}, // "payload": [{"name": "Amazon", "image_url": "https://production...png", "type": "music_service", "sid": 13}, // {"name": "TuneIn", "image_url": "https://production...png", "type": "music_service", "sid": 3}, // {"name": "Local Music", "image_url": "https://production...png", "type": "heos_server", "sid": 1024}, // {"name": "Playlists", "image_url": "https://production...png", "type": "heos_service", "sid": 1025}, // {"name": "History", "image_url": "https://production...png", "type": "heos_service", "sid": 1026}, // {"name": "AUX Input", "image_url": "https://production...png", "type": "heos_service", "sid": 1027}, // {"name": "Favorites", "image_url": "https://production...png", "type": "heos_service", "sid": 1028}]} case 'browse': switch (cmd) { case 'get_music_sources' : if ( (jdata.hasOwnProperty('payload')) ) { this.logDebug("Payload:"+jdata.payload.toString()); for (i=0; i<jdata.payload.length; i++) { var source = jdata.payload[i]; if (source.name=='Favorites') { this.browse(source.sid); } } } break; // {"heos": {"command": "browse/browse", "result": "success", "message": "pid=1262037998&sid=1028&returned=5&count=5"}, // "payload": [{"container": "no", "mid": "s17492", "type": "station", "playable": "yes", "name": "NDR 2 (Adult Contemporary Music)", "image_url": "http://cdn-radiotime-logos.tunein.com/s17492q.png"}, // {"container": "no", "mid": "s158432", "type": "station", "playable": "yes", "name": "Absolut relax (Easy Listening Music)", "image_url": "http://cdn-radiotime-logos.tunein.com/s158432q.png"}, // {"container": "no", "mid": "catalog/stations/A1W7U8U71CGE50/#chunk", "type": "station", "playable": "yes", "name": "Ed Sheeran", "image_url": "https://images-na.ssl-images-amazon.com/images/G/01/Gotham/DE_artist/EdSheeran._SX200_SY200_.jpg"}, // {"container": "no", "mid": "catalog/stations/A1O1J39JGVQ9U1/#chunk", "type": "station", "playable": "yes", "name": "Passenger", "image_url": "https://images-na.ssl-images-amazon.com/images/I/71DsYkU4QaL._SY500_CR150,0,488,488_SX200_SY200_.jpg"}, // {"container": "no", "mid": "catalog/stations/A316JYMKQTS45I/#chunk", "type": "station", "playable": "yes", "name": "Johannes Oerding", "image_url": "https://images-na.ssl-images-amazon.com/images/G/01/Gotham/DE_artist/JohannesOerding._SX200_SY200_.jpg"}], // "options": [{"browse": [{"id": 20, "name": "Remove from HEOS Favorites"}]}]} case 'browse' : if ( (jdata.hasOwnProperty('payload')) ) { for (i=0; i<jdata.payload.length; i++) { var preset = jdata.payload[i]; createState( this.statePath+'presets.'+(i+1)+'.name', preset.name, {name: 'Favoritenname ' }); createState( this.statePath+'presets.'+(i+1)+'.playable', (preset.playable=='yes'?true:false), {name: 'Favorit ist spielbar' }); createState( this.statePath+'presets.'+(i+1)+'.type', preset.type, {name: 'Favorittyp' }); createState( this.statePath+'presets.'+(i+1)+'.image_url', preset.image_url, {name: 'Favoritbild' }); } } break; } break; case 'system': switch (cmd) { case 'sign_in' : this.log('signed in: '+jdata.heos.result); break; } break; } // an die zugehörigen Player weiterleiten if ( jmsg.hasOwnProperty('pid') ) { for (i=0; i<this.players.length; i++) if (jmsg.pid==this.players[i].heosPlayer.pid) { this.players[i].heosPlayer.parseResponse (jdata, jmsg, cmd_group, cmd); break; } } } catch(err) { this.logError( 'parseResponse: '+err.message+'\n '+response ); } } /** sucht die zur ip passenden player-Insanz **/ sendCommandToPlayer(objID, cmd ) { var ip = objID; ip = ip.split('.'); var ip_ = ip[3]; ip = ip_.replace(/_/g,'.'); for (var p=0; p<this.players.length; p++ ) if (this.players[p].ip == ip ) { this.players[p].heosPlayer.sendCommand( cmd ); break; } } /** Für die gefundenen HEOS Plyer entsprechende class HeosPlayer Instanzen bilden **/ startPlayers() { try { var i=0; for (i=0; i<this.players.length; i++) { this.players[i].heosPlayer = new HeosPlayer( this, this.players[i] ); } // Events setzen for (i=0; i<this.players.length; i++) { var statePath = this.players[i].heosPlayer.statePath; // on-Event für command on({id: statePath+'command', change: "any"}, (obj) => { this.sendCommandToPlayer( obj.id, obj.state.val ); }); // on-Event für volume (nur wenn ack=false, also über vis) on({id: statePath+'volume', change:'ne', ack:false }, (obj) => { this.sendCommandToPlayer( obj.id, 'set_volume&level='+obj.state.val ); }); // on-Event für mute (nur wenn ack=false, also über vis) on({id: statePath+'mute', change: 'ne', ack:false}, (obj) => { this.sendCommandToPlayer( obj.id, 'set_mute&state='+ (obj.state.val === true ? 'on' : 'off') ); }); // on-Event für play_mode_shuffle (nur wenn ack=false, also über vis) on({id: statePath+'play_mode_shuffle', change: 'ne', ack:false}, (obj) => { this.sendCommandToPlayer( obj.id, 'set_play_mode&shuffle='+ (obj.state.val === true ? 'on' : 'off') ); }); // on-Event für play_state (nur wenn ack=false, also über vis) on({id: statePath+'play_state', change: 'ne', ack:false}, (obj) => { this.sendCommandToPlayer( obj.id, 'set_play_state&state='+ obj.state.val ); }); } } catch(err) { this.logError( 'startPlayers: '+err.message ); } } getPlayers() { if (this.state == stateCONNECTED) { this.msgs.push( 'heos://player/get_players\n' ); this.sendNextMsg(); } } registerChangeEvents( b ) { if (this.state == stateCONNECTED) { if (b) this.msgs.push( 'heos://system/register_for_change_events?enable=on' ); else this.msgs.push( 'heos://system/register_for_change_events?enable=off' ); this.sendNextMsg(); } } signIn() { if (this.state == stateCONNECTED) { // heos://system/sign_in?un=heos_username&pw=heos_password this.msgs.push( 'heos://system/sign_in?un='+HEOS_USERNAME+'&pw='+HEOS_PASSWORD ); this.sendNextMsg(); } } getMusicSources() { if (this.state == stateCONNECTED) { // heos://browse/get_music_sources this.msgs.push( 'heos://browse/get_music_sources' ); this.sendNextMsg(); } } browse(sid) { if (this.state == stateCONNECTED) { // heos://browse/browse?sid=source_id this.msgs.push( 'heos://browse/browse?sid='+sid ); this.sendNextMsg(); } } sendNextMsg () { if (this.msgs.length>0) { var msg = this.msgs.shift(); this.sendMsg(msg); } } // Nachricht an player senden sendMsg (msg) { this.net_client.write(msg + "\n"); this.logDebug("data sent: "+ msg); } } // end of class Heos /******************** * class HeosPlayer ********************/ class HeosPlayer { constructor(heos, player) { this.heos = heos; this.ip = player.ip; this.name = player.name; this.pid = player.pid; this.model = player.model; this.serial = player.serial; this.statePath = 'javascript.'+instance+'.heos.'+this.ip.replace(/\./g,'_')+"."; this.log('starting HEOS player for IP '+this.ip); this.createStates(); // Initialisierung der States nach 5 Sek setTimeout(() => { this.setState('ip',this.ip); this.setState('name',this.name); this.setState('pid',this.pid); this.setState('model',this.model); this.setState('serial',this.serial); this.sendCommand('get_play_state|get_play_mode|get_now_playing_media|get_volume'); }, 5000 ); setTimeout(() => { this.setState('connected', true); }, 10000 ); } static version () { return "0.2"; } logDebug(msg) { console.debug('[HeosPlayer '+this.ip+'] '+msg); } log(msg) { console.log('[HeosPlayer '+this.ip+'] '+msg); } logWarn(msg) { console.warn('[HeosPlayer '+this.ip+'] '+msg); } logError(msg) { console.error('[HeosPlayer '+this.ip+'] '+msg); } /** Anlage der ioBroker States für den Player **/ createStates() { const states = [ { id:"connected", name:"Verbunden?"}, { id:"command", name:"Kommando für HEOS Aufrufe"}, { id:"ip", name:"IP Adresse"}, { id:"pid", name:"Player-ID "}, { id:"name", name:"Name des Players"}, { id:"model", name:"Modell des Players"}, { id:"serial", name:"Seriennummer des Players"}, { id:"last_error", name:"Letzter Fehler"}, { id:"volume", name:"Aktuelle Lautstärke"}, { id:"mute", name:"Mute aktiviert?"}, { id:"play_state", name:"Aktueller Wiedergabezustand"}, { id:"play_mode_repeat", name:"Wiedergabewiederholung"}, { id:"play_mode_shuffle", name:"Zufällige Wiedergabe"}, { id:"now_playing_media_type", name:"Aktuelle Wiedergabemedium"}, { id:"now_playing_media_song", name:"Aktuelles Lied"}, { id:"now_playing_media_station", name:"Aktuelle Station"}, { id:"now_playing_media_album", name:"Aktuelle Wiedergabemedium"}, { id:"now_playing_media_artist", name:"Aktueller Artist"}, { id:"now_playing_media_image_url", name:"Aktuelles Coverbild"}, { id:"now_playing_media_album_id", name:"Aktuelle Album-ID"}, { id:"now_playing_media_mid", name:"Aktuelle mid"}, { id:"now_playing_media_qid", name:"Aktuelle qid"}, { id:"cur_pos", name:"lfd. Position"}, { id:"duration", name:"Dauer"}, { id:"cur_pos_MMSS", name:"lfd. Position MM:SS"}, { id:"duration_MMSS", name:"Dauer MM:SS"} ]; var def = ""; for (var s=0; s<states.length; s++) { var state = states[s]; if (this.hasOwnProperty(state.id)) def=this[state.id]; else def=''; createState( this.statePath+state.id, def, {name: state.name }); } } /** wandelt einen sek Wert in MM:SS Darstellung um **/ toMMSS (s) { var sec_num = parseInt(s, 10); var minutes = Math.floor(sec_num / 60); var seconds = sec_num - (minutes * 60); if (seconds < 10) {seconds = "0"+seconds;} return minutes+':'+seconds; } /** setState wrapper **/ setState(id,val) { setState(this.statePath+id, val, true); } /** Auswertung der empfangenen Daten **/ parseResponse (jdata, jmsg, cmd_group, cmd) { try { this.logDebug('response type - cmd_group: ' + cmd_group + " - cmd: " + cmd); switch (cmd_group) { case 'event': switch (cmd) { case 'player_playback_error' : this.setState('last_error', jmsg.error.replace(/_/g,' ')); this.logError(jmsg.error.replace(/_/g,' ')); break; case 'player_state_changed' : this.setState("play_state", jmsg.state); break; case 'player_volume_changed' : this.setState("volume", jmsg.level ); this.setState("mute", (jmsg.mute=='on' ? true : false) ); break; case 'player_repeat_mode_changed' : this.setState("play_mode_shuffle", jmsg.shuffle ); break; case 'player_shuffle_mode_changed' : this.setState("play_mode_repeat", jmsg.repeat ); break; case 'player_now_playing_changed' : this.sendCommand('get_now_playing_media'); break; case 'player_now_playing_progress' : this.setState("cur_pos", jmsg.cur_pos / 1000); this.setState("cur_pos_MMSS", this.toMMSS(jmsg.cur_pos / 1000)); this.setState("duration", jmsg.duration / 1000); this.setState("duration_MMSS", this.toMMSS(jmsg.duration / 1000)); break; } break; case 'player': switch (cmd) { case 'set_volume' : case 'get_volume' : if ( getState(this.statePath+"volume").val != jmsg.level) this.setState("volume", jmsg.level); break; case 'set_mute' : case 'get_mute' : this.setState("mute", (jmsg.state=='on' ? true : false) ); break; case 'set_play_state' : case 'get_play_state' : this.setState("play_state", jmsg.state); break; case 'set_play_mode' : case 'get_play_mode' : this.setState("play_mode_repeat", jmsg.repeat); this.setState("play_mode_shuffle", (jmsg.shuffle=='on'?true:false) ); break; case 'get_now_playing_media' : this.setState("now_playing_media_type", jdata.payload.type); this.setState("now_playing_media_song", jdata.payload.song); this.setState("now_playing_media_album", jdata.payload.album); this.setState("now_playing_media_artist", jdata.payload.artist); this.setState("now_playing_media_image_url", jdata.payload.image_url); this.setState("now_playing_media_album_id", jdata.payload.album_id); this.setState("now_playing_media_mid", jdata.payload.mid); this.setState("now_playing_media_qid", jdata.payload.qid); // type == station if (jdata.payload.type=='station') { this.setState("now_playing_media_station", jdata.payload.station); } else { this.setState("now_playing_media_station", null); } break; } break; } // switch } catch(err) { this.logError( 'parseResponse: '+err.message ); } } /** cmd der Form "cmd¶m" werden zur msg heos+cmd+pid+¶m aufbereitet cmd der Form "cmd?param" werden zur msg heos+cmd+?param aufbereitet **/ commandToMsg (cmd) { var param = cmd.split('&'); cmd = param[0]; if ( param.length > 1 ) param='&'+param[1]; else param=''; var cmd_group = 'player'; switch (cmd) { case 'get_play_state': case 'get_play_mode': case 'get_now_playing_media': case 'get_volume': case 'play_next': case 'play_previous': case 'set_mute': // &state=on|off case 'set_volume': // &level=1..100 case 'volume_down': // &step=1..10 case 'volume_up': // &step=1..10 case 'set_play_state': // &state=play|pause|stop case 'set_play_mode': // &repeat=on_all|on_one|off shuffle=on|off break; // browse case 'play_preset': // heos://browse/play_preset?pid=player_id&preset=preset_position cmd_group = 'browse'; break; case 'play_stream': // heos://browse/play_stream?pid=player_id&url=url_path cmd_group = 'browse'; break; } return 'heos://'+cmd_group+'/'+cmd+'?pid=' + this.pid + param; } /** Nachricht (command) an player senden es sind auch mehrere commands, getrennt mit | erlaubt bsp: set_volume&level=20|play_preset&preset=1 **/ sendCommand (command) { this.setState('last_error', ''); var cmds = command.split('|'); for (var c=0; c<cmds.length; c++) { this.heos.msgs.push( this.commandToMsg(cmds[c]) ); } this.heos.sendNextMsg(); } } // end of HeosPlayer /* ----- Heos ----- */ // Heos Instanz erzeugen und verbinden var heos = new Heos( ); heos.connect(); // wenn das Script beendet wird, dann auch die Heos Instanz beenden onStop(function () { heos.disconnect(); }, 0 );
-
@withstu großartig, das funktioniert gut. Jetzt gehen auch die Titel und Cover bei DLNA-Wiedergabe bei mir wieder. Vielen Dank fürs teilen.
Wie genau meinst Du das mit dem Ping Adapter? Fragst Du ab, ob die Box verbunden ist und wenn wieder da, verbindest Du neu?
Könntest Du bitte das Blockly Skript auch posten, das bei Player-Updates neu startet?
Vielen Dank,
Christian -
@withstu Die verstümmelten JSON Pakete habe ich bei mir auch, ohne HUE Bridge, primär, wenn nicht gar ausschlißlich, beim Übertragen der "browse/browse" (z.B. Presets), also großen Datenmengen. Aber nicht immer, nur zufällig. Deinen JSON-Test werde ich übernehmen. Wo hast du noch Änderungen eingebaut? Dann kann ich das in die v2 übernehmen, welche bei mir schon mit "0_userdata.0" States arbeitet. Danke dir.
-
@chrisblu Ich habe für jeden Lautsprecher einen Ping Adapter angelegt. Sobald sich da was ändert und in den HEOS Objekten der Player noch nicht verbunden ist, starte ich das Script neu (bei mir sind die Player nicht dauerhaft verbunden). Übrigens ich hatte total vergessen, dass das connected Flag der Player im disconnect wieder auf false gesetzt wird.
Mein Blockly Heos Script Starter sieht so aus (kann bestimmt auch eleganter gelöst werden, ich nutze jedoch iobroker erst seit kurzem):var DelayedUnlock; schedule("*/15 * * * * *", function () { if (getState("0_userdata.0.scriptData.HEOSScriptStarterRunning").val == true && ((new Date().getTime()) - getState("0_userdata.0.scriptData.HEOSScriptStarterRunning").lc) / 1000 > 300) { setState("0_userdata.0.scriptData.HEOSScriptStarterRunning"/*HEOSScriptStarterRunning*/, false); } if (getState("0_userdata.0.scriptData.HEOSScriptStarterRunning").val == false) { setState("0_userdata.0.scriptData.HEOSScriptStarterRunning"/*HEOSScriptStarterRunning*/, true); DelayedUnlock = false; if (getState("javascript.0.scriptEnabled.common.HEOS").val == true && getState("javascript.0.heos.connected").val == false) { console.log('HEOS not connected. Shutdown.'); setState("javascript.0.scriptEnabled.common.HEOS"/*scriptEnabled.common.HEOS*/, false); } else if (getState("javascript.0.scriptEnabled.common.HEOS").val == true && (getState("javascript.0.heos.192_168_178_43.connected").val != getState("ping.0.server.192_168_178_43").val || getState("javascript.0.heos.192_168_178_44.connected").val != getState("ping.0.server.192_168_178_44").val || getState("javascript.0.heos.192_168_178_45.connected").val != getState("ping.0.server.192_168_178_45").val || getState("javascript.0.heos.192_168_178_46.connected").val != getState("ping.0.server.192_168_178_46").val)) { console.log('HEOS Player updated. Shutdown.'); setState("javascript.0.scriptEnabled.common.HEOS"/*scriptEnabled.common.HEOS*/, false); } else { if (getState("ping.0.server.192_168_178_45").val == false && getState("ping.0.server.192_168_178_44").val == false && getState("ping.0.server.192_168_178_43").val == false && getState("ping.0.server.192_168_178_46").val == false) { if (getState("javascript.0.scriptEnabled.common.HEOS").val == true) { console.log('No Players. Shutdown.'); setState("javascript.0.scriptEnabled.common.HEOS"/*scriptEnabled.common.HEOS*/, false); DelayedUnlock = true; } } else { if (getState("javascript.0.scriptEnabled.common.HEOS").val == false) { console.log('Players available. Startup.'); setState("javascript.0.scriptEnabled.common.HEOS"/*scriptEnabled.common.HEOS*/, true); DelayedUnlock = true; } } } if (DelayedUnlock == true) { setStateDelayed("0_userdata.0.scriptData.HEOSScriptStarterRunning"/*HEOSScriptStarterRunning*/, false, 60000, false); } else { setState("0_userdata.0.scriptData.HEOSScriptStarterRunning"/*HEOSScriptStarterRunning*/, false); } } });
Zu jedem Player gibt es dann noch ein Script, welches im Notfall das connected Flag wieder auf false setzt (und automatisch die Musik und Lautstärker steuert ) :
var DelayedUnlock, Volume, Preset; on({id: new RegExp('javascript\\.0\\.heos\\.192_168_178_45\\.mute' + "|" + 'javascript\\.0\\.heos\\.192_168_178_45\\.connected' + "|" + 'ping\\.0\\.server\\.192_168_178_45' + "|" + 'javascript\\.0\\.heos\\.connected'), change: "ne"}, function (obj) { if (getState("0_userdata.0.scriptData.BadezimmerAutoPlayRunning").val == false) { setState("0_userdata.0.scriptData.BadezimmerAutoPlayRunning"/*scriptData.BadezimmerAutoPlayRunning*/, true); DelayedUnlock = false; if (getState("javascript.0.heos.connected").val == false && getState("javascript.0.heos.192_168_178_45.connected").val == true && getState("ping.0.server.192_168_178_45").val == false) { setState("javascript.0.heos.192_168_178_45.connected"/*Verbunden?*/, false); } if (getState("javascript.0.heos.192_168_178_45.connected").val == true && getState("javascript.0.heos.192_168_178_45.mute").val == false && (getState("javascript.0.heos.192_168_178_45.play_state").val == 'stop' || getState("javascript.0.heos.192_168_178_45.play_state").val == 'pause')) { console.log('Starte Musik im Badezimmer.'); if ((new Date().getHours()) >= 22 || (new Date().getHours()) <= 7) { Volume = getState("0_userdata.0.scriptData.NachtVolume").val; } else { Volume = getState("0_userdata.0.scriptData.TagVolume").val; } setState("javascript.0.heos.192_168_178_45.volume"/*Aktuelle Lautstärke*/, Volume); if (getState("javascript.0.heos.192_168_178_45.now_playing_media_album_id").val.indexOf('ios-ipod-library') + 1 > 0 || getState("javascript.0.heos.192_168_178_45.last_error").val.length > 0) { if ((new Date().getDay() === 0 ? 7 : new Date().getDay()) == 0) { Preset = getState("0_userdata.0.scriptData.SonntagPreset").val; } else { Preset = getState("0_userdata.0.scriptData.DefaultPreset").val; } setState("javascript.0.heos.192_168_178_45.command"/*Kommando für HEOS Aufrufe*/, ('play_preset&preset=' + String(Preset))); } else { setState("javascript.0.heos.192_168_178_45.play_state"/*Aktueller Wiedergabezustand*/, 'play'); } DelayedUnlock = true; } if (DelayedUnlock == true) { setStateDelayed("0_userdata.0.scriptData.BadezimmerAutoPlayRunning"/*scriptData.BadezimmerAutoPlayRunning*/, false, 60000, false); } else { setState("0_userdata.0.scriptData.BadezimmerAutoPlayRunning"/*scriptData.BadezimmerAutoPlayRunning*/, false); } } }); schedule("* * * * *", function () { if (getState("javascript.0.heos.192_168_178_45.connected").val == true) { if ((new Date().getHours()) >= 22 || (new Date().getHours()) <= 7) { if (getState("0_userdata.0.scriptData.BadezimmerAutoVolume").val != 'Nacht') { Volume = getState("0_userdata.0.scriptData.NachtVolume").val; if (getState("javascript.0.heos.192_168_178_45.volume").val != Volume) { setState("javascript.0.heos.192_168_178_45.volume"/*Aktuelle Lautstärke*/, Volume); } setState("0_userdata.0.scriptData.BadezimmerAutoVolume"/*BadezimmerAutoVolume*/, 'Nacht'); } } else { if (getState("0_userdata.0.scriptData.BadezimmerAutoVolume").val != 'Tag') { Volume = getState("0_userdata.0.scriptData.TagVolume").val; if (getState("javascript.0.heos.192_168_178_45.volume").val != Volume) { setState("javascript.0.heos.192_168_178_45.volume"/*Aktuelle Lautstärke*/, Volume); } setState("0_userdata.0.scriptData.BadezimmerAutoVolume"/*BadezimmerAutoVolume*/, 'Tag'); } } } });
-
@Uhula Das mit den verstümmelten Pakten war bei mir auch zufällig und auch immer bei den Presets. Habe folgendes im Script geändert:
- Player connected Flag hinzugefügt (connect und disconnect)
- this.nodessdp_client.stop() anstatt destroy
- onNodeSSDPResponse Funktion: Filter von Nicht-HEOS Packeten hinzugefügt.
- onDate Funktion: JSON parse check hinzugefügt
- parseResponse Funktion: Filter von "command under process" Paketen hinzugefügt
- Switch get_players: Wenn neue Player erkannt wurden, wird ein disconnect durchgeführt (eleganter wäre natürlich die neuen Player ganze ohne disconnect bzw. restart des Scripts aufzunehmen bzw. zu entfernen)
- Das Event player_mute_changed gibt es nicht mehr. Habe das Aktualisieren des Mute States in player_volume_changed aufgenommen
- get_now_playing_media aktualisiert jetzt wieder alle Song States
- Einige Debug Messages...
Btw. die Funktionen sleep und removeFirstOccurrence im Script werden nicht mehr verwendet und können entfernt werden.
-
@withstu Vielen Dank withstu. Ich habe die meisten Dinge davon übernommen, andere anders implementiert. Wenn du Interesse hast, kannst du die neuste Version testen. Hier die Liste der Änderungen:
v2.0 13.03.2020
- das Script überprüft nun, ob alle notwendigen states für Heos und HeosPlayer im ioBroker korrekt erzeugt wurden und gibt eine Warnung mit Script-Neustartaufforderung aus, wenn die states nicht vorhanden sind. Dieses ist notwendig, da das Anlegen von states asynchron erfolgt und einige ms dauern kann
- (WICHTIG!) die states werden nicht mehr in der aktuellen Javascript-Instanz und auch nicht mehr unter der IP des Heos-Players gespeichert, sondern Javascript-Instanzunabhängig unter 0_userdata.0 und seiner Player-ID (pid), da diese sich nicht ändert. Die IP hingegen u.U. schon. Also statt "javascript.X.heos.192_168_2_43" nun als "0_userdata.0.heos.12345678". Wer bereits auf die alten Werte im VIS zugreift, muss dieses im VIS anpassen!
- es wurden Befehle/states für die Gruppensteuerung der Heos-Player hinzugefügt. Beim Start werden die Gruppen ausgelesen und in den Player-states des Gruppenleiters (der erste genannte Player) gespeichert. Über dessen states kann die Gruppe gesteuert werden:
- group_leader : Ist Gruppenleiter
- group_member : Ist Gruppenmitglied
- group_pid : Player pids in der Gruppe
- group_volume : Lautstärke wenn Gruppen-Leiter
- group_name : Name der Gruppe
- group_mute : Gruppe gemutet?
- alle 60 Sek wird überprüft, ob noch alle HEOS-Player erreichbar sind bzw. neue/reconnectete hinzugekommen sind. Neue/reconnectete werden der Liste der HEOS-Player hinzugefügt, fehlende werden gestoppt. Da hierzu das HEOS-Netzwerk gefragt wird (get_players Meldung), kann es bis zu 5 Min dauern bis Änderungen erkannt werden. Der neue HEOS-Player-State "connected" wird entsprechend gesetzt
- jeder HEOS-Player hat einen neuen state "connected", dieser wird nach erfolgreichem Start auf true gesetzt, bei korrektem Beenden des Scripts auf false
- diverse Filter/Korrekturen aus dem Forum übernommen, vielen dank an withstu
Die Script-Datei: heos_new.js
Eine Beispiel-view für die Gruppen: heos_new_groupview.json (nutzt das MDCSS v2, aber an den Widgets kann man auch so das Prinzip erkennen)