From 995ec864485f1b26d87b8d3f32889c863d59a00b Mon Sep 17 00:00:00 2001 From: marcus Date: Sun, 26 Apr 2015 15:41:33 +0200 Subject: [PATCH] Initial implementation --- README.md | 26 ++++++- example/example.js | 18 +++++ package.json | 28 +++++++ src/index.js | 5 ++ src/milight.js | 190 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 266 insertions(+), 1 deletion(-) create mode 100644 example/example.js create mode 100644 package.json create mode 100644 src/index.js create mode 100644 src/milight.js diff --git a/README.md b/README.md index 2b1d71a..108cadf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # node-milight-promise -A node module to control Milight Bulb also under various OEM brands such as Rocket LED Limitless LED Applamp, Easybulb, s`luce, iLight, iBulb, Kreuzer + +A node module to control Milight LED bulbs and OEM equivalents auch as Rocket LED, Limitless LED Applamp, + Easybulb, s`luce, iLight, iBulb, and Kreuzer + +NOTE: This is work in progress. + +## Usage Example + + var Milight = require('../src/index').MilightController; + var commands = require('../src/index').commands; + + + var light = new Milight({ + ip: "255.255.255.255" + }), + zone = 1; + + light.sendCommands(commands.rgbw.on(zone), commands.rgbw.brightness(100)); + for (var x=0; x<256; x++) { + light.sendCommands( commands.rgbw.on(zone), commands.rgbw.hue(x)); + } + light.pause(1000); + light.sendCommands(commands.rgbw.on(zone), commands.rgbw.whiteMode(zone)); + + light.close(); \ No newline at end of file diff --git a/example/example.js b/example/example.js new file mode 100644 index 0000000..b721c47 --- /dev/null +++ b/example/example.js @@ -0,0 +1,18 @@ +var Milight = require('../src/index').MilightController; +var commands = require('../src/index').commands; + + +var light = new Milight({ + ip: "255.255.255.255" + }), + zone = 1; + +light.sendCommands(commands.rgbw.on(zone), commands.rgbw.brightness(100)); +for (var x=0; x<256; x++) { + light.sendCommands( commands.rgbw.on(zone), commands.rgbw.hue(x)); +} +light.pause(1000); +light.sendCommands(commands.rgbw.on(zone), commands.rgbw.whiteMode(zone)); + +light.close(); + diff --git a/package.json b/package.json new file mode 100644 index 0000000..d14cce8 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "node-milight-promise", + "version": "0.0.1", + "description": "A node module to control Milight LED bulbs and OEM equivalents auch as Rocket LED, Limitless LED Applamp, Easybulb, s`luce, iLight, iBulb, and Kreuzer", + "author": { + "name": "Marcus Wittig", + "url": "https://github.com/mwittig/node-milight-promise" + }, + "main": "src/index", + "homepage": "https://github.com/mwittig/node-milight-promise", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/mwittig/node-milight-promise" + }, + "bugs": { + "url": "https://github.com/mwittig/node-milight-promise/issues" + }, + "keywords": [ + "SmartHome", + "Milight", + "WifiLight", + "LED" + ], + "dependencies": { + "bluebird": ">=2.9.24" + } +} \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..7665c63 --- /dev/null +++ b/src/index.js @@ -0,0 +1,5 @@ +module.exports = { + MilightController : require('./milight'), + + commands: require('./commands') +}; \ No newline at end of file diff --git a/src/milight.js b/src/milight.js new file mode 100644 index 0000000..40035a8 --- /dev/null +++ b/src/milight.js @@ -0,0 +1,190 @@ +var Promise = require('bluebird'), + dgram = require('dgram'), + debug = process.env.hasOwnProperty('MILIGHT_DEBUG') ? consoleDebug : function () { + }; + +const + DEFAULT_IP = '255.255.255.255', + DEFAULT_PORT = 8899, + DEFAULT_SEND_MESSAGE_DELAY = 100, + DEFAULT_COMMAND_DELAY = 1; + +// +// Local helper functions +// + +function buffer2hex(buffer) { + result = []; + for (var i = 0; i < buffer.length ; i++) { + result.push('0x' + buffer[i].toString(16)) + } + return result; +} + + +function consoleDebug() { + console.log.apply(this, arguments) +} + +// +// Class MilightController +// + +/** + * + * @param options + * @constructor + */ +var MilightController = function (options) { + options = options || {}; + + this.ip = options.ip || DEFAULT_IP; + this._broadcastMode = this.ip === DEFAULT_IP; + this.port = options.port || DEFAULT_PORT; + this._delayBetweenMessages = options.delayBetweenMessages || DEFAULT_SEND_MESSAGE_DELAY; + this._delayBetweenCommands = options.delayBetweenCommands || DEFAULT_COMMAND_DELAY; + this._socketInit = Promise.resolve(); + this._lastRequest = this._createSocket(); +}; + +// +// Private member functions +// + +MilightController.prototype._createSocket = function () { + var self = this; + + return Promise.settle([self._socketInit]).then(function () { + + return self._socketInit = new Promise(function (resolve, reject) { + if (self.clientSocket) { + return resolve(); + } + else { + debug("Initializing Socket"); + var socket = dgram.createSocket('udp4'); + + if (self._broadcastMode) { + socket.bind(function() { + socket.setBroadcast(true); + self.clientSocket = socket; + debug("Milight: Initializing Socket (broadcast mode) completed"); + return resolve(); + }); + } + else { + self.clientSocket = socket; + debug("Milight: Initializing Socket done"); + return resolve(); + } + } + }); + }); +}; + + +MilightController.prototype._sendThreeByteArray = function (threeByteArray) { + if (!threeByteArray instanceof Array) { + return Promise.reject(new Error("Array argument required")); + } + var buffer = new Buffer(threeByteArray), + self = this; + + return new Promise(function (resolve, reject) { + self._createSocket().then(function() { + self.clientSocket.send(buffer + , 0 + , buffer.length + , self.port + , self.ip + , function (err, bytes) { + if (err) { + debug("UDP socket error:" + err); + return reject(err); + } + else { + debug('Milight: bytesSent=' + bytes +', buffer=[' + buffer2hex(buffer) + ']'); + Promise.delay(self._delayBetweenCommands).then(function () { + return resolve(); + }); + } + } + ); + }).catch(function(error) { + reject(error); + }) + }) +}; + +// +// Public member functions +// + +/** + * + * @param varArgArray + * @returns {*} + */ +MilightController.prototype.sendCommands = function (varArgArray) { + var stackedCommands = [], + varArgs = arguments, + self = this; + + return self._lastRequest = Promise.settle([self._lastRequest]).then(function () { + + for (var i = 0; i < varArgs.length; i++) { + if (!varArgs[i] instanceof Array) { + return Promise.reject(new Error("Array arguments required")); + } + else { + var arg = varArgs[i]; + if (((arg.length) > 0) && (arg[0] instanceof Array)) { + for (var j = 0; j < arg.length; j++) { + stackedCommands.push(self._sendThreeByteArray(arg[j])); + } + } + else { + stackedCommands.push(self._sendThreeByteArray(arg)); + } + } + } + return Promise.settle(stackedCommands).then(function () { + return Promise.delay(self._delayBetweenMessages); + }); + }); +}; + + +/** + * + * @param ms + * @returns {*} + */ +MilightController.prototype.pause = function (ms) { + var self = this; + ms = ms || 100; + + return self._lastRequest = Promise.settle([self._lastRequest]).then(function () { + return Promise.delay(ms); + }) +}; + + +/** + * + * @returns {*} + */ +MilightController.prototype.close = function () { + var self = this; + + return self._lastRequest = Promise.settle([self._lastRequest]).then(function () { + if (self.clientSocket) { + self.clientSocket.close(); + delete self.clientSocket; + } + return Promise.resolve(); + }) +}; + + +module.exports = MilightController; \ No newline at end of file