Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Skripten / Logik
    4. Virtual Devices

    NEWS

    • 15. 05. Wartungsarbeiten am ioBroker Forum

    • Monatsrückblick - April 2025

    • Minor js-controller 7.0.7 Update in latest repo

    Virtual Devices

    This topic has been deleted. Only users with topic management privileges can see it.
    • apollon77
      apollon77 last edited by

      @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 ?!

      1 Reply Last reply Reply Quote 0
      • P
        Pman last edited by

        @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 Reply Last reply Reply Quote 0
        • AlCalzone
          AlCalzone Developer last edited by

          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.

          1 Reply Last reply Reply Quote 0
          • P
            Pman last edited by

            @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 Reply Last reply Reply Quote 0
            • P
              pix last edited by

              @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

              1 Reply Last reply Reply Quote 0
              • P
                Pman last edited by

                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 Reply Last reply Reply Quote 0
                • blauholsten
                  blauholsten Developer last edited by

                  @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
                  
                  1 Reply Last reply Reply Quote 0
                  • P
                    Pman last edited by

                    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 Reply Last reply Reply Quote -1
                    • AlCalzone
                      AlCalzone Developer last edited by

                      @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.

                      1 Reply Last reply Reply Quote 0
                      • P
                        Pman last edited by

                        @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 Reply Last reply Reply Quote 0
                        • P
                          Pman last edited by

                          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 Reply Last reply Reply Quote 0
                          • M
                            maniac last edited by

                            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

                            1 Reply Last reply Reply Quote 0
                            • M
                              maniac last edited by

                              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.

                              1 Reply Last reply Reply Quote 0
                              • ?
                                A Former User last edited by

                                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 Reply Last reply Reply Quote 0
                                • P
                                  Pman last edited by

                                  @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 Reply Last reply Reply Quote 0
                                  • M
                                    maniac last edited by

                                    @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.

                                    1 Reply Last reply Reply Quote 0
                                    • ?
                                      A Former User last edited by

                                      Ich bekomme bei numerischen States immer eine Warning im Log: Wrong type of …: "string". Please fix, while deprecated and will not work in next versions.

                                      Ich vermute es muss ein parseFloat() auf den ausgelesenen Value angewendet werden.

                                      Wo baue ich das am besten ein in das Skript, damit es automatisch für States vom Type number angewendet wird?

                                      1 Reply Last reply Reply Quote 0
                                      • P
                                        Pman last edited by

                                        Also du möchtest einen numerischen State im VirtualDevice und hast als Eingang einen String? Oder genau umgekehrt?

                                        Zur Konvertierung von Werten kann man optional eine Funktion bei jedem read- und write-State angeben. Ich habe im Beispiel beide Fälle abgedeckt.

                                        Bei einer einfachen Typenkonvertierung muss es natürlich nicht bleiben, du kannst mit dem Eingangswert alles mögliche anstellen. Ich habe bei mir zum Beispiel Waschmaschine und andere Geräte an einem Strommessgerät und konvertiere Watt in den Zustand der Maschine (aus, standby, waschen, fertig). Es können auch noch zwei weitere Funktion before und after genutzt werden, um verschiedene Aktionen vor bzw. nach dem schreiben der virtuellen States durchzuführen. Bei der Waschmaschine schicke ich mir z. B. mit dem Pushbullet-Adapter eine Nachricht aufs Handy, wenn der Zustand fertig erreicht wird.

                                        Beispiel Konvertierung

                                        new VirtualDevice({
                                            namespace: 'Beispiel',
                                            name: 'String2Number',
                                            states: {
                                                'string': {
                                                    //dieser State ist vom Typ String
                                                    common: {type: 'string', def: '', read: true, write: true},
                                                    read: {
                                                        //eingehende Werte werden zu String konvertiert
                                                        'state.number': {
                                                            convert: function (val) {
                                                                return val.toString();
                                                            }
                                                        },
                                                    },
                                                    //ausgehende Werte werden zu Number konvertiert
                                                    write: {
                                                        'state.number': {
                                                            convert: function (val) {
                                                                return parseFloat(val);
                                                            }
                                                        },
                                                    }
                                                },
                                                'number': {
                                                    //dieser State ist vom Typ Number
                                                    common: {type: 'number', def: 0, read: true, write: true},
                                                    read: {
                                                        'state.string': {
                                                            //eingehende Werte werden zu Number konvertiert
                                                            convert: function (val) {
                                                                return parseFloat(val);
                                                            }
                                                        },
                                                    },
                                                    write: {
                                                        'state.string': {
                                                            //ausgehende Werte werden zu String konvertiert
                                                            convert: function (val) {
                                                                return val.toString();
                                                            }
                                                        },
                                                    }
                                                }
                                            }
                                        });
                                        
                                        1 Reply Last reply Reply Quote 0
                                        • ?
                                          A Former User last edited by

                                          Ich habe einen numerischen Wert den ich auf einen virtuellen numerischen State schreibe.

                                          Scheinbar liefert getState(…).val auch bei numerischen Werten einen string.

                                          Die Lösung mit der convert Angabe läuft bei mir schon und funktioniert auch. Ich will das nur nicht bei allen devices einbauen. Daher suche ich eine zentrale Lösung.

                                          Hier ist das Problem auch beschrieben: http://forum.iobroker.net/viewtopic.php?t=6819

                                          1 Reply Last reply Reply Quote 0
                                          • P
                                            Pman last edited by

                                            Dort ging es darum, dass bei Änderungen direkt über Admin der falsche Typ genutzt wurde, was aber auch mittlerweile gefixt sein sollte.

                                            Ich kann das verhalten zumindest bei mir so nicht feststellen, vielleicht lohnt eine Suche nach dem Verursacher des falschen Datentyps? Oder du musst noch js-controller oder Admin updaten?

                                            Dieser Codeschnipsel

                                            log (typeof getState("hm-rpc.0.LEQ0535278.2.POWER").val)
                                            

                                            liefert:
                                            ` > 12:55:48.740 [info] javascript.1 Start javascript script.js.VirtualDevices.test

                                            12:55:48.741 [info] javascript.1 script.js.VirtualDevices.test: number `
                                            Also geht bei getState nicht der Typ number verloren.

                                            Ich kann mir nur vorstellen, dass dein Eingangsstate entweder nicht vom Typ number ist (aber trotzdem eine Zahl, aber eben als String, enthält) oder irgendein Skript/Adapter in den korrekten number State trotzdem einen String schreibt.

                                            Als temporären Fix könntest du das Skript so ändern, die mittlere Zeile ist neu, die anderen beiden findest du im Skript (ungetestet!):

                                            if (newDelay !== undefined && newDelay !== null) writeObj.delay = newDelay;
                                            if (this.config.states[state].common.type === 'number') newVal = parseFloat(newVal);
                                            log(newVal + 'writing value ' + val + ' to ' + writeId + ' with delay ' + writeObj.delay, 'debug');
                                            
                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post

                                            Support us

                                            ioBroker
                                            Community Adapters
                                            Donate

                                            480
                                            Online

                                            31.6k
                                            Users

                                            79.5k
                                            Topics

                                            1.3m
                                            Posts

                                            19
                                            45
                                            14903
                                            Loading More Posts
                                            • Oldest to Newest
                                            • Newest to Oldest
                                            • Most Votes
                                            Reply
                                            • Reply as topic
                                            Log in to reply
                                            Community
                                            Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
                                            The ioBroker Community 2014-2023
                                            logo