Stairs/web/client.js

342 lines
8.1 KiB
JavaScript

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);
});
function requestResponse(buffer, callback, withTimeout)
{
if (buffer === null || buffer.length == 0) return;
console.log('> ' + buffer.toString('hex'));
var command = buffer.readInt8(0);
var cancelled = false;
if (typeof(withTimeout) == 'undefined') withTimeout = true;
if (withTimeout)
{
var timeout = setTimeout(function()
{
cancelled = true;
callback(null, true);
clearTimeout(timeout);
}, 2000);
}
registerResponseHandler(command, function(reader, error)
{
if (cancelled) return;
if (withTimeout) clearTimeout(timeout);
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 {
brightness: reader.nextInt16LE(),
easeTime: reader.nextInt16LE()
};
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;
}
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;
}
function lsb(value) { return value & 0xFF; }
function msb(value) { return (value >> 8) & 0xFF; }
function getBrightness(value)
{
if (typeof(value) == 'string' && value.substr(-1) === '%')
return (Number(value.substr(0, value.length - 1)) * 4096 / 100);
return Number(value) || 0;
}
function writeModeData(mode, data)
{
switch (mode)
{
case protocol.Mode.Static:
var brightness = getBrightness(data.brightness);
return new Buffer([protocol.Command.SetMode, mode, lsb(brightness), msb(brightness), lsb(500), msb(500)]);
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));
buffer.writeUInt8(protocol.Command.SetMode, 0);
buffer.writeUInt8(mode, 1);
for (var index = 0; index < valueCount; index++)
buffer.writeUInt16LE(getBrightness(brightness[index]), 2 + (index * 2));
return buffer;
case protocol.Mode.Alternate:
var brightness = getBrightness(data.brightness);
if (typeof(data.interval) == 'undefined') data.interval = 500;
return new Buffer([protocol.Command.SetMode, mode,
lsb(data.interval), msb(data.interval),
lsb(brightness), msb(brightness)]);
case protocol.Mode.Slide:
var brightness = getBrightness(data.brightness);
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),
lsb(brightness), msb(brightness),
data.direction,
lsb(data.fadeOutTime), msb(data.fadeOutTime)]);
}
}
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;
}
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);
}
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);
}
else
callback(null, true);
});
},
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);
}
}