Files
node-screenlogic/index.js
Parnic af02f60791 Code cleanup
Much better organization and ease-of-use now that the proof of concept is established. Still an awful lot to do here, but I'm getting the hang of the whole node.js thing.
Also moved the actual test functionality out to a test script so the module itself doesn't execute anything.
2018-03-30 10:27:01 -05:00

197 lines
4.7 KiB
JavaScript

var dgram = require('dgram');
var net = require('net');
const SmartBuffer = require('smart-buffer').SmartBuffer;
const EventEmitter = require('events');
class SLMessage extends SmartBuffer {
constructor(senderId, messageId) {
super();
this.writeUInt16LE(senderId);
this.writeUInt16LE(messageId);
this._wroteSize = false;
}
toBuffer() {
if (this._wroteSize === false) {
this.insertInt32LE(this.length - 4, 4);
this._wroteSize = true;
} else {
this.writeInt32LE(this.length - 8, 4);
}
return super.toBuffer();
}
writeSLString(str) {
this.writeInt32LE(str.length);
this.writeString(str);
this.skip(4 - (str.length % 4));
}
writeSLBuffer(buf) {
this.writeInt32LE(buf.length);
this.writeBuffer(buf);
}
skip(num) {
if (num > 0) {
this.writeBuffer(Buffer.alloc(num));
}
}
}
class FindUnits extends EventEmitter {
constructor() {
super();
this.finder = dgram.createSocket('udp4');
var _this = this;
this.finder.on('message', function (message, remote) {
_this.foundServer(message, remote);
}).on('close', function() {
console.log('finder closed');
});
}
search() {
var _this = this;
this.finder.bind(function() {
_this.finder.setBroadcast(true);
_this.finder.setMulticastTTL(128);
_this.sendServerBroadcast();
});
}
foundServer(message, remote) {
console.log('Found something');
var server = {
address: remote.address,
type: message.readInt32LE(0),
port: message.readInt16LE(8),
gatewayType: message.readUInt8(10),
gatewaySubtype: message.readUInt8(11),
gatewayName: message.toString('utf8', 12, 28)
};
console.log(' type: ' + server.type + ', host: ' + server.address + ':' + server.port + ', identified as ' + server.gatewayName);
if (server.type === 2) {
this.emit('serverFound', server);
}
}
sendServerBroadcast() {
var message = new Uint8Array(8);
message[0] = 1;
this.finder.send(message, 0, message.length, 1444, "255.255.255.255");
console.log("Looking for ScreenLogic hosts...");
}
close() {
this.finder.close();
}
}
class UnitConnection extends EventEmitter {
constructor(server) {
super();
this.server = server;
this.client = new net.Socket();
var _this = this;
this.client.on('data', function(msg) {
_this.onClientMessage(msg);
}).on('close', function(had_error) {
console.log('unit connection closed');
});
}
close() {
this.client.end();
}
connect() {
console.log("connecting...");
var _this = this;
this.client.connect(this.server.port, this.server.address, function() {
_this.onConnected();
});
}
onConnected() {
console.log('connected');
console.log('sending init message...');
this.client.write('CONNECTSERVERHOST\r\n\r\n');
console.log('sending challenge message...');
var buf = new SLMessage(0, 14);
this.client.write(buf.toBuffer());
}
login() {
console.log('sending login message...');
var buf = new SLMessage(0, 27);
buf.writeInt32LE(348); // schema
buf.writeInt32LE(0); // connection type
buf.writeSLString('ScreenLogicConnect library'); // version
buf.writeSLBuffer(Buffer.alloc(16)); // encoded password. empty/unused for local connections
buf.writeInt32LE(2); // procID
this.client.write(buf.toBuffer());
}
getPoolStatus() {
console.log('sending pool status query...');
var buf = new SLMessage(0, 12526);
buf.writeInt32LE(0);
this.client.write(buf.toBuffer());
}
parsePoolStatus(msg) {
// todo: actually parse the message
this.emit('poolStatus', {});
}
getControllerConfig() {
console.log('sending controller config query...');
var buf = new SLMessage(0, 12532);
buf.writeInt32LE(0);
buf.writeInt32LE(0);
this.client.write(buf.toBuffer());
}
parseControllerConfig(msg) {
// todo: actually parse the message
this.emit('controllerConfig', {});
}
onClientMessage(msg) {
console.log('received message of length ' + msg.length);
var msgType = msg.readInt16LE(2);
if (msgType === 15) {
console.log(" it's a challenge response");
this.login();
} else if (msgType === 28) {
console.log(" it's a login response");
this.emit('loggedIn');
} else if (msgType === 12527) {
console.log(" it's pool status");
this.parsePoolStatus(msg);
} else if (msgType === 12533) {
console.log(" it's controller configuration");
this.parseControllerConfig(msg);
}
}
}
/* debug print full buffer contents:
for (const value of buf.values()) {
console.log(value.toString(16));
}
*/
module.exports = {
FindUnits,
SLMessage,
UnitConnection
}