Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
4cfffdd9df
|
|||
ba6dac4399
|
|||
f406bccb5d
|
|||
a5d207d3aa
|
|||
f271554d89
|
|||
37d40b3386
|
|||
6f1ee3c13f
|
|||
0b990bbc28
|
|||
2aa14cc114
|
|||
bef8e6a379 | |||
5bbbfb6f41 | |||
fa43b3c03e | |||
789f75f4a6
|
|||
1ea8548991
|
|||
542d2f3e94
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@ -11,8 +11,6 @@ on:
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [main]
|
||||
schedule:
|
||||
- cron: '0 8 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
2
.github/workflows/nodejs.yml
vendored
2
.github/workflows/nodejs.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x]
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -5,6 +5,17 @@ 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.7.0 - 2021-10-13
|
||||
|
||||
### Added
|
||||
|
||||
* Added handling/documentation for the scheduleChanged event (#44).
|
||||
* Added support for getting and setting the system date/time.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Documentation updates for `SLGetScheduleData`, more linkage for easy navigation, `addClient`/`removeClient` methods, clarified `coolSetPoint` for spas.
|
||||
|
||||
## v1.6.1 - 2020-11-23
|
||||
|
||||
### Added
|
||||
|
71
README.md
71
README.md
@ -33,6 +33,8 @@ Tested with a Pentair ScreenLogic system on firmware versions 5.2 Build 736.0 Re
|
||||
* [SLCancelDelay](#slcanceldelay)
|
||||
* [SLAddClient](#sladdclient)
|
||||
* [SLRemoveClient](#slremoveclient)
|
||||
* [SLGetSystemTime](#slgetsystemtime)
|
||||
* [SLSetSystemTime](#slsetsystemtime)
|
||||
|
||||
## Usage
|
||||
|
||||
@ -256,15 +258,15 @@ Retrieves a list of schedule events of the specified type. See [`SLGetScheduleDa
|
||||
|
||||
#### addNewScheduleEvent(scheduleType, senderId)
|
||||
|
||||
Adds a new event to the specified schedule type. See [`SLAddNewScheduleEvent`](#sladdnewscheduleevent) documentation for argument values. Emits the `addNewScheduleEvent` event when response is acknowledged. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
Adds a new event to the specified schedule type. See [`SLAddNewScheduleEvent`](#sladdnewscheduleevent) documentation for argument values. Emits either the `addNewScheduleEvent` or `scheduleChanged` event when response is acknowledged (listen for both). `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
#### deleteScheduleEventById(scheduleId, senderId)
|
||||
|
||||
Deletes a scheduled event with specified id. See [`SLDeleteScheduleEventById`](#sldeletescheduleeventbyid) documentation for argument values. Emits the `deleteScheduleById` event when response is acknowledged. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
Deletes a scheduled event with specified id. See [`SLDeleteScheduleEventById`](#sldeletescheduleeventbyid) documentation for argument values. Emits the `deleteScheduleById` or `scheduleChanged` event when response is acknowledged (listen for both). `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
#### setScheduleEventById(scheduleId, circuitId, startTime, stopTime, dayMask, flags, heatCmd, heatSetPoint, senderId)
|
||||
|
||||
Configures a schedule event. See [`SLSetScheduleEventById`](#slsetscheduleeventbyid) documentation for argument values. Emits the `setScheduleEventById` event when response is acknowledged. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
Configures a schedule event. See [`SLSetScheduleEventById`](#slsetscheduleeventbyid) documentation for argument values. Emits the `setScheduleEventById` or `scheduleChanged` event when response is acknowledged (listen for both). `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
#### setCircuitRuntimebyId(circuitId, runTime, senderId)
|
||||
|
||||
@ -284,11 +286,19 @@ Cancels any delays on the system. See [`SLCancelDelay`](#slcanceldelay) document
|
||||
|
||||
#### addClient(clientId, senderId)
|
||||
|
||||
Registers to receive updates from controller when something changes. Takes a random number `clientId` to identify the client. Emits the `poolStatus` event when something changes on the controller. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
Registers to receive updates from controller when something changes. Takes a random number `clientId` to identify the client. Emits the `poolStatus` event when something changes on the controller, and the `addClient` event when the request to add a client is acknowledged. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
#### removeClient(clientId, senderId)
|
||||
|
||||
No longer receive `poolStatus` messages from controller. Takes a random number `clientId` that should match a previously registered client with `addClient`. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
No longer receive `poolStatus` messages from controller. Emits the `removeClient` event when the request to remove a client is acknowledged. Takes a random number `clientId` that should match a previously registered client with `addClient`. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
#### getSystemTime(senderId)
|
||||
|
||||
Retrieves the current time the system is set to. Emits the `getSystemTime` event when response is received. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
#### setSystemTime(date, adjustForDST, senderId)
|
||||
|
||||
Sets the current date and time of the ScreenLogic system. Emits the `setSystemTime` event when request is acknowledged. `date` must be a `Date` instance holding the date/time to set, and `adjustForDST` must be a boolean indicating whether the system should adjust for daylight saving time or not. `senderId` is an optional 16-bit integer and will be present as the `senderId` field on the returned message.
|
||||
|
||||
### Events
|
||||
|
||||
@ -307,10 +317,15 @@ No longer receive `poolStatus` messages from controller. Takes a random number `
|
||||
* `addNewScheduleEvent` - Indicates that a response to `addNewScheduleEvent()` has been received which contains the created `scheduleId` to be used later for setting up the properties. Event handler receives a [`SLAddNewScheduleEvent`](#sladdnewscheduleevent) object.
|
||||
* `deleteScheduleById` - Indicates that a response to `deleteScheduleById()` has been received. Event handler receives a [`SLDeleteScheduleEventById`](#sldeletescheduleeventbyid) object.
|
||||
* `setScheduleEventById` - Indicates that a response to `setScheduleEventById()` has been received. Event handler receives a [`SLSetScheduleEventById`](#slsetscheduleeventbyid) object.
|
||||
* `scheduleChanged` - Indicates that a response to adding, deleting, or setting a schedule has been received. Event handler receives nothing. This seems to be arbitrarily returned sometimes instead of a normal ack by the system.
|
||||
* `setCircuitRuntimeById` - Indicates that a response to `setCircuitRuntimeById()` has been received. Event handler receives a [`SLSetCircuitRuntimeById`](#slsetcircuitruntimebyid) object.
|
||||
* `getPumpStatus` - Indicates that a response to `getPumpStatus()` has been received. Event handler receives a [`SLGetPumpStatus`](#slgetpumpstatus) object.
|
||||
* `setPumpFlow` - Indicates that a response to `setPumpFlow()` has been received. Event handler receives a [`SLSetPumpFlow`](#slsetpumpflow) object.
|
||||
* `cancelDelay` - Indicates that a response to `cancelDelay()` has been received. Event handler receives a [`SLCancelDelay`](#slcanceldelay) object.
|
||||
* `addClient` - Indicates that a response to `addClient()` has been received. Event handler receives a [`SLAddClient`](#sladdclient) object.
|
||||
* `removeClient` - Indicates that a response to `removeClient()` has been received. Event handler receives a [`SLRemoveClient`](#slremoveclient) object.
|
||||
* `getSystemTime` - Indicates that a response to `getSystemTime()` has been received. Event handler receives a [`SLGetSystemTime`](#slgetsystemtime) object.
|
||||
* `setSystemTime` - Indicates that a response to `setSystemTime()` has been received. Event handler receives a [`SLSetSystemTime`](#slsetsystemtime) object if the request was valid, or `null` if the request was invalid (input parameters were not of the required types).
|
||||
* `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)
|
||||
@ -412,7 +427,7 @@ Returns a bool indicating whether the pool is currently active or not.
|
||||
* `currentTemp` - array of size 0-2 indicating current temperature of each body as an integer (pool: 0, spa: 1) (check controller config to see if it's in celsius or fahrenheit)
|
||||
* `heatStatus` - array of size 0-2 indicating whether heat is active or not for each body as an integer (pool: 0, spa: 1)
|
||||
* `setPoint` - array of size 0-2 holding the heating set point for each body as an integer (pool: 0, spa: 1) (check controller config to see if it's in celsius or fahrenheit)
|
||||
* `coolSetPoint` - array of size 0-2 holding the cooling set point for each body as an integer (pool: 0, spa: 1) (check controller config to see if it's in celsius or fahrenheit)
|
||||
* `coolSetPoint` - array of size 0-2 holding the cooling set point for each body as an integer (pool: 0, spa: 1; the spa seems to always track air temperature for this, however) (check controller config to see if it's in celsius or fahrenheit)
|
||||
* `heatMode` - array of size 0-2 indicating whether heating is enabled or not for each body as an integer (pool: 0, spa: 1)
|
||||
* `circuitArray` - array holding all circuits in the system
|
||||
* `id` - integer representing the circuit's ID (spa is 500, pool is 505)
|
||||
@ -657,7 +672,24 @@ Passed as an argument to the emitted `getScheduleData` event. Retrieves a list o
|
||||
|
||||
#### Properties
|
||||
|
||||
* `scheduleType` - 0 indicates regular scheduled events, 1 indicates a run-once event
|
||||
* `eventCount` - the number of `events` returned
|
||||
* `events` - array containing:
|
||||
* `scheduleId` - the associated scheduleId
|
||||
* `circuitId` - the circuit this schedule affects
|
||||
* `startTime` - the start time of the event, specified as a string in 24-hour time, so, for example, 6:00AM would be '0600' (see [conversion functions](#decodetimetime))
|
||||
* `stopTime` - the stop time of the event, specified as a string in 24-hour time, so, for example, 6:00AM would be '0600' (see [conversion functions](#decodetimetime))
|
||||
* `dayMask` - 7-bit mask that determines which days the schedule is active for, MSB is always 0, valid numbers 1-127 (see [conversion functions](#decodedaymaskmask))
|
||||
* `flags`
|
||||
* bit 0 is the schedule type, if 0 then regular event, if 1 its a run-once
|
||||
* bit 1 indicates whether heat setPoint should be changed
|
||||
* `heatCmd` - integer indicating the desired heater mode. Valid values are:
|
||||
* ScreenLogic.HEAT_MODE_OFF
|
||||
* ScreenLogic.HEAT_MODE_SOLAR
|
||||
* ScreenLogic.HEAT_MODE_SOLARPREFERRED
|
||||
* ScreenLogic.HEAT_MODE_HEATPUMP
|
||||
* ScreenLogic.HEAT_MODE_DONTCHANGE
|
||||
* `heatSetPoint` - the temperature set point if heat is to be changed (ignored if bit 1 of flags is 0)
|
||||
* `days` - which days this schedule is active for; this is just the `dayMask` property run through [`decodeDayMask()`](#decodedaymaskmask) for convenience
|
||||
|
||||
### SLSetScheduleEventById
|
||||
|
||||
@ -667,10 +699,10 @@ Passed as an argument to the emitted `setScheduleEventById` event. Configures an
|
||||
|
||||
* `scheduleId` - id of a schedule previously created, see [`SLAddNewScheduleEvent`](#sladdnewscheduleevent)
|
||||
* `circuitId` - id of the circuit to which this event applies
|
||||
* `startTime` - the start time of the event, specified as minutes since midnight
|
||||
* `startTime` - the start time of the event, specified as minutes since midnight (see [conversion functions](#encodetimetime))
|
||||
* example: 6:00am would be 360
|
||||
* example: 6:15am would be 375
|
||||
* `stopTime` - the stop time of the event, specified as minutes since midnight
|
||||
* `stopTime` - the stop time of the event, specified as minutes since midnight (see [conversion functions](#encodetimetime))
|
||||
* `dayMask`
|
||||
* 7-bit mask that determines which days the schedule is active for, MSB is always 0, valid numbers 1-127
|
||||
* `flags`
|
||||
@ -740,3 +772,24 @@ Passed as an argument to the emitted `addClient` event.
|
||||
### SLRemoveClient
|
||||
|
||||
Passed as an argument to the emitted `removeClient` event.
|
||||
|
||||
### SLGetSystemTime
|
||||
|
||||
Contains information about the system's current time and date. Passed as an argument to the emitted `getSystemTime` event.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `year` - short representing current system year
|
||||
* `month` - short representing current system month (where 1 is January, 2 is February, etc.)
|
||||
* `day` - short representing current system day of the month
|
||||
* `dayOfWeek` - short representing current system day of the week (where 0 is Monday and 6 is Sunday)
|
||||
* `hour` - short representing current system hour (24-hour time where 0 is midnight, 13 is 1PM, etc.)
|
||||
* `minute` - short representing current system minute
|
||||
* `second` - short representing current system second
|
||||
* `millisecond` - short representing current system millisecond
|
||||
* `adjustForDST` - bool indicating whether the system should adjust for daylight saving time or not
|
||||
* `date` - `Date` instance representing the current system datetime (convenience, constructed from the above properties)
|
||||
|
||||
### SLSetSystemTime
|
||||
|
||||
Passed as an argument to the emitted `setSystemTime` event.
|
||||
|
34
index.js
34
index.js
@ -297,6 +297,28 @@ class UnitConnection extends EventEmitter {
|
||||
this.client.write(new messages.SLRemoveClient(clientId, senderId).toBuffer());
|
||||
}
|
||||
|
||||
getSystemTime(senderId) {
|
||||
debugUnit('[%d] sending get system time query...', senderId || 0);
|
||||
this.client.write(new messages.SLGetSystemTime(null, senderId).toBuffer());
|
||||
}
|
||||
|
||||
setSystemTime(date, shouldAdjustForDST, senderId) {
|
||||
if (!(date instanceof Date)) {
|
||||
debugUnit('setSystemTime() must receive valid Date object for the date argument');
|
||||
this.emit('setSystemTime', null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof shouldAdjustForDST !== 'boolean') {
|
||||
debugUnit('setSystemTime() must receive a boolean for the shouldAdjustForDST argument');
|
||||
this.emit('setSystemTime', null);
|
||||
return;
|
||||
}
|
||||
|
||||
debugUnit('[%d] sending set system time command...', senderId || 0);
|
||||
this.client.write(new messages.SLSetSystemTime(null, date, shouldAdjustForDST, senderId).toBuffer());
|
||||
}
|
||||
|
||||
onClientMessage(msg) {
|
||||
debugUnit('received message of length %d', msg.length);
|
||||
if (msg.length < 4) {
|
||||
@ -402,6 +424,18 @@ class UnitConnection extends EventEmitter {
|
||||
debugUnit(" it's async pool status");
|
||||
this.emit('poolStatus', new messages.SLPoolStatusMessage(msg));
|
||||
break;
|
||||
case messages.SLGetSystemTime.getResponseId():
|
||||
debugUnit(" it's system time");
|
||||
this.emit('getSystemTime', new messages.SLGetSystemTime(msg));
|
||||
break;
|
||||
case messages.SLSetSystemTime.getResponseId():
|
||||
debugUnit(" it's a set system time ack");
|
||||
this.emit('setSystemTime', new messages.SLSetSystemTime(msg));
|
||||
break;
|
||||
case 12501:
|
||||
debugUnit(" it's a schedule changed notification");
|
||||
this.emit('scheduleChanged');
|
||||
break;
|
||||
case 13:
|
||||
debugUnit(" it's a login failure.");
|
||||
this.emit('loginFailed');
|
||||
|
37
messages/SLGetSystemTime.js
Normal file
37
messages/SLGetSystemTime.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 8110;
|
||||
|
||||
exports.SLGetSystemTime = class SLGetSystemTime extends SLMessage {
|
||||
constructor(buf, senderId) {
|
||||
if (buf) {
|
||||
var size = buf.readInt32LE(4) + 8;
|
||||
super(buf, MSG_ID, size);
|
||||
} else {
|
||||
super(senderId, MSG_ID);
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.year = this.readUInt16LE();
|
||||
this.month = this.readUInt16LE();
|
||||
this.dayOfWeek = this.readUInt16LE();
|
||||
this.day = this.readUInt16LE();
|
||||
this.hour = this.readUInt16LE();
|
||||
this.minute = this.readUInt16LE();
|
||||
this.second = this.readUInt16LE();
|
||||
this.millisecond = this.readUInt16LE();
|
||||
var adjustForDST = this.readInt32LE();
|
||||
this.adjustForDST = adjustForDST === 1;
|
||||
|
||||
this.date = new Date(this.year, this.month - 1, this.day, this.hour, this.minute, this.second);
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -150,6 +150,21 @@ exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
return 0;
|
||||
}
|
||||
|
||||
writeSLDateTime(date) {
|
||||
this.writeInt16LE(date.getFullYear());
|
||||
this.writeInt16LE(date.getMonth() + 1);
|
||||
var dayOfWeek = date.getDay() - 1;
|
||||
if (dayOfWeek < 0) {
|
||||
dayOfWeek = 6;
|
||||
}
|
||||
this.writeInt16LE(dayOfWeek);
|
||||
this.writeInt16LE(date.getDate());
|
||||
this.writeInt16LE(date.getHours());
|
||||
this.writeInt16LE(date.getMinutes());
|
||||
this.writeInt16LE(date.getSeconds());
|
||||
this.writeInt16LE(date.getMilliseconds());
|
||||
}
|
||||
|
||||
static slackForAlignment(val) {
|
||||
return (4 - val % 4) % 4;
|
||||
}
|
||||
|
28
messages/SLSetSystemTime.js
Normal file
28
messages/SLSetSystemTime.js
Normal file
@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 8112;
|
||||
|
||||
exports.SLSetSystemTime = class SLSetSystemTime extends SLMessage {
|
||||
constructor(buf, date, shouldAdjustForDST, senderId) {
|
||||
if (buf) {
|
||||
var size = buf.readInt32LE(4) + 8;
|
||||
super(buf, MSG_ID, size);
|
||||
} else {
|
||||
super(senderId, MSG_ID);
|
||||
|
||||
this.date = date;
|
||||
this.shouldAdjustForDST = shouldAdjustForDST;
|
||||
}
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeSLDateTime(this.date);
|
||||
this.writeInt32LE(this.shouldAdjustForDST ? 1 : 0);
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -25,3 +25,5 @@ exports.SLSetPumpFlow = require('./SLSetPumpFlow.js').SLSetPumpFlow;
|
||||
exports.SLCancelDelay = require('./SLCancelDelay.js').SLCancelDelay;
|
||||
exports.SLAddClient = require('./SLAddClient.js').SLAddClient;
|
||||
exports.SLRemoveClient = require('./SLRemoveClient.js').SLRemoveClient;
|
||||
exports.SLGetSystemTime = require('./SLGetSystemTime.js').SLGetSystemTime;
|
||||
exports.SLSetSystemTime = require('./SLSetSystemTime.js').SLSetSystemTime;
|
||||
|
950
package-lock.json
generated
950
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "node-screenlogic",
|
||||
"description": "Tool for connecting to Pentair ScreenLogic systems on the local network",
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/parnic/node-screenlogic.git",
|
||||
@ -12,13 +12,13 @@
|
||||
"swimmingpool"
|
||||
],
|
||||
"dependencies": {
|
||||
"debug": "^4.3.1",
|
||||
"smart-buffer": "~4.1.0"
|
||||
"debug": "^4.3.2",
|
||||
"smart-buffer": "^4.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.14.0",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-strongloop": "^2.1.0",
|
||||
"mocha": "^8.2.1"
|
||||
"mocha": "^9.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test/*.spec.js",
|
||||
|
@ -172,4 +172,21 @@ describe('SLMessage utilities', function() {
|
||||
assert.strictEqual(decodedMsg.dataLength, decodedMsg.readOffset - 8);
|
||||
}
|
||||
});
|
||||
|
||||
it('encodes Date as SLTime', function() {
|
||||
let msg = new SLMessage();
|
||||
let date = new Date(2021, 8, 6, 22, 8, 5);
|
||||
msg.writeSLDateTime(date);
|
||||
let decodedMsg = new SLMessage(msg.toBuffer());
|
||||
assert.equal(decodedMsg.readUInt16LE(), 2021);
|
||||
// javascript Date() month is 0-based, ScreenLogic month matches the calendar
|
||||
assert.equal(decodedMsg.readUInt16LE(), 9);
|
||||
// ScreenLogic day-of-week starts with Monday as 0
|
||||
assert.equal(decodedMsg.readUInt16LE(), 0);
|
||||
assert.equal(decodedMsg.readUInt16LE(), 6);
|
||||
assert.equal(decodedMsg.readUInt16LE(), 22);
|
||||
assert.equal(decodedMsg.readUInt16LE(), 8);
|
||||
assert.equal(decodedMsg.readUInt16LE(), 5);
|
||||
assert.equal(decodedMsg.readUInt16LE(), 0);
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user