[Vorlage] Denon HEOS Script

Fragen zum Skripten mit ioBroker.javascript
Antworten
Benutzeravatar
Uhula
professional
Beiträge: 177
Registriert: 20.08.2017, 15:20

[Vorlage] Denon HEOS Script

Beitrag von Uhula » 29.01.2018, 12:47

Inspiriert durch http://forum.iobroker.net/viewtopic.php ... 54#p115554 und durch den Ersatz meiner Sonos durch HEOS habe ich ein eigenes Script zur Steuerung der HEOS Player geschrieben. (Leider noch kein Adapter - dazu fehlt mir die Zeit und auch (noch) das KnowHow).

Unten ein Auszug der Beschreibung aus dem Script, näheres dort, welches weiter unten aufgeführt ist.

Und ein Beispiel, wie es im Material Design aussehen kann (ist aber kann MUSS).
heos.png
ve fun!


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!
(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


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

Spoiler: Show hidden text

Code: Alles auswählen

/****************************
 * 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!
 * (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 (write)
 *   Hierüber können der Heos-Klasse Befehle übergeben werden:
 *   connect      : Verbindung zu HEOS aufbauen bzw. erneut aufbauen (praktisch ein reset)
 *   disconnect   : Verbindung zu HEOS beenden
 *   load_presets : lädt die Favoriten neu
 * 
 * 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 = 'mailadr';
const HEOS_PASSWORD = 'password';
const DEBUG = false;


/****************************
 * 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?' });
    on({id: this.statePath+'command', change: "any"}, (obj) => {
            this.executeCommand( obj.state.val );
        });
}

logDebug(msg) { if (DEBUG) console.log('[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;
}

connect() { try {
    setState( this.statePath+"connected", false );
    const NodeSSDP = require('node-ssdp').Client;
	this.nodessdp_client = new NodeSSDP();
	this.nodessdp_client.on('response', (headers, statusCode, rinfo) => this.onNodeSSDPResponse(rinfo) );
	this.nodessdp_client.on('error', error => {	client.close(); this.logError(error); });
	const searchTargetName = 'urn:schemas-denon-com:device:ACT-Denon:1';
	this.nodessdp_client.search(searchTargetName);
} catch(err) { this.logError( 'onResponse: '+err.message ); } }


/** Alle Player stoppen und die TelNet Verbindung schließen 
 **/
disconnect() {
    this.log('disconnecting from HEOS ...');
    unsubscribe('javascript.1.heos');

    if (typeof this.net_client!=='undefined') {
        this.registerChangeEvents( false );
        
        this.net_client.destroy();
        this.net_client.unref();
    }
    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;
    }
}
/** es wurde mindestens ein Player erkannt, nun über dessen IP alle bekannten HEOS Player
 *  durch senden von "player/get_players" ermitteln
 */
onNodeSSDPResponse(rinfo) { try {
    // rinfo {"address":"192.168.2.225","family":"IPv4","port":53871,"size":430}
    if (typeof this.net_client=='undefined') {
        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.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)  );
    }
} catch(err) { this.logError( 'onNodeSSDPResponse: '+err.message ); } }



/** es liegen Antwort(en) vor
 **/
onData(data) {  try {
    data = data.toString();
    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=data.replace(/{"heos":/g, '|{"heos":');    
    var responses = data.split('|');
    responses.shift();
    for (var r=0; r<responses.length; r++ ) {
        this.parseResponse(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 )
        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('parseResponse: '+response); 

    // result ?
    var result = 'success';
    if (jdata.heos.hasOwnProperty('result') ) result = jdata.heos.result;
    if ( result!='success' ) { 
        setState(this.statePath+'last_error', jmsg.text);
        this.logWarn(jmsg.text);
    }

    // cmd auswerten
    var cmd = jdata.heos.command.split('/');
    var cmd_group = cmd[0];
    cmd = cmd[1];

    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();
                    }
                    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')) ) {
                        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 ); } }


/** 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();

    // Initialiserung 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 );

}

static version () { return "0.1"; }

logDebug(msg) { if (DEBUG) console.log('[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:"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 {
    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 );
                    break;
                case 'player_mute_changed' :
                    this.setState("mute", (jmsg.state=='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);
                    // type == station
                    if (jdata.payload.type=='station') {
                        this.setState("now_playing_media_song", jdata.payload.song);
                        this.setState("now_playing_media_station", jdata.payload.station);
                        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);
                        }
                    break;
            }
            break;
    } // switch


} catch(err) { this.logError( 'parseResponse: '+err.message ); } }
 
/** cmd der Form "cmd&param"  werden zur msg heos+cmd+pid+&param 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 );


Uhula - Leise und Weise
ioBroker Produktiv on Pi3 | ioBroker Test on Pi3 | Homematic | Cams | MiLight | Denon HEOS | ... No Alexa ;-)

Benutzeravatar
Uhula
professional
Beiträge: 177
Registriert: 20.08.2017, 15:20

Re: [Vorlage] Denon HEOS Script

Beitrag von Uhula » 29.01.2018, 16:41

Auf Wunsch noch der Export der Widgets für die beiden Views. Sehen aber nur mit Material Design so aus und benötigen die Icons im images-Ordner des Projects. Anpassungen an die eigenen IP Adressen sind notwendig, als Hilfe für eigene Entwürfe aber sinnvoll.
Player-Darstellung
Spoiler: Show hidden text

Code: Alles auswählen

{"tpl":"tplJquiSlider","data":{"oid":"javascript.1.heos.192_168_2_219.cur_pos","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"oid-2":"","oid-2-working":"","max":"{javascript.1.heos.192_168_2_219.duration}","class":"mdui-slider mdui-blue-acc"},"style":{"left":"13px","top":"calc(100% - 195px)","width":"calc(100% - 26px)","height":"4px","line-height":"25px","z-index":3},"widgetSet":"jqui"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.duration_MMSS","g_fixed":true,"g_visibility":false,"g_css_font_text":true,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-subtitle"},"style":{"left":"calc(100% - 52px)","top":"calc(100% - 195px)","height":"20px","width":"39px","line-height":"25px","z-index":3,"text-align":"right"},"widgetSet":"basic"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.cur_pos_MMSS","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-subtitle"},"style":{"left":"13px","top":"calc(100% - 195px)","height":"20px","width":"39px","line-height":"25px","z-index":3},"widgetSet":"basic"},{"tpl":"tplValueStringImg","data":{"oid":"javascript.1.heos.192_168_2_219.now_playing_media_image_url","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":""},"style":{"left":"13px","top":"78px","width":"calc(100% - 26px)","height":"calc(100% - 273px)","line-height":"25px","z-index":"1"},"widgetSet":"basic"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.now_playing_media_artist","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-label"},"style":{"left":"13px","top":"calc(100% - 143px)","width":"calc(100% - 26px)","height":"26px","z-index":"2"},"widgetSet":"basic"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.now_playing_media_station","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-label"},"style":{"left":"13px","top":"52px","width":"calc(100% - 26px)","height":"26px","z-index":1},"widgetSet":"basic"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.now_playing_media_song","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-value "},"style":{"left":"13px","top":"calc(100% - 169px)","width":"calc(100% - 26px)","height":"26px","z-index":"2"},"widgetSet":"basic"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"value":"play_next","src":"/vis.0/MD_Demo/images/chevron_right_white.png","class":"mdui-flatbutton","invert_icon":false},"style":{"left":"221px","top":"calc(100% - 121px)","width":"52px","height":"52px"},"widgetSet":"jqui"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"value":"play_previous","src":"/vis.0/MD_Demo/images/chevron_left_white.png","class":"mdui-flatbutton","invert_icon":false},"style":{"left":"65px","top":"calc(100% - 121px)","width":"52px","height":"52px"},"widgetSet":"jqui"},{"tpl":"tplJquiSlider","data":{"oid":"javascript.1.heos.192_168_2_219.volume","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"oid-2":"","oid-2-working":"","max":"100","class":"mdui-slider ","min":"0","step":"2"},"style":{"left":"65px","top":"calc(100% - 52px)","width":"calc(100% - 121px)","height":"26px","line-height":"25px","z-index":"2"},"widgetSet":"jqui"},{"tpl":"tplJquiToogle","data":{"oid":"javascript.1.heos.192_168_2_219.mute","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"src_true":"/vis.0/MD_Demo/images/volume_off_white.png","src_false":"/vis.0/MD_Demo/images/volume_up_white.png","invert_icon":false,"label":"","alt_true":"AN","alt_false":"AUS","class":"mdui-flatbutton"},"style":{"left":"13px","top":"calc(100% - 65px)","width":"52px","height":"52px"},"widgetSet":"jqui"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"value":"set_play_state&state=pause","src":"/vis.0/main/images/stop_white.png","class":"mdui-flatbutton {v:javascript.1.heos.192_168_2_219.play_state;v!='play'?'mdui-blue-glow'::''}","invert_icon":false},"style":{"left":"117px","top":"calc(100% - 121px)","width":"52px","height":"52px"},"widgetSet":"jqui"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"value":"set_play_state&state=play","src":"/vis.0/main/images/play_white.png","class":"mdui-flatbutton {v:javascript.1.heos.192_168_2_219.play_state;v=='play'?'mdui-blue-glow'::''}","invert_icon":false},"style":{"left":"169px","top":"calc(100% - 121px)","width":"52px","height":"52px"},"widgetSet":"jqui"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"html":"HEOS 1 Uwe","class":"mdui-title"},"style":{"left":"13px","top":"13px","width":"calc(100% - 26px)","height":"25px"},"widgetSet":"basic"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.volume","g_fixed":true,"g_visibility":false,"g_css_font_text":true,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-subtitle"},"style":{"left":"calc(100% - 52px)","top":"calc(100% - 48px)","height":"auto","width":"39px","text-align":"right"},"widgetSet":"basic"},{"tpl":"tplValueString","data":{"oid":"javascript.1.heos.192_168_2_219.last_error","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-value mdui-red"},"style":{"left":"26px","top":"91px","height":"104px","width":"calc(100% - 52px)","z-index":"2"},"widgetSet":"basic"}
Favoriten
Spoiler: Show hidden text

Code: Alles auswählen

[{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"src":"{javascript.1.heos.presets.4.image_url}","value":"play_preset&preset=4","text":"","imageHeight":"","invert_icon":false,"class":"mdui-flatbutton mdui-black-bg"},"style":{"left":"13px","top":"208px","width":"48px","height":"48px"},"widgetSet":"jqui"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"html":"{javascript.1.heos.presets.4.name}","class":"mdui-subtitle"},"style":{"left":"65px","top":"208px","width":"calc(100% - 78px)","height":"48px"},"widgetSet":"basic"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"html":"{javascript.1.heos.presets.3.name}","class":"mdui-subtitle"},"style":{"left":"65px","top":"156px","width":"calc(100% - 78px)","height":"48px"},"widgetSet":"basic"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"html":"{javascript.1.heos.presets.2.name}","class":"mdui-subtitle"},"style":{"left":"65px","top":"104px","width":"calc(100% - 78px)","height":"48px"},"widgetSet":"basic"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"html":"{javascript.1.heos.presets.1.name}","class":"mdui-subtitle"},"style":{"left":"65px","top":"52px","width":"calc(100% - 78px)","height":"48px"},"widgetSet":"basic"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"src":"{javascript.1.heos.presets.3.image_url}","value":"play_preset&preset=3","text":"","imageHeight":"","invert_icon":false,"class":"mdui-flatbutton mdui-black-bg"},"style":{"left":"13px","top":"156px","width":"48px","height":"48px"},"widgetSet":"jqui"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"src":"{javascript.1.heos.presets.2.image_url}","value":"play_preset&preset=2","text":"","imageHeight":"","invert_icon":false,"class":"mdui-flatbutton mdui-black-bg"},"style":{"left":"13px","top":"104px","width":"48px","height":"48px"},"widgetSet":"jqui"},{"tpl":"tplIconState","data":{"oid":"javascript.1.heos.192_168_2_219.command","g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"src":"{javascript.1.heos.presets.1.image_url}","value":"play_preset&preset=1|set_volume&level=20","text":"","imageHeight":"","invert_icon":false,"class":"mdui-flatbutton mdui-black-bg"},"style":{"left":"13px","top":"52px","width":"48px","height":"48px"},"widgetSet":"jqui"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"html":"HEOS 1 Uwe","class":"mdui-title"},"style":{"left":"13px","top":"13px","width":"200px","height":"25px"},"widgetSet":"basic"},{"tpl":"tplHtml","data":{"g_fixed":true,"g_visibility":false,"g_css_font_text":false,"g_css_background":false,"g_css_shadow_padding":false,"g_css_border":false,"g_gestures":false,"g_signals":false,"g_last_change":false,"visibility-cond":"==","visibility-val":1,"visibility-groups-action":"hide","refreshInterval":"0","signals-cond-0":"==","signals-val-0":true,"signals-icon-0":"/vis/signals/lowbattery.png","signals-icon-size-0":0,"signals-blink-0":false,"signals-horz-0":0,"signals-vert-0":0,"signals-hide-edit-0":false,"signals-cond-1":"==","signals-val-1":true,"signals-icon-1":"/vis/signals/lowbattery.png","signals-icon-size-1":0,"signals-blink-1":false,"signals-horz-1":0,"signals-vert-1":0,"signals-hide-edit-1":false,"signals-cond-2":"==","signals-val-2":true,"signals-icon-2":"/vis/signals/lowbattery.png","signals-icon-size-2":0,"signals-blink-2":false,"signals-horz-2":0,"signals-vert-2":0,"signals-hide-edit-2":false,"lc-type":"last-change","lc-is-interval":true,"lc-is-moment":false,"lc-format":"","lc-position-vert":"top","lc-position-horz":"right","lc-offset-vert":0,"lc-offset-horz":0,"lc-font-size":"12px","lc-font-family":"","lc-font-style":"","lc-bkg-color":"","lc-color":"","lc-border-width":"0","lc-border-style":"","lc-border-color":"","lc-border-radius":10,"lc-zindex":0,"class":"mdui-subtitle","html":"Favoriten"},"style":{"left":"13px","top":"31px","width":"200px","height":"20px"},"widgetSet":"basic"}]
Uhula - Leise und Weise
ioBroker Produktiv on Pi3 | ioBroker Test on Pi3 | Homematic | Cams | MiLight | Denon HEOS | ... No Alexa ;-)

obakuhl
starter
Beiträge: 23
Registriert: 18.08.2017, 14:41

Re: [Vorlage] Denon HEOS Script

Beitrag von obakuhl » 04.02.2018, 20:15

Hallo Uhula, erstmal sensationelle Arbeit. Du hast aus dem Script von Brati sehr viel rausgeholt. Die regelmäßige Abfrage scheint in 5000ms Takt zu erfolgen, wenn ich das richtig entnommen haben. Eine sekündliche Abfrage verursacht zu viel Traffic oder was ist der Grund für die 5s Abfrage?

Viele Grüße Ben

Benutzeravatar
Uhula
professional
Beiträge: 177
Registriert: 20.08.2017, 15:20

Re: [Vorlage] Denon HEOS Script

Beitrag von Uhula » 20.02.2018, 14:00

obakuhl hat geschrieben:Eine sekündliche Abfrage verursacht zu viel Traffic oder was ist der Grund für die 5s Abfrage?
Es findet kein Polling, keine regelmäßige Abfrage statt. Stattdessen wird beim HEOS Player ein Callback (registerChangeEvents) in das Script registriert und der HEOS Player sendet dann Änderungen von sich aus dorthin. Offensichtlich macht dieser das für den Play-Fortschritt im 5 Sekunden Rhythmus. Andere Änderungen sind "sofort" da.
Uhula - Leise und Weise
ioBroker Produktiv on Pi3 | ioBroker Test on Pi3 | Homematic | Cams | MiLight | Denon HEOS | ... No Alexa ;-)

Postmymind
starter
Beiträge: 10
Registriert: 07.06.2018, 09:17

Re: [Vorlage] Denon HEOS Script

Beitrag von Postmymind » 07.06.2018, 15:03

Hey, danke für diese herausragende Arbeit! Unglaublich, was du schon rausgeholt hast. Hat es schon jemand geschafft mehrere Heos-Geräte zu gruppieren über ioBroker? Ich habe habe insgesamt 7 Geräte im Haus und würde gern' per Trigger (Sprachbefehl oder Button) z.B. im kompletten Erdgeschoss eine Quelle abspielen, wie es sonst nur über die Heos-App möglich ist. Vielen Dank schon mal für die herausragende Arbeit!

obakuhl
starter
Beiträge: 23
Registriert: 18.08.2017, 14:41

Re: [Vorlage] Denon HEOS Script

Beitrag von obakuhl » 12.06.2018, 14:46

Ich regel das über Skripte, die die einzelnen commands dann an die jeweilige Box sendet. Blockly eignet sich sehr gut dafür. Wenn du verschiedenen Gruppen hast reicht es auch ein Gerät aus dieser Gruppe einen Befehl zu senden (z.B. über "play_preset&preset=xyz") dann spielt es auf allen "dazugehörigen" Heosgeräten auch diese Quelle ab. Die Heosapp brauch ich dadurch überhaupt nicht mehr, ich regel alles über die VIS Oberfläche.

Ist vielleicht nicht die sauberste Lösung, aber es funktioniert sehr gut ;)
Dateianhänge
denon_mute_blockly.png
Beispiel für Mute.
Die äußere (blaue) if Abfrage frägt einen weiteren Status ab, der aber für das Beispiel nicht relevant ist.

Postmymind
starter
Beiträge: 10
Registriert: 07.06.2018, 09:17

Re: [Vorlage] Denon HEOS Script

Beitrag von Postmymind » 15.06.2018, 07:40

Hey! Vielen Dank für die rasche Antwort. Verdammt guter Ansatz aber wie definiere ich vorgefertigte Gruppen?
Entschuldigt mein ioBroker / Skript Einsteigerwissen.
obakuhl hat geschrieben:
12.06.2018, 14:46
Ich regel das über Skripte, die die einzelnen commands dann an die jeweilige Box sendet. Blockly eignet sich sehr gut dafür. Wenn du verschiedenen Gruppen hast reicht es auch ein Gerät aus dieser Gruppe einen Befehl zu senden (z.B. über "play_preset&preset=xyz") dann spielt es auf allen "dazugehörigen" Heosgeräten auch diese Quelle ab. Die Heosapp brauch ich dadurch überhaupt nicht mehr, ich regel alles über die VIS Oberfläche.

Ist vielleicht nicht die sauberste Lösung, aber es funktioniert sehr gut ;)

obakuhl
starter
Beiträge: 23
Registriert: 18.08.2017, 14:41

Re: [Vorlage] Denon HEOS Script

Beitrag von obakuhl » 15.06.2018, 10:40

Das musst du über die App machen. Hier der Link zur Beschreibung wie das geht.

Sonst nochmal der Auszug aus der Supportseite:
Wie kann ich meine HEOS Lautsprecher zu einer Gruppe zusammenfassen?

Die Gruppierung von HEOS Lautsprechern erlaubt es Ihnen, zwei (oder mehrere) HEOS Lautsprecher in unterschiedlichen Räumen oder auch im gleichen Raum mit der gleichen Musik anzusteueren und zu bedienen. Die Gruppierung sorgt auch für einen hervorragende Synchronisation zwischen den einzelnen Lautsprechern, so dass Sie keinen zeitlichen Versatz (Echo) hören, wenn die Lautsprecher in unterschiedlichen Räumen stehen.

Um die HEOS Lautsprecher zu gruppieren, wählen Sie in der HEOS App den Reiter Räume an. Anschliessend tippen Sie den HEOS Lautsprecher an, den Sie zu mit einem anderen HEOS Lautsprecher gruppieren möchen. Schieben Sie ihn auf den anderen HEOS Lautsprecher.

Sie können auch alle HEOS Lautsprecher (max. 16 Stück) auf einmal zusammenfügen. Halten Sie dazu bitte den ersten und letzten HEOS Lautsprecher gedrückt und schieben diese zusammen.

Um einen Lautsprecher aus der Gruppe zu entfernen, gehen Sie bitte genauso vor. Halten Sie den Lautsprecher gedrückt und ziehen ihn aus der Gruppe heraus.

Postmymind
starter
Beiträge: 10
Registriert: 07.06.2018, 09:17

Re: [Vorlage] Denon HEOS Script

Beitrag von Postmymind » 15.06.2018, 10:57

Danke für die schnelle Rückmeldung. Wie ich es in der App mache weiß ich ja ... Hab Heos ja seit Marktstart im Einsatz.

Oder meinst du, dass man die Gruppen über die App nur definiert und dann über io als Variable anspricht, damit man sich den Weg über die App spart?

Hätte ja aber sein können, dass ich das auch über ein Script lösen kann. Meine aktuelle Lösung für die Gruppierung von Räumen ohne die App ist der Harmony Adapter in ioBroker. In Harmony habe ich jeweils Aktivitäten ohne Quellenauswahl für die einzelnen Gruppierungen erstellt, welche dann die Speaker zusammenfasst. Mit dem Spotify Adapter kann ich dann über die Vis das aktive Device wählen.
obakuhl hat geschrieben:
15.06.2018, 10:40
Das musst du über die App machen. Hier der Link zur Beschreibung wie das geht.

Sonst nochmal der Auszug aus der Supportseite:
Wie kann ich meine HEOS Lautsprecher zu einer Gruppe zusammenfassen?

Die Gruppierung von HEOS Lautsprechern erlaubt es Ihnen, zwei (oder mehrere) HEOS Lautsprecher in unterschiedlichen Räumen oder auch im gleichen Raum mit der gleichen Musik anzusteueren und zu bedienen. Die Gruppierung sorgt auch für einen hervorragende Synchronisation zwischen den einzelnen Lautsprechern, so dass Sie keinen zeitlichen Versatz (Echo) hören, wenn die Lautsprecher in unterschiedlichen Räumen stehen.

Um die HEOS Lautsprecher zu gruppieren, wählen Sie in der HEOS App den Reiter Räume an. Anschliessend tippen Sie den HEOS Lautsprecher an, den Sie zu mit einem anderen HEOS Lautsprecher gruppieren möchen. Schieben Sie ihn auf den anderen HEOS Lautsprecher.

Sie können auch alle HEOS Lautsprecher (max. 16 Stück) auf einmal zusammenfügen. Halten Sie dazu bitte den ersten und letzten HEOS Lautsprecher gedrückt und schieben diese zusammen.

Um einen Lautsprecher aus der Gruppe zu entfernen, gehen Sie bitte genauso vor. Halten Sie den Lautsprecher gedrückt und ziehen ihn aus der Gruppe heraus.

Brati
professional
Beiträge: 410
Registriert: 09.09.2015, 13:10

Re: [Vorlage] Denon HEOS Script

Beitrag von Brati » 15.06.2018, 11:12

@Postymind

Die Gruppen solltest du gemäß CLI Protokoll mit den Player PID setzen können:

Code: Alles auswählen

This command is used to perform the following actions:

Create new group:
Creates new group. First player id in the list is group leader.
Ex: heos://group/set_group?pid=3,1,4Modify existing group members:

Adds or delete players from the group. First player id should be the group leader id.
Ex: heos://group/set_group?pid=3,1,5

Ungroup all players in the group
Ungroup players. Player id (pid) should be the group leader id.
Ex: heos://group/set_group?pid=3

Command: heos://group/set_group?pid=player_id_leader, player_id_member_1,…,player_id_member_n
Und das dann im "command" State übergeben. Habe es aber noch nicht getestet. Mein AVR mit 3 Zonen hat nur eine PID und dann gibt es nur eine 2. Box...

Grüße

Brati
46 Geräte mit 243 Kanälen, ioBroker auf Intel NUC mit Tab als Frontend.
Projekt Gartenhaus mit HM

obakuhl
starter
Beiträge: 23
Registriert: 18.08.2017, 14:41

Re: [Vorlage] Denon HEOS Script

Beitrag von obakuhl » 15.06.2018, 11:14

Achso 8-)

Im Skript ist aktuell noch nichts zu Gruppen implementiert, soweit ich das durchschaut habe.

Genau, ich sende die Komandos, wie z.B. einen Lautsprecher muten über ioBroker. Die App verwende ich gar nicht mehr. Wahrscheinlich ist es gar nicht nötig, wenn man alles über Blocklyskripte regelt und die Lautsprecher einzeln anspricht, dass man diese gruppiert. Aber dadurch ist auf jeden Fall sichergestellt, dass die Boxen synchron laufen.
Generell ist aber sicher mögich, dass über die Group-Schnittstelle des CLI Protokolls von HEOS professioneller zu regeln.

Postmymind
starter
Beiträge: 10
Registriert: 07.06.2018, 09:17

Re: [Vorlage] Denon HEOS Script

Beitrag von Postmymind » 15.06.2018, 11:18

Danke! Ich werde mal testen sobald ich Zuhause bin. Erreichen möchte ich z.B., dass ich über verschiedene Buttons und deren "States" im Haus (ob Hue Dimmer Switch o.Ä.) Gruppierungen, Quellenauswahl und Playstate triggern kann. Ich werde mal mit den 7 vorhandenen Heos (1x Link, 6x Heos 3/5) testen. Danke schon mal
Brati hat geschrieben:
15.06.2018, 11:12
@Postymind

Die Gruppen solltest du gemäß CLI Protokoll mit den Player PID setzen können:

Code: Alles auswählen

This command is used to perform the following actions:

Create new group:
Creates new group. First player id in the list is group leader.
Ex: heos://group/set_group?pid=3,1,4Modify existing group members:

Adds or delete players from the group. First player id should be the group leader id.
Ex: heos://group/set_group?pid=3,1,5

Ungroup all players in the group
Ungroup players. Player id (pid) should be the group leader id.
Ex: heos://group/set_group?pid=3

Command: heos://group/set_group?pid=player_id_leader, player_id_member_1,…,player_id_member_n
Und das dann im "command" State übergeben. Habe es aber noch nicht getestet. Mein AVR mit 3 Zonen hat nur eine PID und dann gibt es nur eine 2. Box...

Grüße

Brati

Postmymind
starter
Beiträge: 10
Registriert: 07.06.2018, 09:17

Re: [Vorlage] Denon HEOS Script

Beitrag von Postmymind » 16.07.2018, 18:40

Funktioniert leider nicht. Direkt über Telnet funktioniert die Gruppierung aber über die Kommandozeile des Scripts passiert leider nichts.

Hat sonst noch jemand weitere Ideen, wie man das lösen könnte oder ob man den Befehl über "Command" des Scripts anders senden muss?

Benutzeravatar
spielberger32
starter
Beiträge: 16
Registriert: 12.01.2017, 14:27
Wohnort: Köln

Re: [Vorlage] Denon HEOS Script

Beitrag von spielberger32 » 16.07.2018, 19:00

Probier mal set_group?pid=-123456789,-223456789,-323456789 in die die Command-Option zu schreiben. Bei mir macht das folgendes Skript:

Code: Alles auswählen

setState("javascript.0.heos.192_168_XXX_XXX.command", 'set_group?pid=-123456789,-223456789,-323456789');
Für -123456789 etc natürlich jeweils die PID des Lautsprechers angeben. ;) ACHTUNG: immer mit einem eventuellen MINUS DAVOR.
„Alte Knaben haben genauso ihr Spielzeug wie die jungen, der Unterschied liegt lediglich im Preis.“
Benjamin Franklin

Postmymind
starter
Beiträge: 10
Registriert: 07.06.2018, 09:17

Re: [Vorlage] Denon HEOS Script

Beitrag von Postmymind » 16.07.2018, 19:53

Funktioniert! Danke! So kann ich meine Etagen-Gruppensteuerung realisieren.

Antworten