Compare commits
52 Commits
Author | SHA1 | Date | |
---|---|---|---|
d7d10d7408
|
|||
b80807448d
|
|||
2e5203fb85 | |||
0a0c8c7cd0 | |||
1e13036faf | |||
addd89bec5 | |||
1aea09d95e
|
|||
9e158ae903
|
|||
b2439cd90c | |||
71c606cb94
|
|||
98f759c717
|
|||
407a4ec1f2
|
|||
db522ba5db | |||
626ca5b64f
|
|||
51d5b5a620
|
|||
71dad9a51b
|
|||
c0ac043d7b
|
|||
ee30cd815e
|
|||
bf3902efe8
|
|||
e6acd36562
|
|||
75d642cbfc | |||
219ce110ea
|
|||
cd981c19ef
|
|||
8d908aad5d
|
|||
6013410525
|
|||
95140d112e
|
|||
db28ef4bbc
|
|||
c1ceacbd98
|
|||
9c72e7b61d | |||
def2d8aad4
|
|||
2d6d694aa5 | |||
a59fae5f8c
|
|||
f315bcd6a5
|
|||
a0e2569743
|
|||
9205e3b62b | |||
8e0cb20620
|
|||
98edd602ec
|
|||
ba1fb3fb6c
|
|||
6a1d31c224
|
|||
43725ae083
|
|||
81b3a61c28
|
|||
da9864462b
|
|||
a2d39e7463
|
|||
ed99d411b2
|
|||
5a55c56ac1
|
|||
e24f49285c
|
|||
ba19b6802e
|
|||
c9afb53810
|
|||
22357f11e2
|
|||
22858061f7
|
|||
9337068826
|
|||
0397e8ad8f
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"extends": "strongloop",
|
||||
"rules": {
|
||||
"max-len": [2, 120, 8]
|
||||
"max-len": "off"
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6
|
||||
|
30
.github/workflows/nodejs.yml
vendored
Normal file
30
.github/workflows/nodejs.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm run pretest
|
||||
- run: npm run test-slmessage
|
7
.markdownlint.json
Normal file
7
.markdownlint.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"default": true,
|
||||
"no-duplicate-header": {
|
||||
"siblings_only": true
|
||||
},
|
||||
"line-length": false
|
||||
}
|
10
.vscode/launch.json
vendored
10
.vscode/launch.json
vendored
@ -7,8 +7,14 @@
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Launch Program",
|
||||
"program": "${workspaceFolder}\\example.js"
|
||||
"name": "Example",
|
||||
"program": "${workspaceFolder}/example.js",
|
||||
"skipFiles": [
|
||||
"<node_internals>/**"
|
||||
],
|
||||
"env": {
|
||||
"DEBUG": "sl:*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
60
CHANGELOG.md
60
CHANGELOG.md
@ -1,42 +1,100 @@
|
||||
# Changelog
|
||||
|
||||
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
|
||||
## v1.6.0 - 2020-06-14
|
||||
|
||||
### Added
|
||||
|
||||
* Fleshed out the still-undocumented `SLEquipmentConfigurationMessage` with a few more helper methods for interpreting the data inside.
|
||||
* Helper method for getting a circuit from its device ID on an `SLControllerConfigMessage`.
|
||||
* Support for getting the status of pumps and setting flow speeds per-pump-per-circuit.
|
||||
* Constants for interpreting heat command/mode properties on various messages:
|
||||
* ScreenLogic.HEAT_MODE_OFF
|
||||
* ScreenLogic.HEAT_MODE_SOLAR
|
||||
* ScreenLogic.HEAT_MODE_SOLARPREFERRED
|
||||
* ScreenLogic.HEAT_MODE_HEATPUMP
|
||||
* ScreenLogic.HEAT_MODE_DONTCHANGE
|
||||
* Debug logs using the "debug" NPM package. You'll need to run an `npm install` after updating to this version.
|
||||
* Ability to cancel delays in pool equipment. #20 - thanks @bshep
|
||||
* Ability to register for push messages from the equipment so the connection can be kept open instead of repeatedly reconnecting and polling for changes. See the `addClient()` and `removeClient()` functions on the `UnitConnection` docs.
|
||||
|
||||
## v1.5.0 - 2020-06-06
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for adding, deleting, listing, and updating scheduled events - thanks @bshep
|
||||
* Added egg timer support - thanks @bshep
|
||||
|
||||
## 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 - thanks @schemers
|
||||
|
||||
### 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.
|
||||
* 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.
|
||||
|
||||
## v1.2.0 - 2019-02-22
|
||||
|
||||
### Added
|
||||
|
||||
* Remote connection through Pentair servers
|
||||
* Connecting to password-protected systems (this is only enforced by the ScreenLogic system on remote connections)
|
||||
|
||||
## v1.1.0 - 2018-04-23
|
||||
|
||||
### Added
|
||||
|
||||
* Ability to set circuit state.
|
||||
|
||||
### Fixed
|
||||
|
||||
* FindUnits.sendServerBroadcast() was failing in certain environments.
|
||||
|
||||
## v1.0.1 - 2018-03-31
|
||||
|
||||
### Added
|
||||
|
||||
* Direct connection support.
|
||||
|
||||
## v1.0.0 - 2018-03-31
|
||||
|
||||
* Initial release
|
||||
|
@ -1,5 +1,3 @@
|
||||
/* eslint max-len: "off" */
|
||||
|
||||
'use strict';
|
||||
|
||||
const BLOCK_SIZE = 16;
|
||||
|
342
README.md
342
README.md
@ -4,7 +4,7 @@ This is a Node.JS library for interfacing with Pentair ScreenLogic systems over
|
||||
|
||||
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
|
||||
## Usage
|
||||
|
||||
See example.js for an example of interfacing with the library. Broadly, import the library with
|
||||
|
||||
@ -51,86 +51,94 @@ or
|
||||
```javascript
|
||||
new ScreenLogic.UnitConnection(unit.port, unit.ipAddr, '1234')
|
||||
```
|
||||
|
||||
where `'1234'` is the remote login password.
|
||||
|
||||
Once you've connected with `connect()`, there are a number of methods available and corresponding events for when they've completed successfully. See [UnitConnection](#unitconnection) API reference.
|
||||
|
||||
All communication with a ScreenLogic unit is done via TCP, so responses will come back in the order they were requested.
|
||||
|
||||
# Notes
|
||||
## Notes
|
||||
|
||||
Contributions welcome. There are lots of available messages supported by ScreenLogic that the app doesn't support yet, but can be added pretty easily as needed.
|
||||
|
||||
# Packet format
|
||||
## Packet format
|
||||
|
||||
All ScreenLogic packets are sent with an 8-byte header. The first 2 bytes are a little-endian-encoded sender ID (which is normally specified when making the original request). The second 2 bytes are a little-endian-encoded message ID. The final 4 bytes are a little-endian-encoded length of the data payload on the packet. The data payload is handled per-message.
|
||||
|
||||
# API Reference
|
||||
## API Reference
|
||||
|
||||
Pull requests to document undocumented properties are most welcome.
|
||||
|
||||
## FindUnits
|
||||
### FindUnits
|
||||
|
||||
### constructor()
|
||||
#### constructor()
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
const ScreenLogic = require('node-screenlogic');
|
||||
|
||||
var finder = new ScreenLogic.FindUnits();
|
||||
```
|
||||
|
||||
### search()
|
||||
#### search()
|
||||
|
||||
Begins a UDP broadcast search for available units.
|
||||
|
||||
### close()
|
||||
#### close()
|
||||
|
||||
Closes the socket.
|
||||
|
||||
### Events
|
||||
#### Events
|
||||
|
||||
* `serverFound` - Indicates that a ScreenLogic unit has been found. Event handler receives a [`UnitConnection`](#unitconnection) object.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
finder.on('serverFound', function(server) {
|
||||
var client = new ScreenLogic.UnitConnection(server);
|
||||
})
|
||||
```
|
||||
|
||||
## RemoteLogin
|
||||
* `error` - Indicates that an unhandled error was caught (such as the connection timing out)
|
||||
|
||||
### constructor(systemName)
|
||||
### RemoteLogin
|
||||
|
||||
#### constructor(systemName)
|
||||
|
||||
Argument is the name of a system to connect to in "Pentair: xx-xx-xx" format.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const ScreenLogic = require('./index');
|
||||
|
||||
var remoteLogin = new ScreenLogic.RemoteLogin('Pentair: xx-xx-xx');
|
||||
```
|
||||
|
||||
### connect()
|
||||
#### connect()
|
||||
|
||||
Connects to the dispatcher service and searches for the unit passed to its constructor.
|
||||
|
||||
### close()
|
||||
#### close()
|
||||
|
||||
Closes the connection
|
||||
|
||||
### Events
|
||||
#### 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
|
||||
### UnitConnection
|
||||
|
||||
### constructor(server)
|
||||
#### constructor(server)
|
||||
|
||||
Argument is a server returned from [`FindUnits`](#findunits) `serverFound` event.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
finder.on('serverFound', function(server) {
|
||||
var client = new ScreenLogic.UnitConnection(server);
|
||||
@ -145,67 +153,113 @@ remoteLogin.on('gatewayFound', function(unit) {
|
||||
});
|
||||
```
|
||||
|
||||
### constructor(port, address, password)
|
||||
#### constructor(port, address, password)
|
||||
|
||||
Port is an integer. Address is an IPv4 address of the server as a string. Password is optional; should be the 4-digit password in string form, e.g. `'1234'`.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var client = new ScreenLogic.UnitConnection(80, '10.0.0.85', '1234')
|
||||
```
|
||||
|
||||
### connect()
|
||||
#### connect()
|
||||
|
||||
Connects to the server passed to its constructor.
|
||||
|
||||
Examples:
|
||||
|
||||
```javascript
|
||||
var client = new ScreenLogic.UnitConnection(server);
|
||||
client.connect();
|
||||
```
|
||||
|
||||
### close()
|
||||
#### close()
|
||||
|
||||
Closes the connection.
|
||||
|
||||
### getVersion()
|
||||
#### getVersion()
|
||||
|
||||
Requests the system version string from the connected unit. Emits the `version` event when the response comes back.
|
||||
|
||||
### getPoolStatus()
|
||||
#### getPoolStatus()
|
||||
|
||||
Requests pool status from the connected unit. Emits the `poolStatus` event when the response comes back.
|
||||
|
||||
### getChemicalData()
|
||||
#### getChemicalData()
|
||||
|
||||
Requests chemical data from the connected unit (may require an IntelliChem or similar). Emits the `chemicalData` event when the response comes back.
|
||||
|
||||
### getSaltCellConfig()
|
||||
#### getSaltCellConfig()
|
||||
|
||||
Requests salt cell status/configuration from the connected unit (requires an IntelliChlor or compatible salt cell). Emits the `saltCellConfig` event when the response comes back.
|
||||
|
||||
### getControllerConfig()
|
||||
#### getControllerConfig()
|
||||
|
||||
Requests controller configuration from the connected unit. Emits the `controllerConfig` event when the response comes back.
|
||||
|
||||
### setCircuitState(controllerId, circuitId, circuitState)
|
||||
#### setCircuitState(controllerId, circuitId, circuitState)
|
||||
|
||||
Activates or deactivates a circuit. See [`SLSetCircuitStateMessage`](#slsetcircuitstatemessage) documentation for argument values. Emits the `circuitStateChanged` event when response is acknowledged.
|
||||
|
||||
### setSetPoint(controllerId, bodyType, temperature)
|
||||
#### 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)
|
||||
#### 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)
|
||||
#### 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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### getScheduleData(scheduleType)
|
||||
|
||||
Retrieves a list of schedule events of the specified type. See [`SLGetScheduleData`](#slgetscheduledata) documentation for argument values. Emits the `getScheduleData` event when response is acknowledged.
|
||||
|
||||
#### addNewScheduleEvent(scheduleType)
|
||||
|
||||
Adds a new event to the specified schedule type. See [`SLAddNewScheduleEvent`](#sladdnewscheduleevent) documentation for argument values. Emits the `addNewScheduleEvent` event when response is acknowledged.
|
||||
|
||||
#### deleteScheduleEventById(scheduleId)
|
||||
|
||||
Deletes a scheduled event with specified id. See [`SLDeleteScheduleEventById`](#sldeletescheduleeventbyid) documentation for argument values. Emits the `deleteScheduleById` event when response is acknowledged.
|
||||
|
||||
#### setScheduleEventById(scheduleId, circuitId, startTime, stopTime, dayMask, flags, heatCmd, heatSetPoint)
|
||||
|
||||
Configures a schedule event. See [`SLSetScheduleEventById`](#slsetscheduleeventbyid) documentation for argument values. Emits the `setScheduleEventById` event when response is acknowledged.
|
||||
|
||||
#### setCircuitRuntimebyId(circuitId, runTime)
|
||||
|
||||
Configures default run-time of a circuit, usually referred to as the 'egg timer'. This also applies to 'run-once' programs as this will set the length of the program. See [`SLSetCircuitRuntimeById`](#slsetcircuitruntimebyid) documentation for argument values. Emits the `setCircuitRuntimeById` event when response is acknowledged.
|
||||
|
||||
#### getPumpStatus(pumpId)
|
||||
|
||||
Gets information about the specified pump. See [`SLGetPumpStatus`](#slgetpumpstatus) documentation for argument values. Emits the `getPumpStatus` event when response is acknowledged.
|
||||
|
||||
#### setPumpFlow(pumpId, circuitId, setPoint, isRPMs)
|
||||
|
||||
Sets flow setting for a pump/circuit combination. See [`SLSetPumpFlow`](#slsetpumpflow) documentation for argument values. Emits the `setPumpFlow` event when response is acknowledged.
|
||||
|
||||
#### cancelDelay()
|
||||
|
||||
Cancels any delays on the system. See [`SLCancelDelay`](#slcanceldelay) documentation. Emits the `cancelDelay` event when response is acknowledged.
|
||||
|
||||
#### addClient(senderId)
|
||||
|
||||
Registers to receive updates from controller when something changes. Takes a random number `senderId` to identify the client. Emits the `poolStatus` event when something changes on the controller.
|
||||
|
||||
#### removeClient(senderId)
|
||||
|
||||
No longer receive `poolStatus` messages from controller. Takes a random number `senderId` that should match a previously registered client with `addClient`.
|
||||
|
||||
### 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,11 +270,23 @@ 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.
|
||||
* `getScheduleData` - Indicates that a response to `getScheduleData()` has been received. Event handler receives a [`SLGetScheduleData`](#slgetscheduledata) object.
|
||||
* `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.
|
||||
* `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.
|
||||
* `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)
|
||||
* `unknownCommand` - Indicates that an unknown command was issued to ScreenLogic (should not be possible to trigger when using the supplied `UnitConnection` methods).
|
||||
|
||||
### Properties
|
||||
#### Properties
|
||||
|
||||
* `address` - string representing the IPv4 address of the found server
|
||||
* `type` - integer representing the type of server found (will always be 2)
|
||||
@ -229,39 +295,39 @@ Note that better/more complete handling of lighting is desired, but I have yet t
|
||||
* `gatewaySubtype` - byte
|
||||
* `gatewayName` - string representing the server's name. Will be in the format Pentair: xx-xx-xx
|
||||
|
||||
## SLVersionMessage
|
||||
### SLVersionMessage
|
||||
|
||||
Passed as an argument to the emitted `version` event handler.
|
||||
|
||||
### Properties
|
||||
#### Properties
|
||||
|
||||
* `version` - a string representing the system's version
|
||||
|
||||
## SLPoolStatusMessage
|
||||
### SLPoolStatusMessage
|
||||
|
||||
Passed as an argument to the emitted `poolStatus` event handler.
|
||||
|
||||
### isDeviceReady()
|
||||
#### isDeviceReady()
|
||||
|
||||
Returns a bool indicating whether the device is in a normal operating state.
|
||||
|
||||
### isDeviceSync()
|
||||
#### isDeviceSync()
|
||||
|
||||
Returns a bool.
|
||||
|
||||
### isDeviceServiceMode()
|
||||
#### isDeviceServiceMode()
|
||||
|
||||
Returns a bool indicating whether the device is in service mode or not.
|
||||
|
||||
### isSpaActive()
|
||||
#### isSpaActive()
|
||||
|
||||
Returns a bool indicating whether the spa is currently active or not.
|
||||
|
||||
### isPoolActive()
|
||||
#### isPoolActive()
|
||||
|
||||
Returns a bool indicating whether the pool is currently active or not.
|
||||
|
||||
### Properties
|
||||
#### Properties
|
||||
|
||||
* `ok` - can be interpreted with `isDevice...` methods.
|
||||
* `freezeMode` - byte representing whether the device is in freeze mode or not.
|
||||
@ -290,11 +356,11 @@ Returns a bool indicating whether the pool is currently active or not.
|
||||
* `orpTank` - integer indicating the fill level of the ORP tank
|
||||
* `alarms` - integer indicating how many alarms are currently active
|
||||
|
||||
## SLChemDataMessage
|
||||
### SLChemDataMessage
|
||||
|
||||
Passed as an argument to the emitted `chemicalData` event handler.
|
||||
|
||||
### Properties
|
||||
#### Properties
|
||||
|
||||
* `isValid` - boolean indicating whether we got a valid response back or not
|
||||
* `pH` - float indicating the current pH level
|
||||
@ -313,45 +379,80 @@ Passed as an argument to the emitted `chemicalData` event handler.
|
||||
* `scaling` - boolean indicating whether the water balance is scaling or not
|
||||
* `error` - boolean indicating whether there's currently an error in the chem system or not
|
||||
|
||||
## SLSaltCellConfigMessage
|
||||
### SLSaltCellConfigMessage
|
||||
|
||||
Passed as an argument to the emitted `saltCellConfig` event handler.
|
||||
|
||||
### Properties
|
||||
#### 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
|
||||
|
||||
## SLControllerConfigMessage
|
||||
### 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()
|
||||
#### hasSolar()
|
||||
|
||||
Returns a bool indicating whether the system has solar present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasSolarAsHeatpump()
|
||||
#### hasSolarAsHeatpump()
|
||||
|
||||
Returns a bool indicating whether the system has a solar heatpump (UltraTemp, ThermalFlo) present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasChlorinator()
|
||||
#### hasChlorinator()
|
||||
|
||||
Returns a bool indicating whether the system has a chlorinator present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasCooling()
|
||||
#### hasCooling()
|
||||
|
||||
Returns a bool indicating whether the system has a cooler present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### hasIntellichem()
|
||||
#### hasIntellichem()
|
||||
|
||||
Returns a bool indicating whether the system has an IntelliChem chemical management system present. (Helper method for interpreting the value in `equipFlags`.)
|
||||
|
||||
### Properties
|
||||
#### isEasyTouch()
|
||||
|
||||
Returns a bool indicating whether the system is an EasyTouch system or not. (Helper method for interpreting the value in `controllerType`.)
|
||||
|
||||
#### isIntelliTouch()
|
||||
|
||||
Returns a bool indicating whether the system is an IntelliTouch system or not. (Helper method for interpreting the value in `controllerType`.)
|
||||
|
||||
#### isEasyTouchLite()
|
||||
|
||||
Returns a bool indicating whether the system is an EasyTouch Lite system or not. (Helper method for interpreting the value in `controllerType` and `hwType`.)
|
||||
|
||||
#### isDualBody()
|
||||
|
||||
Returns a bool indicating whether the system is dual-body or not. (Helper method for interpreting the value in `controllerType`.)
|
||||
|
||||
#### isChem2()
|
||||
|
||||
Returns a bool indicating whether the system is a Chem2 system or not. (Helper method for interpreting the value in `controllerType` and `hwType`.)
|
||||
|
||||
#### getCircuitByDeviceId(deviceId)
|
||||
|
||||
Returns the `bodyArray` entry for the circuit matching the given device id. This is most useful with an [`SLGetPumpStatus`](#slgetpumpstatus) message.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `controllerId` - integer indicating the controller's ID
|
||||
* `minSetPoint` - array (size 2) indicating the minimum setpoint available for the pool (index 0) or spa (index 1)
|
||||
@ -381,11 +482,11 @@ Returns a bool indicating whether the system has an IntelliChem chemical managem
|
||||
* `interfaceTabFlags` - integer
|
||||
* `showAlarms` - integer
|
||||
|
||||
## SLSetCircuitStateMessage
|
||||
### SLSetCircuitStateMessage
|
||||
|
||||
Passed as an argument to the emitted `circuitStateChanged` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command.
|
||||
|
||||
### Properties
|
||||
#### 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.
|
||||
@ -393,33 +494,38 @@ 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
|
||||
### 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
|
||||
#### 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
|
||||
### 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
|
||||
#### 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"
|
||||
* `heatMode` - 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
|
||||
|
||||
## SLLightControlMessage
|
||||
### 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
|
||||
#### 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.
|
||||
@ -443,11 +549,11 @@ Passed as an argument to the emitted `sentLightCommand` event. The passed versio
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_WHITE
|
||||
* ScreenLogic.LIGHT_CMD_COLOR_PURPLE
|
||||
|
||||
## SLGetGatewayDataMessage
|
||||
### SLGetGatewayDataMessage
|
||||
|
||||
Passed as an argument to the emitted `gatewayFound` event. Contains information about the remote unit's status and access properties.
|
||||
|
||||
### Properties
|
||||
#### Properties
|
||||
|
||||
* `gatewayFound` - boolean indicating whether a unit was found
|
||||
* `licenseOK` - boolean indicating if the license is valid (I've never seen this be false)
|
||||
@ -455,3 +561,109 @@ Passed as an argument to the emitted `gatewayFound` event. Contains information
|
||||
* `port` - number containing the port to connect to the unit
|
||||
* `portOpen` - boolean indicating whether or not the port is open and able to be connected to
|
||||
* `relayOn` - boolean indicating whether the relay is on (unsure what exactly this indicates; it's always been false in my tests)
|
||||
|
||||
### SLAddNewScheduleEvent
|
||||
|
||||
Passed as an argument to the emitted `addNewScheduleEvent` event. Adds a new event to the specified schedule type, either 0 for regular events or 1 for one-time events.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `scheduleType` - 0 indicates regular scheduled events, 1 indicates a run-once event
|
||||
|
||||
### SLDeleteScheduleEventById
|
||||
|
||||
Passed as an argument to the emitted `deleteScheduleEventById` event. Deletes a scheduled event with specified id.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `scheduleId` - the scheduleId of the schedule to be deleted
|
||||
|
||||
### SLGetScheduleData
|
||||
|
||||
Passed as an argument to the emitted `getScheduleData` event. Retrieves a list of schedule events of the specified type, either 0 for regular events or 1 for one-time events.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `scheduleType` - 0 indicates regular scheduled events, 1 indicates a run-once event
|
||||
|
||||
### SLSetScheduleEventById
|
||||
|
||||
Passed as an argument to the emitted `setScheduleEventById` event. Configures an event with properties as described below.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `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
|
||||
* example: 6:00am would be 360
|
||||
* example: 6:15am would be 375
|
||||
* `stopTime` - the stop time of the event, specified as minutes since midnight
|
||||
* `dayMask`
|
||||
* 7-bit mask that determines which days the schedule is active for, MSB is always 0, valid numbers 1-127
|
||||
* `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)
|
||||
|
||||
### SLSetCircuitRuntimeById
|
||||
|
||||
Passed as an argument to the emitted `setCircuitRuntimebyId` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command. Configures default run-time of a circuit, usually referred to as the 'egg timer'. This also applies to 'run-once' programs as this will set the length of the program.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `circuitId` - id of the circuit to which this event applies to
|
||||
* `runTime` - integer specifying the run time in minutes
|
||||
|
||||
### SLGetPumpStatus
|
||||
|
||||
Passed as an argument to the emitted `getPumpStatus` event. Gets information about the specified pump.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `pumpId` - id of pump to get information about, first pump is 0
|
||||
|
||||
#### Return Values
|
||||
|
||||
* `isRunning` - boolean that says if pump is running
|
||||
* `pumpType` - 0 if invalid pump id or one of the IntelliFlo constants:
|
||||
* ScreenLogic.PUMP_TYPE_INTELLIFLOVF
|
||||
* ScreenLogic.PUMP_TYPE_INTELLIFLOVS
|
||||
* ScreenLogic.PUMP_TYPE_INTELLIFLOVSF
|
||||
* `pumpWatts` - current Watts usage of the pump
|
||||
* `pumpRPMs` - current RPMs of the pump
|
||||
* `pumpGPMs` - current GPMs of the pump
|
||||
* `pumpSetting` - Array of 8 items each containing
|
||||
* `circuitId` - Circuit Id (CircuitId matched data returned by [`SLControllerConfigMessage`](#slcontrollerconfigmessage)'s `getCircuitByDeviceId()`)
|
||||
* `pumpSetPoint` - the set point for this pump/circuit combo (in either RPMs or GPMs depending on the value of `isRPMs`)
|
||||
* `isRPMs` - boolean indicating if the set point is in RPMs (false means it's in GPMs)
|
||||
* `pumpUnknown1` - unknown data; always 0
|
||||
* `pumpUnknown2` - unknown data; always 255
|
||||
|
||||
### SLSetPumpFlow
|
||||
|
||||
Passed as an argument to the emitted `setPumpFlow` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command. Sets flow setting for a pump/circuit combination.
|
||||
|
||||
#### Properties
|
||||
|
||||
* `pumpId` - id of pump to get information about, first pump is 0
|
||||
* `circuitId` - index of circuit for which to change the set point (index is relative to data returned by [`SLGetPumpStatus`](#slgetpumpstatus))
|
||||
* `setPoint` - the value for which to set the pump/circuit combo
|
||||
* `isRPMs` - boolean, `true` for RPMs, `false` for GPMs
|
||||
|
||||
### SLCancelDelay
|
||||
|
||||
Passed as an argument to the emitted `cancelDelay` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the set command.
|
||||
|
||||
### SLAddClient
|
||||
|
||||
Passed as an argument to the emitted `addClient` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the command.
|
||||
|
||||
### SLRemoveClient
|
||||
|
||||
Passed as an argument to the emitted `removeClient` event. The passed version is empty, however, since the response is just an acknowledgement of receipt of the command.
|
||||
|
217
index.js
217
index.js
@ -5,6 +5,9 @@ var net = require('net');
|
||||
const EventEmitter = require('events');
|
||||
const messages = require('./messages');
|
||||
const Encoder = require('./PasswordEncoder').HLEncoder;
|
||||
var debugFind = require('debug')('sl:find');
|
||||
var debugRemote = require('debug')('sl:remote');
|
||||
var debugUnit = require('debug')('sl:unit');
|
||||
|
||||
class FindUnits extends EventEmitter {
|
||||
constructor() {
|
||||
@ -14,7 +17,10 @@ class FindUnits extends EventEmitter {
|
||||
this.finder.on('message', function(message, remote) {
|
||||
_this.foundServer(message, remote);
|
||||
}).on('close', function() {
|
||||
// console.log('finder closed');
|
||||
debugFind('closed');
|
||||
}).on('error', function(e) {
|
||||
debugFind('error: %O', e);
|
||||
_this.emit('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
@ -28,7 +34,7 @@ class FindUnits extends EventEmitter {
|
||||
}
|
||||
|
||||
foundServer(message, remote) {
|
||||
// console.log('Found something');
|
||||
debugFind('found something');
|
||||
if (message.length >= 40) {
|
||||
var server = {
|
||||
address: remote.address,
|
||||
@ -39,11 +45,12 @@ class FindUnits extends EventEmitter {
|
||||
gatewayName: message.toString('utf8', 12, 29),
|
||||
};
|
||||
|
||||
// console.log(' type: ' + server.type + ', host: ' + server.address + ':' + server.port + ',
|
||||
// identified as ' + server.gatewayName);
|
||||
debugFind(' type: ' + server.type + ', host: ' + server.address + ':' + server.port + ', identified as ' + server.gatewayName);
|
||||
if (server.type === 2) {
|
||||
this.emit('serverFound', server);
|
||||
}
|
||||
} else {
|
||||
debugFind(' unexpected message');
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,7 +58,7 @@ class FindUnits extends EventEmitter {
|
||||
var message = Buffer.alloc(8);
|
||||
message[0] = 1;
|
||||
this.finder.send(message, 0, message.length, 1444, '255.255.255.255');
|
||||
// console.log("Looking for ScreenLogic hosts...");
|
||||
debugFind('Looking for ScreenLogic hosts...');
|
||||
}
|
||||
|
||||
close() {
|
||||
@ -69,12 +76,15 @@ class RemoteLogin extends EventEmitter {
|
||||
this.client.on('data', function(msg) {
|
||||
_this.onClientMessage(msg);
|
||||
}).on('close', function(had_error) {
|
||||
// console.log('remote login server connection closed');
|
||||
debugRemote('remote login server connection closed');
|
||||
}).on('error', function(e) {
|
||||
debugRemote('error: %o', e);
|
||||
_this.emit('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
connect() {
|
||||
// console.log('connecting to dispatcher...');
|
||||
debugRemote('connecting to dispatcher...');
|
||||
var _this = this;
|
||||
this.client.connect(500, 'screenlogicserver.pentair.com', function() {
|
||||
_this.onConnected();
|
||||
@ -82,13 +92,13 @@ class RemoteLogin extends EventEmitter {
|
||||
}
|
||||
|
||||
onConnected() {
|
||||
// console.log('connected to dispatcher');
|
||||
debugRemote('connected to dispatcher');
|
||||
|
||||
this.client.write(new messages.SLGetGatewayDataMessage(this.systemName).toBuffer());
|
||||
}
|
||||
|
||||
onClientMessage(msg) {
|
||||
// console.log('received message of length ' + msg.length);
|
||||
debugRemote('received message of length ' + msg.length);
|
||||
if (msg.length < 4) {
|
||||
return;
|
||||
}
|
||||
@ -96,11 +106,11 @@ class RemoteLogin extends EventEmitter {
|
||||
var msgType = msg.readInt16LE(2);
|
||||
switch (msgType) {
|
||||
case messages.SLGetGatewayDataMessage.getResponseId():
|
||||
// console.log(" it's a gateway response");
|
||||
debugRemote(" it's a gateway response");
|
||||
this.emit('gatewayFound', new messages.SLGetGatewayDataMessage(msg));
|
||||
break;
|
||||
default:
|
||||
// console.log(" it's unknown. type: " + msgType);
|
||||
debugRemote(" it's unknown. type: " + msgType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -123,6 +133,7 @@ class UnitConnection extends EventEmitter {
|
||||
|
||||
this.password = password;
|
||||
this.client = new net.Socket();
|
||||
this.client.setKeepAlive(true, 10 * 1000);
|
||||
var _this = this;
|
||||
var buffer = Buffer.alloc(1024);
|
||||
var bufferIdx = 0;
|
||||
@ -145,7 +156,10 @@ class UnitConnection extends EventEmitter {
|
||||
bufferIdx = 0;
|
||||
}
|
||||
}).on('close', function(had_error) {
|
||||
// console.log('unit connection closed');
|
||||
debugUnit('closed');
|
||||
}).on('error', function(e) {
|
||||
debugUnit('error: %o', e);
|
||||
_this.emit('error', e);
|
||||
});
|
||||
}
|
||||
|
||||
@ -154,7 +168,7 @@ class UnitConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
connect() {
|
||||
// console.log("connecting...");
|
||||
debugUnit('connecting...');
|
||||
var _this = this;
|
||||
this.client.connect(this.serverPort, this.serverAddress, function() {
|
||||
_this.onConnected();
|
||||
@ -162,64 +176,129 @@ class UnitConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
onConnected() {
|
||||
// console.log('connected');
|
||||
debugUnit('connected');
|
||||
|
||||
// console.log('sending init message...');
|
||||
debugUnit('sending init message...');
|
||||
this.client.write('CONNECTSERVERHOST\r\n\r\n');
|
||||
|
||||
// console.log('sending challenge message...');
|
||||
debugUnit('sending challenge message...');
|
||||
this.client.write(new messages.SLChallengeMessage().toBuffer());
|
||||
}
|
||||
|
||||
login() {
|
||||
// console.log('sending login message...');
|
||||
debugUnit('sending login message...');
|
||||
var password = new Encoder(this.password).getEncryptedPassword(this.challengeString);
|
||||
this.client.write(new messages.SLLoginMessage(password).toBuffer());
|
||||
}
|
||||
|
||||
getPoolStatus() {
|
||||
// console.log('sending pool status query...');
|
||||
debugUnit('sending pool status query...');
|
||||
this.client.write(new messages.SLPoolStatusMessage().toBuffer());
|
||||
}
|
||||
|
||||
getControllerConfig() {
|
||||
// console.log('sending controller config query...');
|
||||
debugUnit('sending controller config query...');
|
||||
this.client.write(new messages.SLControllerConfigMessage().toBuffer());
|
||||
}
|
||||
|
||||
getChemicalData() {
|
||||
// console.log('sending chemical data query...');
|
||||
debugUnit('sending chemical data query...');
|
||||
this.client.write(new messages.SLChemDataMessage().toBuffer());
|
||||
}
|
||||
|
||||
getSaltCellConfig() {
|
||||
// console.log('sending salt cell config query...');
|
||||
debugUnit('sending salt cell config query...');
|
||||
this.client.write(new messages.SLSaltCellConfigMessage().toBuffer());
|
||||
}
|
||||
|
||||
getVersion() {
|
||||
// console.log('sending version query...');
|
||||
debugUnit('sending version query...');
|
||||
this.client.write(new messages.SLVersionMessage().toBuffer());
|
||||
}
|
||||
|
||||
getEquipmentConfiguration() {
|
||||
debugUnit('sending equipment configuration query...');
|
||||
this.client.write(new messages.SLEquipmentConfigurationMessage().toBuffer());
|
||||
}
|
||||
|
||||
setCircuitState(controllerId, circuitId, circuitState) {
|
||||
debugUnit('sending set circuit state command: controllerId: %d, circuitId: %d, circuitState: %d...', controllerId, circuitId, circuitState);
|
||||
this.client.write(new messages.SLSetCircuitStateMessage(controllerId, circuitId, circuitState).toBuffer());
|
||||
}
|
||||
|
||||
setSetPoint(controllerId, bodyType, temperature) {
|
||||
debugUnit('sending set setpoint command: controllerId: %d, bodyType: %d, temperature: %d...', controllerId, bodyType, temperature);
|
||||
this.client.write(new messages.SLSetHeatSetPointMessage(controllerId, bodyType, temperature).toBuffer());
|
||||
}
|
||||
|
||||
setHeatMode(controllerId, bodyType, heatMode) {
|
||||
debugUnit('sending set heatmode command: controllerId: %d, bodyType: %d, heatMode: %d...', controllerId, bodyType, heatMode);
|
||||
this.client.write(new messages.SLSetHeatModeMessage(controllerId, bodyType, heatMode).toBuffer());
|
||||
}
|
||||
|
||||
sendLightCommand(controllerId, command) {
|
||||
debugUnit('sending light command: controllerId: %d, command: %d...', controllerId, command);
|
||||
this.client.write(new messages.SLLightControlMessage(controllerId, command).toBuffer());
|
||||
}
|
||||
|
||||
setSaltCellOutput(controllerId, poolOutput, spaOutput) {
|
||||
debugUnit('sending set saltcell output command: controllerId: %d, poolOutput: %d, spaOutput: %d...', controllerId, poolOutput, spaOutput);
|
||||
this.client.write(new messages.SLSetSaltCellConfigMessage(controllerId, poolOutput, spaOutput).toBuffer());
|
||||
}
|
||||
|
||||
getScheduleData(scheduleType) {
|
||||
debugUnit('sending set schedule data query for scheduleType: %d...', scheduleType);
|
||||
this.client.write(new messages.SLGetScheduleData(null, scheduleType).toBuffer());
|
||||
}
|
||||
|
||||
addNewScheduleEvent(scheduleType) {
|
||||
debugUnit('sending add new schedule event command for scheduleType: %d...', scheduleType);
|
||||
this.client.write(new messages.SLAddNewScheduleEvent(null, scheduleType).toBuffer());
|
||||
}
|
||||
|
||||
deleteScheduleEventById(scheduleId) {
|
||||
debugUnit('sending delete schedule event command for scheduleId: %d...', scheduleId);
|
||||
this.client.write(new messages.SLDeleteScheduleEventById(scheduleId).toBuffer());
|
||||
}
|
||||
|
||||
// todo: should this just accept a SLSetScheduleEventById message instead of all these args?
|
||||
setScheduleEventById(scheduleId, circuitId, startTime, stopTime, dayMask, flags, heatCmd, heatSetPoint) {
|
||||
debugUnit('sending set schedule event command for scheduleId: %d, circuitId: %d, startTime: %d, stopTime: %d, dayMask: %d, flags: %d, heatCmd: %d, heatSetPoint: %d...', scheduleId, circuitId, startTime, stopTime, dayMask, flags, heatCmd, heatSetPoint);
|
||||
this.client.write(new messages.SLSetScheduleEventById(null, scheduleId, circuitId, startTime, stopTime, dayMask, flags, heatCmd, heatSetPoint).toBuffer());
|
||||
}
|
||||
|
||||
setCircuitRuntimebyId(circuitId, runTime) {
|
||||
debugUnit('sending set circuit runtime command for circuitId: %d, runTime: %d...', circuitId, runTime);
|
||||
this.client.write(new messages.SLSetCircuitRuntimeById(circuitId, runTime).toBuffer());
|
||||
}
|
||||
|
||||
getPumpStatus(pumpId) {
|
||||
debugUnit('sending get pump status command for pumpId: %d...', pumpId);
|
||||
this.client.write(new messages.SLGetPumpStatus(null, pumpId).toBuffer());
|
||||
}
|
||||
|
||||
setPumpFlow(pumpId, circuitId, setPoint, isRPMs) {
|
||||
debugUnit('sending set pump flow command for pumpId: %d, circuitId: %d, setPoint: %d, isRPMs: %d...', pumpId, circuitId, setPoint, isRPMs);
|
||||
this.client.write(new messages.SLSetPumpFlow(pumpId, circuitId, setPoint, isRPMs).toBuffer());
|
||||
}
|
||||
|
||||
cancelDelay() {
|
||||
debugUnit('sending cancel delay command...');
|
||||
this.client.write(new messages.SLCancelDelay().toBuffer());
|
||||
}
|
||||
|
||||
addClient(senderId) {
|
||||
debugUnit('sending add client command...');
|
||||
this.client.write(new messages.SLAddClient(senderId).toBuffer());
|
||||
}
|
||||
|
||||
removeClient(senderId) {
|
||||
debugUnit('sending remove client command...');
|
||||
this.client.write(new messages.SLRemoveClient(senderId).toBuffer());
|
||||
}
|
||||
|
||||
onClientMessage(msg) {
|
||||
// console.log('received message of length ' + msg.length);
|
||||
debugUnit('received message of length %d', msg.length);
|
||||
if (msg.length < 4) {
|
||||
return;
|
||||
}
|
||||
@ -227,60 +306,116 @@ class UnitConnection extends EventEmitter {
|
||||
var msgType = msg.readInt16LE(2);
|
||||
switch (msgType) {
|
||||
case messages.SLChallengeMessage.getResponseId():
|
||||
// console.log(" it's a challenge response");
|
||||
debugUnit(" it's a challenge response");
|
||||
this.challengeString = new messages.SLChallengeMessage(msg).challengeString;
|
||||
this.login();
|
||||
break;
|
||||
case messages.SLLoginMessage.getResponseId():
|
||||
// console.log(" it's a login response");
|
||||
debugUnit(" it's a login response");
|
||||
this.emit('loggedIn');
|
||||
break;
|
||||
case messages.SLPoolStatusMessage.getResponseId():
|
||||
// console.log(" it's pool status");
|
||||
debugUnit(" it's pool status");
|
||||
this.emit('poolStatus', new messages.SLPoolStatusMessage(msg));
|
||||
break;
|
||||
case messages.SLControllerConfigMessage.getResponseId():
|
||||
// console.log(" it's controller configuration");
|
||||
debugUnit(" it's controller configuration");
|
||||
this.emit('controllerConfig', new messages.SLControllerConfigMessage(msg));
|
||||
break;
|
||||
case messages.SLChemDataMessage.getResponseId():
|
||||
// console.log(" it's chem data");
|
||||
debugUnit(" it's chem data");
|
||||
this.emit('chemicalData', new messages.SLChemDataMessage(msg));
|
||||
break;
|
||||
case messages.SLSaltCellConfigMessage.getResponseId():
|
||||
// console.log(" it's salt cell config");
|
||||
debugUnit(" it's salt cell config");
|
||||
this.emit('saltCellConfig', new messages.SLSaltCellConfigMessage(msg));
|
||||
break;
|
||||
case messages.SLVersionMessage.getResponseId():
|
||||
// console.log(" it's version");
|
||||
debugUnit(" it's version");
|
||||
this.emit('version', new messages.SLVersionMessage(msg));
|
||||
break;
|
||||
case messages.SLSetCircuitStateMessage.getResponseId():
|
||||
// console.log(" it's circuit toggle ack");
|
||||
debugUnit(" it's circuit toggle ack");
|
||||
this.emit('circuitStateChanged', new messages.SLSetCircuitStateMessage());
|
||||
break;
|
||||
case messages.SLSetHeatSetPointMessage.getResponseId():
|
||||
// console.log(" it's a setpoint ack");
|
||||
debugUnit(" it's a setpoint ack");
|
||||
this.emit('setPointChanged', new messages.SLSetHeatSetPointMessage());
|
||||
break;
|
||||
case messages.SLSetHeatModeMessage.getResponseId():
|
||||
// console.log(" it's a heater mode ack");
|
||||
debugUnit(" 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");
|
||||
debugUnit(" it's a light control ack");
|
||||
this.emit('sentLightCommand', new messages.SLLightControlMessage());
|
||||
break;
|
||||
case messages.SLSetSaltCellConfigMessage.getResponseId():
|
||||
debugUnit(" it's a set salt cell config ack");
|
||||
this.emit('setSaltCellConfig', new messages.SLSetSaltCellConfigMessage());
|
||||
break;
|
||||
case messages.SLEquipmentConfigurationMessage.getResponseId():
|
||||
debugUnit(" it's equipment configuration");
|
||||
this.emit('equipmentConfiguration', new messages.SLEquipmentConfigurationMessage(msg));
|
||||
break;
|
||||
case messages.SLGetScheduleData.getResponseId():
|
||||
debugUnit(" it's schedule data");
|
||||
this.emit('getScheduleData', new messages.SLGetScheduleData(msg));
|
||||
break;
|
||||
case messages.SLAddNewScheduleEvent.getResponseId():
|
||||
debugUnit(" it's a new schedule event ack");
|
||||
this.emit('addNewScheduleEvent', new messages.SLAddNewScheduleEvent(msg));
|
||||
break;
|
||||
case messages.SLDeleteScheduleEventById.getResponseId():
|
||||
debugUnit(" it's a delete schedule event ack");
|
||||
this.emit('deleteScheduleEventById', new messages.SLDeleteScheduleEventById(msg));
|
||||
break;
|
||||
case messages.SLSetScheduleEventById.getResponseId():
|
||||
debugUnit(" it's a set schedule event ack");
|
||||
this.emit('setScheduleEventById', new messages.SLSetScheduleEventById(msg));
|
||||
break;
|
||||
case messages.SLSetCircuitRuntimeById.getResponseId():
|
||||
debugUnit(" it's a set circuit runtime ack");
|
||||
this.emit('setCircuitRuntimebyId', new messages.SLSetCircuitRuntimeById());
|
||||
break;
|
||||
case messages.SLGetPumpStatus.getResponseId():
|
||||
debugUnit(" it's pump status");
|
||||
this.emit('getPumpStatus', new messages.SLGetPumpStatus(msg));
|
||||
break;
|
||||
case messages.SLSetPumpFlow.getResponseId():
|
||||
debugUnit(" it's a set pump flow ack");
|
||||
this.emit('setPumpFlow', new messages.SLSetPumpFlow());
|
||||
break;
|
||||
case messages.SLCancelDelay.getResponseId():
|
||||
debugUnit(" it's a cancel delay ack");
|
||||
this.emit('cancelDelay', new messages.SLCancelDelay());
|
||||
break;
|
||||
case messages.SLAddClient.getResponseId():
|
||||
debugUnit(" it's an add client ack");
|
||||
this.emit('addClient', new messages.SLCancelDelay());
|
||||
break;
|
||||
case messages.SLRemoveClient.getResponseId():
|
||||
debugUnit(" it's a remove client ack");
|
||||
this.emit('removeClient', new messages.SLCancelDelay());
|
||||
break;
|
||||
case messages.SLPoolStatusMessage.getAsyncResponseId():
|
||||
debugUnit(" it's async pool status");
|
||||
this.emit('poolStatus', new messages.SLPoolStatusMessage(msg));
|
||||
break;
|
||||
case 13:
|
||||
// console.log(" it's a login failure.");
|
||||
debugUnit(" it's a login failure.");
|
||||
this.emit('loginFailed');
|
||||
break;
|
||||
case 30:
|
||||
debugUnit(" it's an unknown command.");
|
||||
this.emit('unknownCommand');
|
||||
break;
|
||||
case 31:
|
||||
// console.log(" it's a parameter failure.");
|
||||
debugUnit(" it's a parameter failure.");
|
||||
this.emit('badParameter');
|
||||
break;
|
||||
default:
|
||||
// console.log(" it's unknown. type: " + msgType);
|
||||
debugUnit(" it's an unknown type: %d", msgType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -314,4 +449,12 @@ module.exports = {
|
||||
LIGHT_CMD_COLOR_RED: 15,
|
||||
LIGHT_CMD_COLOR_WHITE: 16,
|
||||
LIGHT_CMD_COLOR_PURPLE: 17,
|
||||
HEAT_MODE_OFF: 0,
|
||||
HEAT_MODE_SOLAR: 1,
|
||||
HEAT_MODE_SOLARPREFERRED: 2,
|
||||
HEAT_MODE_HEATPUMP: 3,
|
||||
HEAT_MODE_DONTCHANGE: 4,
|
||||
PUMP_TYPE_INTELLIFLOVF: 1,
|
||||
PUMP_TYPE_INTELLIFLOVS: 2,
|
||||
PUMP_TYPE_INTELLIFLOVSF: 3,
|
||||
};
|
||||
|
24
messages/SLAddClient.js
Normal file
24
messages/SLAddClient.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12522;
|
||||
|
||||
exports.SLAddClient = class SLAddClient extends SLMessage {
|
||||
constructor(senderId) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.senderId = senderId;
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(this.senderId);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
38
messages/SLAddNewScheduleEvent.js
Normal file
38
messages/SLAddNewScheduleEvent.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12544;
|
||||
|
||||
|
||||
exports.SLAddNewScheduleEvent = class SLAddNewScheduleEvent extends SLMessage {
|
||||
constructor(buf, scheduleType) {
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
|
||||
if (!buf) {
|
||||
// console.log('Requested Schedule type = ', scheduleType);
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(scheduleType);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.scheduleId = this.readUInt32LE();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
22
messages/SLCancelDelay.js
Normal file
22
messages/SLCancelDelay.js
Normal file
@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12580;
|
||||
|
||||
exports.SLCancelDelay = class SLCancelDelay extends SLMessage {
|
||||
constructor() {
|
||||
super(0, MSG_ID);
|
||||
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(0);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -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 {
|
||||
|
@ -4,9 +4,22 @@ const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12532;
|
||||
|
||||
const CIRCUIT_NAME_VALUE_MAP = [
|
||||
{name: 'Unused', deviceId: 0},
|
||||
{name: 'Solar Active', deviceId: 128},
|
||||
{name: 'Pool or Spa Heater Active', deviceId: 129},
|
||||
{name: 'Pool Heater Active', deviceId: 130},
|
||||
{name: 'Spa Heater Active', deviceId: 131},
|
||||
];
|
||||
|
||||
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);
|
||||
@ -84,22 +97,68 @@ exports.SLControllerConfigMessage = class SLControllerConfigMessage extends SLMe
|
||||
}
|
||||
|
||||
hasSolar() {
|
||||
return !!(this.equipFlags & 0x1)
|
||||
return !!(this.equipFlags & 0x1);
|
||||
}
|
||||
|
||||
hasSolarAsHeatpump() {
|
||||
return !!(this.equipFlags & 0x2)
|
||||
return !!(this.equipFlags & 0x2);
|
||||
}
|
||||
|
||||
hasChlorinator() {
|
||||
return !!(this.equipFlags & 0x4)
|
||||
return !!(this.equipFlags & 0x4);
|
||||
}
|
||||
|
||||
hasCooling() {
|
||||
return !!(this.equipFlags & 0x800)
|
||||
return !!(this.equipFlags & 0x800);
|
||||
}
|
||||
|
||||
hasIntellichem() {
|
||||
return !!(this.equipFlags & 0x8000)
|
||||
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;
|
||||
}
|
||||
|
||||
getCircuitByDeviceId(deviceId) {
|
||||
var deviceArray = this.getCircuitsMap();
|
||||
|
||||
for (var i = 0; i < deviceArray.length; i++) {
|
||||
if (deviceArray[i].deviceId === deviceId) {
|
||||
return deviceArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
getCircuitsMap() {
|
||||
var deviceArray;
|
||||
|
||||
if (this.bodyArray) {
|
||||
deviceArray = this.bodyArray.concat(CIRCUIT_NAME_VALUE_MAP);
|
||||
} else {
|
||||
deviceArray = [].concat(CIRCUIT_NAME_VALUE_MAP);
|
||||
}
|
||||
|
||||
return deviceArray;
|
||||
}
|
||||
|
||||
};
|
||||
|
18
messages/SLDeleteScheduleEventById.js
Normal file
18
messages/SLDeleteScheduleEventById.js
Normal file
@ -0,0 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12546;
|
||||
|
||||
exports.SLDeleteScheduleEventById = class SLDeleteScheduleEventById extends SLMessage {
|
||||
constructor(scheduleId) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(scheduleId);
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
217
messages/SLEquipmentConfigurationMessage.js
Normal file
217
messages/SLEquipmentConfigurationMessage.js
Normal file
@ -0,0 +1,217 @@
|
||||
'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(); // decodeValveData()
|
||||
this.remoteDataArray = this.readSLArray();
|
||||
this.sensorDataArray = this.readSLArray(); // decodeSensorData()
|
||||
this.delayDataArray = this.readSLArray(); // decodeDelayData()
|
||||
this.macroDataArray = this.readSLArray();
|
||||
this.miscDataArray = this.readSLArray(); // decodeMiscData()
|
||||
this.lightDataArray = this.readSLArray();
|
||||
this.flowDataArray = this.readSLArray();
|
||||
this.sgDataArray = this.readSLArray();
|
||||
this.spaFlowDataArray = this.readSLArray();
|
||||
}
|
||||
|
||||
getVersion() {
|
||||
if (this.versionDataArray === null || this.versionDataArray.length < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (this.versionDataArray[0] * 1000) + (this.versionDataArray[1]);
|
||||
}
|
||||
|
||||
getSecondariesCount() {
|
||||
return (this.controllerData & 0x11000000) >> 6;
|
||||
}
|
||||
|
||||
getPumpType(pumpIndex) {
|
||||
if (typeof (pumpIndex) !== 'number') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.flowDataArray === null || this.flowDataArray.length < (pumpIndex + 1) * 45) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let pumpType = this.flowDataArray[(45 * pumpIndex) + 2];
|
||||
if (pumpType <= 3) {
|
||||
return pumpType;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getCircuitRPMs(pumpIndex, circuitDeviceId) {
|
||||
if (typeof (pumpIndex) !== 'number' || typeof (circuitDeviceId) !== 'number') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pumpIndex < 0 || pumpIndex >= 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (this.flowDataArray === null || this.flowDataArray.length < (pumpIndex + 1) * 45) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
let offset = (45 * pumpIndex) + 4 + (i * 2);
|
||||
if (this.flowDataArray[offset] === circuitDeviceId) {
|
||||
let upperBits = this.flowDataArray[offset + 1];
|
||||
let lowerBits = this.flowDataArray[offset + (16 - (i * 2)) + 1 + i];
|
||||
return (upperBits << 8) + lowerBits;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
getNumPumps() {
|
||||
if (this.flowDataArray === null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let numPumps = 0;
|
||||
for (var i = 0; i < this.flowDataArray.length; i += 45) {
|
||||
if (this.flowDataArray[i + 2] !== 0) {
|
||||
numPumps++;
|
||||
}
|
||||
}
|
||||
|
||||
return numPumps;
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
|
||||
decodeSensorData() {
|
||||
var sensors = this.sensorDataArray;
|
||||
|
||||
this.sensors = {};
|
||||
|
||||
this.sensors.poolSolarPresent = this.isBitSet(sensors[0], 1);
|
||||
this.sensors.spaSolarPresent = this.isBitSet(sensors[0], 4);
|
||||
this.sensors.thermaFloCoolPresent = this.isBitSet(sensors[1], 1);
|
||||
this.sensors.solarHeatPumpPresent = this.isBitSet(sensors[2], 4);
|
||||
this.sensors.thermaFloPresent = this.isBitSet(sensors[2], 5);
|
||||
}
|
||||
|
||||
decodeValveData() {
|
||||
var secondaries = this.getSecondariesCount();
|
||||
var isSolarValve0 = false;
|
||||
var isSolarValve1 = false;
|
||||
|
||||
if (!this.sensors) {
|
||||
this.decodeSensorData();
|
||||
}
|
||||
|
||||
if (this.sensors.poolSolarPresent && !this.sensors.solarHeatPumpPresent) {
|
||||
isSolarValve0 = true;
|
||||
}
|
||||
|
||||
if (this.isDualBody()) {
|
||||
if (this.sensors.spaSolarPresent && !this.sensors.thermaFloPresent) {
|
||||
isSolarValve1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
var valveArray = [];
|
||||
|
||||
for (var loadCenterIndex = 0; loadCenterIndex <= secondaries; loadCenterIndex++) {
|
||||
var loadCenterValveData = this.valveDataArray[loadCenterIndex];
|
||||
|
||||
for (var valveIndex = 0; valveIndex < 5; valveIndex++) {
|
||||
var valveObject = {};
|
||||
|
||||
var isSolarValve = false;
|
||||
if (loadCenterIndex === 0) {
|
||||
if (valveIndex === 0 && isSolarValve0) {
|
||||
isSolarValve = true;
|
||||
}
|
||||
if (valveIndex === 1 && isSolarValve1) {
|
||||
isSolarValve = true;
|
||||
}
|
||||
}
|
||||
if (this.isValvePresent(valveIndex, loadCenterValveData)) {
|
||||
var valveDataIndex = (loadCenterIndex * 5) + 4 + valveIndex;
|
||||
var deviceId = this.valveDataArray[valveDataIndex];
|
||||
if (deviceId === 0) {
|
||||
// console.log('unused valve, loadCenterIndex = ' + loadCenterIndex + ' valveIndex = ' + valveIndex);
|
||||
} else if (isSolarValve === true){
|
||||
// console.log('used by solar');
|
||||
} else {
|
||||
valveObject.loadCenterIndex = loadCenterIndex;
|
||||
valveObject.valveIndex = valveIndex;
|
||||
valveObject.valveName = String.fromCharCode(65 + valveIndex);
|
||||
valveObject.loadCenterName = (loadCenterIndex + 1).toString();
|
||||
valveObject.deviceId = deviceId;
|
||||
valveArray.push(valveObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this.valves = valveArray;
|
||||
}
|
||||
|
||||
isValvePresent(valveIndex, loadCenterValveData) {
|
||||
if (valveIndex < 2) {
|
||||
return true;
|
||||
} else {
|
||||
return this.isBitSet(loadCenterValveData, valveIndex);
|
||||
}
|
||||
}
|
||||
|
||||
decodeDelayData() {
|
||||
this.delays = {};
|
||||
|
||||
this.delays.poolPumpOnDuringHeaterCooldown = this.isBitSet(this.delayDataArray[0], 0);
|
||||
this.delays.spaPumpOnDuringHeaterCooldown = this.isBitSet(this.delayDataArray[0], 1);
|
||||
this.delays.pumpOffDuringValveAction = this.isBitSet(this.delayDataArray[0], 7);
|
||||
}
|
||||
|
||||
decodeMiscData() {
|
||||
this.misc = {};
|
||||
|
||||
this.misc.intelliChem = this.isBitSet(this.miscDataArray[3], 0);
|
||||
this.misc.spaManualHeat = this.miscDataArray[4] !== 0;
|
||||
}
|
||||
|
||||
isDualBody() {
|
||||
return this.controllerType === 5;
|
||||
}
|
||||
};
|
@ -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);
|
||||
|
50
messages/SLGetPumpStatus.js
Normal file
50
messages/SLGetPumpStatus.js
Normal file
@ -0,0 +1,50 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12584;
|
||||
|
||||
exports.SLGetPumpStatus = class SLGetPumpStatus extends SLMessage {
|
||||
constructor(buf, pumpId) {
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(pumpId);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.pumpSetting = new Array(8);
|
||||
|
||||
this.pumpType = this.readUInt32LE();
|
||||
this.isRunning = this.readUInt32LE() !== 0; // 0, 1, or 4294967295 (FF FF FF FF)
|
||||
this.pumpWatts = this.readUInt32LE();
|
||||
this.pumpRPMs = this.readUInt32LE();
|
||||
this.pumpUnknown1 = this.readUInt32LE(); // Always 0
|
||||
this.pumpGPMs = this.readUInt32LE();
|
||||
this.pumpUnknown2 = this.readUInt32LE(); // Always 255
|
||||
|
||||
for (var i = 0; i < 8; i++) {
|
||||
this.pumpSetting[i] = {};
|
||||
this.pumpSetting[i].circuitId = this.readUInt32LE();
|
||||
this.pumpSetting[i].pumpSetPoint = this.readUInt32LE();
|
||||
this.pumpSetting[i].isRPMs = this.readUInt32LE() !== 0; // 1 for RPMs; 0 for GPMs
|
||||
}
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
52
messages/SLGetScheduleData.js
Normal file
52
messages/SLGetScheduleData.js
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12542;
|
||||
|
||||
exports.SLGetScheduleData = class SLGetScheduleData extends SLMessage {
|
||||
constructor(buf, scheduleType) {
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(scheduleType);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
decode() {
|
||||
super.decode();
|
||||
|
||||
this.eventCount = this.readUInt32LE();
|
||||
|
||||
this.events = new Array(this.eventCount);
|
||||
|
||||
for (var i = 0; i < this.events.length; i++) {
|
||||
this.events[i] = {};
|
||||
|
||||
this.events[i].scheduleId = this.readUInt32LE();
|
||||
this.events[i].circuitId = this.readUInt32LE();
|
||||
this.events[i].startTime = this.decodeTime(this.readUInt32LE());
|
||||
this.events[i].stopTime = this.decodeTime(this.readUInt32LE());
|
||||
this.events[i].dayMask = this.readUInt32LE();
|
||||
this.events[i].flags = this.readUInt32LE();
|
||||
this.events[i].heatCmd = this.readUInt32LE();
|
||||
this.events[i].heatSetPoint = this.readUInt32LE();
|
||||
this.events[i].days = this.decodeDayMask(this.events[i].dayMask);
|
||||
}
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -2,13 +2,38 @@
|
||||
|
||||
const SmartBuffer = require('smart-buffer').SmartBuffer;
|
||||
|
||||
exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
constructor(senderId, messageId) {
|
||||
super();
|
||||
this.writeUInt16LE(senderId);
|
||||
this.writeUInt16LE(messageId);
|
||||
const DAY_VALUES = [
|
||||
['Mon', 0x1 ],
|
||||
['Tue', 0x2 ],
|
||||
['Wed', 0x4 ],
|
||||
['Thu', 0x8 ],
|
||||
['Fri', 0x10 ],
|
||||
['Sat', 0x20 ],
|
||||
['Sun', 0x40 ],
|
||||
];
|
||||
|
||||
this._wroteSize = false;
|
||||
exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
constructor(senderId, messageId, size) {
|
||||
var options;
|
||||
if (size) {
|
||||
options = {
|
||||
size: size,
|
||||
};
|
||||
}
|
||||
super(options);
|
||||
|
||||
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 +52,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 +69,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 +103,56 @@ exports.SLMessage = class SLMessage extends SmartBuffer {
|
||||
this.dataLength = this.readInt32LE();
|
||||
}
|
||||
|
||||
isBitSet(value, bit) {
|
||||
return ((value >> bit) & 0x1) === 1;
|
||||
}
|
||||
|
||||
decodeTime(rawTime) { // Takes 'rawTime' in mins past midnight and returns military time as a string
|
||||
var retVal;
|
||||
|
||||
retVal = Math.floor(rawTime / 60) * 100 + rawTime % 60;
|
||||
|
||||
retVal = String(retVal).padStart(4, '0');
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
encodeTime(stringTime) { // Takes 'stringTime' as military time and returns mins past midnight
|
||||
return Number(stringTime.substr(0, 2) * 60) + Number(stringTime.substr(2, 2));
|
||||
}
|
||||
|
||||
decodeDayMask(dayMask) {
|
||||
var days = [];
|
||||
|
||||
for (var i = 0; i < 7; i++) {
|
||||
if (this.isBitSet(dayMask, i)) {
|
||||
days.push(DAY_VALUES[i][0]);
|
||||
}
|
||||
}
|
||||
return days;
|
||||
}
|
||||
|
||||
encodeDayMask(daysArray) {
|
||||
var dayMask = 0;
|
||||
|
||||
for (var i = 0; i < daysArray.length; i++) {
|
||||
dayMask += this.getDayValue(daysArray[i]);
|
||||
}
|
||||
return dayMask;
|
||||
}
|
||||
|
||||
getDayValue(dayName) {
|
||||
for (var i = 0; i < DAY_VALUES.length; i++) {
|
||||
if (DAY_VALUES[i][0] === dayName) {
|
||||
return DAY_VALUES[i][1];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static slackForAlignment(val) {
|
||||
return (4 - val % 4) % 4;
|
||||
}
|
||||
|
||||
encode() {}
|
||||
};
|
||||
|
@ -3,13 +3,19 @@
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12526;
|
||||
const ASYNC_MSG_ID = 12500;
|
||||
|
||||
const SPA_CIRCUIT_ID = 500;
|
||||
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 {
|
||||
@ -107,4 +113,8 @@ exports.SLPoolStatusMessage = class SLPoolStatusMessage extends SLMessage {
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
|
||||
static getAsyncResponseId() {
|
||||
return ASYNC_MSG_ID;
|
||||
}
|
||||
};
|
||||
|
24
messages/SLRemoveClient.js
Normal file
24
messages/SLRemoveClient.js
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12524;
|
||||
|
||||
exports.SLRemoveClient = class SLRemoveClient extends SLMessage {
|
||||
constructor(senderId) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.senderId = senderId;
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(this.senderId);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
@ -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 {
|
||||
|
27
messages/SLSetCircuitRuntimeById.js
Normal file
27
messages/SLSetCircuitRuntimeById.js
Normal file
@ -0,0 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12550;
|
||||
|
||||
|
||||
exports.SLSetCircuitRuntimeById = class SLSetCircuitRuntimeById extends SLMessage {
|
||||
constructor(circuitId, runTime) {
|
||||
super(0, MSG_ID);
|
||||
|
||||
this.circuitId = circuitId;
|
||||
this.runTime = runTime;
|
||||
}
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(this.circuitId);
|
||||
this.writeInt32LE(this.runTime);
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
35
messages/SLSetPumpFlow.js
Normal file
35
messages/SLSetPumpFlow.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12586;
|
||||
|
||||
exports.SLSetPumpFlow = class SLSetPumpFlow extends SLMessage {
|
||||
constructor(pumpId, circuitId, setPoint, isRPMs) {
|
||||
super(0, MSG_ID);
|
||||
this.pumpId = pumpId;
|
||||
this.circuitId = circuitId;
|
||||
this.setPoint = setPoint;
|
||||
|
||||
if (isRPMs === true) {
|
||||
this.isRPMs = 1;
|
||||
} else {
|
||||
this.isRPMs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
encode() {
|
||||
this.writeInt32LE(0); // Always 0 in my case
|
||||
this.writeInt32LE(this.pumpId); // presumably pumpId, always 0 in my case
|
||||
this.writeInt32LE(this.circuitId); // This is indexed to the array of circuits returned in GetPumpStatus
|
||||
this.writeInt32LE(this.setPoint);
|
||||
this.writeInt32LE(this.isRPMs); // 0 for GPM, 1 for RPMs
|
||||
|
||||
super.encode();
|
||||
}
|
||||
|
||||
static getResponseId() {
|
||||
return MSG_ID + 1;
|
||||
}
|
||||
};
|
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;
|
||||
}
|
||||
};
|
38
messages/SLSetScheduleEventById.js
Normal file
38
messages/SLSetScheduleEventById.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
const SLMessage = require('./SLMessage.js').SLMessage;
|
||||
|
||||
const MSG_ID = 12548;
|
||||
|
||||
|
||||
exports.SLSetScheduleEventById = class SLSetScheduleEventById extends SLMessage {
|
||||
constructor(buf, scheduleId, circuitId, startTime, stopTime, dayMask, flags, heatCmd, heatSetPoint) {
|
||||
var size;
|
||||
if (buf) {
|
||||
size = buf.readInt32LE(4) + 8;
|
||||
}
|
||||
super(0, MSG_ID, size);
|
||||
|
||||
|
||||
if (!buf) {
|
||||
this.writeInt32LE(0);
|
||||
this.writeInt32LE(scheduleId);
|
||||
this.writeInt32LE(circuitId);
|
||||
this.writeInt32LE(startTime);
|
||||
this.writeInt32LE(stopTime);
|
||||
this.writeInt32LE(dayMask);
|
||||
this.writeInt32LE(flags);
|
||||
this.writeInt32LE(heatCmd);
|
||||
this.writeInt32LE(heatSetPoint);
|
||||
} else {
|
||||
this._wroteSize = true;
|
||||
this.writeBuffer(buf, 0);
|
||||
|
||||
this.decode();
|
||||
}
|
||||
}
|
||||
|
||||
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,16 @@ 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;
|
||||
exports.SLGetScheduleData = require('./SLGetScheduleData.js').SLGetScheduleData;
|
||||
exports.SLAddNewScheduleEvent = require('./SLAddNewScheduleEvent.js').SLAddNewScheduleEvent;
|
||||
exports.SLDeleteScheduleEventById = require('./SLDeleteScheduleEventById.js').SLDeleteScheduleEventById;
|
||||
exports.SLSetScheduleEventById = require('./SLSetScheduleEventById.js').SLSetScheduleEventById;
|
||||
exports.SLSetCircuitRuntimeById = require('./SLSetCircuitRuntimeById.js').SLSetCircuitRuntimeById;
|
||||
exports.SLGetPumpStatus = require('./SLGetPumpStatus.js').SLGetPumpStatus;
|
||||
exports.SLSetPumpFlow = require('./SLSetPumpFlow.js').SLSetPumpFlow;
|
||||
exports.SLCancelDelay = require('./SLCancelDelay.js').SLCancelDelay;
|
||||
exports.SLAddClient = require('./SLAddClient.js').SLAddClient;
|
||||
exports.SLRemoveClient = require('./SLRemoveClient.js').SLRemoveClient;
|
||||
|
1587
package-lock.json
generated
Normal file
1587
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "node-screenlogic",
|
||||
"description": "Tool for connecting to Pentair ScreenLogic systems on the local network",
|
||||
"version": "1.3.1",
|
||||
"version": "1.6.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/parnic/node-screenlogic.git",
|
||||
@ -12,15 +12,17 @@
|
||||
"swimmingpool"
|
||||
],
|
||||
"dependencies": {
|
||||
"smart-buffer": "~4.0.1"
|
||||
"debug": "^4.1.1",
|
||||
"smart-buffer": "~4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^4.19.1",
|
||||
"eslint": "^7.4.0",
|
||||
"eslint-config-strongloop": "^2.1.0",
|
||||
"mocha": "^5.1.1"
|
||||
"mocha": "^8.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test/*.spec.js",
|
||||
"pretest": "eslint --ignore-path .gitignore ."
|
||||
"pretest": "eslint --ignore-path .gitignore .",
|
||||
"test-slmessage": "mocha test/slmessage.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