Added decoding of messages
Also moved things out to their own files and setup require()s to bring it all together.
This commit is contained in:
75
index.js
75
index.js
@ -1,45 +1,7 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
const messages = require('./messages');
|
||||
|
||||
class FindUnits extends EventEmitter {
|
||||
constructor() {
|
||||
@ -124,44 +86,22 @@ class UnitConnection extends EventEmitter {
|
||||
this.client.write('CONNECTSERVERHOST\r\n\r\n');
|
||||
|
||||
console.log('sending challenge message...');
|
||||
var buf = new SLMessage(0, 14);
|
||||
this.client.write(buf.toBuffer());
|
||||
this.client.write(new messages.SLChallengeMessage().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());
|
||||
this.client.write(new messages.SLLoginMessage().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', {});
|
||||
this.client.write(new messages.SLPoolStatusMessage().toBuffer());
|
||||
}
|
||||
|
||||
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', {});
|
||||
this.client.write(new messages.SLControllerConfigMessage().toBuffer());
|
||||
}
|
||||
|
||||
onClientMessage(msg) {
|
||||
@ -175,10 +115,10 @@ class UnitConnection extends EventEmitter {
|
||||
this.emit('loggedIn');
|
||||
} else if (msgType === 12527) {
|
||||
console.log(" it's pool status");
|
||||
this.parsePoolStatus(msg);
|
||||
this.emit('poolStatus', new messages.SLPoolStatusMessage(msg));
|
||||
} else if (msgType === 12533) {
|
||||
console.log(" it's controller configuration");
|
||||
this.parseControllerConfig(msg);
|
||||
this.emit('controllerConfig', new messages.SLControllerConfigMessage(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,6 +131,5 @@ for (const value of buf.values()) {
|
||||
|
||||
module.exports = {
|
||||
FindUnits,
|
||||
SLMessage,
|
||||
UnitConnection
|
||||
}
|
||||
|
7
messages/SLChallengeMessage.js
Executable file
7
messages/SLChallengeMessage.js
Executable file
@ -0,0 +1,7 @@
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
exports.SLChallengeMessage = class SLChallengeMessage extends SLMessage {
|
||||
constructor() {
|
||||
super(0, 14);
|
||||
}
|
||||
}
|
78
messages/SLControllerConfigMessage.js
Executable file
78
messages/SLControllerConfigMessage.js
Executable file
@ -0,0 +1,78 @@
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
exports.SLControllerConfigMessage = class SLControllerConfigMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, 12532);
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(0);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.controllerId = this.readInt32LE();
|
||||
|
||||
this.minSetPoint = new Array(2);
|
||||
this.maxSetPoint = new Array(2);
|
||||
for (let i = 0; i < 2; i++) {
|
||||
this.minSetPoint[i] = this.readUInt8();
|
||||
this.maxSetPoint[i] = this.readUInt8();
|
||||
}
|
||||
|
||||
this.degC = this.readUInt8();
|
||||
this.controllerType = this.readUInt8();
|
||||
this.hwType = this.readUInt8();
|
||||
this.controllerData = this.readUInt8();
|
||||
this.equipFlags = this.readInt32LE();
|
||||
this.genCircuitName = this.readSLString();
|
||||
|
||||
let circuitCount = this.readInt32LE();
|
||||
this.bodyArray = new Array(circuitCount);
|
||||
for (let i = 0; i < circuitCount; i++) {
|
||||
this.bodyArray[i] = {
|
||||
circuitId: this.readInt32LE(),
|
||||
name: this.readSLString(),
|
||||
nameIndex: this.readUInt8(),
|
||||
function: this.readUInt8(),
|
||||
interface: this.readUInt8(),
|
||||
flags: this.readUInt8(),
|
||||
colorSet: this.readUInt8(),
|
||||
colorPos: this.readUInt8(),
|
||||
colorStagger: this.readUInt8(),
|
||||
deviceId: this.readUInt8(),
|
||||
dfaultRt: this.readUInt16LE(),
|
||||
pad1: this.readUInt8(),
|
||||
pad2: this.readUInt8()
|
||||
}
|
||||
}
|
||||
|
||||
let colorCount = this.readInt32LE();
|
||||
this.colorArray = new Array(colorCount);
|
||||
for (let i = 0; i < colorCount; i++) {
|
||||
this.colorArray[i] = {
|
||||
name: this.readSLString(),
|
||||
color: {
|
||||
r: this.readInt32LE() & 0xFF,
|
||||
g: this.readInt32LE() & 0xFF,
|
||||
b: this.readInt32LE() & 0xFF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pumpCircCount = 8;
|
||||
this.pumpCircArray = new Array(pumpCircCount);
|
||||
for (let i = 0; i < pumpCircCount; i++) {
|
||||
this.pumpCircArray[i] = this.readUInt8();
|
||||
}
|
||||
|
||||
this.interfaceTabFlags = this.readInt32LE();
|
||||
this.showAlarms = this.readInt32LE();
|
||||
}
|
||||
}
|
12
messages/SLLoginMessage.js
Executable file
12
messages/SLLoginMessage.js
Executable file
@ -0,0 +1,12 @@
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
exports.SLLoginMessage = class SLLoginMessage extends SLMessage {
|
||||
constructor() {
|
||||
super(0, 27);
|
||||
this.writeInt32LE(348); // schema
|
||||
this.writeInt32LE(0); // connection type
|
||||
this.writeSLString('node-screenlogic'); // version
|
||||
this.writeSLBuffer(Buffer.alloc(16)); // encoded password. empty/unused for local connections
|
||||
this.writeInt32LE(2); // procID
|
||||
}
|
||||
}
|
57
messages/SLMessage.js
Executable file
57
messages/SLMessage.js
Executable file
@ -0,0 +1,57 @@
|
||||
const SmartBuffer = require('smart-buffer').SmartBuffer;
|
||||
|
||||
exports.SLMessage = 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);
|
||||
if (str.length % 4 != 0) {
|
||||
this.skipWrite(4 - (str.length % 4));
|
||||
}
|
||||
}
|
||||
|
||||
readSLString() {
|
||||
var len = this.readInt32LE();
|
||||
var str = this.readString(len);
|
||||
if (len % 4 != 0) {
|
||||
this.readOffset += 4 - (len % 4);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
writeSLBuffer(buf) {
|
||||
this.writeInt32LE(buf.length);
|
||||
this.writeBuffer(buf);
|
||||
}
|
||||
|
||||
skipWrite(num) {
|
||||
if (num > 0) {
|
||||
this.writeBuffer(Buffer.alloc(num));
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
this.readOffset = 0;
|
||||
this.senderId = this.readUInt16LE();
|
||||
this.messageId = this.readUInt16LE();
|
||||
this.dataLength = this.readInt32LE();
|
||||
}
|
||||
}
|
70
messages/SLPoolStatusMessage.js
Executable file
70
messages/SLPoolStatusMessage.js
Executable file
@ -0,0 +1,70 @@
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
exports.SLPoolStatusMessage = class SLPoolStatusMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, 12526);
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.ok = this.readInt32LE();
|
||||
this.freezeMode = this.readUInt8();
|
||||
this.remotes = this.readUInt8();
|
||||
this.poolDelay = this.readUInt8();
|
||||
this.spaDelay = this.readUInt8();
|
||||
this.cleanerDelay = this.readUInt8();
|
||||
this.readOffset += 3;
|
||||
this.airTemp = this.readInt32LE();
|
||||
|
||||
let bodiesCount = this.readInt32LE();
|
||||
if (bodiesCount > 2) {
|
||||
bodiesCount = 2;
|
||||
}
|
||||
this.currentTemp = new Array(bodiesCount);
|
||||
this.heatStatus = new Array(bodiesCount);
|
||||
this.setPoint = new Array(bodiesCount);
|
||||
this.coolSetPoint = new Array(bodiesCount);
|
||||
this.heatMode = new Array(bodiesCount);
|
||||
for (let i = 0; i < bodiesCount; i++) {
|
||||
let bodyType = this.readInt32LE();
|
||||
if (bodyType < 0 || bodyType >= 2) {
|
||||
bodyType = 0;
|
||||
}
|
||||
this.currentTemp[bodyType] = this.readInt32LE();
|
||||
this.heatStatus[bodyType] = this.readInt32LE();
|
||||
this.setPoint[bodyType] = this.readInt32LE();
|
||||
this.coolSetPoint[bodyType] = this.readInt32LE();
|
||||
this.heatMode[bodyType] = this.readInt32LE();
|
||||
}
|
||||
|
||||
let circuitCount = this.readInt32LE();
|
||||
this.circuitArray = new Array(circuitCount);
|
||||
for (let i = 0; i < circuitCount; i++) {
|
||||
this.circuitArray[i] = {
|
||||
id: this.readInt32LE(),
|
||||
state: this.readInt32LE(),
|
||||
colorSet: this.readUInt8(),
|
||||
colorPos: this.readUInt8(),
|
||||
colorStagger: this.readUInt8(),
|
||||
delay: this.readUInt8()
|
||||
}
|
||||
}
|
||||
|
||||
this.pH = this.readInt32LE();
|
||||
this.orp = this.readInt32LE();
|
||||
this.saturation = this.readInt32LE();
|
||||
this.saltPPM = this.readInt32LE();
|
||||
this.pHTank = this.readInt32LE();
|
||||
this.orpTank = this.readInt32LE();
|
||||
this.alarms = this.readInt32LE();
|
||||
}
|
||||
}
|
4
messages/index.js
Executable file
4
messages/index.js
Executable file
@ -0,0 +1,4 @@
|
||||
exports.SLPoolStatusMessage = require("./SLPoolStatusMessage.js").SLPoolStatusMessage;
|
||||
exports.SLControllerConfigMessage = require("./SLControllerConfigMessage.js").SLControllerConfigMessage;
|
||||
exports.SLChallengeMessage = require("./SLChallengeMessage.js").SLChallengeMessage;
|
||||
exports.SLLoginMessage = require("./SLLoginMessage.js").SLLoginMessage;
|
@ -5,6 +5,7 @@
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/parnic/ScreenLogicConnect-NodeJS.git",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"smart-buffer": "~4.0.1"
|
||||
}
|
||||
|
41
test.js
41
test.js
@ -1,18 +1,23 @@
|
||||
const ScreenLogic = require('./index');
|
||||
|
||||
var finder = new ScreenLogic.FindUnits();
|
||||
finder.on('serverFound', function(server) {
|
||||
var client = new ScreenLogic.UnitConnection(server);
|
||||
client.on('loggedIn', function() {
|
||||
this.getPoolStatus();
|
||||
this.getControllerConfig();
|
||||
}).on('poolStatus', function(status) {
|
||||
}).on('controllerConfig', function(config) {
|
||||
client.close();
|
||||
finder.close();
|
||||
});
|
||||
|
||||
client.connect();
|
||||
});
|
||||
|
||||
finder.search();
|
||||
const ScreenLogic = require('./index');
|
||||
|
||||
var finder = new ScreenLogic.FindUnits();
|
||||
finder.on('serverFound', function(server) {
|
||||
var client = new ScreenLogic.UnitConnection(server);
|
||||
client.on('loggedIn', function() {
|
||||
this.getPoolStatus();
|
||||
this.getControllerConfig();
|
||||
}).on('poolStatus', function(status) {
|
||||
console.log(" pool ok=" + status.ok);
|
||||
console.log(" air temp=" + status.airTemp);
|
||||
console.log(" salt ppm=" + status.saltPPM * 50);
|
||||
console.log(" pH=" + status.pH / 100);
|
||||
}).on('controllerConfig', function(config) {
|
||||
console.log(" controller is in celsius=" + config.degC);
|
||||
client.close();
|
||||
finder.close();
|
||||
});
|
||||
|
||||
client.connect();
|
||||
});
|
||||
|
||||
finder.search();
|
||||
|
Reference in New Issue
Block a user