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:
Parnic
2018-03-30 16:07:56 -05:00
parent af02f60791
commit b6ee816fb3
9 changed files with 259 additions and 86 deletions

View File

@ -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
View File

@ -0,0 +1,7 @@
const SLMessage = require('./SLMessage.js').SLMessage;
exports.SLChallengeMessage = class SLChallengeMessage extends SLMessage {
constructor() {
super(0, 14);
}
}

View 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
View 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
View 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
View 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
View 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;

View File

@ -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
View File

@ -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();