diff --git a/node-red-contrib-xiaomi-yeelight/icons/mi-yeelight.png b/icons/yeelight/mi-yeelight.png similarity index 100% rename from node-red-contrib-xiaomi-yeelight/icons/mi-yeelight.png rename to icons/yeelight/mi-yeelight.png diff --git a/node-red-contrib-xiaomi-all/xiaomi-all.html b/node-red-contrib-xiaomi-all/xiaomi-all.html index e63c67a..bd70aaf 100644 --- a/node-red-contrib-xiaomi-all/xiaomi-all.html +++ b/node-red-contrib-xiaomi-all/xiaomi-all.html @@ -19,12 +19,20 @@ oneditprepare: function() { var node = this; + function getOnlyModelsValue(input) { + var cleanOnlyModels = []; + input.forEach((value) => { + cleanOnlyModels = cleanOnlyModels.concat(value.split(',')); + }); + return cleanOnlyModels; + } + function changeGateway(gateway, onlyModels, excludedSids) { var configNodeID = gateway || $('#node-input-gateway').val(); if (configNodeID) { var configNode = RED.nodes.node(configNodeID); if(configNode) { - onlyModels = onlyModels || $('#node-input-onlyModels').val() || []; + onlyModels = getOnlyModelsValue(onlyModels || $('#node-input-onlyModels').val() || []); excludedSids = excludedSids || $('#node-input-excludedSids').val() || []; $('#node-input-excludedSids').empty(); for (key in configNode.deviceList) { diff --git a/node-red-contrib-xiaomi-all/xiaomi-all.js b/node-red-contrib-xiaomi-all/xiaomi-all.js index df3cb30..04c96fc 100644 --- a/node-red-contrib-xiaomi-all/xiaomi-all.js +++ b/node-red-contrib-xiaomi-all/xiaomi-all.js @@ -32,7 +32,7 @@ module.exports = (RED) => { if (this.gateway) { this.on('input', (msg) => { // Filter input - if(msg.payload.model && msg.payload.sid) { + if(msg.payload && msg.payload.model && msg.payload.sid) { if(!this.isDeviceValid(msg.payload)) { msg = null; } diff --git a/node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.js b/node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.js deleted file mode 100644 index 693be4d..0000000 --- a/node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.js +++ /dev/null @@ -1,57 +0,0 @@ -const miDevicesUtils = require('../src/utils'); -const Yeelight = require("yeelight2"); - -module.exports = (RED) => { - function XiaomiYeelightOutputNode(config) { - RED.nodes.createNode(this, config); - this.ip = config.ip; - this.port = config.port; - - this.status({fill:"grey", shape:"ring", text:"na"}); - - this.setupConnection = function(){ - try { - this.light = Yeelight(`yeelight://${this.ip}:${this.port}`); - this.status({fill:"blue", shape:"dot", text:"connected"}); - } catch(err) { - this.status({fill:"red",shape:"ring",text:err.message}); - this.light = null; - this.error(err); - - // try to reconnect in 5 minutes - window.setTimeout((function(self) { - return function() { - self.setupConnection.apply(self, arguments); - } - })(this), 1000*60*5); - } - } - - if (this.ip && this.port) { - this.setupConnection(); - this.on('input', (msg) => { - if(msg.payload === "on") { - this.light && this.light.set_power('on'); - } - else if(msg.payload === "off") { - this.light && this.light.set_power('off'); - } - else if(msg.payload === "toggle") { - this.light && this.light.toggle(); - } - - if(msg.payload.color !== undefined) { - this.light && this.light.set_rgb(msg.payload.color); - } - if(msg.payload.brightness !== undefined) { - this.light && this.light.set_bright(msg.payload.brightness); - } - }); - - this.on('close', () => { - this.light && this.light.exit(); - }); - } - } - RED.nodes.registerType("xiaomi-yeelight out", XiaomiYeelightOutputNode); -}; diff --git a/package.json b/package.json index bfc4283..f6c69b9 100644 --- a/package.json +++ b/package.json @@ -6,10 +6,24 @@ "type": "git", "url": "git+ssh://git@github.com:pierrecle/node-red-contrib-mi-devices.git" }, + "scripts": { + "clean": "rimraf dist", + "build": "npm run clean && npm run build:ts && npm run build:ejs && npm run build:icons", + "build:ts": "tsc --allowUnreachableCode -p .", + "build:ejs": "npm run build:ejs:indexes", + "build:ejs:indexes": "ejs-cli --base-dir src/ --options \"{\\\"NODES_PREFIX\\\": \\\"mi-devices\\\"}\" \"**/index.ejs\" --out dist/", + "build:ejs:devices": "ejs-cli --base-dir src/ --options \"{\\\"NODES_PREFIX\\\": \\\"mi-devices\\\"}\" \"nodes/devices/*.ejs\" --out dist/", + "build:icons": "npm run build:icons:yeelight", + "build:icons:gateway": "cp -pr icons/gateway dist/nodes/gateway/icons", + "build:icons:devices": "cp -pr icons/devices dist/nodes/devices/icons", + "build:icons:actions": "cp -pr icons/actions dist/nodes/actions/icons", + "build:icons:yeelight": "cp -pr icons/yeelight dist/nodes/yeelight/icons" + }, "license": "MIT", "keywords": [ "Xiaomi", "Aqara", + "Yeelight", "node-red" ], "node-red": { @@ -24,7 +38,7 @@ "xiaomi-configurator": "node-red-contrib-xiaomi-configurator/xiaomi-configurator.js", "xiaomi-gateway": "node-red-contrib-xiaomi-gateway/xiaomi-gateway.js", "xiaomi-actions": "node-red-contrib-xiaomi-actions/xiaomi-actions.js", - "xiaomi-yeelight": "node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.js" + "xiaomi-yeelight": "dist/nodes/yeelight/index.js" } }, "author": "Pierre CLEMENT", @@ -33,10 +47,18 @@ }, "dependencies": { "cryptojs": "^2.5.3", - "miio": "0.13.0", - "yeelight2": "^1.3.5" + "lumi-aqara": "^1.4.0", + "miio": "^0.14.1", + "yeelight-wifi": "^2.3.0" }, "engines": { "node": ">=4.4.5" + }, + "devDependencies": { + "@types/node-red": "^0.17.1", + "ejs": "^2.5.7", + "ejs-cli": "^2.0.0", + "rimraf": "^2.6.2", + "typescript": "^2.6.2" } } diff --git a/src/nodes/constants.ts b/src/nodes/constants.ts new file mode 100644 index 0000000..51b56bb --- /dev/null +++ b/src/nodes/constants.ts @@ -0,0 +1,3 @@ +export class Constants { + static readonly NODES_PREFIX = "mi-devices"; +} \ No newline at end of file diff --git a/src/nodes/yeelight/Searcher.ts b/src/nodes/yeelight/Searcher.ts new file mode 100644 index 0000000..b3d67a1 --- /dev/null +++ b/src/nodes/yeelight/Searcher.ts @@ -0,0 +1,33 @@ +import { Red } from "node-red"; + +import * as YeelightSearch from 'yeelight-wifi'; +import { Constants } from "../constants"; + +export class Searcher { + static _bulbs:any[] = []; + + static discover(RED:Red) { + new Promise(() => { + (new YeelightSearch()).on('found', (bulb:any) => { + this._bulbs.push({ + name: bulb.name, + model: bulb.model, + sid: parseInt(bulb.id), + ip: bulb.hostname + }); + RED.nodes.eachNode((tmpNode) => { + if(tmpNode.type.indexOf(`${Constants.NODES_PREFIX}-yeelight configurator`) === 0) { + let tmpNodeInst = RED.nodes.getNode(tmpNode.id); + if(tmpNodeInst.ip === bulb.hostname || tmpNodeInst.sid === parseInt(bulb.id)) { + tmpNodeInst.setBulb(bulb); + } + } + }); + }); + }); + } + + static get bulbs() { + return this._bulbs; + } +} \ No newline at end of file diff --git a/src/nodes/yeelight/YeelightConfigurator.ejs b/src/nodes/yeelight/YeelightConfigurator.ejs new file mode 100644 index 0000000..929972e --- /dev/null +++ b/src/nodes/yeelight/YeelightConfigurator.ejs @@ -0,0 +1,73 @@ + + + + + diff --git a/src/nodes/yeelight/YeelightConfigurator.ts b/src/nodes/yeelight/YeelightConfigurator.ts new file mode 100644 index 0000000..a2146de --- /dev/null +++ b/src/nodes/yeelight/YeelightConfigurator.ts @@ -0,0 +1,41 @@ +import { Red, NodeProperties, NodeStatus, ClearNodeStatus } from "node-red"; +import { Constants } from "../constants"; +import { Searcher } from "./Searcher"; + +export interface IYeelightConfiguratorNode { + ip:string; + sid:number; + bulb:any; + + on(event: "bulbFound", listener: () => void): any; +} + +export default (RED:Red) => { + class YeelightConfigurator { + ip:string; + sid:number; + _bulb:any; + + constructor(props: NodeProperties) { + RED.nodes.createNode( this, props); + let {ip, sid} = props; + this.sid = sid; + this.ip = ip; + } + + set bulb(bulb) { + this._bulb = bulb; + ( this).emit('bulbFound'); + } + + get bulb() { + return this._bulb; + } + } + + RED.nodes.registerType(`${Constants.NODES_PREFIX}-yeelight configurator`, YeelightConfigurator, { + settings: { + miDevicesYeelightConfiguratorDiscoveredBulbs: { value: Searcher.bulbs, exportable: true } + } + }); +}; diff --git a/node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.html b/src/nodes/yeelight/YeelightOut.ejs similarity index 66% rename from node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.html rename to src/nodes/yeelight/YeelightOut.ejs index 19daae7..4ee7404 100644 --- a/node-red-contrib-xiaomi-yeelight/xiaomi-yeelight.html +++ b/src/nodes/yeelight/YeelightOut.ejs @@ -1,11 +1,10 @@ - -