2017-03-25 15:36:04 +00:00
|
|
|
var dgram = require('dgram');
|
|
|
|
var protocol = require('./protocol');
|
|
|
|
var BufferReader = require('buffer-reader');
|
|
|
|
|
|
|
|
|
|
|
|
var responseHandlers = {};
|
|
|
|
|
|
|
|
function registerResponseHandler(command, callback)
|
|
|
|
{
|
|
|
|
if (!responseHandlers.hasOwnProperty(command))
|
|
|
|
responseHandlers[command] = [callback];
|
|
|
|
else
|
|
|
|
responseHandlers[command].push(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function callResponseHandlers(command, reader, error)
|
|
|
|
{
|
|
|
|
if (!responseHandlers.hasOwnProperty(command))
|
|
|
|
return;
|
|
|
|
|
|
|
|
newHandlers = [];
|
|
|
|
responseHandlers[command].forEach(function(callback)
|
|
|
|
{
|
|
|
|
if (!callback(reader, error))
|
|
|
|
newHandlers.push(callback);
|
|
|
|
});
|
|
|
|
|
|
|
|
responseHandlers[command] = newHandlers;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var serverHost = '';
|
|
|
|
var serverPort = 0;
|
|
|
|
|
|
|
|
var client = dgram.createSocket('udp4');
|
|
|
|
client.on('message', function (message, remote)
|
|
|
|
{
|
|
|
|
console.log(message.toString('hex'));
|
|
|
|
if (message.length < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var reader = new BufferReader(message);
|
|
|
|
|
|
|
|
if (reader.nextInt8() !== protocol.Command.Reply)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var command = reader.nextInt8();
|
|
|
|
if (command === protocol.Command.Error)
|
|
|
|
callResponseHandlers(reader.nextInt8(), reader, true)
|
|
|
|
else
|
|
|
|
callResponseHandlers(command, reader, false);
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2017-04-16 11:40:34 +00:00
|
|
|
function requestResponse(buffer, callback, withTimeout)
|
2017-03-25 15:36:04 +00:00
|
|
|
{
|
|
|
|
if (buffer === null || buffer.length == 0) return;
|
|
|
|
console.log('> ' + buffer.toString('hex'));
|
|
|
|
|
|
|
|
var command = buffer.readInt8(0);
|
|
|
|
var cancelled = false;
|
|
|
|
|
2017-04-16 11:40:34 +00:00
|
|
|
if (typeof(withTimeout) == 'undefined') withTimeout = true;
|
|
|
|
if (withTimeout)
|
2017-03-25 15:36:04 +00:00
|
|
|
{
|
2017-04-16 11:40:34 +00:00
|
|
|
var timeout = setTimeout(function()
|
|
|
|
{
|
|
|
|
cancelled = true;
|
|
|
|
callback(null, true);
|
|
|
|
clearTimeout(timeout);
|
|
|
|
}, 2000);
|
|
|
|
}
|
2017-03-25 15:36:04 +00:00
|
|
|
|
|
|
|
registerResponseHandler(command, function(reader, error)
|
|
|
|
{
|
|
|
|
if (cancelled) return;
|
2017-04-16 11:40:34 +00:00
|
|
|
if (withTimeout) clearTimeout(timeout);
|
2017-03-25 15:36:04 +00:00
|
|
|
|
|
|
|
callback(reader, error);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
|
|
|
|
client.send(buffer, 0, buffer.length, serverPort, serverHost, function(err, bytes)
|
|
|
|
{
|
|
|
|
if (err)
|
|
|
|
onError();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function readModeData(mode, reader)
|
|
|
|
{
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case protocol.Mode.Static:
|
|
|
|
return {
|
2017-05-20 08:15:24 +00:00
|
|
|
brightness: reader.nextInt16LE(),
|
|
|
|
easeTime: reader.nextInt16LE()
|
2017-03-25 15:36:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
case protocol.Mode.Custom:
|
|
|
|
var values = [];
|
|
|
|
while (reader.tell() < reader.buf.length)
|
|
|
|
values.push(reader.nextInt16LE());
|
|
|
|
|
|
|
|
return {
|
|
|
|
brightness: values
|
|
|
|
};
|
|
|
|
|
|
|
|
case protocol.Mode.Alternate:
|
|
|
|
return {
|
|
|
|
interval: reader.nextInt16LE(),
|
|
|
|
brightness: reader.nextInt16LE()
|
|
|
|
};
|
|
|
|
|
|
|
|
case protocol.Mode.Slide:
|
|
|
|
return {
|
|
|
|
interval: reader.nextInt16LE(),
|
|
|
|
brightness: reader.nextInt16LE(),
|
|
|
|
direction: reader.nextInt8(),
|
|
|
|
fadeOutTime: reader.nextInt16LE()
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-06 13:27:20 +00:00
|
|
|
function readRangeData(reader)
|
|
|
|
{
|
|
|
|
var data = { useScaling: reader.nextInt8() == 1, values: [] };
|
|
|
|
|
|
|
|
while (reader.tell() < reader.buf.length)
|
|
|
|
data.values.push({ start: reader.nextInt16LE(), end: reader.nextInt16LE() });
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-25 15:36:04 +00:00
|
|
|
function lsb(value) { return value & 0xFF; }
|
|
|
|
function msb(value) { return (value >> 8) & 0xFF; }
|
|
|
|
|
2017-03-27 20:57:41 +00:00
|
|
|
|
|
|
|
function getBrightness(value)
|
|
|
|
{
|
|
|
|
if (typeof(value) == 'string' && value.substr(-1) === '%')
|
|
|
|
return (Number(value.substr(0, value.length - 1)) * 4096 / 100);
|
|
|
|
|
|
|
|
return Number(value) || 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-25 15:36:04 +00:00
|
|
|
function writeModeData(mode, data)
|
|
|
|
{
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case protocol.Mode.Static:
|
2017-03-27 20:57:41 +00:00
|
|
|
var brightness = getBrightness(data.brightness);
|
2017-03-25 15:36:04 +00:00
|
|
|
|
2017-05-20 08:15:24 +00:00
|
|
|
return new Buffer([protocol.Command.SetMode, mode, lsb(brightness), msb(brightness), lsb(500), msb(500)]);
|
2017-03-25 15:36:04 +00:00
|
|
|
|
|
|
|
case protocol.Mode.Custom:
|
|
|
|
var brightness = typeof(data.brightness) !== 'undefined' ? data.brightness.split(',') : [];
|
|
|
|
|
|
|
|
var valueCount = Math.min(16, brightness.length);
|
|
|
|
var buffer = Buffer.alloc(2 + (valueCount * 2));
|
2017-04-16 11:40:34 +00:00
|
|
|
buffer.writeUInt8(protocol.Command.SetMode, 0);
|
|
|
|
buffer.writeUInt8(mode, 1);
|
2017-03-25 15:36:04 +00:00
|
|
|
|
|
|
|
for (var index = 0; index < valueCount; index++)
|
2017-04-16 11:40:34 +00:00
|
|
|
buffer.writeUInt16LE(getBrightness(brightness[index]), 2 + (index * 2));
|
2017-03-25 15:36:04 +00:00
|
|
|
|
|
|
|
return buffer;
|
|
|
|
|
|
|
|
case protocol.Mode.Alternate:
|
2017-03-27 20:57:41 +00:00
|
|
|
var brightness = getBrightness(data.brightness);
|
2017-03-25 15:36:04 +00:00
|
|
|
if (typeof(data.interval) == 'undefined') data.interval = 500;
|
|
|
|
|
|
|
|
return new Buffer([protocol.Command.SetMode, mode,
|
|
|
|
lsb(data.interval), msb(data.interval),
|
2017-03-27 20:57:41 +00:00
|
|
|
lsb(brightness), msb(brightness)]);
|
2017-03-25 15:36:04 +00:00
|
|
|
|
|
|
|
case protocol.Mode.Slide:
|
2017-03-27 20:57:41 +00:00
|
|
|
var brightness = getBrightness(data.brightness);
|
2017-03-25 15:36:04 +00:00
|
|
|
if (typeof(data.interval) == 'undefined') data.interval = 500;
|
|
|
|
if (typeof(data.direction) == 'undefined') data.direction = 0;
|
|
|
|
if (typeof(data.fadeOutTime) == 'undefined') data.fadeOutTime = 0;
|
|
|
|
|
|
|
|
return new Buffer([protocol.Command.SetMode, mode,
|
|
|
|
lsb(data.interval), msb(data.interval),
|
2017-03-27 20:57:41 +00:00
|
|
|
lsb(brightness), msb(brightness),
|
2017-03-25 15:36:04 +00:00
|
|
|
data.direction,
|
|
|
|
lsb(data.fadeOutTime), msb(data.fadeOutTime)]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-05-06 13:27:20 +00:00
|
|
|
function writeRangeData(data)
|
|
|
|
{
|
|
|
|
var start = typeof(data.start) !== 'undefined' ? data.start.split(',') : [];
|
|
|
|
var end = typeof(data.end) !== 'undefined' ? data.end.split(',') : [];
|
|
|
|
|
|
|
|
var valueCount = Math.min(16, start.length, end.length);
|
|
|
|
var buffer = Buffer.alloc(2 + (valueCount * 4));
|
|
|
|
buffer.writeUInt8(protocol.Command.SetRange, 0);
|
|
|
|
buffer.writeUInt8(data.useScaling ? 1 : 0, 1);
|
|
|
|
|
|
|
|
for (var index = 0; index < valueCount; index++)
|
|
|
|
{
|
|
|
|
buffer.writeUInt16LE(getBrightness(start[index]), 2 + (index * 4));
|
|
|
|
buffer.writeUInt16LE(getBrightness(end[index]), 4 + (index * 4));
|
|
|
|
}
|
|
|
|
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-25 15:36:04 +00:00
|
|
|
module.exports =
|
|
|
|
{
|
|
|
|
init: function(host, port)
|
|
|
|
{
|
|
|
|
serverHost = host;
|
|
|
|
serverPort = port;
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
ping: function(callback)
|
|
|
|
{
|
|
|
|
requestResponse(new Buffer([protocol.Command.Ping]),
|
|
|
|
function(reader, error)
|
|
|
|
{
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
callback(
|
|
|
|
{
|
|
|
|
stepCount: reader.nextInt8()
|
|
|
|
}, false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
callback(null, true);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
getMode: function(callback)
|
|
|
|
{
|
|
|
|
requestResponse(new Buffer([protocol.Command.GetMode]),
|
|
|
|
function(reader, error)
|
|
|
|
{
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
var data = { mode: reader.nextInt8() };
|
|
|
|
data.data = readModeData(data.mode, reader);
|
|
|
|
callback(data, false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
callback(null, true);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
setMode: function(mode, data, callback)
|
|
|
|
{
|
|
|
|
if (!protocol.Mode.hasOwnProperty(mode))
|
|
|
|
return;
|
|
|
|
|
|
|
|
requestResponse(writeModeData(protocol.Mode[mode], data),
|
|
|
|
function(reader, error)
|
|
|
|
{
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
var data = { mode: reader.nextInt8() };
|
|
|
|
data.data = readModeData(data.mode, reader);
|
|
|
|
callback(data, false);
|
|
|
|
}
|
2017-05-06 13:27:20 +00:00
|
|
|
else
|
|
|
|
callback(null, true);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
getRange: function(callback)
|
|
|
|
{
|
|
|
|
requestResponse(new Buffer([protocol.Command.GetRange]),
|
|
|
|
function(reader, error)
|
|
|
|
{
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
callback(readRangeData(reader), false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
callback(null, true);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
setRange: function(data, callback)
|
|
|
|
{
|
|
|
|
requestResponse(writeRangeData(data),
|
|
|
|
function(reader, error)
|
|
|
|
{
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
callback(readRangeData(reader), false);
|
|
|
|
}
|
2017-03-25 15:36:04 +00:00
|
|
|
else
|
|
|
|
callback(null, true);
|
|
|
|
});
|
2017-04-16 11:40:34 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
updateFirmware: function(data, callback)
|
|
|
|
{
|
|
|
|
if (typeof(data.host) == 'undefined') data.host = '';
|
|
|
|
if (typeof(data.port) == 'undefined') data.port = 80;
|
|
|
|
if (typeof(data.path) == 'undefined') data.path = '';
|
|
|
|
|
|
|
|
var buffer = Buffer.alloc(1 + (data.host.length + 1) + 2 + (data.path.length + 1));
|
|
|
|
buffer.writeUInt8(protocol.Command.UpdateFirmware, 0);
|
|
|
|
var position = 1;
|
|
|
|
|
|
|
|
buffer.writeUInt16LE(data.port, position);
|
|
|
|
position += 2;
|
|
|
|
|
|
|
|
buffer.write(data.host, position);
|
|
|
|
position += data.host.length;
|
|
|
|
buffer.writeUInt8(0, position);
|
|
|
|
position++;
|
|
|
|
|
|
|
|
buffer.write(data.path, position);
|
|
|
|
position += data.path.length;
|
|
|
|
buffer.writeUInt8(0, position);
|
|
|
|
|
|
|
|
requestResponse(buffer,
|
|
|
|
function(reader, error)
|
|
|
|
{
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
var data = { hasUpdates: reader.nextInt8() == 1 };
|
|
|
|
callback(data, false);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
callback(null, true);
|
|
|
|
}, false);
|
2017-03-25 15:36:04 +00:00
|
|
|
}
|
|
|
|
}
|