nodejs-poolcontroller 8.0.5 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog +15 -0
- package/README.md +10 -17
- package/anslq25/MessagesMock.ts +5 -1
- package/anslq25/boards/MockSystemBoard.ts +1 -1
- package/anslq25/chemistry/MockChlorinator.ts +32 -8
- package/app.ts +10 -2
- package/controller/Equipment.ts +15 -79
- package/controller/State.ts +4 -72
- package/controller/boards/NixieBoard.ts +1 -0
- package/controller/boards/SystemBoard.ts +5 -1
- package/controller/comms/Comms.ts +9 -8
- package/controller/comms/messages/Messages.ts +1 -1
- package/controller/nixie/chemistry/ChemController.ts +64 -31
- package/controller/nixie/circuits/Circuit.ts +66 -31
- package/logger/Logger.ts +61 -18
- package/package.json +1 -1
- package/web/Server.ts +167 -18
- package/web/services/config/Config.ts +4 -3
package/Changelog
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 8.1.0
|
|
4
|
+
1. Support for dual chlorinators with REM chem controllers. It is now possible to have two separate chlorinators controlled in 'dynamic' mode by two separate REM chems. Note: In order for REM chem to control each chlorinator, each needs to be on a dedicated RS-485 port (not shared with an OCP or any other chlorinator).
|
|
5
|
+
|
|
6
|
+
## 8.0.1-8.0.5
|
|
7
|
+
1. Bug fixes including:
|
|
8
|
+
a. schedule end time errors
|
|
9
|
+
b. manual priority
|
|
10
|
+
c. screenlogic recurring schedules
|
|
11
|
+
d. VF pump message sequences
|
|
12
|
+
e. intellibrite themes
|
|
13
|
+
f. schedules are evaluated by bodies first and then everything else
|
|
14
|
+
g. solar stop/start delta logic
|
|
15
|
+
2. Jandy WaterColors support
|
|
16
|
+
3. Initial docker support (github docker actions)
|
|
17
|
+
|
|
3
18
|
## 8.0.0
|
|
4
19
|
1. Refactor comms code to Async
|
|
5
20
|
2. Update dependencies and Node >16
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
```diff
|
|
2
2
|
- INTELLICENTER USERS: Do not upgrade Intellicenter to 2.006. Rollback to 1.064 to use this application.
|
|
3
3
|
```
|
|
4
|
-
# nodejs-poolController - Version 8.
|
|
4
|
+
# nodejs-poolController - Version 8.1
|
|
5
5
|
|
|
6
6
|
## What is nodejs-poolController
|
|
7
7
|
|
|
@@ -26,6 +26,10 @@ Equipment supported
|
|
|
26
26
|
## Latest Changes
|
|
27
27
|
See [Changelog](https://github.com/tagyoureit/nodejs-poolController/blob/master/Changelog)
|
|
28
28
|
|
|
29
|
+
## What's new in 8.1?
|
|
30
|
+
|
|
31
|
+
Support for dual chlorinators with REM chem controllers. It is now possible to have two separate chlorinators controlled in 'dynamic' mode by two separate REM chems. Note: In order for REM chem to control each chlorinator, each needs to be on a dedicated RS-485 port (not shared with an OCP or any other chlorinator).
|
|
32
|
+
|
|
29
33
|
## What's new in 8.0?
|
|
30
34
|
|
|
31
35
|
Screenlogic can now be used as a direct connection point. If you feel that integrating an RS-485 adapter is a bit too much, then this is an option for you. The preferred method is still RS-485 as it is more fully featured.
|
|
@@ -62,8 +66,6 @@ If you don't know anything about NodeJS, these directions might be helpful.
|
|
|
62
66
|
1. Run the app with `npm start`.
|
|
63
67
|
* `npm start` will compile the Typescript code. You should use this every time you download/clone/pull the latest code.
|
|
64
68
|
* `npm run start:cached` will run the app without compiling the code which can be much faster.
|
|
65
|
-
1. Running `npm start` will also create a `config.json` file for your installation. If you need to modify any properties (e.g. the path to your serialport adapter, enabling socat, etc) then stop the app, edit the `config.json` per the [instructions](module_nodejs-poolController--config.json) below, and start the app again.
|
|
66
|
-
1. Verify your pool equipment is correctly identified by inspecting the `/data/*.json` files.
|
|
67
69
|
1. Install a [webclient](module_nodejs-poolController--clients) for a browser experience and/or a [binding](module_nodejs-poolController--bindings) to have two way control with Home Automation systems.
|
|
68
70
|
|
|
69
71
|
For a very thorough walk-through, see [this](https://www.troublefreepool.com/threads/pentair-intellicenter-pool-control-dashboard-instructional-guide.218514/) great thread on Trouble Free Pool. Thanks @MyAZPool.
|
|
@@ -97,16 +99,13 @@ To do anything with this app, you need a client to connect to it. A client can
|
|
|
97
99
|
|
|
98
100
|
## Web Clients
|
|
99
101
|
1. [nodejs-poolController-dashPanel](https://github.com/rstrouse/nodejs-poolController-dashPanel). Full compatibility with IntelliCenter, *Touch, REM (RelayEquipmentManager).
|
|
100
|
-
1. Deprecated - ~~[nodejs-poolController-webClient](http://github.com/tagyoureit/nodejs-poolController-webClient). Built primarily around EasyTouch/IntelliTouch but will work with other systems.~~
|
|
101
102
|
|
|
102
|
-
* This app has the default to only listen to clients from localhost (127.0.0.1). If you need to have clients connect from other machines you will need to change the [ip](#module_nodejs-poolController--config.json) in `config.json`.
|
|
103
103
|
|
|
104
104
|
<a name="module_nodejs-poolController--bindings"></a>
|
|
105
105
|
|
|
106
106
|
## Home Automation Bindings (previously Integrations)
|
|
107
|
-
**NOTE: Existing integrations built of 5.3 or earlier WILL NOT WORK. They need to be upgraded to leverage 6.0. **
|
|
108
107
|
|
|
109
|
-
Available
|
|
108
|
+
Available automations:
|
|
110
109
|
* [Vera Home Automation Hub](https://github.com/rstrouse/nodejs-poolController-veraPlugin) - A plugin that integrates with nodejs-poolController. [Bindings Directions](https://github.com/tagyoureit/nodejs-poolController/wiki/Bindings-Integrations-in-2.0#vera)
|
|
111
110
|
* [Hubitat](https://github.com/bsileo/hubitat_poolcontroller) by @bsileo (prev help from @johnny2678, @donkarnag, @arrmo). [Bindings Directions](https://github.com/tagyoureit/nodejs-poolController/wiki/Bindings-Integrations-in-2.0#smartthingshubitat)
|
|
112
111
|
* [Homebridge/Siri/EVE](https://github.com/gadget-monk/homebridge-poolcontroller) by @gadget-monk, adopted from @leftyflip
|
|
@@ -114,31 +113,25 @@ Available for 6.x:
|
|
|
114
113
|
* [MQTT](https://github.com/crsherman/nodejs-poolController-mqtt) original release by @crsherman, re-write by @kkzonie, testing by @baudfather and others. [Bindings Directions](https://github.com/tagyoureit/nodejs-poolController/wiki/Bindings-Integrations-in-2.0#mqtt)
|
|
115
114
|
* [Homeseer](https://github.com/tagyoureit/nodejs-poolController/wiki/Homeseer-Setup-Instructions) - Integration directions by @miamijerry to integrate Homeseer through MQTT
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
Outdated:
|
|
118
117
|
* [Another SmartThings Controller](https://github.com/dhop90/pentair-pool-controller/blob/master/README.md) by @dhop90
|
|
119
118
|
* [ISY](src/integrations/socketISY.js). Original credit to @blueman2, enhancements by @mayermd
|
|
120
119
|
* [ISY Polyglot NodeServer](https://github.com/brianmtreese/nodejs-pool-controller-polyglotv2) created by @brianmtreese
|
|
121
120
|
|
|
122
121
|
# Support
|
|
123
|
-
1. For discussions, recommendations, designs, and clarifications, we recommend you join the [Github discussions](https://github.com/tagyoureit/nodejs-poolController/discussions
|
|
122
|
+
1. For discussions, recommendations, designs, and clarifications, we recommend you join the [Github discussions](https://github.com/tagyoureit/nodejs-poolController/discussions.
|
|
124
123
|
1. Check the [wiki](https://github.com/tagyoureit/nodejs-poolController/wiki) for tips, tricks and additional documentation.
|
|
125
124
|
1. For bug reports you can open a [github issue](https://github.com/tagyoureit/nodejs-poolController/issues/new),
|
|
126
125
|
|
|
127
|
-
### Virtual Controller
|
|
128
|
-
v6 adds all new configuration and support for virtual pumps, chlorinators (and soon, Intellichem)
|
|
129
|
-
|
|
130
|
-
* [Virtual Pump Directions](https://github.com/tagyoureit/nodejs-poolController/wiki/Virtual-Pump-Controller---v6)
|
|
131
|
-
* [Virtual Chlorinator Directions](https://github.com/tagyoureit/nodejs-poolController/wiki/Virtual-Chlorinator-Controller-v6)
|
|
132
|
-
* Virtual Chem Controller
|
|
133
126
|
|
|
134
127
|
# Changes
|
|
135
128
|
See [Changelog](https://github.com/tagyoureit/nodejs-poolController/blob/master/Changelog)
|
|
136
129
|
|
|
137
|
-
|
|
138
130
|
<a name="module_nodejs-poolController--config.json"></a>
|
|
139
131
|
# Config.json changes
|
|
140
132
|
|
|
141
133
|
## Controller section - changes to the communications for the app
|
|
134
|
+
Most of these can be configured directly from the UI in dashPanel.
|
|
142
135
|
* `rs485Port` - set to the name of you rs485 controller. See [wiki](https://github.com/tagyoureit/nodejs-poolController/wiki/RS-485-Adapter-Details) for details and testing.
|
|
143
136
|
* `portSettings` - should not need to be changed for RS485
|
|
144
137
|
* `mockPort` - opens a "fake" port for this app to communicate on. Can be used with [packet captures/replays](https://github.com/tagyoureit/nodejs-poolController/wiki/How-to-capture-all-packets-for-issue-resolution).
|
|
@@ -182,7 +175,7 @@ See [Changelog](https://github.com/tagyoureit/nodejs-poolController/blob/master/
|
|
|
182
175
|
# License
|
|
183
176
|
|
|
184
177
|
nodejs-poolController. An application to control pool equipment.
|
|
185
|
-
Copyright (C) 2016, 2017. Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
178
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025. Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
186
179
|
|
|
187
180
|
This program is free software: you can redistribute it and/or modify
|
|
188
181
|
it under the terms of the GNU Affero General Public License as
|
package/anslq25/MessagesMock.ts
CHANGED
|
@@ -196,7 +196,11 @@ export class MessagesMock {
|
|
|
196
196
|
HeaterStateMessage.process(outboundMsg);
|
|
197
197
|
break;*/
|
|
198
198
|
case Protocol.Chlorinator:
|
|
199
|
-
|
|
199
|
+
// Only process outbound messages (commands from OCP to chlorinator)
|
|
200
|
+
// Inbound messages (responses from chlorinator to OCP) should not be processed by mock
|
|
201
|
+
if (msg.dest >= 80) {
|
|
202
|
+
mockChlor.process(msg);
|
|
203
|
+
}
|
|
200
204
|
/*
|
|
201
205
|
case Protocol.Hayward:
|
|
202
206
|
PumpStateMessage.processHayward(msg);
|
|
@@ -50,7 +50,7 @@ export class MockSystemBoard {
|
|
|
50
50
|
return await msg.sendAsync();
|
|
51
51
|
// is the controller on a real/physical port or a mock port?
|
|
52
52
|
/* let port = conn.findPortById(sys.anslq25.portId);
|
|
53
|
-
if (port.
|
|
53
|
+
if (port.mock) {
|
|
54
54
|
let inbound = new Inbound();
|
|
55
55
|
inbound.protocol = msg.protocol;
|
|
56
56
|
inbound.header = msg.header;
|
|
@@ -15,13 +15,16 @@ export class MockChlorinator {
|
|
|
15
15
|
switch (inbound.action){
|
|
16
16
|
case 0: // Set control OCP->Chlorinator: [16,2,80,0][0][98,16,3]
|
|
17
17
|
this.chlorSetControl(inbound, response);
|
|
18
|
+
break;
|
|
18
19
|
case 17: // OCP->Chlorinator set output. [16,2,80,17][15][130,16,3]
|
|
19
20
|
this.chlorSetOutput(inbound, response);
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
break;
|
|
22
|
+
case 19: // iChlor keep alive(?) [16, 2, 80, 19][117, 16, 3]
|
|
23
|
+
this.chlorKeepAlive(inbound, response);
|
|
22
24
|
break;
|
|
23
25
|
case 20: // OCP->Chlorinator Get model [16,2,80,20][0][118,16,3]
|
|
24
26
|
this.chlorGetModel(inbound, response);
|
|
27
|
+
break;
|
|
25
28
|
default:
|
|
26
29
|
logger.info(`No mock chlorinator response for ${inbound.toShortPacket()} `);
|
|
27
30
|
}
|
|
@@ -45,13 +48,34 @@ export class MockChlorinator {
|
|
|
45
48
|
/*
|
|
46
49
|
{"port":0,"id":42639,"valid":true,"dir":"out","proto":"chlorinator","pkt":[[],[], [16,2,80,17], [100],[215,16,3]],"ts":"2022-07-19T21:46:00.302-0700"}
|
|
47
50
|
{"port":0,"id":42640,"valid":true,"dir":"in","proto":"chlorinator","for":[42639],"pkt":[[],[],[16,2,0,18],[78,128],[242,16,3]],"ts": "2022-07-19T21:46:00.341-0700"} */
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
// Simulate a response from the chlorinator (inbound message)
|
|
52
|
+
const payload = [Math.floor(this.random(90-56, true))+56, 128];
|
|
53
|
+
const header = [16, 2, 0, 18];
|
|
54
|
+
const term = [242, 16, 3];
|
|
55
|
+
let responseMsg = new Inbound();
|
|
56
|
+
responseMsg.protocol = inbound.protocol;
|
|
57
|
+
responseMsg.portId = inbound.portId;
|
|
58
|
+
responseMsg.header = header;
|
|
59
|
+
responseMsg.payload = payload;
|
|
60
|
+
responseMsg.term = term;
|
|
61
|
+
// The Inbound class will parse header/payload/action/source/dest automatically
|
|
62
|
+
setTimeout(() => {
|
|
63
|
+
let port = conn.findPortById(inbound.portId);
|
|
64
|
+
if (port) {
|
|
65
|
+
port.pushIn(Buffer.from(responseMsg.toPacket()));
|
|
66
|
+
}
|
|
67
|
+
}, 50);
|
|
54
68
|
}
|
|
69
|
+
public chlorKeepAlive(inbound: Inbound, response: Outbound){
|
|
70
|
+
/*
|
|
71
|
+
{"port":0,"id":42647,"valid":true,"dir":"out","proto":"chlorinator","pkt":[[],[], [16,2,80,19],[117,16,3]],"ts":"2022-07-19T21:46:00.645-0700"}
|
|
72
|
+
{"port":0,"id":42648,"valid":true,"dir":"in","proto":"chlorinator","for":[42647],"pkt":[[],[],[16,2,0,20],[0],[118,16,3]],"ts":"2022-07-19T21:46:00.700-0700"} */
|
|
73
|
+
response.action = 20;
|
|
74
|
+
response.appendPayloadBytes(0, 1);
|
|
75
|
+
response.setPayloadByte(0, 0);
|
|
76
|
+
conn.queueSendMessage(response);
|
|
77
|
+
}
|
|
78
|
+
|
|
55
79
|
public chlorGetModel(inbound: Inbound, response: Outbound){
|
|
56
80
|
/*
|
|
57
81
|
{"port":0,"id":42645,"valid":true,"dir":"out","proto":"chlorinator","pkt":[[],[], [16,2,80,20], [0],[118,16,3]],"ts":"2022-07-19T21:46:00.645-0700"}
|
package/app.ts
CHANGED
|
@@ -25,7 +25,7 @@ import { conn } from "./controller/comms/Comms";
|
|
|
25
25
|
import { sys } from "./controller/Equipment";
|
|
26
26
|
|
|
27
27
|
import { state } from "./controller/State";
|
|
28
|
-
import { webApp } from "./web/Server";
|
|
28
|
+
import { webApp, REMInterfaceServer } from "./web/Server";
|
|
29
29
|
import * as readline from 'readline';
|
|
30
30
|
import { sl } from './controller/comms/ScreenLogic'
|
|
31
31
|
|
|
@@ -52,6 +52,9 @@ export async function startPacketCapture(bResetLogs: boolean) {
|
|
|
52
52
|
if (bResetLogs){
|
|
53
53
|
sys.resetSystem();
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
// Start packet capture on the REM server
|
|
57
|
+
await REMInterfaceServer.startPacketCaptureOnRemServer();
|
|
55
58
|
}
|
|
56
59
|
catch (err) {
|
|
57
60
|
console.error(`Error starting replay: ${ err.message }`);
|
|
@@ -61,7 +64,12 @@ export async function stopPacketCaptureAsync() {
|
|
|
61
64
|
let log = config.getSection('log');
|
|
62
65
|
log.app.captureForReplay = false;
|
|
63
66
|
config.setSection('log', log);
|
|
64
|
-
|
|
67
|
+
|
|
68
|
+
// Stop packet capture on the REM server and collect its logs
|
|
69
|
+
let remLogs = await REMInterfaceServer.stopPacketCaptureOnRemServer();
|
|
70
|
+
|
|
71
|
+
// Pass REM logs to the logger for inclusion in the backup
|
|
72
|
+
return logger.stopCaptureForReplayAsync(remLogs);
|
|
65
73
|
}
|
|
66
74
|
export async function stopAsync(): Promise<void> {
|
|
67
75
|
try {
|
package/controller/Equipment.ts
CHANGED
|
@@ -1678,6 +1678,10 @@ export class Chlorinator extends EqItem {
|
|
|
1678
1678
|
public set ignoreSaltReading(val: boolean) { this.setDataVal('ignoreSaltReading', val); }
|
|
1679
1679
|
public get model() { return this.data.model; }
|
|
1680
1680
|
public set model(val: number | any) { this.setDataVal('model', sys.board.valueMaps.chlorinatorModel.encode(val)); }
|
|
1681
|
+
public get ratedLbs(): number {
|
|
1682
|
+
let model = sys.board.valueMaps.chlorinatorModel.get(this.model);
|
|
1683
|
+
return typeof model.chlorinePerSec !== 'undefined' ? model.chlorinePerSec : 0;
|
|
1684
|
+
}
|
|
1681
1685
|
}
|
|
1682
1686
|
export class ValveCollection extends EqItemCollection<Valve> {
|
|
1683
1687
|
constructor(data: any, name?: string) { super(data, name || "valves"); }
|
|
@@ -2207,80 +2211,6 @@ export interface IChemController {
|
|
|
2207
2211
|
}
|
|
2208
2212
|
export class ChemController extends EqItem implements IChemController {
|
|
2209
2213
|
public initData() {
|
|
2210
|
-
//var chemController = {
|
|
2211
|
-
// id: 'number', // Id of the controller
|
|
2212
|
-
// name: 'string', // Name assigned to the controller
|
|
2213
|
-
// type: 'valueMap', // intellichem, rem -- There is an unknown but that should probably go away.
|
|
2214
|
-
// body: 'valueMap', // Body assigned to the chem controller.
|
|
2215
|
-
// address: 'number', // Address for IntelliChem controller only.
|
|
2216
|
-
// isActive: 'booean',
|
|
2217
|
-
// isVirtual: 'boolean', // False if controlled by OCP.
|
|
2218
|
-
// calciumHardness: 'number',
|
|
2219
|
-
// cyanuricAcid: 'number',
|
|
2220
|
-
// alkalinity: 'number',
|
|
2221
|
-
// HMIAdvancedDisplay: 'boolean', // This is related to IntelliChem and determines what is displayed on the controller.
|
|
2222
|
-
// ph: { // pH chemical structure
|
|
2223
|
-
// chemType: 'string', // Constant ph
|
|
2224
|
-
// enabled: 'boolean', // Allows disabling the functions without deleting the settings.
|
|
2225
|
-
// dosingMethod: 'valueMap', // manual, volume, volumeTime.
|
|
2226
|
-
// // manual = The dosing pump is not triggered.
|
|
2227
|
-
// // volume = Time is not considered as a limit to the dosing.
|
|
2228
|
-
// // time = The only limit to the dose is the amount of time.
|
|
2229
|
-
// // volumeTime = Limit the dose by volume or time whichever is sooner.
|
|
2230
|
-
// maxDosingTime: 'number', // The maximum amount of time a dose can occur before mixing.
|
|
2231
|
-
// maxDosingVolume: 'number', // The maximum volume for a dose in mL.
|
|
2232
|
-
// mixingTime: 'number', // Amount of time between in seconds doses that the pump must run before adding another dose.
|
|
2233
|
-
// startDelay: 'number', // The number of seconds that the pump must be running prior to considering a dose.
|
|
2234
|
-
// setpoint: 'number', // Target setpoint for pH
|
|
2235
|
-
// phSupply: 'valueMap', // base or acid.
|
|
2236
|
-
// pump: {
|
|
2237
|
-
// type: 'valueMap', // none, relay, ezo-pmp
|
|
2238
|
-
// connectionId: 'uuid', // Unique identifier for njspc external connections.
|
|
2239
|
-
// deviceBinding: 'string', // Binding value for REM to tell it what device is involved.
|
|
2240
|
-
// ratedFlow: 'number', // The standard flow rate for the pump in mL/min.
|
|
2241
|
-
// },
|
|
2242
|
-
// tank: {
|
|
2243
|
-
// capacity: 'number', // Capacity of the tank in the units provided.
|
|
2244
|
-
// units: 'valueMap' // gal, mL, cL, L, oz, pt, qt.
|
|
2245
|
-
// },
|
|
2246
|
-
// probe: {
|
|
2247
|
-
// connectionId: 'uuid', // A unique identifier that has been generated for connections in njspc.
|
|
2248
|
-
// deviceBinding: 'string', // A mapping value that is used by REM to determine which device is used.
|
|
2249
|
-
// type: 'valueMap' // none, ezo-ph, other.
|
|
2250
|
-
// }
|
|
2251
|
-
|
|
2252
|
-
// },
|
|
2253
|
-
// orp: { // ORP chemical structure
|
|
2254
|
-
// chemType: 'string', // Constant orp
|
|
2255
|
-
// enabled: 'boolean', // Allows disabling the functions without deleting the settings.
|
|
2256
|
-
// dosingMethod: 'valueMap', // manual, volume, volumeTime.
|
|
2257
|
-
// // manual = The dosing pump is not triggered.
|
|
2258
|
-
// // volume = Time is not considered as a limit to the dosing.
|
|
2259
|
-
// // time = The only limit to the dose is the amount of time.
|
|
2260
|
-
// // volumeTime = Limit the dose by volume or time whichever is sooner.
|
|
2261
|
-
// maxDosingTime: 'number', // The maximum amount of time a dose can occur before mixing.
|
|
2262
|
-
// maxDosingVolume: 'number', // The maximum volume for a dose in mL.
|
|
2263
|
-
// mixingTime: 'number', // Amount of time between in seconds doses that the pump must run before adding another dose.
|
|
2264
|
-
// startDelay: 'number', // The number of seconds that the pump must be running prior to considering a dose.
|
|
2265
|
-
// setpoint: 'number', // Target setpoint for ORP
|
|
2266
|
-
// useChlorinator: 'boolean', // Indicates whether the chlorinator will be used for dosing.
|
|
2267
|
-
// pump: {
|
|
2268
|
-
// type: 'valueMap', // none, relay, ezo-pmp
|
|
2269
|
-
// connectionId: 'uuid', // Unique identifier for njspc external connections.
|
|
2270
|
-
// deviceBinding: 'string', // Binding value for REM to tell it what device is involved.
|
|
2271
|
-
// ratedFlow: 'number', // The standard flow rate for the pump in mL/min.
|
|
2272
|
-
// },
|
|
2273
|
-
// tank: {
|
|
2274
|
-
// capacity: 'number', // Capacity of the tank in the units provided.
|
|
2275
|
-
// units: 'valueMap' // gal, mL, cL, L, oz, pt, qt.
|
|
2276
|
-
// },
|
|
2277
|
-
// probe: {
|
|
2278
|
-
// connectionId: 'uuid', // A unique identifier that has been generated for connections in njspc.
|
|
2279
|
-
// deviceBinding: 'string', // A mapping value that is used by REM to determine which device is used.
|
|
2280
|
-
// type: 'valueMap' // none, ezo-orp, other.
|
|
2281
|
-
// }
|
|
2282
|
-
// }
|
|
2283
|
-
//}
|
|
2284
2214
|
if (typeof this.data.lsiRange === 'undefined') this.data.lsiRange = { low: -.5, high: .5, enabled: true };
|
|
2285
2215
|
if (typeof this.data.borates === 'undefined') this.data.borates = 0;
|
|
2286
2216
|
if (typeof this.data.siCalcType === 'undefined') this.data.siCalcType = 0;
|
|
@@ -2299,8 +2229,6 @@ export class ChemController extends EqItem implements IChemController {
|
|
|
2299
2229
|
public set address(val: number) { this.setDataVal('address', val); }
|
|
2300
2230
|
public get isActive(): boolean { return this.data.isActive; }
|
|
2301
2231
|
public set isActive(val: boolean) { this.setDataVal('isActive', val); }
|
|
2302
|
-
// public get isVirtual(): boolean { return this.data.isVirtual; }
|
|
2303
|
-
// public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
|
|
2304
2232
|
public get calciumHardness(): number { return this.data.calciumHardness; }
|
|
2305
2233
|
public set calciumHardness(val: number) { this.setDataVal('calciumHardness', val); }
|
|
2306
2234
|
public get cyanuricAcid(): number { return this.data.cyanuricAcid; }
|
|
@@ -2460,7 +2388,7 @@ export class Chemical extends ChildEqItem implements IChemical {
|
|
|
2460
2388
|
public set startDelay(val: number) { this.setDataVal('startDelay', val); }
|
|
2461
2389
|
public get pump(): ChemicalPump { return new ChemicalPump(this.data, 'pump', this); }
|
|
2462
2390
|
public get tank(): ChemicalTank { return new ChemicalTank(this.data, 'tank', this); }
|
|
2463
|
-
public get chlor():
|
|
2391
|
+
// public get chlor(): Chlorinator { return new ChemicalChlor(this.data, 'chlor', this); }
|
|
2464
2392
|
public get setpoint(): number { return this.data.setpoint; }
|
|
2465
2393
|
public set setpoint(val: number) { this.setDataVal('setpoint', val); }
|
|
2466
2394
|
public get tolerance(): AlarmSetting { return new AlarmSetting(this.data, 'tolerance', this); }
|
|
@@ -2518,6 +2446,14 @@ export class ChemicalORP extends Chemical {
|
|
|
2518
2446
|
}
|
|
2519
2447
|
public get useChlorinator(): boolean { return utils.makeBool(this.data.useChlorinator); }
|
|
2520
2448
|
public set useChlorinator(val: boolean) { this.setDataVal('useChlorinator', val); }
|
|
2449
|
+
public get chlorId(): number {
|
|
2450
|
+
if (typeof this.data.chlorId === 'undefined'){
|
|
2451
|
+
// default to 1st chlorinator if not set; this is a backwards compatibility item when upgrading to 8.1
|
|
2452
|
+
return sys.chlorinators.getItemByIndex(0).id;
|
|
2453
|
+
}
|
|
2454
|
+
return this.data.chlorId;
|
|
2455
|
+
}
|
|
2456
|
+
public set chlorId(val: number) { this.setDataVal('chlorId', val); }
|
|
2521
2457
|
public get phLockout(): number { return this.data.phLockout; }
|
|
2522
2458
|
public set phLockout(val: number) { this.setDataVal('phLockout', val); }
|
|
2523
2459
|
public get probe(): ChemicalORPProbe { return new ChemicalORPProbe(this.data, 'probe', this); }
|
|
@@ -2661,7 +2597,7 @@ export class ChemicalTank extends ChildEqItem {
|
|
|
2661
2597
|
return tank;
|
|
2662
2598
|
}
|
|
2663
2599
|
}
|
|
2664
|
-
export class ChemicalChlor extends ChildEqItem {
|
|
2600
|
+
/* export class ChemicalChlor extends ChildEqItem {
|
|
2665
2601
|
// This whole class is a reference to the first chlorinator.
|
|
2666
2602
|
// This may not follow a best practice
|
|
2667
2603
|
// and certainly won't work for multiple chlors
|
|
@@ -2695,7 +2631,7 @@ export class ChemicalChlor extends ChildEqItem {
|
|
|
2695
2631
|
chlor.model = sys.board.valueMaps.chlorinatorModel.transform(this.model);
|
|
2696
2632
|
return chlor;
|
|
2697
2633
|
}
|
|
2698
|
-
}
|
|
2634
|
+
} */
|
|
2699
2635
|
export class AlarmSetting extends ChildEqItem {
|
|
2700
2636
|
public dataName = 'AlarmSettingConfig';
|
|
2701
2637
|
public initData() {
|
package/controller/State.ts
CHANGED
|
@@ -1096,7 +1096,7 @@ export class ScheduleStateCollection extends EqStateCollection<ScheduleState> {
|
|
|
1096
1096
|
}
|
|
1097
1097
|
st.calcSchedule(state.time, sys.schedules.getItemById(ssched.id));
|
|
1098
1098
|
if (typeof st.startTime === 'undefined') continue;
|
|
1099
|
-
if (ssched.isOn || st.shouldBeOn || st.startTime.getTime() > new Date().getTime()) activeScheds.push(ssched);
|
|
1099
|
+
if (ssched.isOn || st.shouldBeOn || (st.startTime && st.startTime.getTime() > new Date().getTime())) activeScheds.push(ssched);
|
|
1100
1100
|
}
|
|
1101
1101
|
return activeScheds;
|
|
1102
1102
|
}
|
|
@@ -1256,7 +1256,7 @@ export class ScheduleTime extends ChildEqState {
|
|
|
1256
1256
|
let dtCalc = typeof this.calculatedDate !== 'undefined' && typeof this.calculatedDate.getTime === 'function' ? new Date(this.calculatedDate.getTime()).setHours(0, 0, 0, 0) : new Date(1970, 0, 1, 0, 0).getTime();
|
|
1257
1257
|
let recalc = !this.calculated;
|
|
1258
1258
|
if (!recalc && sod.getTime() !== dtCalc) recalc = true;
|
|
1259
|
-
if (!recalc && (this.endTime.getTime() < new Date().getTime() && this.startTime.getTime() < dtCalc)) {
|
|
1259
|
+
if (!recalc && (this.endTime && this.endTime.getTime() < new Date().getTime() && this.startTime && this.startTime.getTime() < dtCalc)) {
|
|
1260
1260
|
recalc = true;
|
|
1261
1261
|
logger.info(`Recalculating expired schedule ${sched.id}`);
|
|
1262
1262
|
}
|
|
@@ -2658,74 +2658,6 @@ export class ChemControllerState extends EqState implements IChemControllerState
|
|
|
2658
2658
|
if (typeof this.data.siCalcType === 'undefined') {
|
|
2659
2659
|
this.data.siCalcType = sys.board.valueMaps.siCalcTypes.transform(0);
|
|
2660
2660
|
}
|
|
2661
|
-
//var chemControllerState = {
|
|
2662
|
-
// lastComm: 'number', // The unix time the chem controller sent its status.
|
|
2663
|
-
// id: 'number', // Id of the chemController.
|
|
2664
|
-
// type: 'valueMap', // intellichem, rem.
|
|
2665
|
-
// address: 'number', // Assigned address if IntelliChem.
|
|
2666
|
-
// name: 'string', // Name assigned to the controller.
|
|
2667
|
-
// status: 'valueMap', // ok, nocomms, setupError
|
|
2668
|
-
// body: 'valueMap', // Body that the chemController is assigned to.
|
|
2669
|
-
// flowDetected: 'boolean', // True if there is currently sufficient flow to read and dose.
|
|
2670
|
-
// flowDelay: 'boolean', // True of the controller is currently under a flow delay.
|
|
2671
|
-
// firmware: 'string', // Firmware version from IntelliChem (this should be in config)
|
|
2672
|
-
// saturationIndex: 'number', // Calculated LSI for the body.
|
|
2673
|
-
// isActive: 'boolean',
|
|
2674
|
-
// alarms: {}, // This has not changed although additional alarms will be added.
|
|
2675
|
-
// warnings: {}, // This has not changed although additional warnings will be added.
|
|
2676
|
-
// chemistryStatus: 'valueMap', // Current water quality status.
|
|
2677
|
-
// ph: {
|
|
2678
|
-
// chemType: 'string', // Constant ph.
|
|
2679
|
-
// dosingTimeRemaining: 'number', // The number of seconds remaining for the current dose.
|
|
2680
|
-
// dosingVolumeRemaining: 'number', // Remaining volume for the current dose in mL.
|
|
2681
|
-
// mixTimeRemaining: 'number', // The number of seconds remaining in the current mix cycle.
|
|
2682
|
-
// dosingStatus: 'valueMap', // dosing, monitoring, mixing.
|
|
2683
|
-
// level: 'number', // The current pH level.
|
|
2684
|
-
// lockout: 'boolean', // True if an attempt to dose was thwarted by error.
|
|
2685
|
-
// manualDosing: 'boolean', // True if the pump is running outside of a dosing command.
|
|
2686
|
-
// dailyLimitReached: 'boolean', // True if the calculated daily limit has been reached based upon body volume.
|
|
2687
|
-
// pump: {
|
|
2688
|
-
// type: 'valueMap', // The defined pump type.
|
|
2689
|
-
// isDosing: 'boolean', // True if the pump is running.
|
|
2690
|
-
// },
|
|
2691
|
-
// tank: {
|
|
2692
|
-
// level: 'number', // The current level for the tank.
|
|
2693
|
-
// capacity: 'number', // Total capacity for the tank.
|
|
2694
|
-
// units: 'valueMap', // nounits, gal, mL, cL, L, oz, pt, qt.
|
|
2695
|
-
// },
|
|
2696
|
-
// probe: {
|
|
2697
|
-
// level: 'number', // Current ph level as measured by the probe.
|
|
2698
|
-
// temperature: 'number', // The temperature used to calculate the adjusted probe level.
|
|
2699
|
-
// tempUnits: 'valueMap' // Units for the temperature C or F.
|
|
2700
|
-
// }
|
|
2701
|
-
// },
|
|
2702
|
-
// orp: {
|
|
2703
|
-
// chemType: 'string', // Constant orp.
|
|
2704
|
-
// dosingTimeRemaining: 'number', // The number of seconds remaining for the current dose.
|
|
2705
|
-
// dosingVolumeRemaining: 'number', // Remaining volume for the current dose in mL.
|
|
2706
|
-
// mixTimeRemaining: 'number', // The number of seconds remaining in the current mix cycle.
|
|
2707
|
-
// dosingStatus: 'valueMap', // dosing, monitoring, mixing.
|
|
2708
|
-
// level: 'number', // The current ORP level.
|
|
2709
|
-
// lockout: 'boolean', // True if an attempt to dose was thwarted by error.
|
|
2710
|
-
// manualDosing: 'boolean', // True if the pump is running outside of a dosing command.
|
|
2711
|
-
// dailyLimitReached: 'boolean', // True if the calculated daily limit has been reached based upon body volume.
|
|
2712
|
-
// pump: {
|
|
2713
|
-
// type: 'valueMap', // The defined pump type.
|
|
2714
|
-
// isDosing: 'boolean', // True if the pump is running.
|
|
2715
|
-
// },
|
|
2716
|
-
// tank: {
|
|
2717
|
-
// level: 'number', // The current level for the tank.
|
|
2718
|
-
// capacity: 'number', // Total capacity for the tank.
|
|
2719
|
-
// units: 'valueMap', // nounits, gal, mL, cL, L, oz, pt, qt.
|
|
2720
|
-
// },
|
|
2721
|
-
// probe: {
|
|
2722
|
-
// level: 'number', // Current ORP level as measured by the probe.
|
|
2723
|
-
// temperature: 'number', // The temperature used to calculate the adjusted probe level.
|
|
2724
|
-
// tempUnits: 'valueMap' // Units for the temperature C or F.
|
|
2725
|
-
// }
|
|
2726
|
-
// }
|
|
2727
|
-
//}
|
|
2728
|
-
|
|
2729
2661
|
}
|
|
2730
2662
|
public dataName: string = 'chemController';
|
|
2731
2663
|
public get lastComm(): number { return this.data.lastComm || 0; }
|
|
@@ -3175,8 +3107,8 @@ export class ChemicalORPState extends ChemicalState {
|
|
|
3175
3107
|
public get chemType() { return 'orp'; }
|
|
3176
3108
|
public set chemType(val) { this.setDataVal('chemType', val); }
|
|
3177
3109
|
public get probe() { return new ChemicalProbeORPState(this.data, 'probe', this); }
|
|
3178
|
-
public get useChlorinator(): boolean { return utils.makeBool(this.data.useChlorinator); }
|
|
3179
|
-
public set useChlorinator(val: boolean) { this.setDataVal('useChlorinator', val); }
|
|
3110
|
+
// public get useChlorinator(): boolean { return utils.makeBool(this.data.useChlorinator); }
|
|
3111
|
+
// public set useChlorinator(val: boolean) { this.setDataVal('useChlorinator', val); }
|
|
3180
3112
|
public get suspendDosing(): boolean {
|
|
3181
3113
|
let cc = this.chemController;
|
|
3182
3114
|
return cc.alarms.comms !== 0 || cc.alarms.orpProbeFault !== 0 || cc.alarms.orpPumpFault !== 0 || cc.alarms.bodyFault !== 0;
|
|
@@ -73,6 +73,7 @@ export class NixieBoard extends SystemBoard {
|
|
|
73
73
|
[14, { name: 'colorlogic', desc: 'ColorLogic', isLight: true, theme: 'colorlogic' }],
|
|
74
74
|
[15, { name: 'spadrain', desc: 'Spa Drain' }],
|
|
75
75
|
[16, { name: 'pooltone', desc: 'Pool Tone', isLight: true, theme: 'pooltone' }],
|
|
76
|
+
[17, { name: 'watercolors', desc: 'WaterColors', isLight: true, theme: 'watercolors' }],
|
|
76
77
|
]);
|
|
77
78
|
this.valueMaps.pumpTypes = new byteValueMap([
|
|
78
79
|
[1, { name: 'ss', desc: 'Single Speed', maxCircuits: 8, hasAddress: false, hasBody: false, maxRelays: 1, relays: [{ id: 1, name: 'Pump On/Off' }]}],
|
|
@@ -3656,8 +3656,12 @@ export class ScheduleCommands extends BoardCommands {
|
|
|
3656
3656
|
ssched.display = sched.display = display;
|
|
3657
3657
|
ssched.startTimeOffset = sched.startTimeOffset = startTimeOffset;
|
|
3658
3658
|
ssched.endTimeOffset = sched.endTimeOffset = endTimeOffset;
|
|
3659
|
-
|
|
3659
|
+
// Nixie controller managing schedules (master = 1), physical OCP (master = 0)
|
|
3660
|
+
if (sys.controllerType === ControllerType.Nixie) {
|
|
3660
3661
|
sched.master = 1;
|
|
3662
|
+
} else {
|
|
3663
|
+
sched.master = 0;
|
|
3664
|
+
}
|
|
3661
3665
|
await ncp.schedules.setScheduleAsync(sched, data);
|
|
3662
3666
|
// update end time in case sched is changed while circuit is on
|
|
3663
3667
|
let cstate = state.circuits.getInterfaceById(sched.circuit);
|
|
@@ -198,7 +198,7 @@ export class Connection {
|
|
|
198
198
|
let c = cfg[section];
|
|
199
199
|
if (typeof c.type === 'undefined') {
|
|
200
200
|
let type = 'local';
|
|
201
|
-
if (c.
|
|
201
|
+
if (c.mock) type = 'mock';
|
|
202
202
|
else if (c.netConnect) type = 'network';
|
|
203
203
|
config.setSection(`controller.${section}`, c);
|
|
204
204
|
console.log(section);
|
|
@@ -260,7 +260,7 @@ export class Connection {
|
|
|
260
260
|
if (anslq25port >= 0) {
|
|
261
261
|
let ports = this.rs485Ports;
|
|
262
262
|
for (let i = 0; i < ports.length; i++) {
|
|
263
|
-
// if (ports[i].
|
|
263
|
+
// if (ports[i].mock) continue;
|
|
264
264
|
if (ports[i].portId === currPort.portId) continue;
|
|
265
265
|
if (ports[i].portId === anslq25port) continue; // don't resend
|
|
266
266
|
if (!ports[i].isOpen) continue;
|
|
@@ -380,7 +380,8 @@ export class Connection {
|
|
|
380
380
|
let msgs = [];
|
|
381
381
|
// conn.queueInboundToBroadcast(msg);
|
|
382
382
|
conn.queueOutboundToBroadcast(msg);
|
|
383
|
-
/* if (msgs.
|
|
383
|
+
/* if (msgs.le
|
|
384
|
+
ngth > 0) {
|
|
384
385
|
msgs.push(msg);
|
|
385
386
|
let promises: Promise<boolean>[] = [];
|
|
386
387
|
for (let i = 0; i < msgs.length; i++) {
|
|
@@ -511,7 +512,7 @@ export class RS485Port {
|
|
|
511
512
|
public reconnects: number = 0;
|
|
512
513
|
public emitter: EventEmitter;
|
|
513
514
|
public get portId() { return typeof this._cfg !== 'undefined' && typeof this._cfg.portId !== 'undefined' ? this._cfg.portId : 0; }
|
|
514
|
-
public get type() { return typeof this._cfg.type !== 'undefined' ? this._cfg.type : this._cfg.netConnect ? 'netConnect' : this._cfg.
|
|
515
|
+
public get type() { return typeof this._cfg.type !== 'undefined' ? this._cfg.type : this._cfg.netConnect ? 'netConnect' : this._cfg.mock ? 'mock' : 'local' };
|
|
515
516
|
public isOpen: boolean = false;
|
|
516
517
|
public closing: boolean = false;
|
|
517
518
|
private _cfg: any;
|
|
@@ -682,7 +683,7 @@ export class RS485Port {
|
|
|
682
683
|
// be open if a hardware interface is used and this method returns.
|
|
683
684
|
sp.open((err) => {
|
|
684
685
|
if (err) {
|
|
685
|
-
this.resetConnTimer();
|
|
686
|
+
if (!this.mock) this.resetConnTimer();
|
|
686
687
|
this.isOpen = false;
|
|
687
688
|
logger.error(`Error opening port ${this.portId}: ${err.message}. ${this._cfg.inactivityRetry > 0 && !this.mock ? `Retry in ${this._cfg.inactivityRetry} seconds` : `Never retrying; (fwiw, inactivityRetry set to ${this._cfg.inactivityRetry})`}`);
|
|
688
689
|
resolve(false);
|
|
@@ -712,7 +713,7 @@ export class RS485Port {
|
|
|
712
713
|
if (!this.mock && !this.isPaused) this.resetConnTimer();
|
|
713
714
|
this.pushIn(data);
|
|
714
715
|
});
|
|
715
|
-
this.resetConnTimer();
|
|
716
|
+
if (!this.mock) this.resetConnTimer();
|
|
716
717
|
this.emitPortStats();
|
|
717
718
|
});
|
|
718
719
|
sp.on('close', (err) => {
|
|
@@ -730,7 +731,7 @@ export class RS485Port {
|
|
|
730
731
|
if (typeof this.writeTimer !== 'undefined') { clearTimeout(this.writeTimer); this.writeTimer = null; }
|
|
731
732
|
this.isOpen = false;
|
|
732
733
|
if (sp.isOpen) sp.close((err) => { }); // call this with the error callback so that it doesn't emit to the error again.
|
|
733
|
-
this.resetConnTimer();
|
|
734
|
+
if (!this.mock) this.resetConnTimer();
|
|
734
735
|
logger.error(`Serial Port ${this.portId}: An error occurred : ${this._cfg.rs485Port}: ${JSON.stringify(err)}`);
|
|
735
736
|
this.emitPortStats();
|
|
736
737
|
|
|
@@ -823,7 +824,7 @@ export class RS485Port {
|
|
|
823
824
|
protected resetConnTimer(...args) {
|
|
824
825
|
//console.log(`resetting connection timer`);
|
|
825
826
|
if (this.connTimer !== null) clearTimeout(this.connTimer);
|
|
826
|
-
if (!this._cfg.
|
|
827
|
+
if (!this._cfg.mock && this._cfg.inactivityRetry > 0 && !this.closing) this.connTimer = setTimeout(async () => {
|
|
827
828
|
try {
|
|
828
829
|
if (this._cfg.netConnect)
|
|
829
830
|
logger.warn(`Inactivity timeout for ${this.portId} serial port ${this._cfg.netHost}:${this._cfg.netPort}/${this._cfg.rs485Port} after ${this._cfg.inactivityRetry} seconds`);
|
|
@@ -873,7 +873,7 @@ class OutboundCommon extends Message {
|
|
|
873
873
|
case Protocol.Heater:
|
|
874
874
|
case Protocol.Hayward:
|
|
875
875
|
this.chkHi = Math.floor(sum / 256);
|
|
876
|
-
this.chkLo = (sum - (
|
|
876
|
+
this.chkLo = (sum - (this.chkHi * 256));
|
|
877
877
|
break;
|
|
878
878
|
case Protocol.AquaLink:
|
|
879
879
|
case Protocol.Chlorinator:
|