diff --git a/MiLight.html b/MiLight.html
index b92983d..e27485e 100644
--- a/MiLight.html
+++ b/MiLight.html
@@ -1,120 +1,89 @@
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/MiLight.js b/MiLight.js
index 4461d0a..300aa79 100644
--- a/MiLight.js
+++ b/MiLight.js
@@ -1,60 +1,168 @@
-module.exports = function(RED) {
- function MiLightPower(config) {
- RED.nodes.createNode(this,config);
- var node = this;
- var MiLight = require('milight');
- var milight = new MiLight({
- host: config.ip,
- broadcast: config.broadcast
- });
- var zone = config.zone;
- this.on('input', function(msg) {
- if (msg.payload == "off") milight.zone(zone).off();
- if (msg.payload == "on") milight.zone(zone).on();
- });
- };
- RED.nodes.registerType("MiLightPower",MiLightPower);
-
- function MiLightRGB(config) {
- RED.nodes.createNode(this,config);
- var node = this;
- var MiLight = require('milight');
-
- var milight = new MiLight({
- host: config.ip,
- broadcast: config.broadcast
- });
- var zone = config.zone;
-
-
- this.on('input', function(msg) {
- if (typeof msg.payload == "string") {
- milight.zone(zone).rgb(msg.payload);
- } else {
- milight.zone(zone).rgb(msg.payload.rgb || "#FFFFFF");
- milight.zone(zone).brightness(msg.payload.brightness) || 100;
- }
- });
- };
- RED.nodes.registerType("MiLightRGB",MiLightRGB);
-
- function MiLightWhite(config) {
- RED.nodes.createNode(this,config);
- var node = this;
- var MiLight = require('milight');
-
- var milight = new MiLight({
- host: config.ip,
- broadcast: config.broadcast
- });
- var zone = config.zone;
-
+module.exports = function (RED) {
+ "use strict";
+
+ var Milight = require('node-milight-promise');
+ var packageFile = require('./package.json');
+ var Color = require('tinycolor2');
+
+ function node(config) {
+
+ RED.nodes.createNode(this, config);
+ var node = this;
- this.on('input', function(msg) {
- milight.zone(zone).white(msg.payload);
- });
- };
- RED.nodes.registerType("MiLightWhite",MiLightWhite);
+ // backwards compatibility with previous versions
+ if (config.bridgetype == null || config.bridgetype === '') {
+ config.bridgetype = 'legacy'
+ }
+ var light = new Milight.MilightController({
+ ip: config.ip,
+ delayBetweenCommands: (config.bridgetype !== 'v6') ? 200 : 100,
+ commandRepeat: 1,
+ type: config.bridgetype,
+ broadcastMode: config.broadcast
+ }),
+ zone = Number(config.zone),
+ bulb = config.bulbtype;
+ if (config.bridgetype === 'v6') {
+ var commands = Milight.commandsV6[bulb];
+ }
+ else if (bulb === 'white') {
+ var commands = Milight.commands[bulb];
+ }
+ else {
+ var commands = Milight.commands2[bulb];
+ }
+
+ this.on('input', function (msg) {
+ function argsHelper(vargs) {
+ var argsArray = [].slice.call(arguments);
+ if (config.bridgetype === 'v6' && bulb !== 'bridge') {
+ return [zone].concat(argsArray);
+ }
+ return argsArray;
+ }
+ function getSelectedObjectValues(sourceObject, keys) {
+ var values = [];
+ keys.forEach(function(key) { values.push(sourceObject[key]) });
+ return values;
+ }
+
+ light.ready().then(function () {
+ var command = msg.command ? msg.command : msg.topic;
+ if (commands == null) {
+ node.error("Selected combination of bridge type and bulb type is not supported");
+ return;
+ }
+ if (bulb !== 'white') {
+ switch (msg.payload) {
+ case 'off':
+ light.sendCommands(commands.off(zone));
+ break;
+ case 'on':
+ light.sendCommands(commands.on(zone));
+ break;
+ case 'disco':
+ light.sendCommands(commands.on(zone));
+ for (var x = 0; x < 256; x += 5) {
+ light.sendCommands(
+ commands.hue.apply(commands, argsHelper(x)));
+ light.pause(100);
+ }
+ break;
+ case 'mode':
+ light.sendCommands(commands.on(zone), commands.effectModeNext(zone));
+ break;
+ case 'speed_up':
+ light.sendCommands(commands.on(zone), commands.effectSpeedUp(zone));
+ break;
+ case 'speed_down':
+ light.sendCommands(commands.on(zone), commands.effectSpeedDown(zone));
+ break;
+ case 'white':
+ light.sendCommands(commands.on(zone), commands.whiteMode(zone));
+ break;
+ case 'night':
+ // nightMode command needs to be sent twice with some bulb types
+ light.sendCommands(commands.nightMode(zone), commands.nightMode(zone));
+ break;
+ default:
+ var value = Number(msg.payload);
+ if (command === 'rgb') {
+ var color = new Color(msg.payload);
+ if (color.isValid()) {
+ var args = argsHelper.apply(
+ node,
+ getSelectedObjectValues(color.toRgb(), ['r', 'g', 'b']));
+ light.sendCommands(commands.on(zone),
+ commands.rgb.apply(commands, args));
+ }
+ else {
+ throw(new Error("Invalid color value: " + msg.payload))
+ }
+ }
+ else if (!isNaN(value)) {
+ if (command === 'brightness')
+ light.sendCommands(
+ commands.on(zone),
+ commands.brightness.apply(commands, argsHelper(value)));
+ else if (command === 'color')
+ light.sendCommands(
+ commands.on(zone),
+ commands.hue.apply(commands, argsHelper(value, true)));
+ else if (command === 'saturation' && bulb === 'fullColor')
+ light.sendCommands(
+ commands.on(zone),
+ commands.saturation(zone, value, true));
+ }
+ break;
+ }
+ } else {
+ switch (msg.payload) {
+ case 'off':
+ light.sendCommands(commands.off(zone));
+ break;
+ case 'on':
+ light.sendCommands(commands.on(zone));
+ break;
+ case 'bright_up':
+ light.sendCommands(commands.brightUp(zone));
+ break;
+ case 'bright_down':
+ light.sendCommands(commands.brightDown(zone));
+ break;
+ case 'cooler':
+ light.sendCommands(commands.cooler(zone));
+ break;
+ case 'warmer':
+ light.sendCommands(commands.warmer(zone));
+ break;
+ case 'bright_max':
+ light.sendCommands(commands.maxBright(zone));
+ break;
+ case 'night':
+ light.sendCommands(commands.nightMode(zone));
+ break;
+ }
+ }
+ }).catch(function (error) {
+ node.error('Milight error: ' + error);
+ });
+ });
+
+ this.on('close', function (done) {
+ light.close()
+ .catch(function (error) {
+ // just log the error as a normal log message
+ // as it is safe to ignore the error at this point
+ node.log(error)
+ })
+ .finally(function () {
+ done()
+ });
+ });
+ }
-}
+ RED.nodes.registerType("MiLight", node);
+ RED.log.info(packageFile.name + '@' + packageFile.version + ' started');
+};
\ No newline at end of file
diff --git a/README.md b/README.md
index 81fa9be..51ba495 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,42 @@
# node-red-contrib-milight
-A Node Red wrapper for https://github.com/oeuillot/node-milight.git
-Currently presents 3 Nodes
-* Milight RGB (set RGB colors)
-* Milight White (set white mode brightness)
-* Milight Power (on/off control)
-## To Do
-Create a single Node which encompasses all functionality
+A Node Red nodes to control all bulb types Milight LED bulbs and OEM equivalents such as Rocket LED, Limitless LED Applamp, Easybulb, s'luce, iLight, iBulb, and Kreuzer.
-## Known Issues
+## Install
-* MiLightRGB has brightness controls, however causes an odd flicker when changing
+```npm i node-red-contrib-milight```
+## Example Flow
+ [{"id":"b5ab74d6.08edc8","type":"MiLight","z":"44cc4fb7.965b3","name":"White Bulb / Legacy Bridge","bridgetype":"legacy","bulbtype":"white","zone":1,"ip":"255.255.255.255","broadcast":true,"x":1035.765625,"y":297,"wires":[]},{"id":"51aa22c0.74d05c","type":"MiLight","z":"44cc4fb7.965b3","name":"Color Bulb / Legacy Bridge","bridgetype":"legacy","bulbtype":"rgbw","zone":1,"ip":"255.255.255.255","broadcast":true,"x":1036.7656211853027,"y":619.0000314712524,"wires":[]},{"id":"26ce02ea.cd2e6e","type":"inject","z":"44cc4fb7.965b3","name":"Off","topic":"","payload":"off","payloadType":"str","repeat":"","crontab":"","once":false,"x":534.7656402587891,"y":553.0000228881836,"wires":[["51aa22c0.74d05c"]]},{"id":"17bef0b.fe9ce0f","type":"inject","z":"44cc4fb7.965b3","name":"On","topic":"","payload":"on","payloadType":"str","repeat":"","crontab":"","once":false,"x":534.7656555175781,"y":607.0000152587891,"wires":[["51aa22c0.74d05c"]]},{"id":"44e9d122.6a83b","type":"inject","z":"44cc4fb7.965b3","name":"white","topic":"","payload":"white","payloadType":"str","repeat":"","crontab":"","once":false,"x":536.765625,"y":662.9999933242798,"wires":[["51aa22c0.74d05c"]]},{"id":"b477791.b31d088","type":"inject","z":"44cc4fb7.965b3","name":"Brightness 10%","topic":"","payload":"10","payloadType":"num","repeat":"","crontab":"","once":false,"x":568.765625,"y":732,"wires":[["e01cd3d0.06b27"]]},{"id":"e01cd3d0.06b27","type":"function","z":"44cc4fb7.965b3","name":"","func":"msg.command = 'brightness';\nreturn msg;","outputs":1,"noerr":0,"x":741.7656478881836,"y":757.0000553131104,"wires":[["51aa22c0.74d05c"]]},{"id":"865a0a9b.be0848","type":"inject","z":"44cc4fb7.965b3","name":"Color","topic":"","payload":"20","payloadType":"num","repeat":"","crontab":"","once":false,"x":536.7656307220459,"y":843.0000419616699,"wires":[["f578b6a1.3d7948"]]},{"id":"f578b6a1.3d7948","type":"function","z":"44cc4fb7.965b3","name":"","func":"msg.command = 'color';\nreturn msg;","outputs":1,"noerr":0,"x":738.7657089233398,"y":845.000057220459,"wires":[["51aa22c0.74d05c"]]},{"id":"2cc2747e.452f4c","type":"inject","z":"44cc4fb7.965b3","name":"On","topic":"","payload":"on","payloadType":"str","repeat":"","crontab":"","once":false,"x":540.7656326293945,"y":168.00000381469727,"wires":[["b5ab74d6.08edc8"]]},{"id":"c8b4fa4d.e53078","type":"inject","z":"44cc4fb7.965b3","name":"Off","topic":"","payload":"off","payloadType":"str","repeat":"","crontab":"","once":false,"x":540.2656383514404,"y":215.00000190734863,"wires":[["b5ab74d6.08edc8"]]},{"id":"9fb07ff8.51bba","type":"inject","z":"44cc4fb7.965b3","name":"Warmer","topic":"","payload":"warmer","payloadType":"str","repeat":"","crontab":"","once":false,"x":538.7656211853027,"y":258.00000381469727,"wires":[["b5ab74d6.08edc8"]]},{"id":"38e7a6b0.c484ca","type":"inject","z":"44cc4fb7.965b3","name":"Cooler","topic":"","payload":"cooler","payloadType":"str","repeat":"","crontab":"","once":false,"x":542.2656402587891,"y":303.00001525878906,"wires":[["b5ab74d6.08edc8"]]},{"id":"287ae328.5c945c","type":"inject","z":"44cc4fb7.965b3","name":"Bright Up","topic":"","payload":"bright_up","payloadType":"str","repeat":"","crontab":"","once":false,"x":548.7656555175781,"y":346.00002670288086,"wires":[["b5ab74d6.08edc8"]]},{"id":"268eff9f.0e175","type":"inject","z":"44cc4fb7.965b3","name":"Bright Down","topic":"","payload":"bright_down","payloadType":"str","repeat":"","crontab":"","once":false,"x":557.7656326293945,"y":392.99999809265137,"wires":[["b5ab74d6.08edc8"]]},{"id":"7f19fef2.a20dc","type":"inject","z":"44cc4fb7.965b3","name":"Bright Max","topic":"","payload":"bright_max","payloadType":"str","repeat":"","crontab":"","once":false,"x":547.7656383514404,"y":438.0000286102295,"wires":[["b5ab74d6.08edc8"]]},{"id":"afe72b66.8856e8","type":"inject","z":"44cc4fb7.965b3","name":"Night","topic":"","payload":"night","payloadType":"str","repeat":"","crontab":"","once":false,"x":539.7656536102295,"y":485.00002098083496,"wires":[["b5ab74d6.08edc8"]]},{"id":"1c0123a9.9f827c","type":"inject","z":"44cc4fb7.965b3","name":"Brightness 100%","topic":"","payload":"100","payloadType":"num","repeat":"","crontab":"","once":false,"x":568.7656555175781,"y":778.750057220459,"wires":[["e01cd3d0.06b27"]]}]
+
+The following bulb types are supported:
+ - WW/CW, aka. "white"
+ - RGB WW, aka. "color"
+ - RGB WW/CW, aka. "full color" (iBox1/iBox2 bridges)
+ - RGB CW bridge light (iBox1 bridge)
+
+To control the bulb pass the command to `msg.payload` as follows:
+
+ - 'on' - Turns the bulb on (all bulb types)
+ - 'off' - Turns the bulb off (all bulb types)
+ - 'night' - Turn the night mode (all bulb types)
+ - 'white' - Sets a color bulb to white (color bulb types only)
+ - 'disco' - Cycles a bulb through all the colors (color bulb types, only)
+ - 'mode' - Cycles through the effect modes (color bulb types only)
+ - 'speed_up' - Increase the speed of effect mode (color bulb types, only)
+ - 'speed_down' - Decrease the speed of effect mode (color bulb types, only)
+ - 'bright_up' - Increase the brightness of the bulb (white bulb, only)
+ - 'bright_down' - Decrease the brightness of the bulb (white bulb, only)
+ - 'cooler' - Make the bulb cooler (white bulb, only)
+ - 'warmer' - Make the bulb warmer (white bulb, only)
+ - 'bright_max' - Make the bulb brightness maximum (white bulb, only)
+ - *Number* - If a number is provided the brightness, color, or saturation value of the color bulb can be controlled
+ by assigning a command verb to `msg.command` or `msg.topic` as follows
+ - 'brightness' - Set brightness, `msg.payload` must contain a number in 0 - 100
+ - 'color' - Set color, `msg.payload` must contain a number in 0 - 255
+ - 'saturation' - Set color saturation (full color bulb type, only), `msg.payload` must contain a number in 0 - 100
+ - *Color String* - If a color string, e.g. "blue" or "rgb(255, 128, 128)" is provided and the command verb "rgb" is
+ assigned to `msg.command` or `msg.topic` the RGB color can be set. Note, however, for "color" and "bridge" type
+ bulbs the Milight hue will be set, only. For "full color" bulbs the saturation and brightness will be also set.
diff --git a/package.json b/package.json
index eb50852..c2bacd9 100644
--- a/package.json
+++ b/package.json
@@ -1,26 +1,47 @@
{
- "name" : "node-red-contrib-milight",
- "version" : "0.0.2",
- "description" : "A Node-RED node to control the MiLight family (limitless led etc) of light globes. A wrapper for https://github.com/oeuillot/node-milight.git",
- "license": "Apache-2.0",
- "keywords": [ "node-red", "milight", "limitless led", "scheduler" ],
- "node-red" : {
- "nodes" : {
- "MiLightRGB": "MiLight.js",
- "MiLightWhite": "MiLight.js",
- "MiLightPower": "MiLight.js"
- }
- },
- "dependencies": {
- "milight" : "1.0.0"
+ "name": "node-red-contrib-milight",
+ "version": "1.0.2",
+ "description": "A Node-RED node to control the MiLight family (limitless led, easybulb) of light.",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "predev": "npm i -g",
+ "dev": "node-red -v"
},
"repository": {
"type": "git",
- "url": "https://github.com/Daniel-t/node-red-contrib-milight.git"
+ "url": "https://github.com/stephenkeep/node-red-contrib-milight.git"
+ },
+ "contributors": [
+ {
+ "name": "Stephen Keep",
+ "email": "stephenkeep@gmail.com"
+ },
+ {
+ "name": "Bilal Al-Saeedi",
+ "email": "bilal_alsaidi@yahoo.com"
+ },
+ {
+ "name": "Marcus Wittig",
+ "email": "wittigmarcus@gmail.com"
+ }
+ ],
+ "author": {
+ "name": "Stephen Keep"
},
- "author": {
- "name": "Daniel Thomas",
- "email": "daniel@networklighthouse.com",
- "url": "https://github.com/Daniel-t"
+ "license": "Apache-2.0",
+ "keywords": [
+ "node-red",
+ "milight",
+ "limitless led",
+ "easybulb"
+ ],
+ "node-red": {
+ "nodes": {
+ "MiLight": "MiLight.js"
}
+ },
+ "dependencies": {
+ "node-milight-promise": "^0.2.31",
+ "tinycolor2": "^1.4.1"
+ }
}