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

    • Neuer Blog: Fotos und Eindrücke aus Solingen

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    Virtual Devices

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

      Moin Moin,

      ich habe den Thread bereits mehrmals gelesen, aber so ganz schlau bin ich doch nich draus geworden.

      Das Schema habe ich soweit verstanden und ich wollte das nun auch bei mir einsetzen, da ich viele Systeme nutze.

      Homematic, Hue Color, XIAOMI, Milight, Harmony, sonoff, wm-bus.

      Nun, es stehen hier mehrere Script im Thread, welche davon sollten jetzt genutzt werden, damit ich meine States einheitlich darstellen kann?

      Die States würde ich auch gerne für mobile.ui sowie cloud nutzen. Hat den schöne, dass nur die Auftauchen die ich wirklich brauche.

      Wer mal ein Thermostat von Homematic in mobile.ui drin hatte, der weiß was da alles so aufpoppt. 🙂

      Gehört das auch unter Global? Oder steht dieses als einziges Unter global?

      /*
      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)
          .................
      
      
      1 Reply Last reply Reply Quote 0
      • modmax
        modmax last edited by

        Das Sript, was Du meinst, gehört unter "global",

        da damit die Erstellung der virtuellen Geräte aufgerufen werden.

        Die anderen Skript stehen bei mir z.B. unter common.virtualDevices.

        Die Skripte dort regeln dann, wie die virtuelle Geräte erzeugt werden.

        Diese sind meist selbst zu schreiben um z.B. Anpassungen an sein

        eigenes System vorzunehmen; oder zu erweitern etc. pp.

        Die vorgestellen Skripte sind daher nun als Leitfaden zu verstehen,

        was dann möglich ist.

        1 Reply Last reply Reply Quote 0
        • H
          hsgev last edited by

          Müssen die Skripte, die die Erstellung der Virtual Devices vornehmen nach der Erstellung weiterlaufen oder kann man die dann stoppen bzw. entfernen?

          1 Reply Last reply Reply Quote 0
          • eumats
            eumats last edited by

            @hsgev:

            Müssen die Skripte, die die Erstellung der Virtual Devices vornehmen nach der Erstellung weiterlaufen oder kann man die dann stoppen bzw. entfernen? `

            weiterlaufen

            1 Reply Last reply Reply Quote 0
            • R
              roughestboy last edited by

              Auch wenn der letzte Beitrag fast schon 1 Jahr her ist...

              Ich habe die Sache mit dem VirtualDevice nachvollzogen und habe das auch grundsätzlich hinbekommen. Ich möchte 2 Tint E14 Lampen gruppieren und steuern, die können Farbe und Warmweiß bis Kaltweiß. Die Farbsteuerung erfolgt mit Hexadezimalangabe, z.B. #FF0000 für rot.
              Mit Scriupten kenne ich nicht quasi gar nicht aus.
              Wie würde man in dem 'State' Abschnitt des Skripts

                  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)
              
                              },
              
                          }
              
                      },
              
                  }
              

              die entsprechende Variable in 'common' definieren und wie würde man die Variable (mit return) zurückschreiben ?

              1 Reply Last reply Reply Quote 0
              • Scrounger
                Scrounger Developer last edited by

                Entwickle aktuell einen Adapter der einige der Funktionalitäten dieses Skriptes abbilden soll.
                Wer interesse hat: [Neuer Adapter] LinkedDevices

                1 Reply Last reply Reply Quote 0
                • H
                  homecineplexx last edited by homecineplexx

                  gibts das script auch in GIT, nur zwecks Versionierung?

                  1 Reply Last reply Reply Quote 0
                  • R
                    roughestboy last edited by

                    Nachtrag. Ich habe ein Skript für 2 Tint-Birnen erstellt, damit lassen sich beide als eine Gruppe ansteuern, sowie Farbe, Farbtemperatur und Helligkeit:

                    new VirtualDevice({
                    
                        namespace: 'WC',
                    
                        name: 'Deckenlicht', //das Gerät wird unter javascript.0.virtualDevice.'namespace'.'name' erstellt
                    
                        states: {
                    
                            //welche States sollen erstellt werden?
                    
                            'level.colorrgb': {
                    
                                //State Konfiguration
                               
                                common: {type: 'string', def: 000000, read: true, write: true},
                               
                                read: {
                     
                                    //von welchen States sollen Werte eingelesen werden?
                                    //Mehrere Geräte müssen innerhalb der Klammern der read-Funktion stehen.
                                    
                                    'zigbee.0.000d6ffffe0200fe.color': { function (val) { return (val) ; } },
                                    'zigbee.0.d0cf5efffefefcaf.color': { function (val) { return (val) ; } },
                    
                                },
                    
                                write: {
                    
                                    //in welche States sollen Werte geschrieben werden?
                                    //Mehrere Geräte müssen innerhalb der Klammern der write-Funktion stehen.
                                    //delay: xxx - schreibe Werte erst nach xxx msec. in den Adapter (Puffer)
                    
                                    'zigbee.0.000d6ffffe0200fe.color': { function (val) { return (val); }, delay: 500 },
                                    'zigbee.0.d0cf5efffefefcaf.color': { function (val) { return (val); }, delay: 500 },
                    
                                }
                    
                            },
                    
                            'level.colortemperature': {
                    
                                //State Konfiguration
                               
                                common: {type: 'number', def: 0, min: 155, max: 555, read: true, write: true},
                    
                                read: {
                     
                                    //von welchen States sollen Werte eingelesen werden?
                                    //Mehrere Geräte müssen innerhalb der Klammern der read-Funktion stehen.
                                    
                                    'zigbee.0.000d6ffffe0200fe.colortemp': { function (val) { return (val) ; } },
                                    'zigbee.0.d0cf5efffefefcaf.colortemp': { function (val) { return (val) ; } },
                    
                                },
                    
                                write: {
                    
                                    //in welche States sollen Werte geschrieben werden?
                                    //Mehrere Geräte müssen innerhalb der Klammern der write-Funktion stehen.
                                    //delay: xxx - schreibe Werte erst nach xxx msec. in den Adapter (Puffer)
                    
                                    'zigbee.0.000d6ffffe0200fe.colortemp': { function (val) { return (val); }, delay: 500 },
                                    'zigbee.0.d0cf5efffefefcaf.colortemp': { function (val) { return (val); }, delay: 500 },
                    
                                }
                    
                            },
                    
                            'level.dimmer': {
                    
                                //State Konfiguration
                               
                                common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true},
                    
                                read: {
                     
                                    //von welchen States sollen Werte eingelesen werden?
                                    //Mehrere Geräte müssen innerhalb der Klammern der read-Funktion stehen.
                                    
                                    'zigbee.0.000d6ffffe0200fe.brightness': { function (val) { return (val) ; } },
                                    'zigbee.0.d0cf5efffefefcaf.brightness': { function (val) { return (val) ; } },
                    
                                },
                    
                                write: {
                    
                                    //in welche States sollen Werte geschrieben werden?
                                    //Mehrere Geräte müssen innerhalb der Klammern der write-Funktion stehen.
                                    //delay: xxx - schreibe Werte erst nach xxx msec. in den Adapter (Puffer)
                    
                                    'zigbee.0.000d6ffffe0200fe.brightness': { function (val) { return (val); }, delay: 500 },
                                    'zigbee.0.d0cf5efffefefcaf.brightness': { function (val) { return (val); }, delay: 500 },
                    
                                }
                    
                            },
                    
                        }
                    
                    });
                    
                    1 Reply Last reply Reply Quote 0
                    • R
                      R1Snake last edited by

                      Hi ich hab das Gefühl ich bin zu blöd dafür.

                      Hab viele Codes ausporbiert jedoch tut sich bei mir einfach nichts.

                      20a60c75-7fda-4df8-a110-f59d85405cc8-image.png ```

                      Ich hab diesen Code ohne Fehler bei mir als Javascript Code ausgeführt und in meinen Objekten tut sich einfach gar nichts

                      de0eae46-3aa7-4330-87fe-d5f960f44a95-image.png

                      Was mache ich falsch?

                      1 Reply Last reply Reply Quote 0
                      • Z
                        Zerberus last edited by Zerberus

                        hi ich habe den Code mal nach TypeScript portiert, macht es einfacher diesen zu verwenden:

                        https://gist.github.com/many20/3a5b047bd221ec107153c0231b575ca3

                        //https://forum.iobroker.net/topic/7751/virtual-devices/2
                        /*
                        VirtualDevice v0.3
                        {1}
                        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
                                        },
                                        ...
                                    },
                                },
                                ...
                            }
                        }
                        */
                        
                        /*
                        
                        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)
                                        },
                                    }
                                },
                            }
                        });
                        
                        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();
                                                }
                                            }
                                        },
                                    }
                                },
                            }
                        });
                        
                        */
                        
                        interface NumberCommon {
                            type: 'number';
                            def: number;
                            min: number;
                            max: number;
                            read: boolean;
                            write: boolean;
                            unit: '%' | string;
                        }
                        
                        //https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/objectsschema.md
                        interface Common {
                            /**
                             *  (optional - (default is mixed==any type) (possible values: number, string, boolean, array, object, mixed, file). As exception the objects with type meta could have common.type=meta.user or meta.folder
                             */
                            type?: 'number' | 'string' | 'boolean' | 'array' | 'object' | 'mixed' | 'file';
                            /**
                             * (optional)
                             */
                            min?: number;
                            /**
                             * (optional)
                             */
                            max?: number;
                            /**
                             * (optional) - increase/decrease interval. E.g. 0.5 for thermostat
                             */
                            step?: number;
                            /**
                             * (optional) - unit of the value E.g. C°
                             */
                            unit?: string;
                            /**
                             * (optional - the default value)
                             */
                            def?: any;
                            /**
                             * (optional - if common.def is set this value is used as ack flag, js-controller 2.0.0+)
                             */
                            defAck?: any;
                            /**
                             * (optional, string or object) - description, object for multilingual description
                             */
                            desc?: string | object;
                            /**
                             * (optional, string or object) - name of the device
                             */
                            name?: string;
                            /**
                             * (boolean, mandatory) - true if state is readable
                             */
                            read: boolean;
                            /**
                             * (boolean, mandatory) - true if state is writable
                             */
                            write: boolean;
                            /**
                             * (string, mandatory) - role of the state (used in user interfaces to indicate which widget to choose, see below)
                             * https://github.com/ioBroker/ioBroker.docs/blob/master/docs/en/dev/stateroles.md
                             */
                            role: 'state' | 'text' | 'button' | string;
                            /**
                             * (optional) attribute of type number with object of possible states {'value': 'valueName', 'value2': 'valueName2', 0: 'OFF', 1: 'ON'}
                             */
                            states?: Record<string, string>;
                            /**
                             * (string, optional) - if this state has helper state WORKING. Here must be written the full name or just the last part if the first parts are the same with actual. Used for HM.LEVEL and normally has value "WORKING"
                             */
                            workingID?: string;
                            /**
                             * (optional) - the structure with custom settings for specific adapters. Like {"influxdb.0": {"enabled": true, "alias": "name"}}. enabled attribute is required and if it is not true, the whole attribute will be deleted.
                             */
                            custom?: any;
                        }
                        
                        interface VirtualDeviceConfigStateRead {
                            trigger?: { ack: boolean; change: "eq" | "ne" | "gt" | "ge" | "lt" | "le" | "any" };
                            convert?: (val: iobJS.StateValue) => iobJS.StateValue;
                            before?: (device: VirtualDevice, value: iobJS.StateValue, triggerId: string, callback: (newVal?: iobJS.StateValue, newDelay?: number) => void) => void;
                            delay?: number;
                            after?: (device: VirtualDevice, value: iobJS.StateValue, triggerId: string) => void;
                            //validFor?: number;
                        }
                        
                        interface VirtualDeviceConfigStateWrite {
                            convert?: (val: iobJS.StateValue) => iobJS.StateValue;
                            before?: (device: VirtualDevice, value: iobJS.StateValue, triggerId: string, callback: (newVal?: iobJS.StateValue, newDelay?: number) => void) => void;
                            delay?: number;
                            after?: (device: VirtualDevice, value: iobJS.StateValue, triggerId: string) => void;
                        }
                        
                        interface VirtualDeviceConfigState {
                            common?: Partial<Common>;
                            native?: Record<string, any>;
                            copy?: string;
                            read?:  Record<string, VirtualDeviceConfigStateRead>; //stateId
                            readLogic?: 'last' | 'max' | 'min' | 'average';
                            write?: Record<string, VirtualDeviceConfigStateWrite>; //stateId
                        }
                        
                        interface VirtualDeviceConfig {
                            namespace: string;
                            name: string;
                            common?: Partial<Common>;
                            native?: Record<string, any>;
                            copy?: string;
                            onCreate?: (device, callback) => void;
                            states: Record<string, VirtualDeviceConfigState>; //stateId will be namespace.name.stateA
                        }
                        
                        export declare interface VirtualDevice {
                            config: VirtualDeviceConfig;
                            namespace: string;
                            name: string;
                            createDevice: (callback: () => void) => void;
                            createStates: (callback: () => void) => void;
                            normalizeState: (state: string) => void;
                            connectState: (state: string) => void;
                            subRead: (trigger: iobJS.SubscribeOptions, readObj: VirtualDeviceConfigStateRead, state: string) => void;
                            convertValue: (val: iobJS.StateValue, func?: (value: iobJS.StateValue) => iobJS.StateValue) => iobJS.StateValue;
                        }
                        
                        //generic virtual device        
                        function VirtualDevice(this: VirtualDevice, config: VirtualDeviceConfig): void {
                            //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: VirtualDevice, ) {
                                this.createStates(function (this: VirtualDevice, ) {
                                    log('created virtual device ' + this.namespace)
                                }.bind(this));
                            }.bind(this));
                        }
                        
                        VirtualDevice.prototype.createDevice = function (this: VirtualDevice, callback: () => void): void {
                            log('creating object for device ' + this.namespace, 'debug');
                            //create device object
                            const obj = this.config.copy ? getObject(this.config.copy) : { common: {}, native: {} };
                        
                            if ((obj.common as any).custom) delete (obj.common as any).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(err, 'warn');
                                    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 (this: VirtualDevice, callback: () => void): void {
                            "use strict";
                            log('creating states for device ' + this.namespace, 'debug');
                            const stateIds = Object.keys(this.config.states);
                            log('creating states ' + JSON.stringify(stateIds), 'debug');
                            let countCreated = 0;
                            for (let i = 0; i < stateIds.length; i++) {
                                let stateId = stateIds[i];
                                this.normalizeState(stateId);
                                const id = this.namespace + '.' + stateId;
                                log('creating state ' + id, 'debug');
                                const obj = this.config.states[stateId].copy ? getObject(this.config.states[stateId].copy) : { common: {}, native: {} };
                        
                                if ((obj.common as any).custom) delete (obj.common as any).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 (this: VirtualDevice, state: string): void {
                            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 = {};
                            }
                         
                            const readIds = Object.keys(this.config.states[state].read);
                            for (let i = 0; i < readIds.length; i++) {
                                const readId = this.config.states[state].read[readIds[i]];
                                if (typeof readId.before !== 'function') {
                                    this.config.states[state].read[readIds[i]].before = function (device, value, triggerId, callback) {
                                        callback()
                                    };
                                }
                                if (typeof readId.after !== 'function') {
                                    this.config.states[state].read[readIds[i]].after = function (device, value, triggerId) {
                                    };
                                }
                            }
                            const writeIds = Object.keys(this.config.states[state].write);
                            for (let i = 0; i < writeIds.length; i++) {
                                const writeId = this.config.states[state].write[writeIds[i]];
                                if (typeof writeId.before !== 'function') {
                                    this.config.states[state].write[writeIds[i]].before = function (device, value, triggerId, callback) {
                                        callback()
                                    };
                                }
                                if (typeof writeId.after !== 'function') {
                                    this.config.states[state].write[writeIds[i]].after = function (device, value, triggerId) {
                                    };
                                }
                            }
                            log('normalized state ' + state, 'debug');
                        }
                        
                        VirtualDevice.prototype.connectState = function (this: VirtualDevice, state: string): void {
                            log('connecting state ' + state, 'debug');
                            const id = this.namespace + '.' + state;
                         
                            //subscribe to read ids
                            const readIds = Object.keys(this.config.states[state].read);
                            for (let 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;
                                }
                                const readObj = this.config.states[state].read[readIds[i]];
                                const trigger: iobJS.SubscribeOptions = 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
                            const writeIds = Object.keys(this.config.states[state].write);
                            const trigger: iobJS.SubscribeOptions = {id: 'javascript.' + instance + '.' + this.namespace + '.' + state, change: 'any', ack: false};
                            on(trigger, function (this: VirtualDevice, obj: { state: ReturnType<typeof getState> }) {
                                "use strict";
                                log('detected change of ' + state, 'debug');
                                for (let i = 0; i < writeIds.length; i++) {
                                    const writeObj = this.config.states[state].write[writeIds[i]];
                                    let val: iobJS.StateValue = this.convertValue(obj.state.val, writeObj.convert);
                                    const writeId = writeIds[i];
                                    log('executing function before for ' + writeId, 'debug');
                                    writeObj.before(this, val, trigger.id.toString(), function (this: VirtualDevice, newVal?: iobJS.StateValue, newDelay?: number) {
                                        if (newVal !== undefined && newVal !== null) val = newVal;
                                        let 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, trigger.id.toString());
                                        }.bind(this));
                                    }.bind(this));
                                }
                            }.bind(this));
                            log('connected ' + state + ' to ' + JSON.stringify(writeIds), 'debug');
                        }
                        
                        VirtualDevice.prototype.subRead = function (this: VirtualDevice, trigger: iobJS.SubscribeOptions, readObj: VirtualDeviceConfigStateRead, state: string): void {
                            const func = function (this: VirtualDevice, obj: { state: ReturnType<typeof getState> }) {
                                let val: iobJS.StateValue = this.convertValue(obj.state.val, readObj.convert);
                         
                                //@todo aggregations
                         
                                log('executing function before for ' + trigger.id.toString(), 'debug');
                                readObj.before(this, val, trigger.id.toString(), function (this: VirtualDevice, newVal?: iobJS.StateValue, newDelay?: number) {
                                    if (newVal !== undefined && newVal !== null) val = newVal;
                                    if (newDelay !== undefined && newDelay !== null) readObj.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, trigger.id.toString());
                                    }.bind(this));
                                }.bind(this));
                            }.bind(this);
                            func({ state: getState(trigger.id.toString()) });
                            on(trigger, func);
                        }
                        
                        VirtualDevice.prototype.convertValue = function (this: VirtualDevice, val: iobJS.StateValue, func: (value: iobJS.StateValue) => iobJS.StateValue): iobJS.StateValue {
                            if (typeof func !== 'function') {
                                return val;
                            }
                            return func(val);
                        }
                        
                        //global
                        function createVirtualDevice(config: VirtualDeviceConfig): VirtualDevice {
                            return new VirtualDevice(config)
                        }
                        
                        

                        ich benutzt mehrer DECT Thermostat von Fritz mit dem entspechenden Adapter, dafür habe ich eine Funktion geschrieben mit der ich schnell mehrere VirtualDevice erzeugen kann:

                        const createVirtualThermostat = (name: string, id: string, isGroup?: boolean) => createVirtualDevice({
                            namespace: 'thermostat',
                            name: name,
                            common: { name: id },
                            states: {
                                name: {
                                    common: { type: 'string', role: 'text', read: true, write: false },
                                    read: {
                                        [id + '.name']: {},
                                    },
                                },
                                manufacturer: {
                                    common: { type: 'string', role: 'text', read: true, write: false },
                                    read: {
                                        [id + '.manufacturer']: {},
                                    },
                                },     
                                id: {
                                    common: { type: 'string', role: 'text', read: true, write: false },
                                    read: {
                                        [id + '.id']: {},
                                    },
                                },
                                ...(!isGroup 
                                    ? {
                                        productname: {
                                            common: { type: 'string', role: 'text', read: true, write: false },
                                            read: {
                                                [id + '.productname']: {},
                                            },
                                        },
                                        battery: {
                                            common: { type: 'number', role: 'state', read: true, write: false },
                                            read: {
                                                [id + '.battery']: {},
                                            },
                                        },
                                        batterylow: {
                                            common: { type: 'boolean', role: 'state', read: true, write: false },
                                            read: {
                                                [id + '.batterylow']: {},
                                            },
                                        },
                                        batterylow_homekit: {
                                            common: { type: 'number', role: 'state', read: true, write: false },
                                            read: {
                                                [id + '.batterylow']: {
                                                    convert: d => d ? 1 : 0,
                                                },              
                                            },
                                        },
                                        actualtemp: {
                                            common: { type: 'number', role: 'state', read: true, write: false },
                                            read: {
                                                [id + '.tist']: {},
                                            },
                                        }
                                    }
                                    : {}
                                ),
                                targettemp: {
                                    common: { type: 'number', role: 'state', read: true, write: true },
                                    read: {
                                        [id + '.tsoll']: {},
                                    },
                                    write: {
                                        [id + '.tsoll']: {},
                                    },
                                },
                                windowopenactiv: {
                                    common: { type: 'boolean', role: 'state', read: true, write: false },
                                    read: {
                                        [id + '.windowopenactiv']: {},
                                    },
                                },
                                errorcode: {
                                    common: { type: 'number', role: 'state', read: true, write: false },
                                    read: {
                                        [id + '.errorcode']: {},
                                    },
                                },
                                error: {
                                    common: { type: 'boolean', role: 'state', read: true, write: false },
                                    read: {
                                        [id + '.errorcode']: {
                                            convert: d => !!d,
                                        },
                                    },
                                },
                                present: {
                                    common: { type: 'boolean', role: 'state', read: true, write: false },
                                    read: {
                                        [id + '.present']: {},
                                    },
                                },
                                operationmode: {
                                    common: { type: 'string', role: 'state', read: true, write: false },
                                    read: {
                                        [id + '.operationmode']: {},
                                    },
                                },
                                hkrmode: {
                                    common: { type: 'number', role: 'state', read: true, write: true },
                                    read: {
                                        [id + '.hkrmode']: {},
                                    },
                                    write: {
                                        [id + '.hkrmode']: {},
                                    },
                                },        
                            }
                        });
                        

                        benutzten kann man das dann so:

                        createVirtualThermostat('temp1', 'fritzdect.0.DECT_099950242551');
                        createVirtualThermostat('temp2', 'fritzdect.0.DECT_133570009104');
                        createVirtualThermostat('temp3', 'fritzdect.0.DECT_133570329192');
                        createVirtualThermostat('temp4', 'fritzdect.0.DECT_133570402960');
                        createVirtualThermostat('temp5', 'fritzdect.0.DECT_133570404888');
                        createVirtualThermostat('temp6', 'fritzdect.0.DECT_133570405128');
                        createVirtualThermostat('temp7', 'fritzdect.0.DECT_140780177080');
                        createVirtualThermostat('temp8', 'fritzdect.0.DECT_140800051032');
                        createVirtualThermostat('temp9', 'fritzdect.0.DECT_140800051048');
                        createVirtualThermostat('temp10', 'fritzdect.0.DECT_140800052056');
                        
                        createVirtualThermostat('grp-temp1', 'fritzdect.0.DECT_grpC58331-3B9ABD4E9', true);
                        
                        J 1 Reply Last reply Reply Quote 0
                        • J
                          j1s2e3 @Zerberus last edited by

                          @zerberus Danke für das TS Code. Habe das jetzt in Benutzung, so wie Du das für das DECT Thermostat verwendest. Ich nehme es für Dimmer, um auch schnell mehrere virtuelle Geräte anzulegen.

                          Was ich noch nicht hinbekommen habe, ist das after. z.B. bei deiner createVirtualThermostat() Funktion: nehmen wir mal an, immer wenn ich targettemp schreibe, dann soll nach dem Schreiben noch hkrmode geschrieben werden.

                              targettemp: {
                                  common: { type: 'number', role: 'state', read: true, write: true },
                                  read: {
                                      [id + '.tsoll']: {},
                                  },
                                  write: {
                                      [id + '.tsoll']: {
                                          after: function (device, value) {
                                              if (value > 25) {
                                                 setState(id + 'hkrmode', 2);
                                              } else {
                                                 setState(id + 'hkrmode', 1);
                                              }
                                          },
                                      },
                                  },
                              },
                          

                          So auf diese Art... das gleiche kommt dann auch noch für convert etc... D.h. in der Funktion, mit welcher man schnell mehrere virtuelle Geräte anlegt, brauche ich unter read: und write: auch noch convert, after etc.

                          Ein (paar) Beispiel(e) dafür wäre(n) super.

                          1 Reply Last reply Reply Quote 0
                          • J
                            j1s2e3 @Pman last edited by

                            @pman Ich habe dein Skipt im "einfachen" Einsatz. Funktioniert sehr gut. Danke!

                            Jetzt habe ich einen Anwendungsfall, wo ich erstmal an meine Grenzen stosse. Javascript ist nicht meine Welt, sondern Embedded Linux.

                            Folgendes Setup:

                            Ich habe einen Enocean RGBW Controller. Der geht soweit in ioBroker. Der hat folgende Datenpunkte:
                            R, G, B, W jeweils 0-1023 als high resolution Dimmwert. 0=aus 1023=max. Helligkeit. Dazu ein DP CMD. Schreibt man dort 0(R), 1(G), 2(B) oder 3(W) rein, dann wird der in R, G, B oder W hinterlegte Dimmwert am LED Strip eingestellt.

                            Ich habe jetzt erstmal einen virtuelles Gerät angelegt, welche die absoluten Dimmwerte 0-1023 in Prozentwerte 0-100% konvertiert. Das geht.

                            Jetzt versuche ich einen DP "ON", welcher nur im virtuellen JS-Gerät existiert, anzulegen. Das klappt erstmal auch.

                            So sieht das Skript aktuell aus:

                            let id_03_woz_A5_FRGBW71L = "enocean.0.ffb2fd86";
                            
                            function roundToTwo(num) {
                                return +(Math.round(num + "e+2")  + "e-2");
                            }
                            
                            new VirtualDevice({
                                namespace: 'enocean_0',
                                name: '03_woz_A5_FRGBW71L',
                                states: {
                                    'R': {
                                        common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                        read: {
                                            [id_03_woz_A5_FRGBW71L + ".R"]: {
                                                convert: function (val) {return roundToTwo((val * 100) / 1023);}
                                            }
                                        },
                                        write: {
                                            [id_03_woz_A5_FRGBW71L + ".R"]: {
                                                convert: function (val) {return Math.round((val * 1023) / 100);}
                                            }
                                        }
                                    },
                                    'G': {
                                        common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                        read: {
                                            [id_03_woz_A5_FRGBW71L + ".G"]: {
                                                convert: function (val) {return roundToTwo((val * 100) / 1023);
                                                }
                                            }
                                        },
                                        write: {
                                            [id_03_woz_A5_FRGBW71L + ".G"]: {
                                                convert: function (val) {return Math.round((val * 1023) / 100);}
                                            }
                                        }
                                    },
                                    'B': {
                                        common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                        read: {
                                            [id_03_woz_A5_FRGBW71L + ".B"]: {
                                                convert: function (val) {return roundToTwo((val * 100) / 1023);}
                                            }
                                        },
                                        write: {
                                            [id_03_woz_A5_FRGBW71L + ".B"]: {
                                                convert: function (val) {return Math.round((val * 1023) / 100);}
                                            }
                                        }
                                    },
                                    'W': {
                                        common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'},
                                        read: {
                                            [id_03_woz_A5_FRGBW71L + ".W"]: {
                                                convert: function (val) {return roundToTwo((val * 100) / 1023);}
                                            }
                                        },
                                        write: {
                                            [id_03_woz_A5_FRGBW71L + ".W"]: {
                                                convert: function (val) {return Math.round((val * 1023) / 100);}
                                            }
                                        }
                                    },
                                    'ON': {
                                        common: {type: 'boolean', def: false, read: true, write: false},
                                        read: {
                                            [id_03_woz_A5_FRGBW71L + ".R"]: {
                                                convert: function (val) {return val == 0 ? false : true;}
                                            },
                                            [id_03_woz_A5_FRGBW71L + ".G"]: {
                                                convert: function (val) {return val == 0 ? false : true;}
                                            },
                                            [id_03_woz_A5_FRGBW71L + ".B"]: {
                                                convert: function (val) {return val == 0 ? false : true;}
                                            },
                                            [id_03_woz_A5_FRGBW71L + ".W"]: {
                                                convert: function (val) {return val == 0 ? false : true;}
                                            }
                                        }
                                    }
                                }
                            });
                            

                            Wie kann ich die Werte für R, G, B und W von ON logisch auswerten, kombinieren?

                            D.h. der virtuelle DP "ON" soll true sein, wenn R, oder G, oder B, oder W größer 0 ist, und false wenn R, G, B und W 0 sind, also keine LED leuchtet?

                            So wie oben, werden zwar die DP R, G, B, W vom Enocean Gerät gelesen, richtig nach true/false konvertiert, aber nicht logisch miteinander verküpft. Ich habe keine Idee, wie ich das im Kontext deines Skriptes anstellen soll???

                            [id_03_woz_A5_FRGBW71L + ".R"]: {
                                convert: function (val) {return val == 0 ? false : true;}
                            },
                            

                            Wie bekommen ich den Returnwert von diesem Statement in eine Variable z.B. is_R, ditto für G, B und W, um dann sowas wie if (is_R || is_G || is_B || is_W) set ON true; else set ON false;

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

                              War schon etwas länger nicht im Forum aktiv, das Skript scheint noch aktiv genutzt zu werden (läuft bei mir auch im 24/7 Einsatz!).
                              Um auf die letzte Frage einzugehen @j1s2e3 :

                              Dafür reicht die convert Funktion nicht aus, du kannst aber before nutzen, dort hast du als Parameter nicht nur den val sondern auch das virtualDevice selber und eine callback funktion, der dein berechneter Wert übergeben wird, erst dann wird geschrieben. Ein etwas umständlicher Weg wäre es, in der Funktion einfach per getState() (aus dem Javascript Adapter) die jeweils anderen Werte abzufragen und dann den ON wert zu berechnen. Da du aber auch Zugriff auf das virtuelle Gerät bekommst kannst du beliebige Werte direkt im Gerät-Objekt speichern.
                              Beispiel (nicht getestet, wirst aber die Idee dahinter verstehen):

                              new VirtualDevice({
                                          namespace: 'enocean_0',
                                          name: '03_woz_A5_FRGBW71L',
                                          onCreate: function (device, callback) {
                                              device.myStorage = {
                                                  R: 0,
                                                  G: 0,
                                                  B: 0,
                                                  W: 0,
                                              };
                                              callback();
                                          },
                                          states: {
                                              //... rgbw states
                                              'ON': {
                                                  common: {type: 'boolean', def: false, read: true, write: false},
                                                  read: {
                                                      [id_03_woz_A5_FRGBW71L + ".R"]: {
                                                          before: function (device, value, callback) {
                                                              device.myStorage.R = value;
                                                              callback(device.myStorage.R || device.myStorage.G || device.myStorage.B || device.myStorage.W);
                                                          }
                                                      },
                                                      [id_03_woz_A5_FRGBW71L + ".G"]: {
                                                          before: function (device, value, callback) {
                                                              device.myStorage.G = value;
                                                              callback(device.myStorage.R || device.myStorage.G || device.myStorage.B || device.myStorage.W);
                                                          }
                                                      },
                                                      [id_03_woz_A5_FRGBW71L + ".B"]: {
                                                          before: function (device, value, callback) {
                                                              device.myStorage.B = value;
                                                              callback(device.myStorage.R || device.myStorage.G || device.myStorage.B || device.myStorage.W);
                                                          }
                                                      },
                                                      [id_03_woz_A5_FRGBW71L + ".W"]: {
                                                          before: function (device, value, callback) {
                                                              device.myStorage.W = value;
                                                              callback(device.myStorage.R || device.myStorage.G || device.myStorage.B || device.myStorage.W);
                                                          }
                                                      }
                                                  }
                                              }
                                          }
                                      });
                              
                              1 Reply Last reply Reply Quote 0
                              • P
                                Pman last edited by Pman

                                Mit Bezug auf die letzte Frage zum Skript möchte ich euch nicht eine neuere Version des Skripts vorenthalten, welche dieses und weitere Probleme einfacher lösen kann. Unter dem Skript noch ein Beispiel, wie man die neue Logik-Funktion nutzen kann.
                                Achtung: Grundsätzlich sollte das Skript mit der alten Version kompatibel sein, allerdings werden die Geräte nun standardmäßig unter "0_userdata.0.virtualDevice" angelegt. Um dies rückgängig zu machen siehe im Skript Zeile 57.
                                Wie immer: vorher dem Update ein Backup machen.

                                /*
                                VirtualDevice v1.0
                                 
                                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) function 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
                                                },
                                                ...
                                            },
                                            logic: { //(optional), default type: last
                                                type, //can be one of: 'last' || 'number.sum' || 'number.max' || 'number.min' || 'number.average' || 'string.concat'
                                                parameters //(optional) depends on type: number {decimals: 1} / string {separator: " "} / 
                                            }, 
                                            write: {
                                                //(optional) states which "stateA" should write to 
                                                'stateId1': {
                                                    convert: function(val) {}, //(optional) function 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.globalNamespace = '0_userdata.0'; //old: 'javascript.' + instance + '.';
                                    this.sandboxNamespace = 'virtualDevice.' + config.namespace + '.' + config.name;
                                    this.namespace = this.globalNamespace + '.' + this.sandboxNamespace;
                                    this.name = config.name;
                                    this.data = {};
                                    
                                
                                    //create virtual device
                                    log('creating virtual device ' + this.sandboxNamespace)
                                    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.sandboxNamespace, 'debug');
                                    //create device object
                                    var obj = this.config.copy ? getObject(this.config.copy) : {common: {}, native: {}};
                                    // @ts-ignore
                                    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(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.sandboxNamespace, '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.sandboxNamespace + '.' + stateId;
                                        log('creating state ' + id, 'debug');
                                        var obj = this.config.states[stateId].copy ? getObject(this.config.states[stateId].copy) : {
                                            common: {},
                                            native: {}
                                        };
                                        // @ts-ignore
                                        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);
                                        }
                                        // @ts-ignore
                                        if (!obj.common.type) {
                                            // @ts-ignore
                                            obj.common.type = 'mixed';
                                        }
                                        // @ts-ignore
                                        if (!obj.common.role) {
                                            // @ts-ignore
                                            obj.common.role = 'state';
                                        }
                                        // @ts-ignore
                                        if (!obj.common.name) {
                                            // @ts-ignore
                                            obj.common.name = stateId;
                                        }
                                        extendObject(this.globalNamespace + '.' + id, {
                                            type: 'state',
                                            common: obj.common,
                                            native: obj.native
                                        }, function (err) {
                                            if (err) {
                                                log('skipping creation of state ' + id, 'debug');
                                            } else {
                                                log('created state ' + id, 'debug');
                                            }
                                            setTimeout(() => {this.connectState(stateId)}, 1000);
                                            countCreated++;
                                            if (countCreated >= stateIds.length) {
                                                log('created ' + countCreated + ' states for device ' + this.namespace, 'debug');
                                                callback();
                                            }
                                        }.bind(this));
                                    }
                                }
                                
                                VirtualDevice.prototype.normalizeState = function (stateId) {
                                    log('normalizing state ' + stateId, 'debug');
                                    if (typeof this.config.states[stateId].read !== 'object') {
                                        this.config.states[stateId].read = {};
                                    }
                                    if (typeof this.config.states[stateId].write !== 'object') {
                                        this.config.states[stateId].write = {};
                                    }
                                
                                    this.data[stateId] = {read: {}, write:{}, val: undefined};
                                    var readIds = Object.keys(this.config.states[stateId].read);
                                    for (var i = 0; i < readIds.length; i++) {
                                        this.data[stateId].read[readIds[i]] = null; 
                                        var readId = this.config.states[stateId].read[readIds[i]];
                                        if (typeof readId.before !== 'function') {
                                            this.config.states[stateId].read[readIds[i]].before = function (device, value, callback) {
                                                callback()
                                            };
                                        }
                                        if (typeof readId.after !== 'function') {
                                            this.config.states[stateId].read[readIds[i]].after = function (device, value) {
                                            };
                                        }
                                    }
                                    var writeIds = Object.keys(this.config.states[stateId].write);
                                    for (i = 0; i < writeIds.length; i++) {
                                        this.data[stateId].write[writeIds[i]] = null; 
                                        var writeId = this.config.states[stateId].write[writeIds[i]];
                                        if (typeof writeId.before !== 'function') {
                                            this.config.states[stateId].write[writeIds[i]].before = function (device, value, callback) {
                                                callback()
                                            };
                                        }
                                        if (typeof writeId.after !== 'function') {
                                            this.config.states[stateId].write[writeIds[i]].after = function (device, value) {
                                            };
                                        }
                                    }
                                    log('normalized state ' + stateId, 'debug');
                                }
                                
                                VirtualDevice.prototype.connectState = function (stateId) {
                                    log('connecting state ' + stateId, 'debug');
                                    let id = this.sandboxNamespace + '.' + stateId;
                                
                                    //subscribe to read ids
                                    let readIds = Object.keys(this.config.states[stateId].read);
                                    for (let i = 0; i < readIds.length; i++) {
                                        //check if state exists
                                        getObject(readIds[i], function (err, obj) {
                                            // @ts-ignore
                                            if (err || obj.type !== 'state') {
                                                log('cannot connect to not existing state: ' + readIds[i] + '|' + err + '|' + JSON.stringify(obj), 'warn');
                                                return;
                                            }
                                            var readObj = this.config.states[stateId].read[readIds[i]];
                                            var trigger_temp = readObj.trigger || {change: 'any'};
                                            trigger_temp.ack = true;
                                            trigger_temp.id = readIds[i];
                                            this.subRead(trigger_temp, readObj, stateId);
                                            log('connected ' + readIds[i] + ' to ' + id, 'debug');
                                        }.bind(this));
                                    }
                                
                                    //subscribe to this state and write to write ids
                                    var writeIds = Object.keys(this.config.states[stateId].write);
                                    var trigger = {id: this.namespace + '.' + stateId, change: 'any', ack: false};
                                    // @ts-ignore
                                    on(trigger, function (obj) {
                                        "use strict";
                                        log('detected change of ' + stateId, 'debug');
                                        for (var i = 0; i < writeIds.length; i++) {
                                            let writeObj = this.config.states[stateId].write[writeIds[i]];
                                            let val = obj.state.val;
                                            try {
                                                val = this.convertValue(obj.state.val, writeObj.convert);
                                            } catch (e) {
                                                log('device "' + this.name + '" caused an error in write convert function:' + e, 'warn');
                                                continue;
                                            }
                                            let writeId = writeIds[i];
                                            log('executing function before for ' + writeId, 'debug');
                                            try {
                                                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('writing value ' + val + ' to ' + writeId + ' with delay ' + delay, 'debug');
                                                    this.data[stateId].val = val;
                                                    setStateDelayed(writeId, val, false, delay || 0, true, function () {
                                                        log('executing function after for ' + writeId, 'debug');
                                                        writeObj.after(this, val);
                                                    }.bind(this));
                                                }.bind(this));
                                            } catch (e) {
                                                log('device "' + this.name + '" caused an error in a write function:' + e, 'warn');
                                                continue;
                                            }
                                        }
                                    }.bind(this));
                                    log('connected ' + stateId + ' to ' + JSON.stringify(writeIds), 'debug');
                                }
                                
                                VirtualDevice.prototype.subRead = function (trigger, readObj, stateId) {
                                    //function to process read states
                                    var onRead = function (obj) {
                                        let val = obj.state.val; 
                                        try {
                                            val = this.convertValue(obj.state.val, readObj.convert);
                                        } catch (e) {
                                            log('device "' + this.name + '" caused an error in read convert function:' + e, 'warn');
                                            return;
                                        }
                                        log('executing function before for ' + trigger.id, 'debug');
                                        try {
                                            readObj.before(this, val, function (newVal, newDelay) {
                                                if (newVal !== undefined && newVal !== null) val = newVal;
                                                if (newDelay !== undefined && newDelay !== null) readObj.delay = newDelay;
                                                log('reading value ' + val + ' into ' + this.namespace + '.' + stateId, 'debug');
                                                this.data[stateId].read[trigger.id] = val; 
                                                let aggregatedVal = this.aggregateRead(stateId, trigger.id);  
                                                this.data[stateId].val = aggregatedVal;              
                                                setStateDelayed(this.namespace + '.' + stateId, aggregatedVal, true, readObj.delay || 0, true, function () {
                                                    log('executing function after for ' + trigger.id, 'debug');
                                                    readObj.after(this, aggregatedVal);
                                                }.bind(this));
                                            }.bind(this));
                                        } catch (e) {
                                            log('device "' + this.name + '" caused an error in a read function:' + e, 'warn');
                                            return;
                                        }
                                    }.bind(this);
                                
                                    //subscribe to state
                                    on(trigger, onRead);
                                
                                    //get state once initially
                                    getState(trigger.id, function (err, state) {
                                        if (!err && state) {
                                            onRead({state: state});
                                        }
                                    });
                                }
                                
                                VirtualDevice.prototype.convertValue = function (val, func) {
                                    if (typeof func !== 'function') {
                                        return val;
                                    }
                                    return func(val);
                                }
                                
                                VirtualDevice.prototype.aggregateRead = function (stateId, readId) {
                                    var aggregatedVal = this.data[stateId].read[readId];
                                    if (!this.config.states[stateId].logic || !this.config.states[stateId].logic.type) {
                                        return aggregatedVal;
                                    }
                                    switch(this.config.states[stateId].logic.type) {
                                        case 'last':
                                            //use value of state that triggered this function
                                            aggregatedVal = this.data[stateId].read[readId];
                                            if (this.config.states[stateId].logic.parameters && typeof this.config.states[stateId].logic.parameters.decimals === 'number') {
                                                aggregatedVal = Number(aggregatedVal.toFixed(this.config.states[stateId].logic.parameters.decimals));
                                            }
                                            break;
                                        case 'number.sum':
                                            var values = Object.values(this.data[stateId].read).filter(value => typeof value === 'number');
                                            aggregatedVal = values.reduce((accumulator, current) => accumulator + current);
                                            if (this.config.states[stateId].logic.parameters && typeof this.config.states[stateId].logic.parameters.decimals === 'number') {
                                                aggregatedVal = Number(aggregatedVal.toFixed(this.config.states[stateId].logic.parameters.decimals));
                                            }
                                            break;
                                        case 'number.min':
                                            var values = Object.values(this.data[stateId].read).filter(value => typeof value === 'number');
                                            aggregatedVal = Math.min(...values);
                                            if (this.config.states[stateId].logic.parameters && typeof this.config.states[stateId].logic.parameters.decimals === 'number') {
                                                aggregatedVal = Number(aggregatedVal.toFixed(this.config.states[stateId].logic.parameters.decimals));
                                            }
                                            break;
                                        case 'number.max':
                                            var values = Object.values(this.data[stateId].read).filter(value => typeof value === 'number');
                                            aggregatedVal = Math.max(...values);
                                            if (this.config.states[stateId].logic.parameters && typeof this.config.states[stateId].logic.parameters.decimals === 'number') {
                                                aggregatedVal = Number(aggregatedVal.toFixed(this.config.states[stateId].logic.parameters.decimals));
                                            }
                                            break;
                                        case 'number.average': 
                                            var values = Object.values(this.data[stateId].read).filter(value => typeof value === 'number');
                                            aggregatedVal = values.length ? values.reduce((accumulator, current) => accumulator + current) / values.length : 0;
                                            if (this.config.states[stateId].logic.parameters && typeof this.config.states[stateId].logic.parameters.decimals === 'number') {
                                                aggregatedVal = Number(aggregatedVal.toFixed(this.config.states[stateId].logic.parameters.decimals));
                                            }
                                            break;
                                        case 'string.concat':
                                            var values = Object.values(this.data[stateId].read).filter(value => typeof value === 'string');
                                            var separator = " ";
                                            if (this.config.states[stateId].logic.parameters && typeof this.config.states[stateId].logic.parameters.separator === 'string') {
                                                separator = this.config.states[stateId].logic.parameters.separator;
                                            }
                                            aggregatedVal = values.join(separator);
                                            break;
                                        default:
                                            //use value of state that triggered this function
                                            aggregatedVal = this.data[stateId].read[readId];
                                            break;
                                    }
                                    return aggregatedVal;
                                }
                                

                                Als Beispiel für die neue Funktion bietet sich ein Gruppierungsgerät an, das die Werte mehrerer Geräte zusammenfasst.
                                In diesem Fall habe ich meine Thermostate zusammengefasst. valveOpen zeigt z.B. die Anzahl der Ventile mit einem Wert > 0 an, maxTemp zeigt die höchste Temperatur an usw.

                                new VirtualDevice({
                                    "namespace": "Heizung.Gruppe",
                                    "name": "alle",
                                    "states": {
                                        "valveOpen": {
                                            "common": {
                                                "type": "number",
                                                "min": 0,
                                                "def": 0,
                                                "read": true,
                                                "write": false,
                                                "unit": "Valves",
                                                "role": "value.counter"
                                            },
                                            "read": {
                                                "0_userdata.0.virtualDevice.Heizung.Wohnzimmer.valve": {
                                                    "convert": function(value) {
                                                        return value ? 1 : 0
                                                    }
                                                },
                                                "0_userdata.0.virtualDevice.Heizung.Badezimmer.valve": {
                                                    "convert": function(value) {
                                                        return value ? 1 : 0
                                                    }
                                                },
                                                "0_userdata.0.virtualDevice.Heizung.Schlafzimmer.valve": {
                                                    "convert": function(value) {
                                                        return value ? 1 : 0
                                                    }
                                                }
                                            },
                                            "logic": {
                                                "type": "number.sum"
                                            }
                                        },
                                        "valveClosed": {
                                            "common": {
                                                "type": "number",
                                                "min": 0,
                                                "def": 0,
                                                "read": true,
                                                "write": false,
                                                "unit": "Valves",
                                                "role": "value.counter"
                                            },
                                            "read": {
                                                "0_userdata.0.virtualDevice.Heizung.Wohnzimmer.valve": {
                                                    "convert": function(value) {
                                                        return value ? 0 : 1
                                                    }
                                                },
                                                "0_userdata.0.virtualDevice.Heizung.Badezimmer.valve": {
                                                    "convert": function(value) {
                                                        return value ? 0 : 1
                                                    }
                                                },
                                                "0_userdata.0.virtualDevice.Heizung.Schlafzimmer.valve": {
                                                    "convert": function(value) {
                                                        return value ? 0 : 1
                                                    }
                                                }
                                            },
                                            "logic": {
                                                "type": "number.sum"
                                            }
                                        },
                                        "minTemp": {
                                            "common": {
                                                "type": "number",
                                                "unit": "°C",
                                                "def": 0,
                                                "min": -10,
                                                "max": 50,
                                                "read": true,
                                                "write": false,
                                                "role": "value.temperature"
                                            },
                                            "read": {
                                                "0_userdata.0.virtualDevice.Heizung.Wohnzimmer.temperature": {},
                                                "0_userdata.0.virtualDevice.Heizung.Badezimmer.temperature": {},
                                                "0_userdata.0.virtualDevice.Heizung.Schlafzimmer.temperature": {}
                                            },
                                            "logic": {
                                                "type": "number.min"
                                            }
                                        },
                                        "maxTemp": {
                                            "common": {
                                                "type": "number",
                                                "unit": "°C",
                                                "def": 0,
                                                "min": -10,
                                                "max": 50,
                                                "read": true,
                                                "write": false,
                                                "role": "value.temperature"
                                            },
                                            "read": {
                                                "0_userdata.0.virtualDevice.Heizung.Wohnzimmer.temperature": {},
                                                "0_userdata.0.virtualDevice.Heizung.Badezimmer.temperature": {},
                                                "0_userdata.0.virtualDevice.Heizung.Schlafzimmer.temperature": {}
                                            },
                                            "logic": {
                                                "type": "number.max"
                                            }
                                        },
                                        "avgTemp": {
                                            "common": {
                                                "type": "number",
                                                "unit": "°C",
                                                "def": 0,
                                                "min": -10,
                                                "max": 50,
                                                "read": true,
                                                "write": false,
                                                "role": "value.temperature"
                                            },
                                            "read": {
                                                "0_userdata.0.virtualDevice.Heizung.Wohnzimmer.temperature": {},
                                                "0_userdata.0.virtualDevice.Heizung.Badezimmer.temperature": {},
                                                "0_userdata.0.virtualDevice.Heizung.Schlafzimmer.temperature": {}
                                            },
                                            "logic": {
                                                "type": "number.average",
                                                "parameters": {
                                                    "decimals": 1
                                                }
                                            }
                                        },
                                        "test": {
                                            "common": {
                                                "type": "string",
                                                "read": true,
                                                "write": false,
                                                "role": "value"
                                            },
                                            "read": {
                                                "0_userdata.0.virtualDevice.Heizung.Wohnzimmer.temperature": {
                                                    "convert": function(value) {
                                                        return value + '°C'
                                                    }
                                                },
                                                "0_userdata.0.virtualDevice.Heizung.Badezimmer.temperature": {
                                                    "convert": function(value) {
                                                        return value + '°C'
                                                    }
                                                },
                                                "0_userdata.0.virtualDevice.Heizung.Schlafzimmer.temperature": {
                                                    "convert": function(value) {
                                                        return value + '°C'
                                                    }
                                                }
                                            },
                                            "logic": {
                                                "type": "string.concat",
                                                "parameters": {
                                                    "separator": " | "
                                                }
                                            }
                                        }
                                    }
                                });
                                
                                Johannes Täuber 1 Reply Last reply Reply Quote 0
                                • Johannes Täuber
                                  Johannes Täuber @Pman last edited by

                                  Hallo zusammen, dieser Thread ist ziehmlich alt. Ich würde gerne wissen ob das immer noch die beste Lösung fuer das virtualisieren von devices ist oder ob es inzwischen andere Sachen gibt! Danke!

                                  Homoran Jey Cee 2 Replies Last reply Reply Quote 0
                                  • Homoran
                                    Homoran Global Moderator Administrators @Johannes Täuber last edited by

                                    @johannes-täuber sagte in Virtual Devices:

                                    ob das immer noch die beste Lösung fuer das virtualisieren von devices ist

                                    was verstehst du darunter?

                                    Vermutlich alias?

                                    1 Reply Last reply Reply Quote 0
                                    • Jey Cee
                                      Jey Cee Developer @Johannes Täuber last edited by

                                      @johannes-täuber Das hängt davon ab ob du neue, also nicht existente Datenpunkte für das Gerät brauchst oder ob du nur existente Bündeln möchtest.

                                      Mit dem devices Adapter lassen sich bestehende Datenpunkte bündeln, diese werden dann als Alias in einer gemeinsamen Objekt Struktur angelegt.

                                      Sobald man eigene Datenpunkte hinzufügen möchte muss man sich das selber Bauen, da es keinen Alias geben kann der mit keinem anderen Datenpunkte verknüpft ist.

                                      1 Reply Last reply Reply Quote 0
                                      • First post
                                        Last post

                                      Support us

                                      ioBroker
                                      Community Adapters
                                      Donate

                                      867
                                      Online

                                      31.9k
                                      Users

                                      80.1k
                                      Topics

                                      1.3m
                                      Posts

                                      21
                                      48
                                      15547
                                      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