Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
a59fae5f8c
|
|||
f315bcd6a5
|
|||
a0e2569743
|
|||
9205e3b62b | |||
8e0cb20620
|
|||
98edd602ec
|
|||
ba1fb3fb6c
|
|||
6a1d31c224
|
|||
43725ae083
|
|||
81b3a61c28
|
|||
da9864462b
|
|||
a2d39e7463
|
|||
ed99d411b2
|
|||
5a55c56ac1
|
|||
e24f49285c
|
|||
ba19b6802e
|
|||
c9afb53810
|
|||
22357f11e2
|
|||
22858061f7
|
|||
9337068826
|
|||
0397e8ad8f
|
|||
c310885598
|
|||
661d8db173
|
|||
e567e9c821 |
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@ -7,8 +7,11 @@
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"program": "${workspaceFolder}\\example.js"
|
||||
"name": "Example",
|
||||
"program": "${workspaceFolder}/example.js",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
21
CHANGELOG.md
21
CHANGELOG.md
@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## v1.4.0 - 2020-05-25
|
||||
### Added
|
||||
* Support for controlling the salt cell generator's output levels.
|
||||
* Helper methods for interpreting `controllerType`.
|
||||
* Experimental support for an Equipment Configuration message (not documented as of yet - `SLEquipmentConfigurationMessage` / `getEquipmentConfiguration()`). This message returns arrays of various information about the equipment, but I don't know how to interpret the information in those arrays yet. Any assistance with decoding this information would be hugely helpful.
|
||||
* `error` handler on all objects for reacting to unhandled node errors.
|
||||
|
||||
### Fixed
|
||||
* VSCode "Example" configuration can now be launched on non-Windows platforms.
|
||||
|
||||
### Changed
|
||||
* Minor memory/performance optimizations.
|
||||
* Running tests no longer changes any state of any pool equipment.
|
||||
|
||||
## v1.3.1 - 2019-12-27
|
||||
### Added
|
||||
* Several methods added to SLControllerConfigMessage for interpreting the equipFlags value.
|
||||
|
||||
### Fixed
|
||||
* server.gatewayName no longer cuts off the last character of the name. #14 - thanks @mikemucc
|
||||
|
||||
## v1.3.0 - 2019-11-26
|
||||
### Added
|
||||
* Ability to set heat setpoint.
|
||||
|
53
README.md
53
README.md
@ -99,6 +99,8 @@ finder.on('serverFound', function(server) {
|
||||
})
|
||||
```
|
||||
|
||||
* `error` - Indicates that an unhandled error was caught (such as the connection timing out)
|
||||
|
||||
## RemoteLogin
|
||||
|
||||
### constructor(systemName)
|
||||
@ -123,6 +125,7 @@ Closes the connection
|
||||
### Events
|
||||
|
||||
* `gatewayFound` - Indicates that the search for the named unit has completed (may or may not be successful). Event handler receives a [`SLGetGatewayDataMessage`](#slgetgatewaydatamessage) argument.
|
||||
* `error` - Indicates that an unhandled error was caught (such as the connection timing out)
|
||||
|
||||
## UnitConnection
|
||||
|
||||
@ -206,6 +209,10 @@ Sends a lighting command. See [`SLLightControlMessage`](#sllightcontrolmessage)
|
||||
|
||||
Note that better/more complete handling of lighting is desired, but I have yet to find all the commands I need to implement to make that happen. This currently sends each command to all lights and there is no ability to send to an individual light. Pull requests adding more functionality here would be most welcome.
|
||||
|
||||
### setSaltCellOutput(controllerId, poolOutput, spaOutput)
|
||||
|
||||
Sets the salt cell's output levels. See [`SLSetSaltCellConfigMessage`](#slsetsaltcellconfigmessage) documentation for argument values. Emits the `setSaltCellConfig` event when response is acknowledged.
|
||||
|
||||
### Events
|
||||
|
||||
* `loggedIn` - Indicates that a connection to the server has been established and the login process completed. `get` methods can be called once this event has been emitted.
|
||||
@ -216,9 +223,12 @@ Note that better/more complete handling of lighting is desired, but I have yet t
|
||||
* `controllerConfig` - Indicates that a response to `getControllerConfig()` has been received. Event handler receives a [`SLControllerConfigMessage`](#slcontrollerconfigmessage) object.
|
||||
* `circuitStateChanged` - Indicates that a response to `setCircuitState()` has been received. Event handler receives a [`SLSetCircuitStateMessage`](#slsetcircuitstatemessage) object.
|
||||
* `setPointChanged` - Indicates that a response to `setSetPoint()` has been received. Event handler receives a [`SLSetHeatSetPointMessage`](#slsetheatsetpointmessage) object.
|
||||
* `heatModeChanged` - Indicates that a response to `setHeatMode()` has been received. Event handler receives a [`SLSetHeatModeMessage`](#slsetheatmodemessage) object.
|
||||
* `sentLightCommand` - Indicates that a response to `sendLightCommand()` has been received. Event handler receives a [`SLLightControlMessage`](#sllightcontrolmessage) object.
|
||||
* `setSaltCellConfig` - Indicates that a response to `setSaltCellOutput()` has been received. Event handler receives a [`SLSetSaltCellConfigMessage`](#slsetsaltcellconfigmessage) object.
|
||||
* `loginFailed` - Indicates that a remote login attempt via supplying a system address and password to `UnitConnection` has failed likely due to the incorrect password being used.
|
||||
* `badParameter` - Indicates that a bad parameter has been supplied to a function. This can be triggered, for example, by sending the wrong controller ID to a `set` function.
|
||||
* `error` - Indicates that an unhandled error was caught (such as the connection timing out)
|
||||
|
||||
### Properties
|
||||
|
||||
@ -320,17 +330,48 @@ Passed as an argument to the emitted `saltCellConfig` event handler.
|
||||
### Properties
|
||||
|
||||
* `installed` - boolean indicating whether a salt cell is installed or not
|
||||
* `status` - integer
|
||||
* `level1` - integer
|
||||
* `level2` - integer
|
||||
* `status` - integer bitmask
|
||||
* `level1` - integer indicating the output level of the salt cell for the pool. I believe this operates on a 0-100 scale
|
||||
* `level2` - integer indicating the output level of the salt cell for the spa. I believe this operates on a 0-100 scale
|
||||
* `salt` - integer indicating salt level in parts-per-million
|
||||
* `flags` - integer
|
||||
* `flags` - integer bitmask
|
||||
* `superChlorTimer` - integer
|
||||
|
||||
## SLSetSaltCellConfigMessage
|
||||
|
||||
Passed as an argument to the emitted `setSaltCellConfig` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command.
|
||||
|
||||
### Properties
|
||||
|
||||
* `controllerId` - integer indicating the ID of the controller to send this command to.
|
||||
* Note that while `SLControllerConfigMessage` includes a controllerId, this ID, in my experience, should always be 0.
|
||||
* `poolOutput` - integer indicating the output level of the salt cell for the pool. I believe this operates on a 0-100 scale.
|
||||
* `spaOutput` - integer indicating the output level of the salt cell for the spa. I believe this operates on a 0-100 scale.
|
||||
|
||||
## SLControllerConfigMessage
|
||||
|
||||
Passed as an argument to the emitted `controllerConfig` event handler.
|
||||
|
||||
### hasSolar()
|
||||
|
||||
Returns a bool indicating whether the system has solar present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasSolarAsHeatpump()
|
||||
|
||||
Returns a bool indicating whether the system has a solar heatpump (UltraTemp, ThermalFlo) present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasChlorinator()
|
||||
|
||||
Returns a bool indicating whether the system has a chlorinator present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasCooling()
|
||||
|
||||
Returns a bool indicating whether the system has a cooler present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasIntellichem()
|
||||
|
||||
Returns a bool indicating whether the system has an IntelliChem chemical management system present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### Properties
|
||||
|
||||
* `controllerId` - integer indicating the controller's ID
|
||||
@ -340,7 +381,7 @@ Passed as an argument to the emitted `controllerConfig` event handler.
|
||||
* `controllerType` - byte
|
||||
* `hwType` - byte
|
||||
* `controllerData` - byte
|
||||
* `equipFlags` - integer
|
||||
* `equipFlags` - integer indicating the type(s) of equipment present in the system (see helper methods above for interpreting these values)
|
||||
* `genCircuitName` - string indicating the circuit name
|
||||
* `bodyArray` - array (size number-of-circuits) holding circuit data
|
||||
* `circuitId` - integer indicating circuit ID (e.g.: 500 is spa, 505 is pool)
|
||||
@ -386,7 +427,7 @@ Passed as an argument to the emitted `setPointChanged` event. The passed version
|
||||
|
||||
## SLSetHeatModeMessage
|
||||
|
||||
Passed as an argument to the emitted `setHeatMode` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command.
|
||||
Passed as an argument to the emitted `heatModeChanged` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command.
|
||||
|
||||
### Properties
|
||||
|
||||
|
23
index.js
23
index.js
@ -15,6 +15,8 @@ class FindUnits extends EventEmitter {
|
||||
_this.foundServer(message, remote);
|
||||
}).on('close', function() {
|
||||
// console.log('finder closed');
|
||||
}).on('error', function(e) {
|
||||
_this.emit('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
@ -36,7 +38,7 @@ class FindUnits extends EventEmitter {
|
||||
port: message.readInt16LE(8),
|
||||
gatewayType: message.readUInt8(10),
|
||||
gatewaySubtype: message.readUInt8(11),
|
||||
gatewayName: message.toString('utf8', 12, 28),
|
||||
gatewayName: message.toString('utf8', 12, 29),
|
||||
};
|
||||
|
||||
// console.log(' type: ' + server.type + ', host: ' + server.address + ':' + server.port + ',
|
||||
@ -70,6 +72,8 @@ class RemoteLogin extends EventEmitter {
|
||||
_this.onClientMessage(msg);
|
||||
}).on('close', function(had_error) {
|
||||
// console.log('remote login server connection closed');
|
||||
}).on('error', function(e) {
|
||||
_this.emit('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
@ -146,6 +150,8 @@ class UnitConnection extends EventEmitter {
|
||||
}
|
||||
}).on('close', function(had_error) {
|
||||
// console.log('unit connection closed');
|
||||
}).on('error', function(e) {
|
||||
_this.emit('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
@ -202,6 +208,10 @@ class UnitConnection extends EventEmitter {
|
||||
this.client.write(new messages.SLVersionMessage().toBuffer());
|
||||
}
|
||||
|
||||
getEquipmentConfiguration() {
|
||||
this.client.write(new messages.SLEquipmentConfigurationMessage().toBuffer());
|
||||
}
|
||||
|
||||
setCircuitState(controllerId, circuitId, circuitState) {
|
||||
this.client.write(new messages.SLSetCircuitStateMessage(controllerId, circuitId, circuitState).toBuffer());
|
||||
}
|
||||
@ -218,6 +228,10 @@ class UnitConnection extends EventEmitter {
|
||||
this.client.write(new messages.SLLightControlMessage(controllerId, command).toBuffer());
|
||||
}
|
||||
|
||||
setSaltCellOutput(controllerId, poolOutput, spaOutput) {
|
||||
this.client.write(new messages.SLSetSaltCellConfigMessage(controllerId, poolOutput, spaOutput).toBuffer());
|
||||
}
|
||||
|
||||
onClientMessage(msg) {
|
||||
// console.log('received message of length ' + msg.length);
|
||||
if (msg.length < 4) {
|
||||
@ -271,6 +285,13 @@ class UnitConnection extends EventEmitter {
|
||||
// console.log(" it's a light control ack");
|
||||
this.emit('sentLightCommand', new messages.SLLightControlMessage());
|
||||
break;
|
||||
case messages.SLSetSaltCellConfigMessage.getResponseId():
|
||||
// console.log(" it's a set salt cell config ack");
|
||||
this.emit('setSaltCellConfig', new messages.SLSetSaltCellConfigMessage());
|
||||
break;
|
||||
case messages.SLEquipmentConfigurationMessage.getResponseId():
|
||||
this.emit('equipmentConfiguration', new messages.SLEquipmentConfigurationMessage(msg));
|
||||
break;
|
||||
case 13:
|
||||
// console.log(" it's a login failure.");
|
||||
this.emit('loginFailed');
|
||||
|
@ -6,7 +6,11 @@ const MSG_ID = 14;
|
||||
|
||||
exports.SLChallengeMessage = class SLChallengeMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (buf) {
|
||||
this._wroteSize = true;
|
||||
|
@ -6,7 +6,12 @@ const MSG_ID = 12592;
|
||||
|
||||
exports.SLChemDataMessage = class SLChemDataMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0); // controller index
|
||||
} else {
|
||||
|
@ -6,7 +6,12 @@ const MSG_ID = 12532;
|
||||
|
||||
exports.SLControllerConfigMessage = class SLControllerConfigMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(0);
|
||||
@ -82,4 +87,44 @@ exports.SLControllerConfigMessage = class SLControllerConfigMessage extends SLMe
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
|
||||
hasSolar() {
|
||||
return !!(this.equipFlags & 0x1);
|
||||
}
|
||||
|
||||
hasSolarAsHeatpump() {
|
||||
return !!(this.equipFlags & 0x2);
|
||||
}
|
||||
|
||||
hasChlorinator() {
|
||||
return !!(this.equipFlags & 0x4);
|
||||
}
|
||||
|
||||
hasCooling() {
|
||||
return !!(this.equipFlags & 0x800);
|
||||
}
|
||||
|
||||
hasIntellichem() {
|
||||
return !!(this.equipFlags & 0x8000);
|
||||
}
|
||||
|
||||
isEasyTouch() {
|
||||
return this.controllerType === 14 || this.controllerType === 13;
|
||||
}
|
||||
|
||||
isIntelliTouch() {
|
||||
return this.controllerType !== 14 && this.controllerType !== 13 && this.controllerType !== 10;
|
||||
}
|
||||
|
||||
isEasyTouchLite() {
|
||||
return this.controllerType === 13 && (this.hwType & 4) !== 0;
|
||||
}
|
||||
|
||||
isDualBody() {
|
||||
return this.controllerType === 5;
|
||||
}
|
||||
|
||||
isChem2() {
|
||||
return this.controllerType === 252 && this.hwType === 2;
|
||||
}
|
||||
};
|
||||
|
51
messages/SLEquipmentConfigurationMessage.js
Normal file
51
messages/SLEquipmentConfigurationMessage.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12566;
|
||||
|
||||
exports.SLEquipmentConfigurationMessage = class SLEquipmentConfigurationMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(0);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.controllerType = this.readUInt8();
|
||||
this.hardwareType = this.readUInt8();
|
||||
this.readUInt8();
|
||||
this.readUInt8();
|
||||
this.controllerData = this.readInt32LE();
|
||||
this.versionDataArray = this.readSLArray();
|
||||
this.speedDataArray = this.readSLArray();
|
||||
this.valveDataArray = this.readSLArray();
|
||||
this.remoteDataArray = this.readSLArray();
|
||||
this.sensorDataArray = this.readSLArray();
|
||||
this.delayDataArray = this.readSLArray();
|
||||
this.macroDataArray = this.readSLArray();
|
||||
this.miscDataArray = this.readSLArray();
|
||||
this.lightDataArray = this.readSLArray();
|
||||
this.flowDataArray = this.readSLArray();
|
||||
this.sgDataArray = this.readSLArray();
|
||||
this.spaFlowDataArray = this.readSLArray();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -6,7 +6,11 @@ const MSG_ID = 18003;
|
||||
|
||||
exports.SLGetGatewayDataMessage = class SLGetGatewayDataMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf && typeof buf === 'object') {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (typeof buf === 'string') {
|
||||
this.writeSLString(buf);
|
||||
|
@ -3,12 +3,27 @@
|
||||
const SmartBuffer = require('smart-buffer').SmartBuffer;
|
||||
|
||||
exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
constructor(senderId, messageId) {
|
||||
super();
|
||||
this.writeUInt16LE(senderId);
|
||||
this.writeUInt16LE(messageId);
|
||||
constructor(senderId, messageId, size) {
|
||||
var options;
|
||||
if (size) {
|
||||
options = {
|
||||
size: size,
|
||||
};
|
||||
}
|
||||
super(options);
|
||||
|
||||
this._wroteSize = false;
|
||||
if (typeof senderId === 'number' || typeof senderId === 'undefined') {
|
||||
this.writeUInt16LE(senderId || 0);
|
||||
this.writeUInt16LE(messageId || 0);
|
||||
|
||||
this._wroteSize = false;
|
||||
} else if (senderId) {
|
||||
this._wroteSize = true;
|
||||
var buffer = senderId;
|
||||
this.writeBuffer(buffer, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
toBuffer() {
|
||||
@ -27,17 +42,13 @@ exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
writeSLString(str) {
|
||||
this.writeInt32LE(str.length);
|
||||
this.writeString(str);
|
||||
if (str.length % 4 !== 0) {
|
||||
this.skipWrite(4 - str.length % 4);
|
||||
}
|
||||
this.skipWrite(SLMessage.slackForAlignment(str.length));
|
||||
}
|
||||
|
||||
readSLString() {
|
||||
var len = this.readInt32LE();
|
||||
var str = this.readString(len);
|
||||
if (len % 4 !== 0) {
|
||||
this.readOffset += 4 - len % 4;
|
||||
}
|
||||
this.readOffset += SLMessage.slackForAlignment(len);
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -48,9 +59,25 @@ exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
|
||||
writeSLArray(arr) {
|
||||
this.writeInt32LE(arr.length);
|
||||
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
this.writeUInt8(arr[i]);
|
||||
}
|
||||
|
||||
this.skipWrite(SLMessage.slackForAlignment(arr.length));
|
||||
}
|
||||
|
||||
readSLArray() {
|
||||
var len = this.readInt32LE();
|
||||
|
||||
var retval = new Array(len);
|
||||
for (var i = 0; i < len; i++) {
|
||||
retval[i] = this.readUInt8();
|
||||
}
|
||||
|
||||
this.readOffset += SLMessage.slackForAlignment(len);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
skipWrite(num) {
|
||||
@ -66,5 +93,9 @@ exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
this.dataLength = this.readInt32LE();
|
||||
}
|
||||
|
||||
static slackForAlignment(val) {
|
||||
return (4 - val % 4) % 4;
|
||||
}
|
||||
|
||||
encode() {}
|
||||
};
|
||||
|
@ -9,7 +9,12 @@ const POOL_CIRCUIT_ID = 505;
|
||||
|
||||
exports.SLPoolStatusMessage = class SLPoolStatusMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
} else {
|
||||
|
@ -6,7 +6,12 @@ const MSG_ID = 12572;
|
||||
|
||||
exports.SLSaltCellConfigMessage = class SLSaltCellConfigMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0); // controller index
|
||||
} else {
|
||||
|
29
messages/SLSetSaltCellConfigMessage.js
Normal file
29
messages/SLSetSaltCellConfigMessage.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12576;
|
||||
|
||||
exports.SLSetSaltCellConfigMessage = class SLSetSaltCellConfigMessage extends SLMessage {
|
||||
constructor(controllerIndex, poolOutput, spaOutput) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.controllerIndex = controllerIndex;
|
||||
this.poolOutput = poolOutput;
|
||||
this.spaOutput = spaOutput;
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(this.controllerIndex || 0);
|
||||
this.writeInt32LE(this.poolOutput || 0);
|
||||
this.writeInt32LE(this.spaOutput || 0);
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(0);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -6,7 +6,12 @@ const MSG_ID = 8120;
|
||||
|
||||
exports.SLVersionMessage = class SLVersionMessage extends SLMessage {
|
||||
constructor(buf) {
|
||||
super(0, MSG_ID);
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (buf) {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
@ -12,3 +12,6 @@ exports.SLGetGatewayDataMessage = require('./SLGetGatewayDataMessage.js').SLGetG
|
||||
exports.SLSetHeatSetPointMessage = require('./SLSetHeatSetPoint.js').SLSetHeatSetPoint;
|
||||
exports.SLSetHeatModeMessage = require('./SLSetHeatMode.js').SLSetHeatMode;
|
||||
exports.SLLightControlMessage = require('./SLLightControl.js').SLLightControl;
|
||||
exports.SLSetSaltCellConfigMessage = require('./SLSetSaltCellConfigMessage.js').SLSetSaltCellConfigMessage;
|
||||
exports.SLEquipmentConfigurationMessage =
|
||||
require('./SLEquipmentConfigurationMessage.js').SLEquipmentConfigurationMessage;
|
||||
|
1795
package-lock.json
generated
Normal file
1795
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "node-screenlogic",
|
||||
"description": "Tool for connecting to Pentair ScreenLogic systems on the local network",
|
||||
"version": "1.3.0",
|
||||
"version": "1.4.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/parnic/node-screenlogic.git",
|
||||
@ -12,12 +12,12 @@
|
||||
"swimmingpool"
|
||||
],
|
||||
"dependencies": {
|
||||
"smart-buffer": "~4.0.1"
|
||||
"smart-buffer": "~4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^4.19.1",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-strongloop": "^2.1.0",
|
||||
"mocha": "^5.1.1"
|
||||
"mocha": "^7.1.1"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test/*.spec.js",
|
||||
|
175
test/slmessage.spec.js
Normal file
175
test/slmessage.spec.js
Normal file
@ -0,0 +1,175 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('../messages/SLMessage.js').SLMessage;
|
||||
const assert = require('assert');
|
||||
|
||||
function slMessageLen(str) {
|
||||
// strings have length prefixed on them as an int32 for an additional 4b.
|
||||
// strings are dword aligned, so if str.length is 21, dword alignment pushes it up to 24
|
||||
return 4 + str.length + SLMessage.slackForAlignment(str.length);
|
||||
}
|
||||
|
||||
describe('SLMessage utilities', () => {
|
||||
// message header = senderId, messageId, bodyLen.
|
||||
// senderId and messageId are int16's, so 2b each. bodyLen is an int32, so 4b. total 8b.
|
||||
let msgHeaderLen = 8;
|
||||
|
||||
it('sets senderId and messageId properly', function() {
|
||||
{
|
||||
let msg = new SLMessage(123, 456);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.senderId, 123);
|
||||
assert.strictEqual(decodedMsg.messageId, 456);
|
||||
assert.strictEqual(decodedMsg.dataLength, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage(0, 65534);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.senderId, 0);
|
||||
assert.strictEqual(decodedMsg.messageId, 65534);
|
||||
assert.strictEqual(decodedMsg.dataLength, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.senderId, 0);
|
||||
assert.strictEqual(decodedMsg.messageId, 0);
|
||||
assert.strictEqual(decodedMsg.dataLength, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage(123);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.senderId, 123);
|
||||
assert.strictEqual(decodedMsg.messageId, 0);
|
||||
assert.strictEqual(decodedMsg.dataLength, 0);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage(0);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.senderId, 0);
|
||||
assert.strictEqual(decodedMsg.messageId, 0);
|
||||
assert.strictEqual(decodedMsg.dataLength, 0);
|
||||
}
|
||||
});
|
||||
|
||||
it('encodes and decodes SLStrings', function() {
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let testStr = 'this is a test string';
|
||||
msg.writeSLString(testStr);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.readSLString(), testStr, 'did not receive serialized message properly');
|
||||
assert.strictEqual(SLMessage.slackForAlignment(testStr.length), 3);
|
||||
// SLString byte length = 4 + 21 + 3 = 28b
|
||||
assert.strictEqual(slMessageLen(testStr),
|
||||
4 + testStr.length + SLMessage.slackForAlignment(testStr.length));
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(testStr), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let testStr = '1';
|
||||
msg.writeSLString(testStr);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.readSLString(), testStr, 'did not receive serialized message properly');
|
||||
assert.strictEqual(SLMessage.slackForAlignment(testStr.length), 3);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(testStr), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let testStr = '12';
|
||||
msg.writeSLString(testStr);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.readSLString(), testStr, 'did not receive serialized message properly');
|
||||
assert.strictEqual(SLMessage.slackForAlignment(testStr.length), 2);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(testStr), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let testStr = '123';
|
||||
msg.writeSLString(testStr);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.readSLString(), testStr, 'did not receive serialized message properly');
|
||||
assert.strictEqual(SLMessage.slackForAlignment(testStr.length), 1);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(testStr), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let testStr = '1234';
|
||||
msg.writeSLString(testStr);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.strictEqual(decodedMsg.readSLString(), testStr, 'did not receive serialized message properly');
|
||||
assert.strictEqual(SLMessage.slackForAlignment(testStr.length), 0);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(testStr), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
});
|
||||
|
||||
it('encodes and decodes SLArrays', function() {
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let list = [];
|
||||
msg.writeSLArray(list);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.deepStrictEqual(decodedMsg.readSLArray(), list);
|
||||
assert.strictEqual(SLMessage.slackForAlignment(list.length), 0);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(list), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let list = [1];
|
||||
msg.writeSLArray(list);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.deepStrictEqual(decodedMsg.readSLArray(), list);
|
||||
assert.strictEqual(SLMessage.slackForAlignment(list.length), 3);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(list), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let list = [1, 2];
|
||||
msg.writeSLArray(list);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.deepStrictEqual(decodedMsg.readSLArray(), list);
|
||||
assert.strictEqual(SLMessage.slackForAlignment(list.length), 2);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(list), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let list = [1, 2, 3];
|
||||
msg.writeSLArray(list);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.deepStrictEqual(decodedMsg.readSLArray(), list);
|
||||
assert.strictEqual(SLMessage.slackForAlignment(list.length), 1);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(list), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
|
||||
{
|
||||
let msg = new SLMessage();
|
||||
let list = [1, 2, 3, 4];
|
||||
msg.writeSLArray(list);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.deepStrictEqual(decodedMsg.readSLArray(), list);
|
||||
assert.strictEqual(SLMessage.slackForAlignment(list.length), 0);
|
||||
assert.strictEqual(decodedMsg.readOffset, msgHeaderLen + slMessageLen(list), 'read offset was invalid');
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
});
|
||||
});
|
@ -25,10 +25,10 @@ describe('Unit', () => {
|
||||
unit.close();
|
||||
});
|
||||
|
||||
let circuit;
|
||||
// let circuit;
|
||||
it('gets pool status', done => {
|
||||
unit.on('poolStatus', status => {
|
||||
circuit = status.circuitArray[0];
|
||||
/* circuit = */status.circuitArray[0];
|
||||
done();
|
||||
});
|
||||
|
||||
@ -63,10 +63,12 @@ describe('Unit', () => {
|
||||
unit.getVersion();
|
||||
});
|
||||
|
||||
/* uncomment this and the `circuit` stuff above to test setting state
|
||||
it('sets circuit state', done => {
|
||||
unit.on('circuitStateChanged', () => {
|
||||
done();
|
||||
});
|
||||
unit.setCircuitState(0, circuit.id, circuit.state);
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
Reference in New Issue
Block a user