NEWS
Virtual Devices
-
-
Dass jemand klingelt erkennt man überhaupt nur daran, dass er erneut auf TRUE gesetzt wird.
state.lc (last change) wäre noch eine Möglichkeit es zu erkennen.
Das stimmt, es gibt noch ein paar versteckte Möglichkeiten, allerdings finde ich es in VIS deutlich einfacher einen Wechsel von false auf true zu visualisieren. In FLOT ist es je nach Darstellung auch von Vorteil. -
Ich habe den Lampen-Zähler erweitert zu einem Lampen-Gruppierungs-Gerät. Man kann alle enthaltenen Lampen gleichzeitig ein- oder ausschalten sowie Dimmen. Der Dimmer ist dabei "smart", also dimmt nur alle bereits eingeschaltete Lampen und orientiert sich an der Lampe, mit dem höchsten level. Beispiel: Lampe 1: 20%, Lampe 2: 40% -> Gruppenlevel: 40%. Level wird auf 80% gestellt: Lampe1 -> 40%, Lampe2 -> 80%. Das Verhältnis der Helligkeiten bleibt also gegeben. So kann ich einen Raum mit mehreren Lampen insgesamt heller bekommen und dabei die eingestellten Lichtverhältnisse bewahren.
`/* * Level (0-100) and Switch (true/false) states supported * usage: new VirtualLampGroup('Name', ['LampId1.level','LampId2.level','LampId3.on']) */ function VirtualLampGroup(name, states) { "use strict"; var config = { namespace: 'Lamp.Group', name: name, onCreate: function (device, callback) { device.levels = {}; device.counter = {init: true}; callback(); }, states: { 'on': { common: {type: 'number', min: 0, def: 0, read: true, write: false, unit: 'Lamps'}, read: {} }, 'off': { common: {type: 'number', min: 0, def: 0, read: true, write: false, unit: 'Lamps'}, }, 'allOffSwitch': { common: {type: 'boolean', def: false, read: true, write: true}, write: {} }, 'level': { common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'}, read: {}, write: {} } } }; for (var i = 0; i < states.length; i++) { let state = states[i]; config.states.on.read[state] = { before: function (device, value, callback) { if (value > 0 && !device.counter[state]) { device.counter[state] = true; setState(device.namespace + '.off', Object.keys(device.config.states.on.read).length - Object.keys(device.counter).length, true); callback(Object.keys(device.counter).length); } else if (value <= 0 && (device.counter[state] || device.counter.init)) { delete device.counter[state]; delete device.counter.init; setState(device.namespace + '.off', Object.keys(device.config.states.on.read).length - Object.keys(device.counter).length, true); callback(Object.keys(device.counter).length); } }, after: function (device, value) { if (value) { setState(device.namespace + '.allOffSwitch', true, true); } else { setState(device.namespace + '.allOffSwitch', false, true); } } }; config.states.allOffSwitch.write[state] = { before: function (device, value, callback) { if (typeof getState(state).val === 'number') { value ? callback(100) : callback(0); return; } callback(value); } }; config.states.level.read[state] = { before: function (device, value, callback) { if (typeof getState(state).val !== 'number') { return; } device.levels[state] = value; var newLevel = Math.max.apply(null, Object.keys(device.levels).map(function (key) { return device.levels[key]; })); if (!getState(device.namespace + '.level').ack || newLevel !== getState(device.namespace + '.level').val) { callback(newLevel); } } }; config.states.level.write[state] = { before: function (device, value, callback) { if (typeof getState(state).val !== 'number') { return; } var newLevel; if (Math.max.apply(null, Object.keys(device.levels).map(function (key) { return device.levels[key]; })) === 0) { newLevel = value; } else { newLevel = Math.ceil(getState(state).val * value / Math.max.apply(null, Object.keys(device.levels).map(function (key) { return device.levels[key]; }))); } if (newLevel !== getState(state).val) { callback(newLevel); } } } } return new VirtualDevice(config); }`[/i]
-
Hi.
Ich wollte das Script auf meine Bedürfnisse anpassen und habe dies so gemacht.
new VirtualDevice({ namespace: 'Lampen', name: 'Esszimmer', //das Gerät wird unter javascript.0.virtualDevice.Hue.Wohnzimmer erstellt states: { //welche States sollen erstellt werden? 'level': { //State Konfiguration common: {type: 'number', def: 0, min: 0, max: 100, read: true, write: true, unit: '%'}, read: { //von welchen States sollen Werte eingelesen werden? 'tradfri.0.L-65539.lightbulb.brightness': { convert: function (val) { //wert soll konvertiert werden return Math.floor(val * 100 / 254); } }, 'tradfri.0.L-65540.lightbulb.brightness': { convert: function (val) { //wert soll konvertiert werden return Math.floor(val * 100 / 254); } }, }, write: { //in welche States sollen Werte geschrieben werden? 'tradfri.0.L-65539.lightbulb.brightness': { convert: function (val) { //wert soll konvertiert werden return Math.ceil(val * 254 / 100); }, }, 'tradfri.0.L-65540.lightbulb.brightness': { convert: function (val) { //wert soll konvertiert werden return Math.ceil(val * 254 / 100); }, delay: 1500 // schreibe Werte erst nach 1,5 Sekunden in den Adapter (Puffer) }, } }, } });
Allerdings bekomme ich das Script nicht zum laufen, auch werden keine Datenpunkte angelegt.
Auch wenn ich das Script von oben 1zu1 übernehme bekomme ich diesen Fehler.
! javascript.0 2017-10-22 10:08:05.628 error at ContextifyScript.Script.runInContext (vm.js:35:29)
! javascript.0 2017-10-22 10:08:05.627 error at script.js.Licht.Virtual.Esszimmer_Lampen:1:5
! javascript.0 2017-10-22 10:08:05.626 error ReferenceError: VirtualDevice is not defined
! javascript.0 2017-10-22 10:08:05.626 error ^
! javascript.0 2017-10-22 10:08:05.625 error new VirtualDevice({
! javascript.0 2017-10-22 10:08:05.624 error script.js.Licht.Virtual.Esszimmer_Lampen: script.js.Licht.Virtual.Esszimmer_Lampen:1
! javascript.0 2017-10-22 10:08:05.622 info Start javascript script.js.Licht.Virtual.Esszimmer_Lampen
! javascript.0 2017-10-22 10:08:03.850 info Stop script script.js.Licht.Virtual.Esszimmer_Lampen -
Habs gefunden. War wohl noch ein Windows Zeilenumbruch drin. ^ :evil:
Es wird jetzt ein Gerät "Lampen" angelegt. Aber wie nutze ich das jetzt? Datenpunkte sind nicht vorhanden.
Edit: Jetzt geht es doch wieder nicht, nachdem ich das Script nochmal gestartet habe. Und auch das Gerät Lampen ist wieder weg.
Verstehe ich nicht.
-
Das Skript funktioniert sehr gut.
Ich nutze hauptsächlich zwave Devices von Fibaro.
Diese steuer ich nicht direkt, sondern habe alle wichtigen States als virtuelle States innerhalb javascript.0 angelegt und synchronisiere diese.
Insbesondere für History und Vis finde ich die virtuelle Eben sehr hilfreich.
Daraus nun noch virtuelle Devices zu machen ist eine sehr schöne Erweiterung ist schön übersichtlich.
Wie binde ich das Skript von @pman am besten ein?
Als globales Skript?
Wie strukturiert ihr eure Skripte um alle Devices sinnvoll zu verwalten im javascript?
Viele kleine Skripte z.B. je Raum, oder lieber alles in ein Skript?
-
Allerdings bekomme ich das Script nicht zum laufen, auch werden keine Datenpunkte angelegt.
Auch wenn ich das Script von oben 1zu1 übernehme bekomme ich diesen Fehler. `
Ich habe dein Skript bei mir eingefügt und es funktioniert.error ReferenceError: VirtualDevice is not defined
Du hast glaube ich das Hauptskript aus dem ersten Post (ganz unten) nicht geladen. Du müsstest das entweder noch vor dieses Skript kopieren oder unter "global" einfügen.
@nisio:Wie binde ich das Skript von @pman am besten ein?
Als globales Skript?
Wie strukturiert ihr eure Skripte um alle Devices sinnvoll zu verwalten im javascript?
Viele kleine Skripte z.B. je Raum, oder lieber alles in ein Skript? `
Hallo, freut mich, dass du das Skript nützlich findest.
Ich habe das Skript selber in Global gepackt, was übrigens einfach bedeutet, dass es intern in jedes andere Skript eingefügt wird.
Dann habe ich bei den Skripten einen Ordner erstellt "VirtualDevices" und darin dann pro Gewerk ein Skript, also z. B. für Heizung, Lampen, Weiße Ware, usw. .
Wenn es sich lohnt habe ich noch eine kleine Hilfsfunktion gebaut, die aus wenigen Datenpunkten das ganze VirtualDevice erstellt. Das lohnt eigentlich immer, wenn man mehrere gleiche Geräte aus gleichen Datenpunkten erstellt. Bei Heizung ist das bei mir der Fall, da ich 7 Homematic Thermostate und Stellantriebe je gemeinsam in ein VirtualDevice gepackt habe. Das Heizungs-VirtualDevice macht übrigens das Modusumschalten bei Homematic Thermostaten sehr viel einfacher. So sieht dann bei mir das Skript Heizung aus:
function virtualHomematicThermostat(name, thermostatId, valveId) { var config = { namespace: 'Heizung', name: name, states: { 'reachable': { common: {type: 'boolean', def: false, read: true, write: false}, read: { [thermostatId + '.0.UNREACH']: { convert: function(value) { return !value; } }, } }, 'reachableValve': { common: {type: 'boolean', def: false, read: true, write: false}, read: { [valveId + '.0.UNREACH']: { convert: function(value) { return !value; } }, } }, 'lowBat': { common: {type: 'boolean', def: false, read: true, write: false}, read: { [thermostatId + '.0.LOWBAT']: {}, } }, 'lowBatValve': { common: {type: 'boolean', def: false, read: true, write: false}, read: { [valveId + '.0.LOWBAT']: {}, } }, 'temperature': { common: { "type": "number", "unit": "°C", "def": 0, "min": -10, "max": 50, "read": true, "write": false }, read: { [thermostatId + '.1.TEMPERATURE']: {}, } }, 'humidity': { common: { "type": "number", "unit": "%", "def": 0, "min": 0, "max": 99, "read": true, "write": false }, read: { [thermostatId + '.1.HUMIDITY']: {}, } }, 'temperatureSet': { common: { "type": "number", "unit": "°C", "def": 20, "min": 4.5, "max": 30.5, "read": true, "write": true }, read: { [thermostatId + '.2.SET_TEMPERATURE']: {}, }, write: { [thermostatId + '.2.SET_TEMPERATURE']: { delay: 1500 }, } }, 'valve': { common: { "def": 0, "type": "number", "read": true, "write": false, "min": 0, "max": 99, "unit": "%", }, read: { [valveId + '.4.VALVE_STATE']: {}, } }, 'mode': { common: { "def": 0, "type": "number", "read": true, "write": true, "min": 0, "max": 1, "states": { "0": "AUTO-MODE", "1": "MANU-MODE", }, }, read: { [thermostatId + '.2.CONTROL_MODE']: {}, }, write: { [thermostatId + '.2.AUTO_MODE']: { delay: 1500, before: function (device, value, callback) { if (value !== 0){ clearStateDelayed(thermostatId + '.2.AUTO_MODE'); return; } callback(true); } }, [thermostatId + '.2.MANU_MODE']: { delay: 1500, before: function (device, value, callback) { if (value !== 1){ clearStateDelayed(thermostatId + '.2.MANU_MODE'); return; } callback(getState(thermostatId + '.2.SET_TEMPERATURE').val); } }, } }, } } if (!valveId) { delete config.states.lowBatValve; delete config.states.reachableValve; delete config.states.valve; } return new VirtualDevice(config); } //Wohnzimmer new virtualHomematicThermostat('Wohnzimmer', 'hm-rpc.0.LEQ1...', 'hm-rpc.0.LEQ2...'); //HWR new virtualHomematicThermostat('HWR', 'hm-rpc.0.MEQ1...', ''); //Badezimmer new virtualHomematicThermostat('Badezimmer', 'hm-rpc.0.LEQ3...', 'hm-rpc.0.LEQ4...'); //WC new virtualHomematicThermostat('WC', 'hm-rpc.0.LEQ5...', 'hm-rpc.0.MEQ6...'); //Arbeitszimmer new virtualHomematicThermostat('Arbeitszimmer', 'hm-rpc.0.LEQ7...', 'hm-rpc.0.MEQ8...'); //Gaestezimmer new virtualHomematicThermostat('Gaestezimmer', 'hm-rpc.0.LEQ9...', 'hm-rpc.0.MEQ10...'); //Schlafzimmer new virtualHomematicThermostat('Schlafzimmer', 'hm-rpc.0.LEQ11...', 'hm-rpc.0.ME12...');
-
Allerdings bekomme ich das Script nicht zum laufen, auch werden keine Datenpunkte angelegt.
Auch wenn ich das Script von oben 1zu1 übernehme bekomme ich diesen Fehler. `
Ich habe dein Skript bei mir eingefügt und es funktioniert.error ReferenceError: VirtualDevice is not defined
Du hast glaube ich das Hauptskript aus dem ersten Post (ganz unten) nicht geladen. Du müsstest das entweder noch vor dieses Skript kopieren oder unter "global" einfügen. `
Danke, das war es.
-
Ich bekomme bei numerischen States immer eine Warning im Log: Wrong type of …: "string". Please fix, while deprecated and will not work in next versions.
Ich vermute es muss ein parseFloat() auf den ausgelesenen Value angewendet werden.
Wo baue ich das am besten ein in das Skript, damit es automatisch für States vom Type number angewendet wird?
-
Also du möchtest einen numerischen State im VirtualDevice und hast als Eingang einen String? Oder genau umgekehrt?
Zur Konvertierung von Werten kann man optional eine Funktion bei jedem read- und write-State angeben. Ich habe im Beispiel beide Fälle abgedeckt.
Bei einer einfachen Typenkonvertierung muss es natürlich nicht bleiben, du kannst mit dem Eingangswert alles mögliche anstellen. Ich habe bei mir zum Beispiel Waschmaschine und andere Geräte an einem Strommessgerät und konvertiere Watt in den Zustand der Maschine (aus, standby, waschen, fertig). Es können auch noch zwei weitere Funktion before und after genutzt werden, um verschiedene Aktionen vor bzw. nach dem schreiben der virtuellen States durchzuführen. Bei der Waschmaschine schicke ich mir z. B. mit dem Pushbullet-Adapter eine Nachricht aufs Handy, wenn der Zustand fertig erreicht wird.
Beispiel Konvertierung
new VirtualDevice({ namespace: 'Beispiel', name: 'String2Number', states: { 'string': { //dieser State ist vom Typ String common: {type: 'string', def: '', read: true, write: true}, read: { //eingehende Werte werden zu String konvertiert 'state.number': { convert: function (val) { return val.toString(); } }, }, //ausgehende Werte werden zu Number konvertiert write: { 'state.number': { convert: function (val) { return parseFloat(val); } }, } }, 'number': { //dieser State ist vom Typ Number common: {type: 'number', def: 0, read: true, write: true}, read: { 'state.string': { //eingehende Werte werden zu Number konvertiert convert: function (val) { return parseFloat(val); } }, }, write: { 'state.string': { //ausgehende Werte werden zu String konvertiert convert: function (val) { return val.toString(); } }, } } } });
-
Ich habe einen numerischen Wert den ich auf einen virtuellen numerischen State schreibe.
Scheinbar liefert getState(…).val auch bei numerischen Werten einen string.
Die Lösung mit der convert Angabe läuft bei mir schon und funktioniert auch. Ich will das nur nicht bei allen devices einbauen. Daher suche ich eine zentrale Lösung.
Hier ist das Problem auch beschrieben: http://forum.iobroker.net/viewtopic.php?t=6819
-
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.test12: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');
-
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.
-
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
-
Dann wird der Fehler im ZWave Adapter liegen, der Strings reinschreibt, statt Number.
-
Denke auch an einen Bug im zwave Adapter.
Hab mal ein Topic eröffnet dazu mit ein paar mehr Details:
-
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.
-
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.
-
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.
-
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) .................