Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
c310885598
|
|||
661d8db173
|
|||
e567e9c821 | |||
b8ed58e070
|
|||
757a3be7d1 | |||
1dcbe3883b
|
|||
b95f6ff777
|
|||
5ec3fbb802
|
|||
3b774923eb
|
|||
64edc5879b
|
|||
cf3d3cba78
|
|||
1aa1dddb49
|
|||
3824437d7a |
14
CHANGELOG.md
14
CHANGELOG.md
@ -4,6 +4,20 @@ 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.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.
|
||||
* Ability to set heat mode.
|
||||
* Event for supplying incorrect parameters to `set` functions.
|
||||
* Ability to send limited selection of light commands.
|
||||
|
||||
## v1.2.1 - 2019-03-26
|
||||
### Fixed
|
||||
* Messages larger than 1024 bytes are now handled properly.
|
||||
|
92
README.md
92
README.md
@ -2,7 +2,7 @@
|
||||
|
||||
This is a Node.JS library for interfacing with Pentair ScreenLogic systems over your local network or remotely through the Pentair dispatcher. Local connections require a Pentair ScreenLogic device on the same network (a network which supports UDP broadcasts).
|
||||
|
||||
Tested on node v8.11.1, v10.15.1 with a system on firmware version 5.2 Build 736.0 Rel
|
||||
Tested on node v8.11.1, v10.15.1 with a system on firmware versions 5.2 Build 736.0 Rel, 5.2 Build 738.0 Rel
|
||||
|
||||
# Usage
|
||||
|
||||
@ -192,6 +192,20 @@ Requests controller configuration from the connected unit. Emits the `controller
|
||||
|
||||
Activates or deactivates a circuit. See [`SLSetCircuitStateMessage`](#slsetcircuitstatemessage) documentation for argument values. Emits the `circuitStateChanged` event when response is acknowledged.
|
||||
|
||||
### setSetPoint(controllerId, bodyType, temperature)
|
||||
|
||||
Sets the heating setpoint for any body. See [`SLSetHeatSetPointMessage`](#slsetheatsetpointmessage) documentation for argument values. Emits the `setPointChanged` event when response is acknowledged.
|
||||
|
||||
### setHeatMode(controllerId, bodyType, heatMode)
|
||||
|
||||
Sets the preferred heat mode. See [`SLSetHeatModeMessage`](#slsetheatmodemessage) documentation for argument values. Emits the `heatModeChanged` event when response is acknowledged.
|
||||
|
||||
### sendLightCommand(controllerId, command)
|
||||
|
||||
Sends a lighting command. See [`SLLightControlMessage`](#sllightcontrolmessage) documentation for argument values. Emits the `sentLightCommand` event when response is acknowledged.
|
||||
|
||||
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.
|
||||
|
||||
### 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.
|
||||
@ -201,6 +215,10 @@ Activates or deactivates a circuit. See [`SLSetCircuitStateMessage`](#slsetcircu
|
||||
* `saltCellConfig` - Indicates that a response to `getSaltCellConfig()` has been received. Event handler receives a [`SLSaltCellConfigMessage`](#slsaltcellconfigmessage) object.
|
||||
* `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.
|
||||
* `sentLightCommand` - Indicates that a response to `sendLightCommand()` has been received. Event handler receives a [`SLLightControlMessage`](#sllightcontrolmessage) 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.
|
||||
|
||||
### Properties
|
||||
|
||||
@ -313,6 +331,26 @@ Passed as an argument to the emitted `saltCellConfig` event handler.
|
||||
|
||||
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
|
||||
@ -322,7 +360,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)
|
||||
@ -355,6 +393,56 @@ Passed as an argument to the emitted `circuitStateChanged` event. The passed ver
|
||||
* This ID can be retrieved from `SLControllerConfigMessage`'s `bodyArray` property.
|
||||
* `circuitState` - integer indicating whether to switch the circuit on (`1`) or off (`0`).
|
||||
|
||||
## SLSetHeatSetPointMessage
|
||||
|
||||
Passed as an argument to the emitted `setPointChanged` 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.
|
||||
* `bodyType` - integer indicating the type of body to set the setpoint of. The pool is body `0` and the spa is body `1`.
|
||||
* `temperature` - integer indicating the desired setpoint. This is presumably in whatever units your system is set to (celsius or fahrenheit).
|
||||
|
||||
## 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.
|
||||
|
||||
### 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.
|
||||
* `bodyType` - integer indicating the type of body to set the setpoint of. The pool is body `0` and the spa is body `1`.
|
||||
* `heatMode` - integer indicating the desired heater mode. Valid values are: 0: "Off", 1: "Solar", 2 : "Solar Preferred", 3 : "Heat Pump", 4: "Don't Change"
|
||||
|
||||
## SLLightControlMessage
|
||||
|
||||
Passed as an argument to the emitted `sentLightCommand` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the light 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.
|
||||
* `command` - integer indicating which command to send to the lights. Valid values are:
|
||||
* ScreenLogic.LIGHT_CMD_LIGHTS_OFF
|
||||
* ScreenLogic.LIGHT_CMD_LIGHTS_ON
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_SET
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_SYNC
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_SWIM
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_MODE_PARTY
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_MODE_ROMANCE
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_MODE_CARIBBEAN
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_MODE_AMERICAN
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_MODE_SUNSET
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_MODE_ROYAL
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_SET_SAVE
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_SET_RECALL
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_BLUE
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_GREEN
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_RED
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_WHITE
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_PURPLE
|
||||
|
||||
## SLGetGatewayDataMessage
|
||||
|
||||
Passed as an argument to the emitted `gatewayFound` event. Contains information about the remote unit's status and access properties.
|
||||
|
49
heatmodetest.js
Executable file
49
heatmodetest.js
Executable file
@ -0,0 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const ScreenLogic = require('./index');
|
||||
|
||||
// use this to find and connect to units local to the network this is running on
|
||||
var finder = new ScreenLogic.FindUnits();
|
||||
finder.on('serverFound', function(server) {
|
||||
finder.close();
|
||||
connect(new ScreenLogic.UnitConnection(server));
|
||||
});
|
||||
|
||||
finder.search();
|
||||
|
||||
// use this if you want to use a direct connection to a known unit
|
||||
// connect(new ScreenLogic.UnitConnection(80, '10.0.0.85'));
|
||||
|
||||
// use this to remote connect to a system by name (going through the Pentair servers)
|
||||
// const systemName = 'Pentair: xx-xx-xx';
|
||||
// const password = '1234';
|
||||
|
||||
// var remote = new ScreenLogic.RemoteLogin(systemName);
|
||||
// remote.on('gatewayFound', function(unit) {
|
||||
// remote.close();
|
||||
// if (unit && unit.gatewayFound) {
|
||||
// console.log('unit ' + remote.systemName + ' found at ' + unit.ipAddr + ':' + unit.port);
|
||||
// connect(new ScreenLogic.UnitConnection(unit.port, unit.ipAddr, password));
|
||||
// } else {
|
||||
// console.log('no unit found by that name');
|
||||
// }
|
||||
// });
|
||||
|
||||
// remote.connect();
|
||||
|
||||
// generic connection method used by all above examples
|
||||
function connect(client) {
|
||||
client.on('loggedIn', function() {
|
||||
this.getVersion();
|
||||
}).on('version', function(version) {
|
||||
this.setHeatMode(0, 0, 3);
|
||||
console.log(' version=' + version.version);
|
||||
}).on('heatModeChanged', function() {
|
||||
client.close();
|
||||
}).on('loginFailed', function() {
|
||||
console.log(' unable to login (wrong password?)');
|
||||
client.close();
|
||||
});
|
||||
|
||||
client.connect();
|
||||
}
|
48
index.js
48
index.js
@ -36,7 +36,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 + ',
|
||||
@ -206,6 +206,18 @@ class UnitConnection extends EventEmitter {
|
||||
this.client.write(new messages.SLSetCircuitStateMessage(controllerId, circuitId, circuitState).toBuffer());
|
||||
}
|
||||
|
||||
setSetPoint(controllerId, bodyType, temperature) {
|
||||
this.client.write(new messages.SLSetHeatSetPointMessage(controllerId, bodyType, temperature).toBuffer());
|
||||
}
|
||||
|
||||
setHeatMode(controllerId, bodyType, heatMode) {
|
||||
this.client.write(new messages.SLSetHeatModeMessage(controllerId, bodyType, heatMode).toBuffer());
|
||||
}
|
||||
|
||||
sendLightCommand(controllerId, command) {
|
||||
this.client.write(new messages.SLLightControlMessage(controllerId, command).toBuffer());
|
||||
}
|
||||
|
||||
onClientMessage(msg) {
|
||||
// console.log('received message of length ' + msg.length);
|
||||
if (msg.length < 4) {
|
||||
@ -247,10 +259,26 @@ class UnitConnection extends EventEmitter {
|
||||
// console.log(" it's circuit toggle ack");
|
||||
this.emit('circuitStateChanged', new messages.SLSetCircuitStateMessage());
|
||||
break;
|
||||
case messages.SLSetHeatSetPointMessage.getResponseId():
|
||||
// console.log(" it's a setpoint ack");
|
||||
this.emit('setPointChanged', new messages.SLSetHeatSetPointMessage());
|
||||
break;
|
||||
case messages.SLSetHeatModeMessage.getResponseId():
|
||||
// console.log(" it's a heater mode ack");
|
||||
this.emit('heatModeChanged', new messages.SLSetHeatModeMessage());
|
||||
break;
|
||||
case messages.SLLightControlMessage.getResponseId():
|
||||
// console.log(" it's a light control ack");
|
||||
this.emit('sentLightCommand', new messages.SLLightControlMessage());
|
||||
break;
|
||||
case 13:
|
||||
// console.log(" it's a login failure.");
|
||||
this.emit('loginFailed');
|
||||
break;
|
||||
case 31:
|
||||
// console.log(" it's a parameter failure.");
|
||||
this.emit('badParameter');
|
||||
break;
|
||||
default:
|
||||
// console.log(" it's unknown. type: " + msgType);
|
||||
break;
|
||||
@ -268,4 +296,22 @@ module.exports = {
|
||||
FindUnits,
|
||||
RemoteLogin,
|
||||
UnitConnection,
|
||||
LIGHT_CMD_LIGHTS_OFF: 0,
|
||||
LIGHT_CMD_LIGHTS_ON: 1,
|
||||
LIGHT_CMD_COLOR_SET: 2,
|
||||
LIGHT_CMD_COLOR_SYNC: 3,
|
||||
LIGHT_CMD_COLOR_SWIM: 4,
|
||||
LIGHT_CMD_COLOR_MODE_PARTY: 5,
|
||||
LIGHT_CMD_COLOR_MODE_ROMANCE: 6,
|
||||
LIGHT_CMD_COLOR_MODE_CARIBBEAN: 7,
|
||||
LIGHT_CMD_COLOR_MODE_AMERICAN: 8,
|
||||
LIGHT_CMD_COLOR_MODE_SUNSET: 9,
|
||||
LIGHT_CMD_COLOR_MODE_ROYAL: 10,
|
||||
LIGHT_CMD_COLOR_SET_SAVE: 11,
|
||||
LIGHT_CMD_COLOR_SET_RECALL: 12,
|
||||
LIGHT_CMD_COLOR_BLUE: 13,
|
||||
LIGHT_CMD_COLOR_GREEN: 14,
|
||||
LIGHT_CMD_COLOR_RED: 15,
|
||||
LIGHT_CMD_COLOR_WHITE: 16,
|
||||
LIGHT_CMD_COLOR_PURPLE: 17,
|
||||
};
|
||||
|
@ -82,4 +82,24 @@ 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)
|
||||
}
|
||||
};
|
||||
|
25
messages/SLLightControl.js
Normal file
25
messages/SLLightControl.js
Normal file
@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12556;
|
||||
|
||||
exports.SLLightControl = class SLLightControl extends SLMessage {
|
||||
constructor(controllerIndex, command) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.controllerIndex = controllerIndex;
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(this.controllerIndex || 0);
|
||||
this.writeInt32LE(this.command || 0);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -87,20 +87,21 @@ exports.SLPoolStatusMessage = class SLPoolStatusMessage extends SLMessage {
|
||||
return this.ok === 3;
|
||||
}
|
||||
|
||||
isSpaActive() {
|
||||
circuitData(id) {
|
||||
for (let i = 0; i < this.circuitArray.length; i++) {
|
||||
if (this.circuitArray[i].id === SPA_CIRCUIT_ID) {
|
||||
return this.circuitArray[i].state === 1;
|
||||
if (this.circuitArray[i].id === id) {
|
||||
return this.circuitArray[i];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
isSpaActive() {
|
||||
return this.circuitData(SPA_CIRCUIT_ID).state === 1;
|
||||
}
|
||||
|
||||
isPoolActive() {
|
||||
for (let i = 0; i < this.circuitArray.length; i++) {
|
||||
if (this.circuitArray[i].id === POOL_CIRCUIT_ID) {
|
||||
return this.circuitArray[i].state === 1;
|
||||
}
|
||||
}
|
||||
return this.circuitData(POOL_CIRCUIT_ID).state === 1;
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
|
29
messages/SLSetHeatMode.js
Normal file
29
messages/SLSetHeatMode.js
Normal file
@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12538;
|
||||
|
||||
exports.SLSetHeatMode = class SLSetHeatMode extends SLMessage {
|
||||
constructor(controllerIndex, bodyType, heatMode) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.controllerIndex = controllerIndex;
|
||||
this.bodyType = bodyType;
|
||||
this.heatMode = heatMode;
|
||||
// heatmodes:
|
||||
// 0: "Off", 1: "Solar", 2 : "Solar Preferred", 3 : "Heat Pump", 4: "Don't Change"
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(this.controllerIndex || 0);
|
||||
this.writeInt32LE(this.bodyType || 0);
|
||||
this.writeInt32LE(this.heatMode || 0);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
27
messages/SLSetHeatSetPoint.js
Normal file
27
messages/SLSetHeatSetPoint.js
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12528;
|
||||
|
||||
exports.SLSetHeatSetPoint = class SLSetHeatSetPoint extends SLMessage {
|
||||
constructor(controllerIndex, bodyType, temperature) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.controllerIndex = controllerIndex;
|
||||
this.bodyType = bodyType;
|
||||
this.temperature = temperature;
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(this.controllerIndex || 0);
|
||||
this.writeInt32LE(this.bodyType || 0);
|
||||
this.writeInt32LE(this.temperature || 0);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -9,3 +9,6 @@ exports.SLSaltCellConfigMessage = require('./SLSaltCellConfigMessage.js').SLSalt
|
||||
exports.SLVersionMessage = require('./SLVersionMessage.js').SLVersionMessage;
|
||||
exports.SLSetCircuitStateMessage = require('./SLSetCircuitStateMessage.js').SLSetCircuitStateMessage;
|
||||
exports.SLGetGatewayDataMessage = require('./SLGetGatewayDataMessage.js').SLGetGatewayDataMessage;
|
||||
exports.SLSetHeatSetPointMessage = require('./SLSetHeatSetPoint.js').SLSetHeatSetPoint;
|
||||
exports.SLSetHeatModeMessage = require('./SLSetHeatMode.js').SLSetHeatMode;
|
||||
exports.SLLightControlMessage = require('./SLLightControl.js').SLLightControl;
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "node-screenlogic",
|
||||
"description": "Tool for connecting to Pentair ScreenLogic systems on the local network",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.1",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/parnic/node-screenlogic.git",
|
||||
|
Reference in New Issue
Block a user