diff --git a/README.md b/README.md index df5e196..cc76257 100644 --- a/README.md +++ b/README.md @@ -76,8 +76,14 @@ If you change here something, you lose your password! - [ ] Integrate Yeelight - [ ] Handle Xiaomi Cube -- [ ] Add device SID in output -- [ ] Code cleanup +- [ ] Add filter on "all" node +- [ ] Set action status when no token available +- [ ] Add gateway status +- [ ] Update icons +- [ ] Refactor socket and set on/off actions +- [X] Add device SID in output +- [X] Remove different output styles +- [X] Code cleanup ## Sources diff --git a/node-red-contrib-xiaomi-actions/xiaomi-actions.js b/node-red-contrib-xiaomi-actions/xiaomi-actions.js index a00d02b..1c6d9e8 100644 --- a/node-red-contrib-xiaomi-actions/xiaomi-actions.js +++ b/node-red-contrib-xiaomi-actions/xiaomi-actions.js @@ -1,154 +1,106 @@ -module.exports = function(RED) { - "use strict"; - var miDevicesUtils = require('../utils'); +const miDevicesUtils = require('../src/utils'); +module.exports = (RED) => { + /********************************************* + Read data from Gateway + *********************************************/ function XiaomiActionRead(config) { RED.nodes.createNode(this, config); - var node = this; - node.on('input', function(msg) { + this.on('input', (msg) => { if(msg.sid) { - msg.payload = { - cmd: "read", - sid: msg.sid - }; - node.send(msg); + msg.payload = { cmd: "read", sid: msg.sid }; + this.send(msg); } }); } RED.nodes.registerType("xiaomi-actions read", XiaomiActionRead); - + /********************************************* + Get registred ids of devices on gateway + *********************************************/ function XiaomiActionGetIdList(config) { RED.nodes.createNode(this, config); - var node = this; - node.on('input', function(msg) { - msg.payload = { - cmd: "get_id_list" - }; + this.on('input', (msg) => { + msg.payload = { cmd: "get_id_list" }; node.send(msg); }); } RED.nodes.registerType("xiaomi-actions get_id_list", XiaomiActionGetIdList); - + /********************************************* + Virtual single click on a button + *********************************************/ function XiaomiActionSingleClick(config) { RED.nodes.createNode(this, config); - var node = this; - node.on('input', function(msg) { - if(msg.gateway && msg.sid && msg.gateway.key && msg.gateway.lastToken) { - msg.payload = { - cmd: "write", - data: { - status: "click", - sid: msg.sid, - key: miDevicesUtils.getGatewayKey(msg.gateway.key, msg.gateway.lastToken) - } - }; - node.send(msg); - } + this.on('input', (msg) => { + this.gateway = msg.gateway; + miDevicesUtils.sendWritePayloadToGateway(this, msg, {status: "click", sid: msg.sid}); }); } RED.nodes.registerType("xiaomi-actions click", XiaomiActionSingleClick); - + /********************************************* + Virtual Double click on a button + *********************************************/ function XiaomiActionDoubleClick(config) { RED.nodes.createNode(this, config); - var node = this; - node.on('input', function(msg) { - if(msg.gateway && msg.sid && msg.gateway.key && msg.gateway.lastToken) { - msg.payload = { - cmd: "write", - data: { - status: "double_click", - sid: msg.sid, - key: miDevicesUtils.getGatewayKey(msg.gateway.key, msg.gateway.lastToken) - } - }; - node.send(msg); - } + this.on('input', (msg) => { + miDevicesUtils.sendWritePayloadToGateway(this, msg, {status: "double_click", sid: msg.sid}); }); } RED.nodes.registerType("xiaomi-actions double_click", XiaomiActionDoubleClick); - - - + /********************************************* + Set the gateway light + *********************************************/ function XiaomiActionGatewayLight(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); this.color = RED.nodes.getNode(config.color); this.brightness = RED.nodes.getNode(config.brightness); - var node = this; - node.on('input', function(msg) { - if(node.gateway && node.gateway.sid && node.gateway.key && node.gateway.lastToken) { - var color = msg.color || node.color; - var brightness = msg.brightness || node.brightness; - var rgb = miDevicesUtils.computeColorValue(brightness, color.red, color.green, color.blue); - msg.payload = { - cmd: "write", - data: { - rgb: rgb, - sid: node.gateway.sid, - key: miDevicesUtils.getGatewayKey(node.gateway.key, node.gateway.lastToken) - } - }; - node.send(msg); - } + this.on('input', (msg) => { + let color = msg.color || this.color; + let brightness = msg.brightness || this.brightness; + let rgb = miDevicesUtils.computeColorValue(brightness, color.red, color.green, color.blue); + miDevicesUtils.sendWritePayloadToGateway(this, msg, {rgb: rgb}); }); } RED.nodes.registerType("xiaomi-actions gateway_light", XiaomiActionGatewayLight); - - - + /********************************************* + Play a sound on the gateway + *********************************************/ function XiaomiActionGatewaySound(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); this.mid = config.mid; this.volume = config.volume; - var node = this; - node.on('input', function(msg) { - if(node.gateway && node.gateway.sid && node.gateway.key && node.gateway.lastToken) { - msg.payload = { - cmd: "write", - data: { - mid: parseInt(msg.mid || node.mid), - volume: parseInt(msg.volume || node.volume), - sid: node.gateway.sid, - key: miDevicesUtils.getGatewayKey(node.gateway.key, node.gateway.lastToken) - } - }; - node.send(msg); - } + this.on('input', (msg) => { + miDevicesUtils.sendWritePayloadToGateway(this, msg, { + mid: parseInt(msg.mid || this.mid), + volume: parseInt(msg.volume || this.volume) + }); }); } RED.nodes.registerType("xiaomi-actions gateway_sound", XiaomiActionGatewaySound); - - + /********************************************* + Stop playing a sound on the gateway + *********************************************/ function XiaomiActionGatewayStopSound(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); - var node = this; - node.on('input', function(msg) { - if(node.gateway && node.gateway.sid && node.gateway.key && node.gateway.lastToken) { - msg.payload = { - cmd: "write", - data: { - mid: 1000, - sid: node.gateway.sid, - key: miDevicesUtils.getGatewayKey(node.gateway.key, node.gateway.lastToken) - } - }; - node.send(msg); - } + this.on('input', function(msg) { + miDevicesUtils.sendWritePayloadToGateway(this, msg, { + mid: 1000 + }); }); } RED.nodes.registerType("xiaomi-actions gateway_stop_sound", XiaomiActionGatewayStopSound); diff --git a/node-red-contrib-xiaomi-all/xiaomi-all.html b/node-red-contrib-xiaomi-all/xiaomi-all.html index ec815d6..a9cf956 100644 --- a/node-red-contrib-xiaomi-all/xiaomi-all.html +++ b/node-red-contrib-xiaomi-all/xiaomi-all.html @@ -40,4 +40,13 @@ + +

Details

+

Sample payload:

+

[
+  {sid: "128d0901db1fa8", desc: "Door sensor" model: "magnet"},
+  {sid: "151d0401ab2491", desc: "Heat sensor", model: "sensor_ht"},
+  {sid: "658d030171427c", desc: "Button", model: "switch"}
+]
+

diff --git a/node-red-contrib-xiaomi-all/xiaomi-all.js b/node-red-contrib-xiaomi-all/xiaomi-all.js index 39174f8..9c21337 100644 --- a/node-red-contrib-xiaomi-all/xiaomi-all.js +++ b/node-red-contrib-xiaomi-all/xiaomi-all.js @@ -1,18 +1,12 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var miDevicesUtils = require('../utils'); - +module.exports = (RED) => { function XiaomiAllNode(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); - var node = this; - if (this.gateway) { - node.on('input', function(msg) { - msg.payload = node.gateway.deviceList; - node.send(msg); + this.on('input', (msg) => { + msg.payload = this.gateway.deviceList; + this.send(msg); }); } } diff --git a/node-red-contrib-xiaomi-configurator/xiaomi-configurator.html b/node-red-contrib-xiaomi-configurator/xiaomi-configurator.html index 18dd598..a37f99f 100644 --- a/node-red-contrib-xiaomi-configurator/xiaomi-configurator.html +++ b/node-red-contrib-xiaomi-configurator/xiaomi-configurator.html @@ -3,7 +3,7 @@ category: 'config', defaults: { name: {value: ""}, - ip: {value: ""}, + ip: {value: "", required: true}, sid: {value: ""}, deviceList: {value:[{ sid:"", desc:"", model:"plug"}]}, key: {value: ""} diff --git a/node-red-contrib-xiaomi-configurator/xiaomi-configurator.js b/node-red-contrib-xiaomi-configurator/xiaomi-configurator.js index 3cd08d7..efb0c53 100644 --- a/node-red-contrib-xiaomi-configurator/xiaomi-configurator.js +++ b/node-red-contrib-xiaomi-configurator/xiaomi-configurator.js @@ -1,5 +1,4 @@ -module.exports = function(RED) { - +module.exports = (RED) => { function XiaomiConfiguratorNode(n) { RED.nodes.createNode(this, n); this.name = n.name; @@ -12,5 +11,4 @@ module.exports = function(RED) { } RED.nodes.registerType("xiaomi-configurator", XiaomiConfiguratorNode); - } diff --git a/node-red-contrib-xiaomi-gateway/icons/gateway-icon.png b/node-red-contrib-xiaomi-gateway/icons/gateway-icon.png deleted file mode 100644 index 802a2c3..0000000 Binary files a/node-red-contrib-xiaomi-gateway/icons/gateway-icon.png and /dev/null differ diff --git a/node-red-contrib-xiaomi-gateway/icons/mijia.png b/node-red-contrib-xiaomi-gateway/icons/mijia.png new file mode 100644 index 0000000..7f73eae Binary files /dev/null and b/node-red-contrib-xiaomi-gateway/icons/mijia.png differ diff --git a/node-red-contrib-xiaomi-gateway/xiaomi-gateway.html b/node-red-contrib-xiaomi-gateway/xiaomi-gateway.html index 4b84869..04dbcd0 100644 --- a/node-red-contrib-xiaomi-gateway/xiaomi-gateway.html +++ b/node-red-contrib-xiaomi-gateway/xiaomi-gateway.html @@ -11,7 +11,7 @@ outputs: 1, outputLabels: ["Gateway"], paletteLabel: "gateway", - icon: "gateway-icon.png", + icon: "mijia.png", label: function () { return this.name || "xiaomi-gateway"; } diff --git a/node-red-contrib-xiaomi-gateway/xiaomi-gateway.js b/node-red-contrib-xiaomi-gateway/xiaomi-gateway.js index 75dc238..5a5d3e9 100644 --- a/node-red-contrib-xiaomi-gateway/xiaomi-gateway.js +++ b/node-red-contrib-xiaomi-gateway/xiaomi-gateway.js @@ -1,42 +1,39 @@ -module.exports = function(RED) { - "use strict"; - var dgram = require('dgram'); - var miDevicesUtils = require('../utils'); +const dgram = require('dgram'); // Given by udp node +const miDevicesUtils = require('../src/utils'); + +// UDP node copy/paste... + +module.exports = (RED) => { var udpInputPortsInUse = {}; function XiaomiGatewayNode(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); - var node = this; - if (this.gateway) { - node.on('input', function(msg) { + this.on('input', (msg) => { // var payload = JSON.parse(msg); var payload = msg.payload; - node.log("Received message from: " + payload.model + " sid: " + payload.sid + " payload: " + payload.data); + //this.log("Received message from: " + payload.model + " sid: " + payload.sid + " payload: " + payload.data); // Input from gateway if(payload.sid) { - if (payload.sid == node.gateway.sid && ["gateway"].indexOf(payload.model) >= 0) { + if (payload.sid == this.gateway.sid) { if(payload.data.rgb) { var decomposed = miDevicesUtils.computeColor(payload.data.rgb); payload.data.brightness = decomposed.brightness; payload.data.color = decomposed.color; } - node.send([msg]); + this.send([msg]); } } // Prepare for request else { - msg.gateway = node.gateway; - msg.sid = node.gateway.sid; - node.send(msg); + msg.gateway = this.gateway; + msg.sid = this.gateway.sid; + this.send(msg); } }); - - node.on("close", function() { - }); } } RED.nodes.registerType("xiaomi-gateway", XiaomiGatewayNode); @@ -50,10 +47,9 @@ module.exports = function(RED) { this.iface = null; this.addr = n.ip; this.ipv = this.ip && this.ip.indexOf(":") >= 0 ? "udp6" : "udp4"; - var node = this; - var opts = {type:node.ipv, reuseAddr:true}; - if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; } + var opts = {type:this.ipv, reuseAddr:true}; + if (process.version.indexOf("v0.10") === 0) { opts = this.ipv; } var server; if (!udpInputPortsInUse.hasOwnProperty(this.port)) { @@ -61,24 +57,24 @@ module.exports = function(RED) { udpInputPortsInUse[this.port] = server; } else { - node.warn(RED._("udp.errors.alreadyused",node.port)); + this.warn(RED._("udp.errors.alreadyused",this.port)); server = udpInputPortsInUse[this.port]; // re-use existing } - if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; } + if (process.version.indexOf("v0.10") === 0) { opts = this.ipv; } - server.on("error", function (err) { - if ((err.code == "EACCES") && (node.port < 1024)) { - node.error(RED._("udp.errors.access-error")); + server.on("error", (err) => { + if ((err.code == "EACCES") && (this.port < 1024)) { + this.error(RED._("udp.errors.access-error")); } else { - node.error(RED._("udp.errors.error",{error:err.code})); + this.error(RED._("udp.errors.error",{error:err.code})); } server.close(); }); - server.on('message', function (message, remote) { + server.on('message', (message, remote) => { var msg; - if(remote.address == node.addr) { + if(remote.address == this.addr) { var msg = message.toString('utf8'); var jsonMsg = JSON.parse(msg); if(jsonMsg.data) { @@ -88,51 +84,51 @@ module.exports = function(RED) { } } msg = { payload: jsonMsg }; - if(jsonMsg.token && node.gateway && jsonMsg.data.ip && jsonMsg.data.ip === node.gateway.ip) { - node.gateway.lastToken = jsonMsg.token; - if(!node.gateway.sid) { - node.gateway.sid = jsonMsg.sid; + if(jsonMsg.token && this.gateway && jsonMsg.data.ip && jsonMsg.data.ip === this.gateway.ip) { + this.gateway.lastToken = jsonMsg.token; + if(!this.gateway.sid) { + this.gateway.sid = jsonMsg.sid; } } - node.send(msg); + this.send(msg); } }); - server.on('listening', function () { + server.on('listening', () => { var address = server.address(); - node.log(RED._("udp.status.listener-at",{host:address.address,port:address.port})); + this.log(RED._("udp.status.listener-at",{host:address.address,port:address.port})); server.setBroadcast(true); try { server.setMulticastTTL(128); - server.addMembership(node.group,node.iface); - node.log(RED._("udp.status.mc-group",{group:node.group})); + server.addMembership(this.group,this.iface); + this.log(RED._("udp.status.mc-group",{group:this.group})); } catch (e) { if (e.errno == "EINVAL") { - node.error(RED._("udp.errors.bad-mcaddress")); + this.error(RED._("udp.errors.bad-mcaddress")); } else if (e.errno == "ENODEV") { - node.error(RED._("udp.errors.interface")); + this.error(RED._("udp.errors.interface")); } else { - node.error(RED._("udp.errors.error",{error:e.errno})); + this.error(RED._("udp.errors.error",{error:e.errno})); } } }); - node.on("close", function() { - if (udpInputPortsInUse.hasOwnProperty(node.port)) { - delete udpInputPortsInUse[node.port]; + this.on("close", () => { + if (udpInputPortsInUse.hasOwnProperty(this.port)) { + delete udpInputPortsInUse[this.port]; } try { server.close(); - node.log(RED._("udp.status.listener-stopped")); + this.log(RED._("udp.status.listener-stopped")); } catch (err) { - //node.error(err); + //this.error(err); } }); - try { server.bind(node.port,node.iface); } + try { server.bind(this.port, this.iface); } catch(e) { } // Don't worry if already bound } - RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-ports.read'), function(req,res) { + RED.httpAdmin.get('/udp-ports/:id', RED.auth.needsPermission('udp-ports.read'), (req,res) => { res.json(Object.keys(udpInputPortsInUse)); }); RED.nodes.registerType("xiaomi-gateway in",GatewayIn); @@ -147,10 +143,9 @@ module.exports = function(RED) { this.addr = n.ip; this.ipv = this.ip && this.ip.indexOf(":") >= 0 ? "udp6" : "udp4"; this.multicast = false; - var node = this; - var opts = {type:node.ipv, reuseAddr:true}; - if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; } + var opts = {type:this.ipv, reuseAddr:true}; + if (process.version.indexOf("v0.10") === 0) { opts = this.ipv; } var sock; if (udpInputPortsInUse[this.outport]) { @@ -158,7 +153,7 @@ module.exports = function(RED) { } else { sock = dgram.createSocket(opts); // default to udp4 - sock.on("error", function(err) { + sock.on("error", (err) => { // Any async error will also get reported in the sock.send call. // This handler is needed to ensure the error marked as handled to // prevent it going to the global error handler and shutting node-red @@ -168,27 +163,27 @@ module.exports = function(RED) { } if (!udpInputPortsInUse[this.outport]) { - sock.bind(node.outport); - node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port})); + sock.bind(this.outport); + this.log(RED._("udp.status.ready",{outport:this.outport,host:this.addr,port:this.port})); } else { - node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port})); + this.log(RED._("udp.status.ready-nolocal",{host:this.addr,port:this.port})); } - node.on("input", function(msg) { + this.on("input", (msg) => { if (msg.hasOwnProperty("payload")) { - var add = node.addr || msg.ip || ""; - var por = node.port || msg.port || 0; + var add = this.addr || msg.ip || ""; + var por = this.port || msg.port || 0; if (add === "") { - node.warn(RED._("udp.errors.ip-notset")); + this.warn(RED._("udp.errors.ip-notset")); } else if (por === 0) { - node.warn(RED._("udp.errors.port-notset")); + this.warn(RED._("udp.errors.port-notset")); } else if (isNaN(por) || (por < 1) || (por > 65535)) { - node.warn(RED._("udp.errors.port-invalid")); + this.warn(RED._("udp.errors.port-invalid")); } else { var message = Buffer.from(JSON.stringify(msg.payload)); - sock.send(message, 0, message.length, por, add, function(err, bytes) { + sock.send(message, 0, message.length, por, add, (err, bytes) => { if (err) { - node.error("udp : "+err,msg); + this.error("udp : "+err,msg); } message = null; }); @@ -196,15 +191,15 @@ module.exports = function(RED) { } }); - node.on("close", function() { - if (udpInputPortsInUse.hasOwnProperty(node.outport)) { - delete udpInputPortsInUse[node.outport]; + this.on("close", () => { + if (udpInputPortsInUse.hasOwnProperty(this.outport)) { + delete udpInputPortsInUse[this.outport]; } try { sock.close(); - node.log(RED._("udp.status.output-stopped")); + this.log(RED._("udp.status.output-stopped")); } catch (err) { - //node.error(err); + //this.error(err); } }); } diff --git a/node-red-contrib-xiaomi-ht/xiaomi-ht.html b/node-red-contrib-xiaomi-ht/xiaomi-ht.html index 8ec2204..f06ed98 100644 --- a/node-red-contrib-xiaomi-ht/xiaomi-ht.html +++ b/node-red-contrib-xiaomi-ht/xiaomi-ht.html @@ -5,16 +5,10 @@ defaults: { gateway: {value:"", type:"xiaomi-configurator"}, name: {value: ""}, - sid: {value: "", required: true}, - temperature: {value: "{{temperature}}"}, - humidity: {value: "{{humidity}}"}, - pressure: {value: "{{pressure}}"}, - divide: {value: true}, - output: {value: "0"} + sid: {value: "", required: true} }, inputs: 1, - outputs: 3, - outputLabels: ["Temperature","Humidity","Pressure"], + outputs: 1, paletteLabel: "sensor HT", icon: "thermometer-icon.png", label: function () { @@ -55,16 +49,7 @@ changeGateway("sensor_ht"); }); - $("#node-input-output").change(function () { - if ($(this).val() == "2") { - $(".node-input-msg").show(); - } else { - $(".node-input-msg").hide(); - } - }); - $(".node-input-msg").hide(); - $("#node-input-output").val(node.output); }, oneditsave: function() { var node = this; @@ -86,31 +71,6 @@ -
- - -
-
- - -
-
- - -
-
- - -
-
- -    - -
diff --git a/node-red-contrib-xiaomi-ht/xiaomi-ht.js b/node-red-contrib-xiaomi-ht/xiaomi-ht.js index f4c0efa..4abf767 100644 --- a/node-red-contrib-xiaomi-ht/xiaomi-ht.js +++ b/node-red-contrib-xiaomi-ht/xiaomi-ht.js @@ -1,97 +1,39 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var dgram = require('dgram'); - var miDevicesUtils = require('../utils'); +const miDevicesUtils = require('../src/utils'); +module.exports = (RED) => { function XiaomiHtNode(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); this.sid = config.sid; - this.output = config.output; - this.temperature = config.temperature; - this.humidity = config.humidity; - this.divide = config.divide; - var node = this; - node.status({fill:"grey", shape:"ring", text:"battery - na"}); + this.status({fill:"grey", shape:"ring", text:"battery - na"}); if (this.gateway) { - node.on('input', function(msg) { - // var payload = JSON.parse(msg); - var payload = msg.payload; - node.log("Received message from: " + payload.model + " sid: " + payload.sid + " payload: " + payload.data); + this.on('input', (msg) => { + let payload = msg.payload; // Input from gateway - if(payload.sid) { - if (payload.sid == node.sid && ["sensor_ht", "weather.v1"].indexOf(payload.model) >= 0) { - var data = payload.data; - miDevicesUtils.setStatus(node, data); - - if (node.output == "0") { - node.send([msg]); - } else if (node.output == "1") { - var temp = null; - var humidity = null; - var pressure = null; - - if (data.temperature) { - temp = {"payload": data.temperature}; + if (payload.sid) { + if (payload.sid == this.sid) { + miDevicesUtils.setStatus(this, payload.data); + ["temperature", "humidity", "pressure"].forEach((dataType) => { + if(payload.data[dataType]) { + payload.data[dataType] = parseInt(payload.data[dataType])/100; } - - if (data.humidity) { - humidity = {"payload": data.humidity}; - } - - if (data.pressure) { - pressure = {"payload": data.pressure}; - } - node.send([temp, humidity, pressure]); - } else if (node.output == "2") { - var temp = null; - var humidity = null; - var pressure = null; - - if (data.temperature) { - if (this.divide) { - data.temperature = String(data.temperature / 100); - } - temp = {"payload": mustache.render(node.temperature, data)} - } - - if (data.humidity) { - if (this.divide) { - data.humidity = String(data.humidity / 100); - } - humidity = {"payload": mustache.render(node.humidity, data)} - } - - if (data.pressure) { - if (this.divide) { - data.pressure = String(data.pressure / 100); - } - pressure = {"payload": mustache.render(node.pressure, data)} - } - node.send([temp, humidity, pressure]); - } + }); + } + else { + msg = null; } } // Prepare for request else { - miDevicesUtils.prepareForGatewayRequest(node, msg); - node.send(msg); + miDevicesUtils.prepareForGatewayRequest(this, msg); } + this.send(msg); }); - - node.on("close", function() { - }); - - } else { - // no gateway configured } - } RED.nodes.registerType("xiaomi-ht", XiaomiHtNode); - -} +}; diff --git a/node-red-contrib-xiaomi-magnet/xiaomi-magnet.html b/node-red-contrib-xiaomi-magnet/xiaomi-magnet.html index 9cf92ed..67cc4e7 100644 --- a/node-red-contrib-xiaomi-magnet/xiaomi-magnet.html +++ b/node-red-contrib-xiaomi-magnet/xiaomi-magnet.html @@ -5,14 +5,10 @@ defaults: { gateway: {value:"", type:"xiaomi-configurator"}, name: {value: ""}, - sid: {value: "", required: true}, - openmsg: {value: ""}, - closemsg: {value: ""}, - output: {value: "0"} + sid: {value: "", required: true} }, inputs: 1, outputs: 1, - outputLabels: ["Status"], paletteLabel: "contact", icon: "door-icon.png", label: function () { @@ -52,14 +48,6 @@ $("#node-input-gateway").change(function () { changeGateway("magnet"); }); - - $("#node-input-output").change(function () { - if ($(this).val() == "2") { - $(".node-input-msg").show(); - } else { - $(".node-input-msg").hide(); - } - }); }, oneditsave: function() { var node = this; @@ -81,22 +69,6 @@ -
- - -
-
- - -
-
- - -
diff --git a/node-red-contrib-xiaomi-magnet/xiaomi-magnet.js b/node-red-contrib-xiaomi-magnet/xiaomi-magnet.js index cfb0d3a..772f464 100644 --- a/node-red-contrib-xiaomi-magnet/xiaomi-magnet.js +++ b/node-red-contrib-xiaomi-magnet/xiaomi-magnet.js @@ -1,78 +1,8 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var miDevicesUtils = require('../utils'); +const miDevicesUtils = require('../src/utils'); +module.exports = (RED) => { function XiaomiMagnetNode(config) { - RED.nodes.createNode(this, config); - this.gateway = RED.nodes.getNode(config.gateway); - this.sid = config.sid; - this.output = config.output; - this.openmsg = config.openmsg; - this.closemsg = config.closemsg; - - var node = this; - var state = ""; - - // node.status({fill:"yellow", shape:"dot", text:"unknown state"}); - node.status({fill:"grey", shape:"ring", text:"battery - na"}); - - if (this.gateway) { - node.on('input', function(msg) { - // var payload = JSON.parse(msg); - var payload = msg.payload; - - if(payload.sid) { - if (payload.sid == node.sid && ["magnet", "sensor_magnet.aq2"].indexOf(payload.model) >= 0) { - var data = payload.data; - - // if (data.status && data.status == "open") { - // node.status({fill:"green", shape:"dot", text:"open"}); - // state = "open"; - // } else if (data.status && data.status == "close") { - // node.status({fill:"red", shape:"dot", text:"closed"}); - // state = "closed"; - // } - miDevicesUtils.setStatus(node, data); - - - if (node.output == "0") { - node.send([msg]); - } else if (node.output == "1") { - var status = null; - - if (data.status) { - status = {"payload": data.status}; - } - node.send([status]); - } else if (node.output == "2") { - var status = null; - - if (data.status === 'open') { - status = {"payload": mustache.render(node.openmsg, data)} - } else { - status = {"payload": mustache.render(node.closemsg, data)} - } - node.send([status]); - } - } - } - // Prepare for request - else { - miDevicesUtils.prepareForGatewayRequest(node, msg); - node.send(msg); - } - }); - - node.on("close", function() { - }); - - } else { - // no gateway configured - } - + miDevicesUtils.defaultNode(RED, config, this); } - RED.nodes.registerType("xiaomi-magnet", XiaomiMagnetNode); - -} +}; diff --git a/node-red-contrib-xiaomi-motion/xiaomi-motion.html b/node-red-contrib-xiaomi-motion/xiaomi-motion.html index 21d9602..4fab653 100644 --- a/node-red-contrib-xiaomi-motion/xiaomi-motion.html +++ b/node-red-contrib-xiaomi-motion/xiaomi-motion.html @@ -11,8 +11,7 @@ output: {value: "0"} }, inputs: 1, - outputs: 2, - outputLabels: ["Status"], + outputs: 1, paletteLabel: "motion", icon: "motion-icon.png", label: function () { @@ -52,14 +51,6 @@ $("#node-input-gateway").change(function () { changeGateway("magnet"); }); - - $("#node-input-output").change(function () { - if ($(this).val() == "2") { - $(".node-input-msg").show(); - } else { - $(".node-input-msg").hide(); - } - }); }, oneditsave: function() { var node = this; @@ -81,22 +72,6 @@ -
- - -
-
- - -
-
- - -
diff --git a/node-red-contrib-xiaomi-motion/xiaomi-motion.js b/node-red-contrib-xiaomi-motion/xiaomi-motion.js index 1981a0a..aa78e71 100644 --- a/node-red-contrib-xiaomi-motion/xiaomi-motion.js +++ b/node-red-contrib-xiaomi-motion/xiaomi-motion.js @@ -1,84 +1,8 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var miDevicesUtils = require('../utils'); +const miDevicesUtils = require('../src/utils'); +module.exports = (RED) => { function XiaomiMotionNode(config) { - RED.nodes.createNode(this, config); - this.gateway = RED.nodes.getNode(config.gateway); - this.sid = config.sid; - this.output = config.output; - this.motionmsg = config.motionmsg; - this.nomotionmsg = config.nomotionmsg; - - var node = this; - var state = ""; - - // node.status({fill:"yellow", shape:"dot", text:"unknown state"}); - node.status({fill:"grey", shape:"ring", text:"battery - na"}); - - if (this.gateway) { - node.on('input', function(msg) { - // var payload = JSON.parse(msg); - var payload = msg.payload; - - if (payload.sid) { - if (payload.sid == node.sid && payload.model == "motion") { - var data = payload.data; - - // if (data.status && data.status == "open") { - // node.status({fill:"green", shape:"dot", text:"open"}); - // state = "open"; - // } else if (data.status && data.status == "close") { - // node.status({fill:"red", shape:"dot", text:"closed"}); - // state = "closed"; - // } - miDevicesUtils.setStatus(node, data); - - - if (node.output == "0") { - node.send([msg]); - } else if (node.output == "1") { - var status = null; - var duration = null; - - if (data.status) { - status = {"payload": data.status}; - } - if (data.no_motion) { - status = {"payload": "no_motion"}; - duration = {"payload": {"no_motion": data.no_motion}}; - } - - node.send([[status], [duration]]); - } else if (node.output == "2") { - var status = null; - - if (data.status === 'motion') { - status = {"payload": mustache.render(node.motionmsg, data)} - } else { - status = {"payload": mustache.render(node.nomotionmsg, data)} - } - node.send([status]); - } - } - } - // Prepare for request - else { - miDevicesUtils.prepareForGatewayRequest(node, msg); - node.send(msg); - } - }); - - node.on("close", function() { - }); - - } else { - // no gateway configured - } - + miDevicesUtils.defaultNode(RED, config, this); } - RED.nodes.registerType("xiaomi-motion", XiaomiMotionNode); - -} +}; diff --git a/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.html b/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.html index fd5ad6b..41d1969 100644 --- a/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.html +++ b/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.html @@ -16,21 +16,6 @@ icon: "outlet-wifi-icon.png", label: function () { return this.name || "xiaomi-plug-wifi"; - }, - oneditprepare: function() { - var node = this; - - $("#node-input-output").change(function () { - if ($(this).val() == "2") { - $(".node-input-msg").show(); - } else { - $(".node-input-msg").hide(); - } - }); - - }, - oneditsave: function() { - } }); @@ -44,22 +29,6 @@ -
- - -
-
- - -
-
- - -
diff --git a/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.js b/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.js index 55ebf58..a742099 100644 --- a/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.js +++ b/node-red-contrib-xiaomi-socket-wifi/xiaomi-socket-wifi.js @@ -1,8 +1,6 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var crypto = require("crypto"); - var miio = require("miio"); +const miio = require("miio"); + +module.exports = (RED) => { var connectionState = "timeout"; var retryTimer; var delayedStatusMsgTimer; @@ -11,23 +9,18 @@ module.exports = function(RED) { function XiaomiPlugWifiNode(config) { RED.nodes.createNode(this, config); this.ip = config.ip; - this.output = config.output; - this.onmsg = config.onmsg; - this.offmsg = config.offmsg; this.plug = null; - var node = this; + this.status({fill: "yellow", shape: "dot", text: "connecting"}); - node.status({fill: "yellow", shape: "dot", text: "connecting"}); - - miio.device({address: node.ip}) - .then(function (plug) { - node.plug = plug; - node.status({fill:"green", shape:"dot", text:"connected"}); + miio.device({address: this.ip}) + .then((plug) => { + this.plug = plug; + this.status({fill:"green", shape:"dot", text:"connected"}); connectionState = "connected"; delayedStatusMsgUpdate(node); - node.plug.on('propertyChanged', function(e) { + this.plug.on('propertyChanged', (e) => { if (e.property === "power") { if (e.value['0']) { setState("on"); @@ -38,73 +31,58 @@ module.exports = function(RED) { }); watchdog(); }) - .catch(function (error) { + .catch((error) => { connectionState = "reconnecting"; watchdog(); }) - node.on('input', function (msg) { + this.on('input', (msg) => { var payload = msg.payload; if (connectionState === "connected") { if (payload == 'on') { - node.plug.setPower(true); + this.plug.setPower(true); } if (payload == 'off') { - node.plug.setPower(false); + this.plug.setPower(false); } } }); - node.on('close', function (done) { + this.on('close', (done) => { if (retryTimer) { clearTimeout(retryTimer); } if (delayedStatusMsgTimer) { clearTimeout(delayedStatusMsgTimer); } - if (node.plug) { - node.plug.destroy(); + if (this.plug) { + this.plug.destroy(); } done(); }); - var setState = function(state) { - if (node.plug) { - var status = null; - var info = {"payload": { - "id": node.plug.id, - "type": node.plug.type, - "model": node.plug.model, - "capabilities": node.plug.capabilities, - "address": node.plug.address, - "port": node.plug.port, - "power": node.plug.power() - }}; - - if (state === "on") { - node.status({fill:"green", shape:"dot", text:"on"}); - status = {"payload": mustache.render(node.onmsg, info.payload)} - } - if (state === "off") { - node.status({fill:"red", shape:"dot", text:"off"}); - status = {"payload": mustache.render(node.offmsg, info.payload)} - } - - if (node.output == 0) { - status = info; - } else if (node.output == "1") { - status = {"payload": state} - } else if (node.output == "2") { - // do nothing, just send status parsed with mustache - } - node.send([status]); + var setState = (state) => { + if (this.plug) { + let status = { + payload: { + id: this.plug.id, + type: this.plug.type, + model: this.plug.model, + capabilities: this.plug.capabilities, + address: this.plug.address, + port: this.plug.port, + power: this.plug.power(), + state: state + } + }; + this.send(status); } }; - var delayedStatusMsgUpdate = function() { - delayedStatusMsgTimer = setTimeout(function() { - if (node.plug.power()['0']) { + var delayedStatusMsgUpdate = () => { + delayedStatusMsgTimer = setTimeout(() => { + if (this.plug.power()['0']) { setState("on"); } else { setState("off"); @@ -112,12 +90,12 @@ module.exports = function(RED) { }, 1500); }; - var discoverDevice = function() { - miio.device({address: node.ip}) - .then(function (plug) { - if (node.plug == null) { - node.plug = plug; - node.plug.on('propertyChanged', function(e) { + var discoverDevice = () => { + miio.device({address: this.ip}) + .then((plug) => { + if (this.plug == null) { + this.plug = plug; + this.plug.on('propertyChanged', (e) => { if (e.property === "power") { if (e.value['0']) { setState("on"); @@ -128,28 +106,30 @@ module.exports = function(RED) { }); } if (connectionState === "reconnecting") { - node.status({fill:"green", shape:"dot", text:"connected"}); + this.status({fill:"green", shape:"dot", text:"connected"}); connectionState = "connected"; delayedStatusMsgUpdate(); } }) - .catch(function (error) { + .catch((error) => { connectionState = "reconnecting"; - if (node.plug) { - node.plug.destroy(); - node.plug = null; + if (this.plug) { + this.plug.destroy(); + this.plug = null; } }) }; - var watchdog = function() { - setTimeout(function retryTimer() { + var watchdog = () => { + var node = this; + function retryTimer() { discoverDevice(); if (connectionState === "reconnecting") { node.status({fill: "red", shape: "dot", text: "reconnecting"}); } setTimeout(retryTimer, 30000); - }, 30000); + } + setTimeout(retryTimer, 30000); } } diff --git a/node-red-contrib-xiaomi-socket/xiaomi-socket.html b/node-red-contrib-xiaomi-socket/xiaomi-socket.html index 6a6269b..b2d9559 100644 --- a/node-red-contrib-xiaomi-socket/xiaomi-socket.html +++ b/node-red-contrib-xiaomi-socket/xiaomi-socket.html @@ -11,8 +11,7 @@ output: {value: "0"} }, inputs: 1, - outputs: 2, - outputLabels: ["Status","Control"], + outputs: 1, paletteLabel: "plug (zigbee)", icon: "outlet-icon.png", label: function () { @@ -26,14 +25,6 @@ // Get the config node using the ID: var configNode = RED.nodes.node(configNodeID); - $("#node-input-output").change(function () { - if ($(this).val() == "2") { - $(".node-input-msg").show(); - } else { - $(".node-input-msg").hide(); - } - }); - $("#node-input-gateway").change(function () { }); @@ -66,22 +57,6 @@ -
- - -
-
- - -
-
- - -
diff --git a/node-red-contrib-xiaomi-socket/xiaomi-socket.js b/node-red-contrib-xiaomi-socket/xiaomi-socket.js index 12e3052..e48bd09 100644 --- a/node-red-contrib-xiaomi-socket/xiaomi-socket.js +++ b/node-red-contrib-xiaomi-socket/xiaomi-socket.js @@ -1,26 +1,19 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var crypto = require("crypto"); +const crypto = require("crypto"); +module.exports = (RED) => { function XiaomiPlugNode(config) { RED.nodes.createNode(this, config); this.gateway = RED.nodes.getNode(config.gateway); this.sid = config.sid; - this.output = config.output; - this.onmsg = config.onmsg; - this.offmsg = config.offmsg; this.key = this.gateway.key; - var node = this; var currentToken = ""; var state = ""; - node.status({fill:"yellow", shape:"ring", text:"waiting for key"}); + this.status({fill:"yellow", shape:"ring", text:"waiting for key"}); if (this.gateway && this.key != "") { - node.on('input', function(msg) { - // var payload = JSON.parse(msg); + this.on('input', (msg) => { var payload = msg.payload; if (payload.cmd == "heartbeat" && payload.model == "gateway") { @@ -70,37 +63,12 @@ module.exports = function(RED) { state = "off"; } - if (node.output == "0") { - msg.payload = payload; - node.send([msg]); - } else if (node.output == "1") { - var status = null; - - if (data.status) { - status = {"payload": data.status}; - } - node.send([status]); - } else if (node.output == "2") { - var status = null; - - if (data.status === 'on') { - status = {"payload": mustache.render(node.onmsg, data)} - } else { - status = {"payload": mustache.render(node.offmsg, data)} - } - node.send([status]); - } + msg.payload = payload; + node.send([msg]); } }); - - node.on("close", function() { - }); - - } else { - // no gateway configured - if (this.key == "") { - node.status({fill:"red", shape:"dot", text:"no key configured"}); - } + } else if (this.key == "") { + node.status({fill:"red", shape:"dot", text:"no key configured"}); } } diff --git a/node-red-contrib-xiaomi-switch/xiaomi-switch.html b/node-red-contrib-xiaomi-switch/xiaomi-switch.html index ac4098e..cceefb7 100644 --- a/node-red-contrib-xiaomi-switch/xiaomi-switch.html +++ b/node-red-contrib-xiaomi-switch/xiaomi-switch.html @@ -11,8 +11,7 @@ output: {value: "0"} }, inputs: 1, - outputs: 2, - outputLabels: ["Single-click", "Double-click"], + outputs: 1, paletteLabel: "switch", icon: "light-icon.png", label: function () { @@ -53,14 +52,6 @@ changeGateway("switch"); }); - $("#node-input-output").change(function () { - if ($(this).val() == "2") { - $(".node-input-msg").show(); - } else { - $(".node-input-msg").hide(); - } - }); - $(".node-input-msg").hide(); $("#node-input-output").val(node.output); }, @@ -84,22 +75,6 @@ -
- - -
-
- - -
-
- - -
diff --git a/node-red-contrib-xiaomi-switch/xiaomi-switch.js b/node-red-contrib-xiaomi-switch/xiaomi-switch.js index fb19d3c..633b544 100644 --- a/node-red-contrib-xiaomi-switch/xiaomi-switch.js +++ b/node-red-contrib-xiaomi-switch/xiaomi-switch.js @@ -1,71 +1,8 @@ -module.exports = function(RED) { - "use strict"; - var mustache = require("mustache"); - var miDevicesUtils = require('../utils'); +const miDevicesUtils = require('../src/utils'); +module.exports = (RED) => { function XiaomiSwitchNode(config) { - RED.nodes.createNode(this, config); - this.gateway = RED.nodes.getNode(config.gateway); - this.sid = config.sid; - this.output = config.output; - this.outmsg = config.outmsg; - this.outmsgdbcl = config.outmsgdbcl; - - var node = this; - - node.status({fill:"grey", shape:"ring", text:"battery - na"}); - - if (this.gateway) { - node.on('input', function(msg) { - // var payload = JSON.parse(msg); - var payload = msg.payload; - - // Input from gateway - if (payload.sid) { - if (payload.sid == node.sid && ["switch", "sensor_switch.aq2"].indexOf(payload.model) >= 0) { - var data = payload.data; - miDevicesUtils.setStatus(node, data); - - if (node.output == "0") { - node.send([msg]); - } else if (node.output == "1") { - var status = null; - - if (data.status) { - status = {"payload": data.status}; - } - node.send([status]); - } else if (node.output == "2") { - var status = null; - - if (data.status && data.status == "click") { - status = {"payload": mustache.render(node.outmsg, data)} - node.send([[status],[]]); - } - - if (data.status && data.status == "double_click") { - status = {"payload": mustache.render(node.outmsgdbcl, data)} - node.send([[],[status]]); - } - } - } - } - // Prepare for request - else { - miDevicesUtils.prepareForGatewayRequest(node, msg); - node.send(msg); - } - }); - - node.on("close", function() { - }); - - } else { - // no gateway configured - } - + miDevicesUtils.defaultNode(RED, config, this); } - RED.nodes.registerType("xiaomi-switch", XiaomiSwitchNode); - -} +}; diff --git a/package.json b/package.json index 0426ee1..feec7d1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red-contrib-mi-devices", - "version": "1.0.0", + "version": "1.0.1", "description": "A set of nodes to control some of the popular Xiaomi sensors which are connected to the Xiaomi Gateway, and the Gateway itself.", "repository": { "type": "git", @@ -32,7 +32,9 @@ }, "dependencies": { "cryptojs": "^2.5.3", - "mustache": "^2.3.0", "miio": "0.13.0" + }, + "engines": { + "node" : ">=4.4.5" } } diff --git a/utils.js b/src/utils.js similarity index 61% rename from utils.js rename to src/utils.js index 44a7caa..ef84202 100644 --- a/utils.js +++ b/src/utils.js @@ -7,7 +7,7 @@ module.exports = { When full, CR2032 batteries are between 3 and 3.4V http://farnell.com/datasheets/1496885.pdf */ - return Math.min(Math.round((voltage - 2200) / 14), 100); + return Math.min(Math.round((voltage - 2200) / 10), 100); }, setStatus: function(node, data) { if (data.voltage) { @@ -39,6 +39,18 @@ module.exports = { return key; }, + sendWritePayloadToGateway: function(node, msg, data) { + let gateway = node.gateway; + if(gateway && gateway.sid && gateway.key && gateway.lastToken) { + data.sid = data.sid || gateway.sid; + data.key = this.getGatewayKey(gateway.key, gateway.lastToken); + msg.payload = { + cmd: "write", + data: data + }; + node.send(msg); + } + }, prepareForGatewayRequest: function(node, msg) { msg.sid = node.sid; msg.gateway = node.gateway; @@ -64,5 +76,31 @@ module.exports = { brightness: brightness, color: { red: red, green: green, blue: blue } }; + }, + defaultNode: function(RED, config, node) { + RED.nodes.createNode(node, config); + node.gateway = RED.nodes.getNode(config.gateway); + node.sid = config.sid; + + node.status({fill:"grey", shape:"ring", text:"battery - na"}); + + if (node.gateway) { + node.on('input', (msg) => { + let payload = msg.payload; + + // Input from gateway + if (payload.sid) { + if (payload.sid == node.sid) { + miDevicesUtils.setStatus(node, payload.data); + node.send([msg]); + } + } + // Prepare for request + else { + miDevicesUtils.prepareForGatewayRequest(node, msg); + node.send(msg); + } + }); + } } }