iwy_master
Version:
Controls IWY Light Master via TCP scocket
282 lines (222 loc) • 5.87 kB
JavaScript
EventEmitter = require('events').EventEmitter;
net = require('net');
util = require('util');
Color = require('color');
/*
constants
*/
var DEFAULT_PORT = 5577,
WHITE = 'WHITE',
COLOR = 'COLOR';
var ON_VALUE = 0x23,
OFF_VALUE = 0x24,
ON_MSG = [0xcc, ON_VALUE, 0x33],
OFF_MSG = [0xcc, OFF_VALUE, 0x33],
MODE = { WHITE: 0x0F, COLOR: 0xF0},
STATE_REQUEST_MSG = [0xef, 0x01, 0x77];
/*
constructor
*/
function IwyMaster(host, port, device) {
if (host === null || host === undefined) {
throw new Error('host address requiered');
}
if(typeof port === 'string') {
device = port;
port = null;
}
this._host = host;
this._port = port || DEFAULT_PORT;
this._device = device || IwyMaster.DEVICES.IWY_MASTER;
this._powerState = null;
this._mode = null;
this._brightness = null;
this._color = Color();
}
/*
inherit from EventEmitter
*/
util.inherits(IwyMaster, EventEmitter);
/*
class variables
*/
IwyMaster.DEVICES = {
IWY_MASTER: 'iwy-master',
WIFI370: 'wifi370'
}
/*
private methods
*/
IwyMaster.prototype._lightMsg = function() {
if(this._device === IwyMaster.DEVICES.WIFI370){
return new Buffer([
0x56,
this._color.rgb().r,
this._color.rgb().g,
this._color.rgb().b,
0xAA
]);
} else {
return new Buffer([
0x56,
this._color.rgb().r,
this._color.rgb().g,
this._color.rgb().b,
Math.round(this._brightness * 2.55, 0),
MODE[this._mode],
0xAA
]);
}
}
IwyMaster.prototype._requestState = function(client, cb) {
var msg = new Buffer(STATE_REQUEST_MSG);
this.once('stateUpdated', cb);
this.once('connTimeout', cb);
client.write(msg, function (err) {
if(err) cb(err);
});
}
IwyMaster.prototype._send = function(overwriteFn, cb) {
var self = this;
var errorEmitter = function(err) {
if(!err) return;
self.emit('error', err);
}
var errorCb = cb || errorEmitter;
this.once('connTimeout', errorCb);
this._connect(function (err, client) {
if(err) return errorCb(err);
var currentPowerState = self._powerState;
self._requestState(client, function() {
overwriteFn();
var onOff = function (cb) {
if(currentPowerState !== self._powerState) {
var msg = self._powerState === true ? ON_MSG : OFF_MSG;
msg = new Buffer(msg);
client.write(msg, function (err) {
cb(err);
});
} else {
cb();
}
}
onOff(function(err) {
if(err) {
client.end();
return errorCb(err, self._getStateObj());
}
client.write(self._lightMsg(), function (err) {
client.end();
errorCb(err, self._getStateObj());
});
});
});
});
}
IwyMaster.prototype._getStateObj = function () {
return {
power: this._powerState,
mode: this._mode,
brightness: this._brightness,
color: this._color.rgb()
}
}
IwyMaster.prototype._receiveState = function(data) {
this._powerState = data[2] === ON_VALUE;
this._color.rgb(data[6], data[7], data[8]);
if(data[6] === 0 && data[7] === 0 && data[8] === 0) {
this._mode = WHITE;
// cast it in a scale between 0 and 100
this._brightness = Math.round((data[9] / 255) * 100, 0);
}
else {
this._mode = COLOR;
// in case of color mode we can just assume the brightness
this._brightness = this._color.hsl().l
}
this.emit('stateUpdated');
}
IwyMaster.prototype._connect = function(cb) {
var self = this,
isCbExecuted = false;
var client = new net.Socket();
var timeoutId = setTimeout(function() {
var err = new Error('connection timeout');
client.destroy();
if(!isCbExecuted) {
cb(err);
} else {
self.emit('connTimeout', err);
}
}, 2000);
client.on('close', function() {
clearTimeout(timeoutId);
});
client.on('error', function(error) {
client.destroy();
isCbExecuted = true;
cb(error);
});
// only used to get the current state of the device
client.on('data', this._receiveState.bind(this));
client.connect(this._port, this._host, function(err) {
isCbExecuted = true;
cb(err, client);
});
}
/*
public methods
*/
IwyMaster.prototype.switchOn = function(callback) {
var overwrite = function() {
this._powerState = true;
}
this._send(overwrite.bind(this), callback);
}
IwyMaster.prototype.switchOff = function(callback) {
var overwrite = function() {
this._powerState = false;
}
this._send(overwrite.bind(this), callback);
}
IwyMaster.prototype.getState = function(callback) {
var overwrite = function() {}
this._send(overwrite.bind(this), callback);
}
IwyMaster.prototype.setWhite = function(callback) {
var overwrite = function() {
this._mode = WHITE;
this._color.rgb(0, 0, 0);
}
this._send(overwrite.bind(this), callback);
}
IwyMaster.prototype.setColor = function(red, green, blue, callback) {
if(!arguments.hasOwnProperty('2') || typeof(arguments['2']) != 'number') {
throw new Error('worng arguments');
}
for (key in arguments) {
if(arguments[key] < 0 || arguments[key] > 255) {
throw new Error('value must be between 0 and 255!');
}
}
var overwrite = function() {
this._mode = COLOR;
this._color.rgb(red, green, blue);
}
this._send(overwrite.bind(this), callback);
}
IwyMaster.prototype.setBrightness = function(brightness, callback) {
if(brightness < 0 || brightness > 100) {
throw new Error('brightness must be a value between 0 and 100!');
}
var overwrite = function() {
this._brightness = brightness;
if(this._mode === COLOR) {
hsl = this._color.hsl();
hsl.l = brightness;
this._color = this._color.hsl(hsl);
}
}
this._send(overwrite.bind(this), callback);
}
module.exports = IwyMaster;