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

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

Community Forum

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

NEWS

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

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

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

Virtual Devices

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

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

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

    Dieser Codeschnipsel

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

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

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

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

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

    if (newDelay !== undefined && newDelay !== null) writeObj.delay = newDelay;
    if (this.config.states[state].common.type === 'number') newVal = parseFloat(newVal);
    log(newVal + 'writing value ' + val + ' to ' + writeId + ' with delay ' + writeObj.delay, 'debug');
    
    1 Antwort Letzte Antwort
    0
    • AlCalzoneA Offline
      AlCalzoneA Offline
      AlCalzone
      Developer
      schrieb am zuletzt editiert von
      #25

      Der Fehler > Wrong type of …: "string". Please fix, while deprecated and will not work in next versions. tritt auf, wenn in einen State der nicht vom typ "string" ist, ein string geschrieben wird, woher auch immer. Analog für andere Datentypen (number => nicht-number, boolean => nicht-boolean, etc…).

      Mit Lesen hat das wenn nur indirekt zu tun.

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

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

        Ich verwende zwave Sensoren.

        Das auslesen des Temperaturwertes ist angeblich ein string:

        log (typeof getState("zwave.0.NODE17.SENSOR_MULTILEVEL.Temperature_1").val);
        
        20:39:38.999	[info]	javascript.0 Start javascript script.js.test.dummy
        20:39:38.999	[info]	javascript.0 script.js.test.dummy: string
        

        Der Sensor ist ein "FIBARO System FGMS001-ZW5 Motion Sensor":

        {
          "common": {
            "name": "Temperature",
            "type": "number",
            "role": "value.temperature",
            "read": true,
            "write": false
          },
          "native": {
            "value_id": "17-49-1-1",
            "type": "decimal",
            "genre": "user",
            "label": "Temperature",
            "units": "C",
            "help": "",
            "node_id": 17,
            "class_id": 49,
            "instance": 1,
            "index": 1,
            "min": 0,
            "max": 0,
            "read_only": true,
            "write_only": false,
            "is_polled": false
          },
          "acl": {
            "object": 1636,
            "owner": "system.user.admin",
            "ownerGroup": "system.group.administrator",
            "state": 1636
          },
          "_id": "zwave.0.NODE17.SENSOR_MULTILEVEL.Temperature_1",
          "type": "state"
        }
        

        Alle Adapter sind aktuell:

        ioBroker.zwave: 0.8.0

        ioBroker.javascript; 3.4.0

        ioBroker.js-controller: 1.1.3

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

          Dann wird der Fehler im ZWave Adapter liegen, der Strings reinschreibt, statt Number.

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

            Denke auch an einen Bug im zwave Adapter.

            Hab mal ein Topic eröffnet dazu mit ein paar mehr Details:

            http://forum.iobroker.net/viewtopic.php?f=22&t=8718

            1 Antwort Letzte Antwort
            0
            • modmaxM Offline
              modmaxM Offline
              modmax
              schrieb am zuletzt editiert von
              #29

              Da ich das gleiche Problem hatte (da mir gerade wieder ein Fensterkontakt gecrasht ist),

              hab ich nun die Sensoren und Schalter in virtuelle Devices verpackt.

              Jedoch hab ich das Skript "VirtualDevice" noch ergänzt, um ggfs. einen eigenes common/native-Gespann ins Device zu bringen.

              ! var obj; if (typeof this.config.copy == 'string') { obj = getObject(this.config.copy); } else if (typeof this.config.copy == 'object') { obj = this.config.copy; } else { obj = {common: {}, native: {}}; } !

              Für meine Homematic-Schalter nutze ich folgendes Skript:

              ! ````
              // virtuelle HM-Schalter
              new virtualHomematicSwitch('Wohnzimmer_Lampe', 'hm-rpc.0.ABC1234567');
              // virtuelle HM-IP-Schalter
              new virtualHomematicIpSwitch('Schlafzimmer_Lampe', 'hm-rpc.1.CBA7654321', 4);
              ! // -----------------
              ! function virtualHomematicSwitch(name, deviceId) {
              var config = {
              namespace: 'Schalter',
              name: name,
              copy: {common: {"name":name,"role":"switch"}, native: {}},
              states: {
              'unreachable': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.unreachable', role: 'indicator.unreach'},
              read: {
              [deviceId + '.0.UNREACH']: {}
              }
              },
              'lowBat': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.lowBat', role: 'indicator.battery'},
              read: {
              [deviceId + '.0.LOWBAT']: {}
              }
              },
              'dutyCycle': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.dutyCycle', role: 'value'},
              read: {
              [deviceId + '.0.DUTYCYCLE']: {}
              }
              },
              'working': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.working', role: 'indicator.working'},
              read: {
              [deviceId + '.1.WORKING']: {}
              }
              },
              'state': {
              common: {type: 'boolean', def: false, read: true, write: true, name: name+'.state', role: 'state'},
              read: {
              [deviceId + '.1.STATE']: {}
              } ,
              write: {
              [deviceId + '.1.STATE']: {}
              }

                      }
                  }
              }
              return new VirtualDevice(config);
              

              }

              ! // -----------------
              ! function virtualHomematicIpSwitch(name, deviceId, switchState) {
              var config = {
              namespace: 'Schalter',
              name: name,
              copy: {common: {"name":name,"role":"switch"}, native: {}},
              states: {
              'unreachable': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.unreachable', role: 'indicator.unreach'},
              read: {
              [deviceId + '.0.UNREACH']: {}
              }
              },
              'lowBat': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.lowBat', role: 'indicator.battery'},
              },
              'dutyCycle': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.dutyCycle', role: 'value'},
              read: {
              [deviceId + '.0.DUTY_CYCLE']: {}
              }
              },
              'working': {
              common: {type: 'boolean', def: false, read: true, write: false, name: name+'.working', role: 'indicator.working'},
              },
              'state': {
              common: {type: 'boolean', def: false, read: true, write: true, name: name+'.state', role: 'state'},
              read: {
              [deviceId + '.' + switchState + '.STATE']: {}
              } ,
              write: {
              [deviceId + '.' + switchState + '.STATE']: {}
              }

                      }
                  }
              }
              return new VirtualDevice(config);
              

              }

              ! ````

              Und für die Sensoren folgendes Skript:

              ! // Homematic Tür/Fenster Sensoren new virtualHomematicShutterContact('Wohnzimmer_Fenster', 'hm-rpc.0.ABC1234567'); // Homematic Drehgriffkontakte new virtualHomematicRotaryHandleSensor('Schlafzimmer_Fenster', 'hm-rpc.0.CBA7654321'); ! // ----------------- ! function virtualHomematicShutterContact(name, deviceId) { var config = { namespace: 'Sensor', name: name, copy: {common: {"name":name,"role":"sensor"}, native: {}}, states: { 'unreachable': { common: {type: 'boolean', def: false, read: true, write: false, name: name+'.unreachable', role: 'indicator.unreach'}, read: { [deviceId + '.0.UNREACH']: {} } }, 'lowBat': { common: {type: 'boolean', def: false, read: true, write: false, name: name+'.lowBat', role: 'indicator.battery'}, read: { [deviceId + '.0.LOWBAT']: {} } }, 'state': { common: {type: 'boolean', def: false, read: true, write: false, name: name+'.state', role: 'state'}, read: { [deviceId + '.1.STATE']: {} } } } } return new VirtualDevice(config); } ! function virtualHomematicRotaryHandleSensor(name, deviceId) { var config = { namespace: 'Sensor', name: name, copy: {common: {"name":name,"role":"sensor"}, native: {}}, states: { 'unreachable': { common: {type: 'boolean', def: false, read: true, write: false, name: name+'.unreachable', role: 'indicator.unreach'}, read: { [deviceId + '.0.UNREACH']: {} } }, 'lowBat': { common: {type: 'boolean', def: false, read: true, write: false, name: name+'.lowBat', role: 'indicator.battery'}, read: { [deviceId + '.0.LOWBAT']: {} } }, 'state': { common: {type: 'boolean', def: false, read: true, write: false, name: name+'.state', role: 'state'}, read: { [deviceId + '.1.STATE']: { convert: function(value) { return value !== 0; } } } ! }, 'handle': { common: {type: 'number', def: 0, read: true, write: false, states: {0: 'closed', 1: 'tilted', 2: 'open'}, name: name+'.handle', role: 'state'}, read: { [deviceId + '.1.STATE']: {} } } } } return new VirtualDevice(config); } !

              Für die HUE-Lampen habe ich das Skripot modifiziert, da ich zu jeder HUE-Lampe einen CUxD-Switch habe.

              Hier wolten ich sowohl über Alexa, als auch HueApp, als auch ioBroker, als auch CUxD-Schalter

              die komplette Synchronisation übernehmen; so daß alles per ioBroker synchronisiert wird.

              ! ````
              // Hue-Lampen verknüft mit CUxD-Schalter
              new virtualHueLamp("hue.0.hue_bridge.Stehlampe", "hm-rpc.2.CUX4001110.1.STATE");
              ! // -----------------
              ! function virtualHueLamp(lampId, switchId) {
              var lampObj = getObject(lampId);
              var lampName = lampObj.common.name.split('.').splice(-1)[0];

              var config = {
                  namespace: 'Hue',
                  name: lampName,
                  copy: {common: {"name":lampName,"role":lampObj.common.role}, native: {type: lampObj.native.type, model: lampObj.native.modelid}},
                  states: {
                      'unreachable': {
                          common: {type: 'boolean', def: false, read: true, write: false, name: lampName+'.unreachable', role: 'indicator.unreach'},
                          read: {
                              [lampId + '.reachable']: {
                                  convert: function(value) {
                                      return !value;
                                  }
                              }
                          }                  
                      },
                      'level': {
                          common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%', name: lampName+'.level', role: 'level.dimmer'},
                          read: {
                              [lampId + '.bri']: {
                                  convert: function(value) {
                                      return Math.floor(value*100/254);
                                  },
                                  before: function (device, value, callback) {
                                      if (value > 0) {
                                          setState(device.namespace+'.lastLevel', value, true);
                                      }
                                      callback();
                                  }
                              }
                          },
                          write: {
                              [lampId + '.bri']: {
                                  convert: function(val) {
                                      return Math.ceil(val*254/100);
                                  },
                                  delay: 800
                              }
                          }
                      },
                      'lastLevel': {
                          common: {type: 'number', def: 100, min: 0, max: 100, read: true, write: false, unit: '%', name: lampName+'.lastLevel', role: 'value'},
                          read: {}
                      },
                      'command': {
                          common: {type: 'string', def: '', read: true, write: true, name: lampName+'.command', role: 'command'},
                          write: {
                              [lampId + '.command']: {
                                  delay: 800
                              }
                          }
                      }
                  }
              }
              
              if (switchId) {
                  config.states['switch'] = {
                      common: {type: 'boolean', def: false, read: true, write: false, name: lampName+'.switch', role: 'state'},
                      read: {
                          [switchId]: {
                              after: function (device, value) {
                                  var level = getState(device.namespace + '.level').val;
                                  if (value === false && level > 0) {
                                      setState(device.namespace + '.level', 0);
                                  } else if (value === true && level <= 0) {
                                      var lastLevel = getState(device.namespace + '.lastLevel').val;
                                      setState(device.namespace + '.level', lastLevel);
                                  }
                              }
                          }
                      }
                  }
              

              ! config.states.level.read[lampId + '.bri'].after = function (device, value) {
              if (value > 0 && getState(switchId).val === false) {
              setStateDelayed(switchId, true, 200);
              } else if (value <= 0) {
              setStateDelayed(switchId, false, 200);
              }
              }
              ! config.states.command.write[lampId + '.command'].before = function (device, value, callback) {
              var parsed;
              try{
              parsed = JSON.parse(value);
              } catch(e) {
              return;
              }
              if (parsed.level > 0 && getState(switchId).val === false) {
              //if switch is off and level is greater 0 turn on switch
              setStateDelayed(switchId, true, false, 800, true, function(){
              callback(value, 3500);
              });
              } else if (parsed.level <= 0){
              //if level is set to 0 turn off switch and set level 0
              setStateDelayed(switchId, false, false, 800, true, function(){
              callback(null, 0);
              });
              } else {
              callback();
              }
              }
              }

              if (lampObj.native && lampObj.native.type === 'Color temperature light' || lampObj.native.type === 'Extended color light') {
                  config.states.ct = {
                      common: {min: 2000, max: 6500, unit: 'K', step: 10, name: lampName+'.ct', role: 'level.color.temperature'},
                      read: {
                          [lampId + '.ct']: {
                              convert: function(val) {
                                  return Math.max(2000, Math.min(6500, Math.round(Math.pow(10,6)/val)));  
                              }
                          }
                      },
                      write: {
                          [lampId + '.ct']: {
                              convert: function(val) {
                                  return Math.max(153, Math.min(500, Math.round(Math.pow(10,6)/val))); 
                              },
                              delay: 1500
                          }              
                      }
                  }
              }
              
              if (lampObj.native && lampObj.native.type === 'Color light' || lampObj.native.type === 'Extended color light') {
                  config.states.xy = {
                      common: {read: true, write: true, type: "string", name: lampName+'.xy', role: 'level.color.xy'},
                      read: {
                          [lampId + '.xy']: {}
                      },
                      write: {
                          [lampId + '.xy']: {
                              delay: 800
                          }
                      }
                  }    
              }
              
              return new VirtualDevice(config);
              

              }

              ! ````

              Vielleicht gibt es noch weitere Ideen.

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

                Ich habe das Skript jetzt seit einiger Zeit sehr erfolgreich im Einsatz.

                Wie geht es nun weiter?

                Plant jemand einen Adapter daraus zu erstellen?

                Oder den Adapter ioBroker.wrapper damit zu ergänzen?

                Oder gleich in ioBroker als zentrale Funktion aufnehmen?

                Ich habe bislang keine Erfahrung mit der Adapter Entwicklung, würde mich aber beteiligen.

                1 Antwort Letzte Antwort
                0
                • modmaxM Offline
                  modmaxM Offline
                  modmax
                  schrieb am zuletzt editiert von
                  #31

                  Von Adaptern habe ich (bisher) keine Ahnung.

                  Wüßte aktuell auch nicht, wie man sowas umsetzen kann.

                  Da ich den Adapter Broadlink2 im Einsatz habe, habe ich nun einige 433Mhz-Funksteckdosen ebenfalls als virtuelle Geräte eingerichtet,

                  so daß man mit dem STATE eines solchen Geräts auch Funksteckdosen ein/ausschalten kann.

                  Zuerst das globale Skript dazu, um einen Toggle zu erstellen.

                  Hier muß aber der Broadlink-Code angepaßt werden.

                  ! ````
                  /*
                  VirtualBroadlinkToggle 0.1
                  ! name - name of device
                  namespace - create state within this namespace (device ID will be namespace.name)
                  onCode - on-Code
                  offCode - off-Code
                  */
                  ! // generic virtual broadlink toggle
                  function VirtualBroadlinkToggle(namespace, name, onCode, offCode) {
                  this.namespace = 'virtualDevice.' + namespace + '.' + name;
                  this.name = name;
                  this.onCode = onCode;
                  this.offCode = offCode;

                  // create virtual device
                  this.createDevice(function () {
                      this.createStates(function () {
                          console.log('created virtual device ' + this.namespace);
                      }.bind(this));
                  }.bind(this));
                  

                  }

                  ! VirtualBroadlinkToggle.prototype.createDevice = function (callback) {
                  log('creating object for device ' + this.namespace, 'debug');
                  ! extendObject('javascript.' + instance + '.' + this.namespace, {
                  type: "device",
                  common: {"name": this.name, "role": "switch"},
                  native: {}
                  }, function (err) {
                  if (err) {
                  log('could not create virtual device: ' + this.namespace, 'warn');
                  return;
                  }
                  log('created object for device ' + this.namespace, 'debug');
                  callback();
                  }.bind(this));
                  }
                  ! VirtualBroadlinkToggle.prototype.createStates = function (callback) {
                  "use strict";
                  log('creating states for device ' + this.namespace, 'debug');
                  var id = this.namespace + '.state';

                  createState(id, {type: 'boolean', def: false, min: false, max: true, name: this.name+'.state', role: 'state'}, function (err) {
                      if (err) {
                          log('skipping creation of state ' + id, 'debug');
                      } else {
                          log('created state ' + id, 'debug');
                      }
                      this.connectState(id);
                      callback();
                  }.bind(this));
                  

                  }

                  ! VirtualBroadlinkToggle.prototype.connectState = function (stateId) {
                  var trigger = {id: 'javascript.' + instance + '.' + stateId, change: 'any', ack: false};
                  on(trigger, function (obj) {
                  var code = obj.state.val ? this.onCode : this.offCode;
                  sendTo('broadlink2.0', 'send_code', 'RM:RMPROSUB-ID.CODE_'+code);
                  }.bind(this));
                  }
                  ! ````

                  Die virtuelle Geräte (die nur einen state haben) kann man dann wie folgt anlegen:

                  new VirtualBroadlinkToggle("Schalter", "Funksteckdose", "onCode", "offcode");
                  
                  

                  So kann man die Funksteckdosen auch ohne Skripting steuern und an/aus-schalten.

                  Für Alexa kann man dann die Datenpunkte ebenfalls nehmen, oder je nach Laune verwenden.

                  1 Antwort Letzte Antwort
                  0
                  • P Offline
                    P Offline
                    ple
                    schrieb am zuletzt editiert von
                    #32

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

                    Intel Nuc + Proxmox

                    1 Antwort Letzte Antwort
                    0
                    • modmaxM Offline
                      modmaxM Offline
                      modmax
                      schrieb am zuletzt editiert von
                      #33

                      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 Antwort Letzte Antwort
                      0
                      • H Offline
                        H Offline
                        hsgev
                        schrieb am zuletzt editiert von
                        #34

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

                        1 Antwort Letzte Antwort
                        0
                        • eumatsE Offline
                          eumatsE Offline
                          eumats
                          schrieb am zuletzt editiert von
                          #35

                          @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 Antwort Letzte Antwort
                          0
                          • R Offline
                            R Offline
                            roughestboy
                            schrieb am zuletzt editiert von
                            #36

                            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 Antwort Letzte Antwort
                            0
                            • ScroungerS Offline
                              ScroungerS Offline
                              Scrounger
                              Developer
                              schrieb am zuletzt editiert von
                              #37

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

                              <a href="https://github.com/Scrounger/ioBroker.linkeddevices">LinkedDevices Adapter</a>

                              <a href="https://github.com/Scrounger/ioBroker.vis-materialdesign">Material Design Widgets</a>

                              1 Antwort Letzte Antwort
                              0
                              • H Offline
                                H Offline
                                homecineplexx
                                schrieb am zuletzt editiert von homecineplexx
                                #38

                                gibts das script auch in GIT, nur zwecks Versionierung?

                                1 Antwort Letzte Antwort
                                0
                                • R Offline
                                  R Offline
                                  roughestboy
                                  schrieb am zuletzt editiert von
                                  #39

                                  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 Antwort Letzte Antwort
                                  0
                                  • R Offline
                                    R Offline
                                    R1Snake
                                    schrieb am zuletzt editiert von
                                    #40

                                    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 Antwort Letzte Antwort
                                    0
                                    • Z Offline
                                      Z Offline
                                      Zerberus
                                      schrieb am zuletzt editiert von Zerberus
                                      #41

                                      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 Antwort Letzte Antwort
                                      0
                                      • Z 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 Offline
                                        J Offline
                                        j1s2e3
                                        schrieb am zuletzt editiert von
                                        #42

                                        @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 Antwort Letzte Antwort
                                        0
                                        • P Pman

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

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

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

                                          Problem:

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

                                          Skriptbeschreibung:

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

                                          Beispiel:

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

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

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

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

                                          Es ist aber noch mehr möglich:

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

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

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

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

                                          Skript

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

                                          @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 Antwort Letzte Antwort
                                          0
                                          Antworten
                                          • In einem neuen Thema antworten
                                          Anmelden zum Antworten
                                          • Älteste zuerst
                                          • Neuste zuerst
                                          • Meiste Stimmen


                                          Support us

                                          ioBroker
                                          Community Adapters
                                          Donate

                                          712

                                          Online

                                          32.4k

                                          Benutzer

                                          81.4k

                                          Themen

                                          1.3m

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

                                          • Du hast noch kein Konto? Registrieren

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