Skip to content
  • Home
  • Aktuell
  • Tags
  • 0 Ungelesen 0
  • Kategorien
  • Unreplied
  • Beliebt
  • GitHub
  • Docu
  • Hilfe
Skins
  • Light
  • Brite
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Standard: (Kein Skin)
  • Kein Skin
Einklappen
ioBroker Logo

Community Forum

  1. ioBroker Community Home
  2. Deutsch
  3. Skripten / Logik
  4. Virtual Devices

NEWS

  • UPDATE 31.10.: Amazon Alexa - ioBroker Skill läuft aus ?
    apollon77A
    apollon77
    48
    3
    8.4k

  • Monatsrückblick – September 2025
    BluefoxB
    Bluefox
    13
    1
    2.0k

  • Neues Video "KI im Smart Home" - ioBroker plus n8n
    BluefoxB
    Bluefox
    15
    1
    2.5k

Virtual Devices

Geplant Angeheftet Gesperrt Verschoben Skripten / Logik
48 Beiträge 21 Kommentatoren 18.8k Aufrufe 11 Watching
  • Älteste zuerst
  • Neuste zuerst
  • Meiste Stimmen
Antworten
  • In einem neuen Thema antworten
Anmelden zum Antworten
Dieses Thema wurde gelöscht. Nur Nutzer mit entsprechenden Rechten können es sehen.
  • P Offline
    P Offline
    Pman
    schrieb am zuletzt editiert von
    #1

    Ich weiß, es gibt schon Adapter wie Scene und andere, um Geräte zu gruppieren usw.

    Ich möchte mit diesem Skript deutlich weiter gehen und damit eine Vielzahl an Problemen beseitigen, welche bei mir beim Arbeiten mit ioBroker häufig auftreten. Eigentlich würde sich das Skript sogar gut eignen, um daraus einen Adapter zu bauen, allerdings fehlt mir dafür ein GUI. Das Skript ist ganz neu und evtl. nicht frei von Bugs, funktioniert bei mir aber sehr zuverlässig.

    Damit das Script lauffähig ist muss der Javascript Adapter "Nicht auf alle Zustände beim Start abonnieren" ausgeschaltet haben und "Erlaube das Kommando setObject" muss eingschaltet sein.

    Problem:

    Bei der Arbeitsteilung zwischen Adaptern, History, Vis usw. kommt es häufig zu Unklarheiten, an welcher Stelle überhaupt gewisse Funktionalität erbracht werden soll. So hat z. B. ein Dimmer in Homematic einen Datenpunkt "LEVEL", welcher Werte von 0-100 annehmen kann. Bei HUE ist es "bri" mit 0-254 und bei Z-Wave sind es 0-99. Bei Hue habe ich vor einiger Zeit in den Adapter den Wert "level" mit der Homematic-Logik hinzugefügt, diesen gibt es in HUE eigentlich nicht. Und eigentlich halte ich es für falsch, solche Logiken in Geräte-Adapter einzubauen, diese sollten die Daten so zur Verfügung stellen, wie es die Geräte tun. Spätestens bei Vis würde ich aber gerne jede Lampe mit Werten 0-100 steuern. Manche Widgets versuchen auch mit möglichst vielen Werten klar zu kommen. Oft reicht das nicht und ich stehe wieder vor dem Problem, dass ich kein passendes Widget finde, um eine gewisse Funktion auszuführen. Dimmer sind hier nur ein Beispiel, wo ich Geräte verschiedener Hersteller auf einen Standard bringen möchte. Wer schonmal versucht hat, den Betriebsmodus eines Homematic-Thermostat mit einem Knopf zu steuern, weiß wovon ich rede. Das Thermostat besitzt nämlich gleich 5 States um den Betriebsmodus zu wechseln und einen weiteren um diesen anzuzeigen. Hier kommt dieses Skript als Vermittler ins Spiel.

    Skriptbeschreibung:

    Das Skript eignet sich, um Geräte zu virtualisieren bzw. ganz neue Geräte zu erstellen. Soll heißen, es werden States in einem Namespace unter javascript.x.virtualDevice.* erstellt. Jeder dieser States kann Werte von mehreren Geräten erhalten und auch wieder in mehrere andere Geräte schreiben. Dabei können bedarfsweise Konvertierungen oder andere Funktionen ausgeführt werden. Das Skript ist so universell gehalten und dabei vergleichsweise einfach zu steuern, dass ich damit alle meine Probleme lösen konnte. Wenn man schon viel mit ioBroker gearbeitet hat, werden einem die Vorteile einer Geräte-Virtualisierung schnell klar. Besonders bei der Arbeit mit History und Vis sowie der Verwaltung von Zugriffsrechten bietet es eine Menge Vorteile, weswegen ich mittlerweile alle Geräte virtualisiert habe. So geht mir nie eine History verloren, wenn ich einen Aktor austausche und ich muss auch nichts an den Views in Vis ändern.

    Beispiel:

    Hier erstelle ich eine "Kopie" einer Hue-Lux Lampe, also nur dimmbar, keine Farben. Dabei benötige ich nur den Dimm-Wert, alle anderen States aus dem Adapter brauche ich für mein virtuelles Gerät nicht. Um auf das oben genannte Beispiel einzugehen nutze ich hier den 'bri'-Wert aus dem HUE-Adapter:

    new VirtualDevice({
        namespace: 'Hue',
        name: 'Wohnzimmer', //das Gerät wird unter javascript.0.virtualDevice.Hue.Wohnzimmer erstellt
        states: {
            //welche States sollen erstellt werden?
            'level': {
                //State Konfiguration
                common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                read: {
                    //von welchen States sollen Werte eingelesen werden?
                    'hue.0.hue_bridge.Wohnzimmer_Decke.bri': {
                        convert: function (val) { //wert soll konvertiert werden
                            return Math.floor(val * 100 / 254);
                        }
                    },
                },
                write: {
                    //in welche States sollen Werte geschrieben werden?
                    'hue.0.hue_bridge.Wohnzimmer_Decke.bri': {
                        convert: function (val) { //wert soll konvertiert werden
                            return Math.ceil(val * 254 / 100);
                        },
                        delay: 1500 // schreibe Werte erst nach 1,5 Sekunden in den Adapter (Puffer)
                    },
                }
            },
        }
    });
    

    Damit erstelle ich ein Gerät unter javascript.0.virtualDevice.Hue.Wohnzimmer, welches eine Datenpunkt level hat. Dieser Datenpunkt nimmt Werte zwischen 0 und 100 entgegen und konvertiert diese zu 0-254 und umgekehrt.

    Man sieht hier auch, dass man unter level.read und level.write weitere States einfügen kann, so dass gleich mehrere Lampen gesteuert werden. Diese müssten auch nicht alle vom HUE-Adapter kommen und können ganz unterschiedliche Werte erwarten.

    Es ist aber noch mehr möglich:

    Ich habe im Wandschalter für die Hue-Lampe ein Z-Wave Relay verbaut. So kann ich die Hue-Lampe klassisch an der Wand ein und ausschalten, aber auch über ioBroker vom Aus-Zustand einschalten. Ich möchte aber nicht erst umständlich das Relay schalten und dann die Hue-Lampe. Ich möchte, dass das Relay ganz automatisch einschaltet, wenn ich level auf einen Wert größer 0 setze. Dazu löse ich vor dem Schreiben von level in den Hue-Adapter eine Funktion aus:

    new VirtualDevice({
        namespace: 'Hue',
        name: 'Wohnzimmer', //das Gerät wird unter javascript.0.virtualDevice.Hue.Wohnzimmer erstellt
        states: {
            //welche States sollen erstellt werden?
            'level': {
                //State Konfiguration
                common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                read: {
                    //von welchen States sollen Werte eingelesen werden?
                    'hue.0.hue_bridge.Wohnzimmer_Decke.bri': {
                        convert: function (val) { //wert soll konvertiert werden
                            return Math.floor(val * 100 / 254);
                        }
                    },
                },
                write: {
                    //in welche States sollen Werte geschrieben werden?
                    'hue.0.hue_bridge.Wohnzimmer_Decke.bri': {
                        convert: function (val) { //wert soll konvertiert werden
                            return Math.ceil(val * 254 / 100);
                        },
                        delay: 1500, // schreibe Werte erst nach 1,5 Sekunden in den Adapter (Puffer)
                        before: function (device, value, callback) {
                            if (value > 0 && getState('zwave.0.NODE10.SWITCH_BINARY.Switch_1').val === false) {
                                //if switch is off and value is greater 0 turn on switch and set long delay
                                setStateDelayed(switchId, true, false, 1500, true, function () {
                                    callback(value, 3500);
                                });
                            } else if (value <= 0) {
                                //if level is set to 0 turn off switch and set level 0
                                setStateDelayed(switchId, false, false, 1500, true, function () {
                                    callback(0, 0);
                                });
                            } else {
                                callback();
                            }
                        }
                    },
                }
            },
        }
    });
    

    Ich hoffe anhand dieses Beispiels werden einige Funktionen des Skripts deutlich. Bei Lampen mit Farbtemperatur habe ich den ct-Wert in Kelvin konvertiert, so dass ich die mir bekannte Weißtemperatureinheit eingeben kann, z. B. 2700k für Warmweiß.

    Homematic-Thermostate habe ich zusammen mit Stellantrieb in ein einziges virtuelles Gerät zusammengefügt und dabei die Moduswahl auf einen Datenpunkt reduziert.

    Skript

    `/*
    VirtualDevice v0.3
    
    Structure of config:
    {
        name: 'name', //name of device
        namespace: '', //create device within this namespace (device ID will be namespace.name)
        common: {}, //(optional)
        native: {}, //(optional)
        copy: objectId, //(optional) ID of device or channel to copy common and native from
        onCreate: function(device, callback) {} //called once on device creation
        states: {
            'stateA': { //State Id will be namespace.name.stateA
                common: {}, //(optional)
                native: {}, //(optional)
                copy: stateId,
                read: {
                    //(optional) states which should write to "stateA"
                    'stateId1': {
                        trigger: {ack: true, change: 'any'} //(optional) see https://github.com/ioBroker/ioBroker.javascript#on---subscribe-on-changes-or-updates-of-some-state
                        convert: function(val) {}, //(optional) functions should return converted value 
                        before: function(device, value, callback) {}, //(optional) called before writing new state. has to call callback or new value will not be written 
                        delay: 0, //(optional) delay in ms before new value gets written
                        after: function(device, value) {}, //(optional) called after new value has been written
                        validFor: //(optional) ms, to ignore old data which did not change for a long time.  
                    },
                    ...
                },
                readLogic: 'last' || 'max' || 'min' || 'average', //(optional) default: last (only last implemented)
                write: {
                    //(optional) states which "stateA" should write to 
                    'stateId1': {
                        convert: function(val) {}, //(optional) functions should return converted value 
                        before: function(device, value, callback) {}, //(optional) called before writing new state. has to call callback or new value will not be written 
                        delay: 0, //(optional) delay in ms before new value gets written
                        after: function(device, value) {}, //(optional) called after new value has been written
                    },     
                    'stateId3': {
                        convert: function(val) {}, //(optional) functions should return converted value 
                        before: function(device, value, callback) {}, //(optional) called before writing new state. has to call callback or new value will not be written 
                        delay: 0, //(optional) delay in ms before new value gets written
                        after: function(device, value) {}, //(optional) called after new value has been written
                    },
                    ...
                },
            },
            ...
        }
    }
    */
    
    //generic virtual device        
    function VirtualDevice(config) {
        //sanity check
        if (typeof config !== 'object' || typeof config.namespace !== 'string' || typeof config.name !== 'string' || typeof config.states !== 'object') {
            log('sanity check failed, no device created', 'warn');
            return;
        }
    
        this.config = config;
        this.namespace = 'virtualDevice.' + config.namespace + '.' + config.name;
        this.name = config.name;
    
        //create virtual device
        log('creating virtual device ' + this.namespace)
        this.createDevice(function () {
            this.createStates(function () {
                log('created virtual device ' + this.namespace)
            }.bind(this));
        }.bind(this));
    }
    
    VirtualDevice.prototype.createDevice = function (callback) {
        log('creating object for device ' + this.namespace, 'debug');
        //create device object
        var obj = this.config.copy ? getObject(this.config.copy) : {common: {}, native: {}};
        delete obj.common.custom;
        if (typeof this.config.common === 'object') {
            obj.common = Object.assign(obj.common, this.config.common);
        }
        if (typeof this.config.native === 'object') {
            obj.native = Object.assign(obj.native, this.config.native);
        }
        extendObject('javascript.' + instance + '.' + this.namespace, {
            type: "device",
            common: obj.common,
            native: obj.native
        }, function (err) {
            if (err) {
                log('could not create virtual device: ' + this.namespace, 'warn');
                return;
            }
            log('created object for device ' + this.namespace, 'debug');
            if (typeof this.config.onCreate === 'function') {
                this.config.onCreate(this, callback);
            } else {
                callback();
            }
        }.bind(this));
    }
    
    VirtualDevice.prototype.createStates = function (callback) {
        "use strict";
        log('creating states for device ' + this.namespace, 'debug');
        var stateIds = Object.keys(this.config.states);
        log('creating states ' + JSON.stringify(stateIds), 'debug');
        var countCreated = 0;
        for (var i = 0; i < stateIds.length; i++) {
            let stateId = stateIds[i];
            this.normalizeState(stateId);
            var id = this.namespace + '.' + stateId;
            log('creating state ' + id, 'debug');
            var obj = this.config.states[stateId].copy ? getObject(this.config.states[stateId].copy) : {
                common: {},
                native: {}
            };
            delete obj.common.custom;
            if (typeof this.config.states[stateId].common === 'object') {
                obj.common = Object.assign(obj.common, this.config.states[stateId].common);
            }
            if (typeof this.config.states[stateId].native === 'object') {
                obj.native = Object.assign(obj.native, this.config.states[stateId].native);
            }
            createState(id, obj.common, obj.native, function (err) {
                if (err) {
                    log('skipping creation of state ' + id, 'debug');
                } else {
                    log('created state ' + id, 'debug');
                }
                this.connectState(stateId);
                countCreated++;
                if (countCreated >= stateIds.length) {
                    log('created ' + countCreated + ' states for device ' + this.namespace, 'debug');
                    callback();
                }
            }.bind(this));
        }
    }
    
    VirtualDevice.prototype.normalizeState = function (state) {
        log('normalizing state ' + state, 'debug');
        if (typeof this.config.states[state].read !== 'object') {
            this.config.states[state].read = {};
        }
        if (typeof this.config.states[state].write !== 'object') {
            this.config.states[state].write = {};
        }
    
        var readIds = Object.keys(this.config.states[state].read);
        for (var i = 0; i < readIds.length; i++) {
            var readId = this.config.states[state].read[readIds[i]];
            if (typeof readId.before !== 'function') {
                this.config.states[state].read[readIds[i]].before = function (device, value, callback) {
                    callback()
                };
            }
            if (typeof readId.after !== 'function') {
                this.config.states[state].read[readIds[i]].after = function (device, value) {
                };
            }
        }
        var writeIds = Object.keys(this.config.states[state].write);
        for (i = 0; i < writeIds.length; i++) {
            var writeId = this.config.states[state].write[writeIds[i]];
            if (typeof writeId.before !== 'function') {
                this.config.states[state].write[writeIds[i]].before = function (device, value, callback) {
                    callback()
                };
            }
            if (typeof writeId.after !== 'function') {
                this.config.states[state].write[writeIds[i]].after = function (device, value) {
                };
            }
        }
        log('normalized state ' + state, 'debug');
    }
    
    VirtualDevice.prototype.connectState = function (state) {
        log('connecting state ' + state, 'debug');
        var id = this.namespace + '.' + state;
    
        //subscribe to read ids
        var readIds = Object.keys(this.config.states[state].read);
        for (var i = 0; i < readIds.length; i++) {
            if (getState(readIds[i]).notExist === true) { //check if state exists
                log('cannot connect to not existing state: ' + readIds[i], 'warn');
                continue;
            }
            var readObj = this.config.states[state].read[readIds[i]];
            var trigger = readObj.trigger || {change: 'any'};
            trigger.ack = true;
            trigger.id = readIds[i];
            this.subRead(trigger, readObj, state);
            log('connected ' + readIds[i] + ' to ' + id, 'debug');
        }
    
        //subscribe to this state and write to write ids
        var writeIds = Object.keys(this.config.states[state].write);
        var trigger = {id: 'javascript.' + instance + '.' + this.namespace + '.' + state, change: 'any', ack: false};
        on(trigger, function (obj) {
            "use strict";
            log('detected change of ' + state, 'debug');
            for (var i = 0; i < writeIds.length; i++) {
                let writeObj = this.config.states[state].write[writeIds[i]];
                let val = this.convertValue(obj.state.val, writeObj.convert);
                let writeId = writeIds[i];
                log('executing function before for ' + writeId, 'debug');
                writeObj.before(this, val, function (newVal, newDelay) {
                    if (newVal !== undefined && newVal !== null) val = newVal;
                    var delay = writeObj.delay;
                    if (newDelay !== undefined && newDelay !== null) delay = newDelay;
                    log(newVal + 'writing value ' + val + ' to ' + writeId + ' with delay ' + delay, 'debug');
                    setStateDelayed(writeId, val, false, delay || 0, true, function () {
                        log('executing function after for ' + writeId, 'debug');
                        writeObj.after(this, val);
                    }.bind(this));
                }.bind(this));
            }
        }.bind(this));
        log('connected ' + state + ' to ' + JSON.stringify(writeIds), 'debug');
    }
    
    VirtualDevice.prototype.subRead = function (trigger, readObj, state) {
        var func = function (obj) {
            var val = this.convertValue(obj.state.val, readObj.convert);
    
            //@todo aggregations
    
            log('executing function before for ' + trigger.id, 'debug');
            readObj.before(this, val, function (newVal, newDelay) {
                if (newVal !== undefined && newVal !== null) val = newVal;
                if (newDelay !== undefined && newDelay !== null) writeObj.delay = newDelay;
                log('reading value ' + val + ' to ' + this.namespace + '.' + state, 'debug');
                setStateDelayed(this.namespace + '.' + state, val, true, readObj.delay || 0, true, function () {
                    log('executing function after for ' + trigger.id, 'debug');
                    readObj.after(this, val);
                }.bind(this));
            }.bind(this));
        }.bind(this);
        func({state: getState(trigger.id)});
        on(trigger, func);
    }
    
    VirtualDevice.prototype.convertValue = function (val, func) {
        if (typeof func !== 'function') {
            return val;
        }
        return func(val);
    }`[/i][/i][/i][/i][/i][/i]
    
    J 1 Antwort Letzte Antwort
    3
    • DutchmanD Offline
      DutchmanD Offline
      Dutchman
      Developer Most Active Administrators
      schrieb am zuletzt editiert von
      #2

      Hi, sieht gut aus. Bin mir nicht sicher ob ich es richtig verstehe ist die functionalitaet nicht verglaichbar wie mit de "home" adapter ?

      https://github.com/ioBroker/ioBroker.home

      1 Antwort Letzte Antwort
      0
      • Jey CeeJ Online
        Jey CeeJ Online
        Jey Cee
        Developer
        schrieb am zuletzt editiert von
        #3

        Interessant das du dieses Thema jetzt gerade aufgreifst, vorgestern hab ich mir über das Bündeln mehrere Sensoren und Aktoren Gedanken gemacht.

        Ich setze schon seit längerem Luftentfeuchter ein, dazu verwende ich einen Temp-Feuchtigkeits Sensor von HM und einen Zwischenstecker. Es hat mich schon öfter gestört das diese Kombination nicht als Gerät gelistet wird.

        Mein Vorschlag zusätzlich zu deinem Skript, es wird bei den Aufzählungen neben Raum und Funktion noch Gerät Hinzugefügt.

        Und es ist zu Überlegen ob diese Virtual Device Lösung sogar in iobroker als Zentrale Funktion eingebaut werden sollte. Ich denke es würde den Umgang mit iobroker vereinfachen.

        Gesendet von Unterwegs

        Persönlicher Support
        Spenden -> paypal.me/J3YC33

        1 Antwort Letzte Antwort
        0
        • P Offline
          P Offline
          pix
          schrieb am zuletzt editiert von
          #4

          Hallo,

          ein super Sache. Ich habe ähnliche Probleme mit dem homepilot Adapter gehabt (Invertierte Darstellung der Rollläden, 0 = offen 100 = zu). Ich habe eine zweite Datenpunkt Level_invert im Adapter angelegt.

          @Pman:

          Besonders bei der Arbeit mit History und Vis sowie der Verwaltung von Zugriffsrechten bietet es eine Menge Vorteile, weswegen ich mittlerweile alle Geräte virtualisiert habe. So geht mir nie eine History verloren, wenn ich einen Aktor austausche und ich muss auch nichts an den Views in Vis ändern. `

          Diesen Punkt finde ich sehr wichtig. Leider zu oft kommt es vor, dass ich Geräte tauschen muss und dann ALLE Skripte durchforste, wo ein Datenpunkt verwendet wurde. Habe auch schon überlegt, einfach global alle Geräte in Stringvariable zu schreiben.

          const idGERAET   =  "hm-rpc.0.1234567.STATE";
          

          Dann muss ich aber u.a. die id namentlich zu sehr präzisieren, damit alle ids später zu unterscheiden sind

          const idKIZILampeSchrankRechtsSTATE   =  "hm-rpc.0.1234567.STATE";
          

          Und in History brächte das auch keinen Vorteil.

          Ich stimme Jey Cee zu und bin auch der Meinung, diese Funktion sollte im Controller verbaut sein. Es sollte so ein "Anfänger-taugliches" Verzeichnis aufgebaut werden können, das man dann in VIS verwendet. zB.

          Homematic - Lampe - Kinderzimmer - Schrank - rechts - State
          

          Gruß

          Pix

          ioBroker auf Ubuntu in Proxmox (früher Mac mini (bis OS X 10.12.6 Sierra), VIS via iOS; angeschlossen: Homematic CCU2, Homepilot 1, ConBee II, einige Wemos, Sonos, Unifi CK+Protect, Homekit, Homebridge; KEIN blockly! Github-Profil

          1 Antwort Letzte Antwort
          0
          • apollon77A Offline
            apollon77A Offline
            apollon77
            schrieb am zuletzt editiert von
            #5

            @pix:

            … dann ALLE Skripte durchforste, wo ein Datenpunkt verwendet wurde. `

            So als Mini-Off-Topic-Tipp: Adater js2fs … der legt alle Skripte als Files auf dem Rechner ab wo der Adapter läuft und überwacht dann alle Änderungen und hält so alles in beide Richtungen immer aktuell. ich nehme inzwischen nur noch das um Skripte zu editieren.

            Damit ist ein "Global File Search&Replace" mega einfach :-))

            Zum Thread an sich: Super Nummer: idealerweise sollte man versuchen das in den Wrapper-Adapter mit einzubauen, oder ?!

            Beitrag hat geholfen? Votet rechts unten im Beitrag :-) https://paypal.me/Apollon77 / https://github.com/sponsors/Apollon77

            • Debug-Log für Instanz einschalten? Admin -> Instanzen -> Expertenmodus -> Instanz aufklappen - Loglevel ändern
            • Logfiles auf Platte /opt/iobroker/log/… nutzen, Admin schneidet Zeilen ab
            1 Antwort Letzte Antwort
            0
            • P Offline
              P Offline
              Pman
              schrieb am zuletzt editiert von
              #6

              @Dutchman:

              Hi, sieht gut aus. Bin mir nicht sicher ob ich es richtig verstehe ist die functionalitaet nicht verglaichbar wie mit de "home" adapter ?

              https://github.com/ioBroker/ioBroker.home `
              So ähnlich, die Funkionalität des Adapters ging mir aber noch nicht weit genug.

              @Jey Cee:

              Ich setze schon seit längerem Luftentfeuchter ein, dazu verwende ich einen Temp-Feuchtigkeits Sensor von HM und einen Zwischenstecker. Es hat mich schon öfter gestört das diese Kombination nicht als Gerät gelistet wird. `
              Die Kombination von Geräten zu einem Logischen Gerät finde ich auch wichtig. Wie bei dem Beispiel mit Hue und vorgeschaltetem Relay ist es bei der Bedienung unerheblich, dass sich zwei Geräte dahinter verbergen. Ebenso bei Thermostat + Stellantrieb und vielen andere Kombinationen. Zusätzlich habe ich mir mit dem Skript komplett virtuelle Geräte wie Trockner, Wasch- und Spülmaschine angelegt, deren Status ich aus dem Stromverbrauch ermittle.

              @pix:

              ein super Sache. Ich habe ähnliche Probleme mit dem homepilot Adapter gehabt (Invertierte Darstellung der Rollläden, 0 = offen 100 = zu). Ich habe eine zweite Datenpunkt Level_invert im Adapter angelegt. `
              Das beobachte ich, dass solche Logiken in Adaptern umgesetzt werden. Habe ich bei Hue auch so gemacht. Ich persönlich fände eine extra Logikschicht zwischen Geräte-Adapter und anderen Adaptern aber sinnvoller. Man kann direkt im Adapter ohnehin nicht jeden Anwendungsfall berücksichtigen und bläht den Adaptercode auf. Es wäre in diesem Zusammenhang auch sicher sinnvoll Standards für z. B. Rolläden, Lampen, Thermostate usw. festzulegen, so dass Widgets in Vis ganz bestimmte Eingangswerte erwarten können. Viele Widgets versuchen ja ebenfalls gewisse Logiken umzusetzen und sich den verschiedenen Geräten anzupassen, werden aber kompliziert zu bedienen. Hier fände ich langfristig eine klare Aufgabenteilung besser, z. B. mit solchen virtuellen Geräten als Vermittler.

              @apollon77:

              Zum Thread an sich: Super Nummer: idealerweise sollte man versuchen das in den Wrapper-Adapter mit einzubauen, oder ?! `
              Ich hätte nichts dagegen, wenn jemand auf dieser Grundlage einen Adapter schreibt und eine passende GUI baut, welche ein "erklicken" der Konfiguration für ein virtualDevice erlaubt. Eine Schwierigkeit wird sein, dass Skripte als Eingabe erlaubt sein müssen (before, after, convert). Vielleicht kann man da die Sandbox, welche in javascript schon existiert, nutzen?

              1 Antwort Letzte Antwort
              0
              • AlCalzoneA Offline
                AlCalzoneA Offline
                AlCalzone
                Developer
                schrieb am zuletzt editiert von
                #7

                Coole Sache! Ich hatte ja schon mal mit einer ZWave-Bridge "begonnen", um die ZWave-Geräte Alexa-freundlicher zu machen. Dein Skript geht da einen guten Schritt weiter, gerade was das Zusammenfassen von Geräten zu einem angeht.

                Da sollte man definitiv einen Adapter draus bauen mit ner vernünftigen UI.

                Eventuell sollte man hier auch den Extension-Gedanken weiterverfolgen, der anscheinend beim Web-Adapter eingeflossen ist.

                Sprich:

                • Virtual Devices ist der Kern-Adapter, der sucht beim Start nach anderen Adaptern mit Typ "extension".

                • In diesen anderen Adaptern muss eine bestimmte Schnittstellen-Funktion existieren, die in der io-package.json benannt ist.

                • Virtual Devices ruft diese Funktion auf, um von den Extension-Adaptern die Definitionen der virtuellen Geräte zu erhalten.

                Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                1 Antwort Letzte Antwort
                0
                • P Offline
                  P Offline
                  Pman
                  schrieb am zuletzt editiert von
                  #8

                  @AlCalzone:

                  Eventuell sollte man hier auch den Extension-Gedanken weiterverfolgen, der anscheinend beim Web-Adapter eingeflossen ist.

                  Sprich:

                  • Virtual Devices ist der Kern-Adapter, der sucht beim Start nach anderen Adaptern mit Typ "extension".

                  • In diesen anderen Adaptern muss eine bestimmte Schnittstellen-Funktion existieren, die in der io-package.json benannt ist.

                  • Virtual Devices ruft diese Funktion auf, um von den Extension-Adaptern die Definitionen der virtuellen Geräte zu erhalten. `

                  Das ist eine gute Idee, solche Definitionen könnten natürlich auch schon mit Geräte-Adaptern geliefert werden. Nicht zum Zusammenfassen von Geräten, aber zum Standardisieren. Ein Geräte-Adapter wie Hue könnte so nur die tatsächlich vorhandenen Datenpunkte verwalten und zusätzlich ein virtual Device Template mitliefern, wodurch die Lampen in einen noch zu definierenden Lampen-Standard gebracht werden. Unter anderem mit level 0-100, Weißtemperatur in Kelvin usw.

                  Ich habe mir selber schon eine Funktion geschrieben, welche genau das macht, ich gebe eine Hue-Lampe (ggf. plus Schalter, s.o) an und bekomme ein passendes virtualDevice geliefert. Die Funktion erkennt automatisch, ob es sich um ein Lux, Color , extended Color oder Color temperature light handelt und ruft virtualDevice mit entsprechender Konfiguration auf.

                  Virtual HUE:

                  //ohne Relay
                  new VirtualHue('hue.0.hue_bridge.Wohnzimmer_Decke');
                  //mit Relay
                  new VirtualHue("hue.0.hue_bridge.Schlafzimmer", "zwave.0.NODE8.SWITCH_BINARY.Switch_1", "zwave.0.NODE8.ready");
                  
                  function VirtualHue(lampId, switchId, reachableId) {
                      var lampObj = getObject(lampId);
                      var config = {
                          namespace: 'Lamp.Hue',
                          name: lampId.split('.').splice(-1)[0],
                          copy: lampId,
                          states: {
                              'reachable': {
                                  common: {type: 'boolean', def: false, read: true, write: false},
                                  read: {
                                      [lampId + '.reachable']: {
                                          after: function(device, value) {
                                              if (!value) {
                                                  setStateDelayed(device.namespace + '.level', 0, true, 1500, true);
                                              }
                                          }
                                      },
                                  }                  
                              },
                              'level': {
                                  common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                  read: {
                                      [lampId + '.bri']: {
                                          convert: function(val) {
                                              return Math.floor(val*100/254);
                                          }                  
                                      }
                                  },
                                  write: {
                                      [lampId + '.bri']: {
                                          convert: function(val) {
                                              return Math.ceil(val*254/100);
                                          },  
                                          delay: 1500
                                      },
                                  }
                              },
                              'command': {
                                  common: {type: 'string', def: '', read: true, write: true},
                                  write: {
                                      [lampId + '.command']: {
                                          delay: 1500
                                      },
                                  }
                              },
                          }
                      }
                  
                      if (switchId) {
                          config.states['switch'] = {
                              common: {type: 'boolean', def: false, read: true, write: false},
                              read: {
                                  [switchId]: {
                                      after: function (device, value) {
                                          if (value === false && getState(device.namespace + '.level').val > 0) {
                                              setState(device.namespace + '.level', 0, true);
                                          }
                                      }
                                  },
                              } 
                          };
                  
                          config.states.level.read[lampId + '.bri'].before = function (device, value, callback) {
                              if (value > 0 && getState(switchId).val === false) {
                                  //ignore new value, if switch is off 
                                  return;
                              }          
                              if (value === 0 && getState(switchId).val === true) {
                                  //ignore new value if lamp is still not reachable
                                  return;
                              }
                              callback();
                          }
                  
                          config.states.level.write[lampId + '.bri'].before = function (device, value, callback) {
                              if (value > 0 && getState(switchId).val === false) {
                                  //if switch is off and value is greater 0 turn on switch and set long delay
                                  setStateDelayed(switchId, true, false, 1500, true, function(){
                                      if (value < 254) {
                                          callback(value,5000);
                                      }
                                  });
                              } else if (value <= 0){
                                  //if level is set to 0 turn off switch and set level 0
                                  setStateDelayed(switchId, false, false, 1500, true, function(){
                                      callback(0, 0);
                                  });
                              } else {
                                  callback();
                              }                        
                          }
                  
                          config.states.command.write[lampId + '.command'].before = function (device, value, callback) {
                              var parsed;
                              try{ 
                                  parsed = JSON.parse(value);
                              } catch(e) {
                                  return;
                              }
                              if (parsed.level > 0 && getState(switchId).val === false) {
                                  //if switch is off and level is greater 0 turn on switch and set long delay
                                  setStateDelayed(switchId, true, false, 1500, true, function(){
                                      callback(value, 3500);
                                  });
                              } else if (parsed.level <= 0){
                                  //if level is set to 0 turn off switch and set level 0
                                  setStateDelayed(switchId, false, false, 1500, true, function(){
                                      callback(null, 0);
                                  });
                              } else {
                                  callback();
                              }  
                          }
                      }
                  
                      if (reachableId) {
                          config.states.reachable.read =  {
                              [reachableId]: {
                                  after: function(device, value) {
                                      if (!value) {
                                          setStateDelayed(device.namespace + '.level', 0, true, 1500, true);
                                      }
                                  }
                              },
                          }
                      }
                  
                      if (lampObj.native && lampObj.native.type === 'Color temperature light' || lampObj.native.type === 'Extended color light') {
                          config.states.ct = {
                              common: {min: 2000, max: 6500, unit: 'K', step: 10},
                              read: {
                                  [lampId + '.ct']: {
                                      convert: function(val) {
                                          return Math.max(2000, Math.min(6500, Math.round(Math.pow(10,6)/val)));  
                                      }
                                  }
                              },
                              write: {
                                  [lampId + '.ct']: {
                                      convert: function(val) {
                                          return Math.max(153, Math.min(500, Math.round(Math.pow(10,6)/val))); 
                                      },
                                      delay: 1500
                                  }              
                              }
                          }
                      }
                  
                      if (lampObj.native && lampObj.native.type === 'Color light' || lampObj.native.type === 'Extended color light') {
                          config.states.xy = {
                              common: {read: true, write: true, type: "string"},
                              read: {
                                  [lampId + '.xy']: {}
                              },
                              write: {
                                  [lampId + '.xy']: {
                                      delay: 1500
                                  }
                              }
                          }    
                      }
                  
                      return new VirtualDevice(config);
                  }
                  
                  1 Antwort Letzte Antwort
                  0
                  • P Offline
                    P Offline
                    pix
                    schrieb am zuletzt editiert von
                    #9

                    @apollon77:

                    @pix:

                    … dann ALLE Skripte durchforste, wo ein Datenpunkt verwendet wurde. `

                    So als Mini-Off-Topic-Tipp: Adater js2fs … der legt alle Skripte als Files auf dem Rechner ab wo der Adapter läuft und überwacht dann alle Änderungen und hält so alles in beide Richtungen immer aktuell. ich nehme inzwischen nur noch das um Skripte zu editieren.

                    Damit ist ein "Global File Search&Replace" mega einfach :-)) `
                    Danke für Zurück-Ins-Gedächtnis-Holen! Installiert, läuft, bin begistert!

                    Pix

                    ioBroker auf Ubuntu in Proxmox (früher Mac mini (bis OS X 10.12.6 Sierra), VIS via iOS; angeschlossen: Homematic CCU2, Homepilot 1, ConBee II, einige Wemos, Sonos, Unifi CK+Protect, Homekit, Homebridge; KEIN blockly! Github-Profil

                    1 Antwort Letzte Antwort
                    0
                    • P Offline
                      P Offline
                      Pman
                      schrieb am zuletzt editiert von
                      #10

                      Hier noch ein Lampen-An-Zähler mit allOff-Funktion

                      `function VirtualCounter(name, states) {
                          "use strict";
                          var config = {
                              namespace: 'Lamp.Counter',
                              name: name,
                              states: {
                                  'on': {
                                      common: {type: 'number', min:0, def: 0, read: true, write: false, unit:'Lamps'},
                                      read: {},
                                  },
                                  'off': {
                                      common: {type: 'number', min: 0, def: 0, read: true, write: false, unit:'Lamps'},
                                  },
                                  'allOff': {
                                      common: {type: 'boolean', def: true, read: true, write: true},
                                      write: {}
                                  },
                                  'allOffSwitch': {
                                      common: {type: 'boolean', def: false, read: true, write: true},
                                      write: {}               
                                  }
                              }
                          }
                      
                          for (var i = 0; i < states.length; i++) {
                              let state = states[i];
                              config.states.on.read[state] = {
                                  before: function(device, value, callback) {
                                      if (device.counter === undefined){
                                          device.counter = {};
                                          var init = true;
                                      } 
                                      if(value > 0 && !device.counter[state]) {
                                          device.counter[state] = true;
                                          setState(device.namespace + '.off', Object.keys(device.config.states.on.read).length - Object.keys(device.counter).length, true);
                                          callback(Object.keys(device.counter).length);
                                      } else if (value <= 0 && (device.counter[state] || init)) {
                                          delete device.counter[state];
                                          setState(device.namespace + '.off', Object.keys(device.config.states.on.read).length - Object.keys(device.counter).length, true);
                                          callback(Object.keys(device.counter).length);
                                      }
                                  },
                                  after: function(device, value) {
                                      if (value) {
                                          setState(device.namespace + '.allOff', false, true);
                                          setState(device.namespace + '.allOffSwitch', true, true);
                                      }
                                      else {
                                          setState(device.namespace + '.allOff', true, true);
                                          setState(device.namespace + '.allOffSwitch', false, true);
                                      }
                                  }
                              }
                              config.states.allOff.write[state] = {
                                  before: function(device, value, callback) {
                                      if (typeof getState(state).val === 'number'){
                                          value ? callback(0) : callback(100);    
                                          return;
                                      } 
                                      callback(!value);
                                  }
                              }
                              config.states.allOffSwitch.write[state] = {
                                  before: function(device, value, callback) {
                                      if (typeof getState(state).val === 'number'){
                                          value ? callback(100) : callback(0);    
                                          return;
                                      } 
                                      callback(value);
                                  }
                              }
                          }
                      
                          return new VirtualDevice(config);
                      }`
                      
                      Verwendet wird er so:
                      `~~[code]~~new VirtualLampCounter('Wohnzimmer', [
                          'javascript.1.virtualDevice.Lamp.Dimmer.Wohnzimmer_Regal.level',
                          'javascript.1.virtualDevice.Lamp.Hue.Wohnzimmer_Konsole.level',
                          'javascript.1.virtualDevice.Lamp.Hue.Wohnzimmer_Pillar.level',
                          'javascript.1.virtualDevice.Lamp.Hue.Wohnzimmer_Sofa.level',
                      ]);[/code]`
                      Es werden Dimmer (0-100) und Schalter (false/true) unterstützt.
                      
                      Erstellt werden vier Zustände:
                      
                      - on: Anzahl der eingeschalteten Lampen
                      
                      - off: Anzahl der ausgeschalteten Lampen
                      
                      - allOff: alle Lampen ausschalten (alle einschalten ist auch möglich)
                      
                      - allOffSwitch: invertiert zu allOff, ist also true, wenn mindestens eine Lampe eingeschaltet ist und false, wenn alle ausgeschaltet sind[/i]
                      
                      1 Antwort Letzte Antwort
                      0
                      • blauholstenB Offline
                        blauholstenB Offline
                        blauholsten
                        Developer
                        schrieb am zuletzt editiert von
                        #11

                        @apollon77:

                        So als Mini-Off-Topic-Tipp: Adater js2fs … der legt alle Skripte als Files auf dem Rechner ab wo der Adapter läuft und überwacht dann alle Änderungen und hält so alles in beide Richtungen immer aktuell. ich nehme inzwischen nur noch das um Skripte zu editieren.

                        Damit ist ein "Global File Search&Replace" mega einfach :-)) `
                        Hi,

                        wollte ich gerade mal ausprobieren! Ging leider nicht,,,

                        host.iobrokerDEV	2017-09-26 20:18:41.295	error	instance system.adapter.js2fs.0 terminated with code 0 (OK)
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at /opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:668:15
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at Files.readDir (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:462:48)
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at Array.forEach ()
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at forEach (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:466:29)
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at Files.readDir (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:462:48)
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at Array.forEach ()
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at forEach (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:466:29)
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at Files.readDir (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:462:48)
                        Caught	2017-09-26 20:18:41.295	error	by controller[0]: at Array.forEach ()
                        Caught	2017-09-26 20:18:41.294	error	by controller[0]: at forEach (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:465:30)
                        Caught	2017-09-26 20:18:41.294	error	by controller[0]: TypeError: stat.isDirectory is not a function
                        js2fs.0	2017-09-26 20:18:41.281	error	at /opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:668:15
                        js2fs.0	2017-09-26 20:18:41.281	error	at Files.readDir (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:462:48)
                        js2fs.0	2017-09-26 20:18:41.281	error	at Array.forEach ()
                        js2fs.0	2017-09-26 20:18:41.281	error	at forEach (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:466:29)
                        js2fs.0	2017-09-26 20:18:41.281	error	at Files.readDir (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:462:48)
                        js2fs.0	2017-09-26 20:18:41.281	error	at Array.forEach ()
                        js2fs.0	2017-09-26 20:18:41.281	error	at forEach (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:466:29)
                        js2fs.0	2017-09-26 20:18:41.281	error	at Files.readDir (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:462:48)
                        js2fs.0	2017-09-26 20:18:41.281	error	at Array.forEach ()
                        js2fs.0	2017-09-26 20:18:41.281	error	at forEach (/opt/iobroker/node_modules/iobroker.js2fs/js2fs.js:465:30)
                        js2fs.0	2017-09-26 20:18:41.281	error	TypeError: stat.isDirectory is not a function
                        js2fs.0	2017-09-26 20:18:41.281	error	uncaught exception: stat.isDirectory is not a function
                        js2fs.0	2017-09-26 20:18:41.208	info	starting. Version 0.0.17 in /opt/iobroker/node_modules/iobroker.js2fs, node: v8.5.0
                        

                        Entwickler vom: - Viessman Adapter
                        - Alarm Adapter

                        1 Antwort Letzte Antwort
                        0
                        • P Offline
                          P Offline
                          Pman
                          schrieb am zuletzt editiert von
                          #12

                          Ein weiteres Problem stellten für mich Datenpunkte dar, welche immer nur auf TRUE gesetzt werden, nie auf FALSE. Beispiel wäre hier der Homematic Klingelsensor. Sobald jemand klingelt, wird der Datenpunkt auf TRUE gesetzt und bleibt dann TRUE. Dass jemand klingelt erkennt man überhaupt nur daran, dass er erneut auf TRUE gesetzt wird.

                          Mit diesem VirtualDevice setze ich den Wert 30 Sekunden später wieder auf False:

                          TRUE/TRUE zu TRUE/FALSE:

                          new VirtualDevice({
                              namespace: 'Sensoren.Tueren',
                              name: 'Klingel',
                              states: {
                                  'value': {
                                      common: {type: 'boolean', def: false, read: true, write: false},
                                      read: {
                                          'hm-rpc.0.MEQ123456.1.PRESS_SHORT': {
                                              before: function (device, value, callback) {
                                                  if (!device.init) { //do not set to true on init
                                                      device.init = true;
                                                      return;
                                                  }
                                                  callback();  
                                              },
                                              after: function (device, value) {
                                                  if (value) {
                                                      setStateDelayed(device.namespace + '.value', false, true, 30000, true);
                                                  }
                                              }
                                          },
                                      },
                                  },
                                  'reachable': {
                                      common: {type: 'boolean', def: false, read: true, write: false},
                                      read: {
                                          'hm-rpc.0.MEQ123456.0.UNREACH': {
                                              convert: function(val) {
                                                  return !val;
                                              },
                                          },
                                      },
                                  },
                                  'lowBat': {
                                      common: {type: 'boolean', def: false, read: true, write: false},
                                      read: {
                                          'hm-rpc.0.MEQ123456.0.LOWBAT': {},
                                      }                  
                                  },
                              }
                          });
                          
                          1 Antwort Letzte Antwort
                          -1
                          • AlCalzoneA Offline
                            AlCalzoneA Offline
                            AlCalzone
                            Developer
                            schrieb am zuletzt editiert von
                            #13

                            @Pman:

                            Dass jemand klingelt erkennt man überhaupt nur daran, dass er erneut auf TRUE gesetzt wird. `
                            state.lc (last change) wäre noch eine Möglichkeit es zu erkennen.

                            Warum `sudo` böse ist: https://forum.iobroker.net/post/17109

                            1 Antwort Letzte Antwort
                            0
                            • P Offline
                              P Offline
                              Pman
                              schrieb am zuletzt editiert von
                              #14

                              @AlCalzone:

                              @Pman:

                              Dass jemand klingelt erkennt man überhaupt nur daran, dass er erneut auf TRUE gesetzt wird. state.lc (last change) wäre noch eine Möglichkeit es zu erkennen.
                              Das stimmt, es gibt noch ein paar versteckte Möglichkeiten, allerdings finde ich es in VIS deutlich einfacher einen Wechsel von false auf true zu visualisieren. In FLOT ist es je nach Darstellung auch von Vorteil.

                              1 Antwort Letzte Antwort
                              0
                              • P Offline
                                P Offline
                                Pman
                                schrieb am zuletzt editiert von
                                #15

                                Ich habe den Lampen-Zähler erweitert zu einem Lampen-Gruppierungs-Gerät. Man kann alle enthaltenen Lampen gleichzeitig ein- oder ausschalten sowie Dimmen. Der Dimmer ist dabei "smart", also dimmt nur alle bereits eingeschaltete Lampen und orientiert sich an der Lampe, mit dem höchsten level. Beispiel: Lampe 1: 20%, Lampe 2: 40% -> Gruppenlevel: 40%. Level wird auf 80% gestellt: Lampe1 -> 40%, Lampe2 -> 80%. Das Verhältnis der Helligkeiten bleibt also gegeben. So kann ich einen Raum mit mehreren Lampen insgesamt heller bekommen und dabei die eingestellten Lichtverhältnisse bewahren.

                                `/*
                                * Level (0-100) and Switch (true/false) states supported
                                * usage: new VirtualLampGroup('Name', ['LampId1.level','LampId2.level','LampId3.on'])
                                */
                                function VirtualLampGroup(name, states) {
                                    "use strict";
                                    var config = {
                                        namespace: 'Lamp.Group',
                                        name: name,
                                        onCreate: function (device, callback) {
                                            device.levels = {};
                                            device.counter = {init: true};
                                            callback();
                                        },
                                        states: {
                                            'on': {
                                                common: {type: 'number', min: 0, def: 0, read: true, write: false, unit: 'Lamps'},
                                                read: {}
                                            },
                                            'off': {
                                                common: {type: 'number', min: 0, def: 0, read: true, write: false, unit: 'Lamps'},
                                            },
                                            'allOffSwitch': {
                                                common: {type: 'boolean', def: false, read: true, write: true},
                                                write: {}
                                            },
                                            'level': {
                                                common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                                read: {},
                                                write: {}
                                            }
                                        }
                                    };
                                
                                    for (var i = 0; i < states.length; i++) {
                                        let state = states[i];
                                        config.states.on.read[state] = {
                                            before: function (device, value, callback) {
                                                if (value > 0 && !device.counter[state]) {
                                                    device.counter[state] = true;
                                                    setState(device.namespace + '.off', Object.keys(device.config.states.on.read).length - Object.keys(device.counter).length, true);
                                                    callback(Object.keys(device.counter).length);
                                                } else if (value <= 0 && (device.counter[state] || device.counter.init)) {
                                                    delete device.counter[state];
                                                    delete device.counter.init;
                                                    setState(device.namespace + '.off', Object.keys(device.config.states.on.read).length - Object.keys(device.counter).length, true);
                                                    callback(Object.keys(device.counter).length);
                                                }
                                            },
                                            after: function (device, value) {
                                                if (value) {
                                                    setState(device.namespace + '.allOffSwitch', true, true);
                                                }
                                                else {
                                                    setState(device.namespace + '.allOffSwitch', false, true);
                                                }
                                            }
                                        };
                                        config.states.allOffSwitch.write[state] = {
                                            before: function (device, value, callback) {
                                                if (typeof getState(state).val === 'number') {
                                                    value ? callback(100) : callback(0);
                                                    return;
                                                }
                                                callback(value);
                                            }
                                        };
                                        config.states.level.read[state] = {
                                            before: function (device, value, callback) {
                                                if (typeof getState(state).val !== 'number') {
                                                    return;
                                                }
                                                device.levels[state] = value;
                                                var newLevel = Math.max.apply(null, Object.keys(device.levels).map(function (key) {
                                                    return device.levels[key];
                                                }));
                                                if (!getState(device.namespace + '.level').ack || newLevel !== getState(device.namespace + '.level').val) {
                                                    callback(newLevel);
                                                }
                                            }
                                        };
                                        config.states.level.write[state] = {
                                            before: function (device, value, callback) {
                                                if (typeof getState(state).val !== 'number') {
                                                    return;
                                                }
                                                var newLevel;
                                                if (Math.max.apply(null, Object.keys(device.levels).map(function (key) {
                                                        return device.levels[key];
                                                    })) === 0) {
                                                    newLevel = value;
                                                } else {
                                                    newLevel = Math.ceil(getState(state).val * value / Math.max.apply(null, Object.keys(device.levels).map(function (key) {
                                                        return device.levels[key];
                                                    })));
                                                }
                                                if (newLevel !== getState(state).val) {
                                                    callback(newLevel);
                                                }
                                            }
                                        }
                                    }
                                
                                    return new VirtualDevice(config);
                                }`[/i]
                                
                                1 Antwort Letzte Antwort
                                0
                                • M Offline
                                  M Offline
                                  maniac
                                  schrieb am zuletzt editiert von
                                  #16

                                  Hi.

                                  Ich wollte das Script auf meine Bedürfnisse anpassen und habe dies so gemacht.

                                  new VirtualDevice({
                                      namespace: 'Lampen',
                                      name: 'Esszimmer', //das Gerät wird unter javascript.0.virtualDevice.Hue.Wohnzimmer erstellt
                                      states: {
                                          //welche States sollen erstellt werden?
                                          'level': {
                                              //State Konfiguration
                                              common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                              read: {
                                                  //von welchen States sollen Werte eingelesen werden?
                                                  'tradfri.0.L-65539.lightbulb.brightness': {
                                                      convert: function (val) { //wert soll konvertiert werden
                                                          return Math.floor(val * 100 / 254);
                                                      }
                                                  },
                                                  'tradfri.0.L-65540.lightbulb.brightness': {
                                                      convert: function (val) { //wert soll konvertiert werden
                                                          return Math.floor(val * 100 / 254);
                                                      }
                                                  },
                                              },
                                              write: {
                                                  //in welche States sollen Werte geschrieben werden?
                                                  'tradfri.0.L-65539.lightbulb.brightness': {
                                                      convert: function (val) { //wert soll konvertiert werden
                                                          return Math.ceil(val * 254 / 100);
                                                      },
                                  
                                                  },
                                                  'tradfri.0.L-65540.lightbulb.brightness': {
                                                      convert: function (val) { //wert soll konvertiert werden
                                                          return Math.ceil(val * 254 / 100);
                                                      },
                                  
                                                      delay: 1500 // schreibe Werte erst nach 1,5 Sekunden in den Adapter (Puffer)
                                                  },
                                              }
                                          },
                                      }
                                  });
                                  

                                  Allerdings bekomme ich das Script nicht zum laufen, auch werden keine Datenpunkte angelegt.

                                  Auch wenn ich das Script von oben 1zu1 übernehme bekomme ich diesen Fehler.

                                  ! javascript.0 2017-10-22 10:08:05.628 error at ContextifyScript.Script.runInContext (vm.js:35:29)
                                  ! javascript.0 2017-10-22 10:08:05.627 error at script.js.Licht.Virtual.Esszimmer_Lampen:1:5
                                  ! javascript.0 2017-10-22 10:08:05.626 error ReferenceError: VirtualDevice is not defined
                                  ! javascript.0 2017-10-22 10:08:05.626 error ^
                                  ! javascript.0 2017-10-22 10:08:05.625 error new VirtualDevice({
                                  ! javascript.0 2017-10-22 10:08:05.624 error script.js.Licht.Virtual.Esszimmer_Lampen: script.js.Licht.Virtual.Esszimmer_Lampen:1
                                  ! javascript.0 2017-10-22 10:08:05.622 info Start javascript script.js.Licht.Virtual.Esszimmer_Lampen
                                  ! javascript.0 2017-10-22 10:08:03.850 info Stop script script.js.Licht.Virtual.Esszimmer_Lampen

                                  Gruß Frank…

                                  1 Antwort Letzte Antwort
                                  0
                                  • M Offline
                                    M Offline
                                    maniac
                                    schrieb am zuletzt editiert von
                                    #17

                                    Habs gefunden. War wohl noch ein Windows Zeilenumbruch drin. ^ :evil:

                                    Es wird jetzt ein Gerät "Lampen" angelegt. Aber wie nutze ich das jetzt? Datenpunkte sind nicht vorhanden.

                                    Edit: Jetzt geht es doch wieder nicht, nachdem ich das Script nochmal gestartet habe. Und auch das Gerät Lampen ist wieder weg.

                                    Verstehe ich nicht.

                                    Gruß Frank…

                                    1 Antwort Letzte Antwort
                                    0
                                    • ? Offline
                                      ? Offline
                                      Ein ehemaliger Benutzer
                                      schrieb am zuletzt editiert von
                                      #18

                                      Das Skript funktioniert sehr gut.

                                      Ich nutze hauptsächlich zwave Devices von Fibaro.

                                      Diese steuer ich nicht direkt, sondern habe alle wichtigen States als virtuelle States innerhalb javascript.0 angelegt und synchronisiere diese.

                                      Insbesondere für History und Vis finde ich die virtuelle Eben sehr hilfreich.

                                      Daraus nun noch virtuelle Devices zu machen ist eine sehr schöne Erweiterung ist schön übersichtlich.

                                      Wie binde ich das Skript von @pman am besten ein?

                                      Als globales Skript?

                                      Wie strukturiert ihr eure Skripte um alle Devices sinnvoll zu verwalten im javascript?

                                      Viele kleine Skripte z.B. je Raum, oder lieber alles in ein Skript?

                                      1 Antwort Letzte Antwort
                                      0
                                      • P Offline
                                        P Offline
                                        Pman
                                        schrieb am zuletzt editiert von
                                        #19

                                        @maniac:

                                        Allerdings bekomme ich das Script nicht zum laufen, auch werden keine Datenpunkte angelegt.

                                        Auch wenn ich das Script von oben 1zu1 übernehme bekomme ich diesen Fehler. `
                                        Ich habe dein Skript bei mir eingefügt und es funktioniert.

                                        error	ReferenceError: VirtualDevice is not defined
                                        

                                        Du hast glaube ich das Hauptskript aus dem ersten Post (ganz unten) nicht geladen. Du müsstest das entweder noch vor dieses Skript kopieren oder unter "global" einfügen.

                                        @nisio:

                                        Wie binde ich das Skript von @pman am besten ein?

                                        Als globales Skript?

                                        Wie strukturiert ihr eure Skripte um alle Devices sinnvoll zu verwalten im javascript?

                                        Viele kleine Skripte z.B. je Raum, oder lieber alles in ein Skript? `

                                        Hallo, freut mich, dass du das Skript nützlich findest.

                                        Ich habe das Skript selber in Global gepackt, was übrigens einfach bedeutet, dass es intern in jedes andere Skript eingefügt wird.

                                        Dann habe ich bei den Skripten einen Ordner erstellt "VirtualDevices" und darin dann pro Gewerk ein Skript, also z. B. für Heizung, Lampen, Weiße Ware, usw. .

                                        Wenn es sich lohnt habe ich noch eine kleine Hilfsfunktion gebaut, die aus wenigen Datenpunkten das ganze VirtualDevice erstellt. Das lohnt eigentlich immer, wenn man mehrere gleiche Geräte aus gleichen Datenpunkten erstellt. Bei Heizung ist das bei mir der Fall, da ich 7 Homematic Thermostate und Stellantriebe je gemeinsam in ein VirtualDevice gepackt habe. Das Heizungs-VirtualDevice macht übrigens das Modusumschalten bei Homematic Thermostaten sehr viel einfacher. So sieht dann bei mir das Skript Heizung aus:

                                        function virtualHomematicThermostat(name, thermostatId, valveId) {
                                            var config = {
                                                namespace: 'Heizung',
                                                name: name,
                                                states: {
                                                    'reachable': {
                                                        common: {type: 'boolean', def: false, read: true, write: false},
                                                        read: {
                                                            [thermostatId + '.0.UNREACH']: {
                                                                convert: function(value) {
                                                                    return !value;
                                                                }
                                                            },
                                                        }                  
                                                    },
                                                    'reachableValve': {
                                                        common: {type: 'boolean', def: false, read: true, write: false},
                                                        read: {
                                                            [valveId + '.0.UNREACH']: {
                                                                convert: function(value) {
                                                                    return !value;
                                                                }
                                                            },
                                                        }                  
                                                    },
                                                    'lowBat': {
                                                        common: {type: 'boolean', def: false, read: true, write: false},
                                                        read: {
                                                            [thermostatId + '.0.LOWBAT']: {},
                                                        }                  
                                                    },
                                                    'lowBatValve': {
                                                        common: {type: 'boolean', def: false, read: true, write: false},
                                                        read: {
                                                            [valveId + '.0.LOWBAT']: {},
                                                        }                  
                                                    },
                                                    'temperature': {
                                                        common: {
                                                            "type": "number",
                                                            "unit": "°C",
                                                            "def": 0,
                                                            "min": -10,
                                                            "max": 50,
                                                            "read": true,
                                                            "write": false
                                                        },
                                                        read: {
                                                            [thermostatId + '.1.TEMPERATURE']: {},
                                                        }                  
                                                    },
                                                    'humidity': {
                                                        common: {
                                                            "type": "number",
                                                            "unit": "%",
                                                            "def": 0,
                                                            "min": 0,
                                                            "max": 99,
                                                            "read": true,
                                                            "write": false
                                                        },
                                                        read: {
                                                            [thermostatId + '.1.HUMIDITY']: {},
                                                        }                  
                                                    },
                                                    'temperatureSet': {
                                                        common: {
                                                            "type": "number",
                                                            "unit": "°C",
                                                            "def": 20,
                                                            "min": 4.5,
                                                            "max": 30.5,
                                                            "read": true,
                                                            "write": true
                                                        },
                                                        read: {
                                                            [thermostatId + '.2.SET_TEMPERATURE']: {},
                                                        },
                                                        write: {
                                                            [thermostatId + '.2.SET_TEMPERATURE']: {
                                                                delay: 1500
                                                            },
                                                        }
                                                    },
                                                    'valve': {
                                                        common: {
                                                            "def": 0,
                                                            "type": "number",
                                                            "read": true,
                                                            "write": false,
                                                            "min": 0,
                                                            "max": 99,
                                                            "unit": "%",
                                                        },
                                                        read: {
                                                            [valveId + '.4.VALVE_STATE']: {},
                                                        }
                                                    },
                                                    'mode': {
                                                        common: {
                                                            "def": 0,
                                                            "type": "number",
                                                            "read": true,
                                                            "write": true,
                                                            "min": 0,
                                                            "max": 1,
                                                            "states": {
                                                              "0": "AUTO-MODE",
                                                              "1": "MANU-MODE",
                                                            },
                                                        },
                                                        read: {
                                                            [thermostatId + '.2.CONTROL_MODE']: {},
                                                        },
                                                        write: {
                                                            [thermostatId + '.2.AUTO_MODE']: {
                                                                delay: 1500,
                                                                before: function (device, value, callback) {
                                                                    if (value !== 0){
                                                                        clearStateDelayed(thermostatId + '.2.AUTO_MODE');
                                                                        return;
                                                                    } 
                                                                    callback(true);
                                                                }
                                                            },
                                                            [thermostatId + '.2.MANU_MODE']: {
                                                                delay: 1500,
                                                                before: function (device, value, callback) {
                                                                    if (value !== 1){
                                                                        clearStateDelayed(thermostatId + '.2.MANU_MODE');
                                                                        return;
                                                                    } 
                                                                    callback(getState(thermostatId + '.2.SET_TEMPERATURE').val);
                                                                }
                                                            },
                                                        }
                                                    },
                                                }
                                            }
                                        
                                            if (!valveId) {
                                                delete config.states.lowBatValve;
                                                delete config.states.reachableValve;
                                                delete config.states.valve;
                                            }
                                        
                                            return new VirtualDevice(config);
                                        }
                                        
                                        //Wohnzimmer
                                        new virtualHomematicThermostat('Wohnzimmer', 'hm-rpc.0.LEQ1...', 'hm-rpc.0.LEQ2...');
                                        
                                        //HWR
                                        new virtualHomematicThermostat('HWR', 'hm-rpc.0.MEQ1...', '');
                                        
                                        //Badezimmer
                                        new virtualHomematicThermostat('Badezimmer', 'hm-rpc.0.LEQ3...', 'hm-rpc.0.LEQ4...');
                                        
                                        //WC
                                        new virtualHomematicThermostat('WC', 'hm-rpc.0.LEQ5...', 'hm-rpc.0.MEQ6...');
                                        
                                        //Arbeitszimmer
                                        new virtualHomematicThermostat('Arbeitszimmer', 'hm-rpc.0.LEQ7...', 'hm-rpc.0.MEQ8...');
                                        
                                        //Gaestezimmer
                                        new virtualHomematicThermostat('Gaestezimmer', 'hm-rpc.0.LEQ9...', 'hm-rpc.0.MEQ10...');
                                        
                                        //Schlafzimmer
                                        new virtualHomematicThermostat('Schlafzimmer', 'hm-rpc.0.LEQ11...', 'hm-rpc.0.ME12...');
                                        
                                        1 Antwort Letzte Antwort
                                        0
                                        • M Offline
                                          M Offline
                                          maniac
                                          schrieb am zuletzt editiert von
                                          #20

                                          @Pman:

                                          @maniac:

                                          Allerdings bekomme ich das Script nicht zum laufen, auch werden keine Datenpunkte angelegt.

                                          Auch wenn ich das Script von oben 1zu1 übernehme bekomme ich diesen Fehler. `
                                          Ich habe dein Skript bei mir eingefügt und es funktioniert.

                                          error	ReferenceError: VirtualDevice is not defined
                                          

                                          Du hast glaube ich das Hauptskript aus dem ersten Post (ganz unten) nicht geladen. Du müsstest das entweder noch vor dieses Skript kopieren oder unter "global" einfügen. `

                                          Danke, das war es.

                                          Gruß Frank…

                                          1 Antwort Letzte Antwort
                                          0
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          740

                                          Online

                                          32.4k

                                          Benutzer

                                          81.4k

                                          Themen

                                          1.3m

                                          Beiträge
                                          Community
                                          Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen | Einwilligungseinstellungen
                                          ioBroker Community 2014-2025
                                          logo
                                          • Anmelden

                                          • Du hast noch kein Konto? Registrieren

                                          • Anmelden oder registrieren, um zu suchen
                                          • Erster Beitrag
                                            Letzter Beitrag
                                          0
                                          • Home
                                          • Aktuell
                                          • Tags
                                          • Ungelesen 0
                                          • Kategorien
                                          • Unreplied
                                          • Beliebt
                                          • GitHub
                                          • Docu
                                          • Hilfe