nodejs-poolcontroller 7.6.1 → 7.7.0
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/.eslintrc.json +44 -44
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/CONTRIBUTING.md +74 -74
- package/Changelog +220 -215
- package/Dockerfile +17 -17
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +191 -191
- package/app.ts +1 -1
- package/config/Config.ts +14 -0
- package/config/VersionCheck.ts +2 -2
- package/controller/Constants.ts +2 -1
- package/controller/Equipment.ts +2484 -2459
- package/controller/Errors.ts +180 -180
- package/controller/Lockouts.ts +502 -436
- package/controller/State.ts +106 -30
- package/controller/boards/AquaLinkBoard.ts +1000 -0
- package/controller/boards/BoardFactory.ts +49 -45
- package/controller/boards/EasyTouchBoard.ts +2859 -2653
- package/controller/boards/IntelliCenterBoard.ts +4198 -4230
- package/controller/boards/IntelliComBoard.ts +63 -63
- package/controller/boards/IntelliTouchBoard.ts +273 -241
- package/controller/boards/NixieBoard.ts +1728 -1675
- package/controller/boards/SystemBoard.ts +4925 -4697
- package/controller/comms/Comms.ts +442 -479
- package/controller/comms/messages/Messages.ts +171 -25
- package/controller/comms/messages/config/ChlorinatorMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitGroupMessage.ts +0 -0
- package/controller/comms/messages/config/CircuitMessage.ts +1 -0
- package/controller/comms/messages/config/ConfigMessage.ts +0 -0
- package/controller/comms/messages/config/CoverMessage.ts +0 -0
- package/controller/comms/messages/config/CustomNameMessage.ts +30 -30
- package/controller/comms/messages/config/EquipmentMessage.ts +0 -0
- package/controller/comms/messages/config/ExternalMessage.ts +0 -0
- package/controller/comms/messages/config/FeatureMessage.ts +0 -0
- package/controller/comms/messages/config/GeneralMessage.ts +0 -0
- package/controller/comms/messages/config/HeaterMessage.ts +142 -10
- package/controller/comms/messages/config/IntellichemMessage.ts +0 -0
- package/controller/comms/messages/config/OptionsMessage.ts +4 -21
- package/controller/comms/messages/config/PumpMessage.ts +53 -35
- package/controller/comms/messages/config/RemoteMessage.ts +0 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +350 -347
- package/controller/comms/messages/config/SecurityMessage.ts +0 -0
- package/controller/comms/messages/config/ValveMessage.ts +1 -1
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +38 -86
- package/controller/comms/messages/status/EquipmentStateMessage.ts +58 -22
- package/controller/comms/messages/status/HeaterStateMessage.ts +116 -86
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +445 -445
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +35 -35
- package/controller/comms/messages/status/PumpStateMessage.ts +23 -1
- package/controller/comms/messages/status/VersionMessage.ts +0 -0
- package/controller/nixie/Nixie.ts +162 -162
- package/controller/nixie/NixieEquipment.ts +103 -103
- package/controller/nixie/bodies/Body.ts +120 -120
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2511 -2498
- package/controller/nixie/chemistry/Chlorinator.ts +363 -314
- package/controller/nixie/circuits/Circuit.ts +261 -248
- package/controller/nixie/heaters/Heater.ts +650 -648
- package/controller/nixie/pumps/Pump.ts +906 -661
- package/controller/nixie/schedules/Schedule.ts +313 -257
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +306 -286
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +0 -0
- package/package.json +56 -56
- package/tsconfig.json +25 -25
- package/web/Server.ts +92 -47
- package/web/bindings/aqualinkD.json +505 -0
- package/web/bindings/influxDB.json +1051 -1021
- package/web/bindings/mqtt.json +702 -654
- package/web/bindings/mqttAlt.json +731 -684
- package/web/bindings/rulesManager.json +54 -54
- package/web/bindings/smartThings-Hubitat.json +31 -31
- package/web/bindings/valveRelays.json +20 -20
- package/web/bindings/vera.json +25 -25
- package/web/interfaces/baseInterface.ts +137 -136
- package/web/interfaces/httpInterface.ts +145 -124
- package/web/interfaces/influxInterface.ts +276 -245
- package/web/interfaces/mqttInterface.ts +535 -475
- package/web/services/config/Config.ts +39 -18
- package/web/services/config/ConfigSocket.ts +0 -0
- package/web/services/state/State.ts +10 -0
- package/web/services/state/StateSocket.ts +4 -4
- package/web/services/utilities/Utilities.ts +44 -42
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
- package/config copy.json +0 -300
- package/issue_template.md +0 -52
|
@@ -1,314 +1,363 @@
|
|
|
1
|
-
import { InvalidEquipmentDataError, InvalidEquipmentIdError, InvalidOperationError } from '../../Errors';
|
|
2
|
-
import { utils, Timestamp } from '../../Constants';
|
|
3
|
-
import { logger } from '../../../logger/Logger';
|
|
4
|
-
|
|
5
|
-
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
|
-
import { Chlorinator, sys, ChlorinatorCollection } from "../../../controller/Equipment";
|
|
7
|
-
import { ChlorinatorState, state, } from "../../State";
|
|
8
|
-
import { setTimeout, clearTimeout } from 'timers';
|
|
9
|
-
import { webApp, InterfaceServerResponse } from "../../../web/Server";
|
|
10
|
-
import { Outbound, Protocol, Response } from '../../comms/messages/Messages';
|
|
11
|
-
import { conn } from '../../comms/Comms';
|
|
12
|
-
import { ncp } from '../Nixie';
|
|
13
|
-
|
|
14
|
-
export class NixieChlorinatorCollection extends NixieEquipmentCollection<NixieChlorinator> {
|
|
15
|
-
public async deleteChlorinatorAsync(id: number) {
|
|
16
|
-
try {
|
|
17
|
-
// Since we don't have hash tables per se in TS go through all the entries and remove every one that
|
|
18
|
-
// matches the id. This will ensure cleanup of a dirty array.
|
|
19
|
-
for (let i = this.length - 1; i >= 0; i--) {
|
|
20
|
-
let c = this[i];
|
|
21
|
-
if (c.id === id) {
|
|
22
|
-
await ncp.chemControllers.deleteChlorAsync(c as NixieChlorinator);
|
|
23
|
-
await c.closeAsync();
|
|
24
|
-
this.splice(i, 1);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
} catch (err) { logger.error(`NCP: Error removing chlorinator`); }
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
public async setChlorinatorAsync(chlor: Chlorinator, data: any) {
|
|
31
|
-
// By the time we get here we know that we are in control and this is a REMChem.
|
|
32
|
-
try {
|
|
33
|
-
let c: NixieChlorinator = this.find(elem => elem.id === chlor.id) as NixieChlorinator;
|
|
34
|
-
if (typeof c === 'undefined') {
|
|
35
|
-
chlor.master = 1;
|
|
36
|
-
c = new NixieChlorinator(this.controlPanel, chlor);
|
|
37
|
-
this.push(c);
|
|
38
|
-
await c.setChlorinatorAsync(data);
|
|
39
|
-
logger.info(`A Chlorinator was not found for id #${chlor.id} starting Nixie Chlorinator`);
|
|
40
|
-
await c.initAsync();
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
await c.setChlorinatorAsync(data);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (err) { logger.error(`setChlorinatorAsync: ${err.message}`); return Promise.reject(err); }
|
|
47
|
-
}
|
|
48
|
-
public async initAsync(chlorinators: ChlorinatorCollection) {
|
|
49
|
-
try {
|
|
50
|
-
for (let i = 0; i < chlorinators.length; i++) {
|
|
51
|
-
let cc = chlorinators.getItemByIndex(i);
|
|
52
|
-
if (cc.master === 1) {
|
|
53
|
-
if (typeof this.find(elem => elem.id === cc.id) === 'undefined') {
|
|
54
|
-
logger.info(`Initializing Nixie chlorinator ${cc.name}`);
|
|
55
|
-
let ncc = new NixieChlorinator(this.controlPanel, cc);
|
|
56
|
-
this.push(ncc);
|
|
57
|
-
await ncc.initAsync();
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (err) { logger.error(`initAsync: ${err.message}`); return Promise.reject(err); }
|
|
63
|
-
}
|
|
64
|
-
public async closeAsync() {
|
|
65
|
-
try {
|
|
66
|
-
for (let i = this.length - 1; i >= 0; i--) {
|
|
67
|
-
try {
|
|
68
|
-
await this[i].closeAsync();
|
|
69
|
-
this.splice(i, 1);
|
|
70
|
-
} catch (err) { logger.error(`Error stopping Nixie Chlorinator ${err}`); }
|
|
71
|
-
}
|
|
72
|
-
} catch (err) { } // Don't bail if we have an error
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
export class NixieChlorinator extends NixieEquipment {
|
|
76
|
-
public pollingInterval: number = 3000;
|
|
77
|
-
private _pollTimer: NodeJS.Timeout = null;
|
|
78
|
-
private superChlorinating: boolean = false;
|
|
79
|
-
private superChlorStart: number = 0;
|
|
80
|
-
public chlor: Chlorinator;
|
|
81
|
-
public bodyOnTime: number;
|
|
82
|
-
protected _suspendPolling: number = 0;
|
|
83
|
-
public closing = false;
|
|
84
|
-
constructor(ncp: INixieControlPanel, chlor: Chlorinator) {
|
|
85
|
-
super(ncp);
|
|
86
|
-
this.chlor = chlor;
|
|
87
|
-
}
|
|
88
|
-
public get id(): number { return typeof this.chlor !== 'undefined' ? this.chlor.id : -1; }
|
|
89
|
-
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
90
|
-
public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1));
|
|
91
|
-
public
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
let
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
let
|
|
102
|
-
let
|
|
103
|
-
let
|
|
104
|
-
let
|
|
105
|
-
let
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
chlor.
|
|
121
|
-
schlor.
|
|
122
|
-
schlor.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
public async
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
let
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
1
|
+
import { InvalidEquipmentDataError, InvalidEquipmentIdError, InvalidOperationError } from '../../Errors';
|
|
2
|
+
import { utils, Timestamp, ControllerType } from '../../Constants';
|
|
3
|
+
import { logger } from '../../../logger/Logger';
|
|
4
|
+
|
|
5
|
+
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
|
+
import { Chlorinator, sys, ChlorinatorCollection } from "../../../controller/Equipment";
|
|
7
|
+
import { ChlorinatorState, state, } from "../../State";
|
|
8
|
+
import { setTimeout, clearTimeout } from 'timers';
|
|
9
|
+
import { webApp, InterfaceServerResponse } from "../../../web/Server";
|
|
10
|
+
import { Outbound, Protocol, Response } from '../../comms/messages/Messages';
|
|
11
|
+
import { conn } from '../../comms/Comms';
|
|
12
|
+
import { ncp } from '../Nixie';
|
|
13
|
+
|
|
14
|
+
export class NixieChlorinatorCollection extends NixieEquipmentCollection<NixieChlorinator> {
|
|
15
|
+
public async deleteChlorinatorAsync(id: number) {
|
|
16
|
+
try {
|
|
17
|
+
// Since we don't have hash tables per se in TS go through all the entries and remove every one that
|
|
18
|
+
// matches the id. This will ensure cleanup of a dirty array.
|
|
19
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
20
|
+
let c = this[i];
|
|
21
|
+
if (c.id === id) {
|
|
22
|
+
await ncp.chemControllers.deleteChlorAsync(c as NixieChlorinator);
|
|
23
|
+
await c.closeAsync();
|
|
24
|
+
this.splice(i, 1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch (err) { logger.error(`NCP: Error removing chlorinator`); }
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
public async setChlorinatorAsync(chlor: Chlorinator, data: any) {
|
|
31
|
+
// By the time we get here we know that we are in control and this is a REMChem.
|
|
32
|
+
try {
|
|
33
|
+
let c: NixieChlorinator = this.find(elem => elem.id === chlor.id) as NixieChlorinator;
|
|
34
|
+
if (typeof c === 'undefined') {
|
|
35
|
+
chlor.master = 1;
|
|
36
|
+
c = new NixieChlorinator(this.controlPanel, chlor);
|
|
37
|
+
this.push(c);
|
|
38
|
+
await c.setChlorinatorAsync(data);
|
|
39
|
+
logger.info(`A Chlorinator was not found for id #${chlor.id} starting Nixie Chlorinator`);
|
|
40
|
+
await c.initAsync();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
await c.setChlorinatorAsync(data);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (err) { logger.error(`setChlorinatorAsync: ${err.message}`); return Promise.reject(err); }
|
|
47
|
+
}
|
|
48
|
+
public async initAsync(chlorinators: ChlorinatorCollection) {
|
|
49
|
+
try {
|
|
50
|
+
for (let i = 0; i < chlorinators.length; i++) {
|
|
51
|
+
let cc = chlorinators.getItemByIndex(i);
|
|
52
|
+
if (cc.master === 1) {
|
|
53
|
+
if (typeof this.find(elem => elem.id === cc.id) === 'undefined') {
|
|
54
|
+
logger.info(`Initializing Nixie chlorinator ${cc.name}`);
|
|
55
|
+
let ncc = new NixieChlorinator(this.controlPanel, cc);
|
|
56
|
+
this.push(ncc);
|
|
57
|
+
await ncc.initAsync();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) { logger.error(`initAsync: ${err.message}`); return Promise.reject(err); }
|
|
63
|
+
}
|
|
64
|
+
public async closeAsync() {
|
|
65
|
+
try {
|
|
66
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
67
|
+
try {
|
|
68
|
+
await this[i].closeAsync();
|
|
69
|
+
this.splice(i, 1);
|
|
70
|
+
} catch (err) { logger.error(`Error stopping Nixie Chlorinator ${err}`); }
|
|
71
|
+
}
|
|
72
|
+
} catch (err) { } // Don't bail if we have an error
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export class NixieChlorinator extends NixieEquipment {
|
|
76
|
+
public pollingInterval: number = 3000;
|
|
77
|
+
private _pollTimer: NodeJS.Timeout = null;
|
|
78
|
+
private superChlorinating: boolean = false;
|
|
79
|
+
private superChlorStart: number = 0;
|
|
80
|
+
public chlor: Chlorinator;
|
|
81
|
+
public bodyOnTime: number;
|
|
82
|
+
protected _suspendPolling: number = 0;
|
|
83
|
+
public closing = false;
|
|
84
|
+
constructor(ncp: INixieControlPanel, chlor: Chlorinator) {
|
|
85
|
+
super(ncp);
|
|
86
|
+
this.chlor = chlor;
|
|
87
|
+
}
|
|
88
|
+
public get id(): number { return typeof this.chlor !== 'undefined' ? this.chlor.id : -1; }
|
|
89
|
+
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
90
|
+
public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
|
|
91
|
+
public get superChlorRemaining(): number {
|
|
92
|
+
if (typeof this.superChlorStart === 'undefined' || this.superChlorStart === 0 || !this.chlor.superChlor) return 0;
|
|
93
|
+
return Math.max(Math.floor(((this.chlor.superChlorHours * 3600 * 1000) - (new Date().getTime() - this.superChlorStart)) / 1000), 0);
|
|
94
|
+
}
|
|
95
|
+
public async setChlorinatorAsync(data: any) {
|
|
96
|
+
try {
|
|
97
|
+
let chlor = this.chlor;
|
|
98
|
+
if (chlor.type === sys.board.valueMaps.chlorinatorType.getValue('intellichlor')) {
|
|
99
|
+
|
|
100
|
+
}
|
|
101
|
+
let poolSetpoint = typeof data.poolSetpoint !== 'undefined' ? parseInt(data.poolSetpoint, 10) : chlor.poolSetpoint;
|
|
102
|
+
let spaSetpoint = typeof data.spaSetpoint !== 'undefined' ? parseInt(data.spaSetpoint, 10) : chlor.spaSetpoint;
|
|
103
|
+
let body = sys.board.bodies.mapBodyAssociation(typeof data.body === 'undefined' ? chlor.body : data.body);
|
|
104
|
+
let superChlor = typeof data.superChlor !== 'undefined' ? utils.makeBool(data.superChlor) : typeof data.superChlorinate !== 'undefined' ? utils.makeBool(data.superChlorinate) : chlor.superChlor;
|
|
105
|
+
let chlorType = typeof data.type !== 'undefined' ? sys.board.valueMaps.chlorinatorType.encode(data.type) : chlor.type || 0;
|
|
106
|
+
let superChlorHours = typeof data.superChlorHours !== 'undefined' ? parseInt(data.superChlorHours, 10) : chlor.superChlorHours;
|
|
107
|
+
let disabled = typeof data.disabled !== 'undefined' ? utils.makeBool(data.disabled) : chlor.disabled;
|
|
108
|
+
let isDosing = typeof data.isDosing !== 'undefined' ? utils.makeBool(data.isDosing) : chlor.isDosing;
|
|
109
|
+
let model = typeof data.model !== 'undefined' ? sys.board.valueMaps.chlorinatorModel.encode(data.model) : chlor.model || 0;
|
|
110
|
+
let portId = typeof data.portId !== 'undefined' ? parseInt(data.portId, 10) : chlor.portId;
|
|
111
|
+
if (portId === 0 && sys.controllerType !== ControllerType.Nixie) return Promise.reject(new InvalidEquipmentDataError(`You may not install a chlorinator on an ${sys.controllerType} system that is assigned to the Primary Port that is under Nixe control`, 'Chlorinator', portId));
|
|
112
|
+
if (portId !== chlor.portId && sys.chlorinators.count(elem => elem.id !== this.chlor.id && elem.portId === portId && elem.master !== 2) > 0) return Promise.reject(new InvalidEquipmentDataError(`Another chlorinator is installed on port #${portId}. Only one chlorinator can be installed per port.`, 'Chlorinator', portId));
|
|
113
|
+
if (isNaN(portId)) return Promise.reject(new InvalidEquipmentDataError(`Invalid port Id`, 'Chlorinator', data.portId));
|
|
114
|
+
if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Invalid body assignment`, 'Chlorinator', data.body || chlor.body));
|
|
115
|
+
if (isNaN(poolSetpoint)) poolSetpoint = 0;
|
|
116
|
+
if (isNaN(spaSetpoint)) spaSetpoint = 0;
|
|
117
|
+
|
|
118
|
+
chlor.ignoreSaltReading = (typeof data.ignoreSaltReading !== 'undefined') ? utils.makeBool(data.ignoreSaltReading) : utils.makeBool(chlor.ignoreSaltReading);
|
|
119
|
+
// Do a final validation pass so we dont send this off in a mess.
|
|
120
|
+
let schlor = state.chlorinators.getItemById(chlor.id, true);
|
|
121
|
+
schlor.poolSetpoint = chlor.poolSetpoint = poolSetpoint;
|
|
122
|
+
schlor.spaSetpoint = chlor.spaSetpoint = spaSetpoint;
|
|
123
|
+
schlor.superChlor = chlor.superChlor = superChlor;
|
|
124
|
+
schlor.superChlorHours = chlor.superChlorHours = superChlorHours;
|
|
125
|
+
schlor.type = chlor.type = chlorType;
|
|
126
|
+
schlor.model = chlor.model = model;
|
|
127
|
+
schlor.body = chlor.body = body.val;
|
|
128
|
+
chlor.portId = portId;
|
|
129
|
+
chlor.disabled = disabled;
|
|
130
|
+
chlor.isDosing = isDosing;
|
|
131
|
+
schlor.name = chlor.name = data.name || chlor.name || `Chlorinator ${chlor.id}`;
|
|
132
|
+
schlor.isActive = chlor.isActive = true;
|
|
133
|
+
if (!chlor.superChlor) {
|
|
134
|
+
this.superChlorStart = 0;
|
|
135
|
+
this.superChlorinating = false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (err) { logger.error(`setChlorinatorAsync: ${err.message}`); return Promise.reject(err); }
|
|
139
|
+
}
|
|
140
|
+
public async closeAsync() {
|
|
141
|
+
try {
|
|
142
|
+
this.closing = true; // This will tell the polling cycle to stop what it is doing and don't restart.
|
|
143
|
+
if (this._pollTimer) {
|
|
144
|
+
clearTimeout(this._pollTimer);
|
|
145
|
+
this._pollTimer = undefined;
|
|
146
|
+
}
|
|
147
|
+
// Let the Nixie equipment do its thing if it needs to.
|
|
148
|
+
await super.closeAsync();
|
|
149
|
+
}
|
|
150
|
+
catch (err) { logger.error(`Chlorinator closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
151
|
+
}
|
|
152
|
+
public async initAsync() {
|
|
153
|
+
try {
|
|
154
|
+
// Start our polling but only after we clean up any other polling going on.
|
|
155
|
+
if (this._pollTimer) {
|
|
156
|
+
clearTimeout(this._pollTimer);
|
|
157
|
+
this._pollTimer = undefined;
|
|
158
|
+
}
|
|
159
|
+
this.closing = false;
|
|
160
|
+
this._suspendPolling = 0;
|
|
161
|
+
// During startup it won't be uncommon for the comms to be out. This will be because the body will be off so don't stress it so much.
|
|
162
|
+
this.pollEquipment();
|
|
163
|
+
} catch (err) { logger.error(`Error initializing ${this.chlor.name} : ${err.message}`); }
|
|
164
|
+
}
|
|
165
|
+
public isBodyOn() { return sys.board.bodies.isBodyOn(this.chlor.body); }
|
|
166
|
+
public setSuperChlor(cstate: ChlorinatorState) {
|
|
167
|
+
if (this.chlor.superChlor) {
|
|
168
|
+
if (!this.superChlorinating) {
|
|
169
|
+
// Deal with the start time.
|
|
170
|
+
let hours = this.chlor.superChlorHours * 3600;
|
|
171
|
+
let offset = cstate.superChlorRemaining > 0 ? Math.max((hours - (hours - cstate.superChlorRemaining)), 0) : 0;
|
|
172
|
+
this.superChlorStart = new Date().getTime() - offset;
|
|
173
|
+
this.superChlorinating = true;
|
|
174
|
+
}
|
|
175
|
+
cstate.superChlorRemaining = this.superChlorRemaining;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
this.superChlorStart = 0;
|
|
179
|
+
this.superChlorinating = false;
|
|
180
|
+
cstate.superChlorRemaining = 0;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
public async pollEquipment() {
|
|
184
|
+
let self = this;
|
|
185
|
+
try {
|
|
186
|
+
if (this._pollTimer) {
|
|
187
|
+
clearTimeout(this._pollTimer);
|
|
188
|
+
this._pollTimer = undefined;
|
|
189
|
+
}
|
|
190
|
+
if (!this.suspendPolling) {
|
|
191
|
+
logger.debug(`Begin sending chlorinator messages ${this.chlor.name}`);
|
|
192
|
+
try {
|
|
193
|
+
this.suspendPolling = true;
|
|
194
|
+
if (!this.closing) await this.takeControl();
|
|
195
|
+
if (!this.closing) await utils.sleep(300);
|
|
196
|
+
if (!this.closing) await this.setOutput();
|
|
197
|
+
if (!this.closing) await utils.sleep(300);
|
|
198
|
+
if (!this.closing) await this.getModel();
|
|
199
|
+
} catch (err) {
|
|
200
|
+
// We only display an error here if the body is on. The chlorinator should be powered down when it is not.
|
|
201
|
+
if (this.isBodyOn()) logger.error(`Chlorinator ${this.chlor.name} comms failure: ${err.message}`);
|
|
202
|
+
}
|
|
203
|
+
finally { this.suspendPolling = false; }
|
|
204
|
+
}
|
|
205
|
+
} catch (err) {
|
|
206
|
+
// Comms failure will be handeled by the message processor.
|
|
207
|
+
logger.error(`Chlorinator ${this.chlor.name} comms failure: ${err.message}`);
|
|
208
|
+
}
|
|
209
|
+
finally { if(!this.closing) this._pollTimer = setTimeout(() => {self.pollEquipment();}, this.pollingInterval); }
|
|
210
|
+
}
|
|
211
|
+
public async takeControl(): Promise<boolean> {
|
|
212
|
+
try {
|
|
213
|
+
let cstate = state.chlorinators.getItemById(this.chlor.id, true);
|
|
214
|
+
// The sequence is as follows.
|
|
215
|
+
// 0 = Disabled control panel by taking control of it.
|
|
216
|
+
// 17 = Set the current setpoint
|
|
217
|
+
// 20 = Request the status
|
|
218
|
+
// Disable the control panel by sending an action 0 the chlorinator should respond with an action 1.
|
|
219
|
+
//[16, 2, 80, 0][0][98, 16, 3]
|
|
220
|
+
let success = await new Promise<boolean>((resolve, reject) => {
|
|
221
|
+
if (conn.isPortEnabled(this.chlor.portId || 0)) {
|
|
222
|
+
let out = Outbound.create({
|
|
223
|
+
portId: this.chlor.portId || 0,
|
|
224
|
+
protocol: Protocol.Chlorinator,
|
|
225
|
+
//dest: this.chlor.id,
|
|
226
|
+
dest: 1,
|
|
227
|
+
action: 0,
|
|
228
|
+
payload: [0],
|
|
229
|
+
retries: 3, // IntelliCenter tries 4 times to get a response.
|
|
230
|
+
response: Response.create({ protocol: Protocol.Chlorinator, action: 1 }),
|
|
231
|
+
onAbort: () => { this.chlor.superChlor = cstate.superChlor = false; this.setSuperChlor(cstate); },
|
|
232
|
+
onComplete: (err) => {
|
|
233
|
+
if (err) {
|
|
234
|
+
// This flag is cleared in ChlorinatorStateMessage
|
|
235
|
+
this.chlor.superChlor = cstate.superChlor = false;
|
|
236
|
+
this.setSuperChlor(cstate);
|
|
237
|
+
cstate.status = 128;
|
|
238
|
+
resolve(false);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
// If this is successful the action 1 message will have been
|
|
242
|
+
// digested by ChlorinatorStateMessage and the lastComm will have been set clearing the
|
|
243
|
+
// communication lost flag.
|
|
244
|
+
resolve(true);
|
|
245
|
+
}
|
|
246
|
+
cstate.emitEquipmentChange();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
conn.queueSendMessage(out);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
cstate.status = 0;
|
|
253
|
+
resolve(true);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return success;
|
|
257
|
+
} catch (err) { logger.error(`Communication error with Chlorinator ${this.chlor.name} : ${err.message}`); }
|
|
258
|
+
}
|
|
259
|
+
public async setOutput(): Promise<boolean> {
|
|
260
|
+
try {
|
|
261
|
+
// A couple of things need to be in place before setting the output.
|
|
262
|
+
// 1. The chlorinator will have to have responded to the takeControl message.
|
|
263
|
+
// 2. If the body is not on then we need to send it a 0 output. This is just in case the
|
|
264
|
+
// chlorinator is not wired into the filter relay. The current output should be 0 if no body is on.
|
|
265
|
+
// 3. If we are superchlorinating and the remaing superChlor time is > 0 then we need to keep it at 100%.
|
|
266
|
+
// 4. If the chlorinator disabled flag is set then we need to make sure the setpoint is 0.
|
|
267
|
+
let cstate = state.chlorinators.getItemById(this.chlor.id, true);
|
|
268
|
+
let setpoint = 0;
|
|
269
|
+
if (this.isBodyOn()) {
|
|
270
|
+
if (sys.equipment.shared) {
|
|
271
|
+
let body = state.temps.bodies.getBodyIsOn();
|
|
272
|
+
setpoint = (body.id === 1) ? this.chlor.poolSetpoint : this.chlor.spaSetpoint;
|
|
273
|
+
}
|
|
274
|
+
else setpoint = this.chlor.body === 0 ? this.chlor.poolSetpoint : this.chlor.spaSetpoint;
|
|
275
|
+
if (this.chlor.isDosing) setpoint = 100;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
this.chlor.superChlor = cstate.superChlor = false;
|
|
279
|
+
this.setSuperChlor(cstate);
|
|
280
|
+
}
|
|
281
|
+
if (this.chlor.disabled === true) setpoint = 0; // Our target should be 0 because we have other things going on. For instance,
|
|
282
|
+
// RKS: Not sure if it needs to be smart enough to force an off message when the comms die.
|
|
283
|
+
//if (cstate.status === 128) setpoint = 0; // If we haven't been able to get a response from the clorinator tell is to turn itself off.
|
|
284
|
+
// Perhaps we will be luckier on the next poll cycle.
|
|
285
|
+
// Tell the chlorinator that we are to use the current output.
|
|
286
|
+
//[16, 2, 80, 17][0][115, 16, 3]
|
|
287
|
+
cstate.targetOutput = cstate.superChlor ? 100 : setpoint;
|
|
288
|
+
let success = await new Promise<boolean>((resolve, reject) => {
|
|
289
|
+
if (conn.isPortEnabled(this.chlor.portId || 0)) {
|
|
290
|
+
let out = Outbound.create({
|
|
291
|
+
portId: this.chlor.portId || 0,
|
|
292
|
+
protocol: Protocol.Chlorinator,
|
|
293
|
+
//dest: this.chlor.id,
|
|
294
|
+
dest: 1,
|
|
295
|
+
action: 17,
|
|
296
|
+
payload: [cstate.targetOutput],
|
|
297
|
+
retries: 7, // IntelliCenter tries 8 times to make this happen.
|
|
298
|
+
response: Response.create({ protocol: Protocol.Chlorinator, action: 18 }),
|
|
299
|
+
onAbort: () => { },
|
|
300
|
+
onComplete: (err) => {
|
|
301
|
+
if (err) {
|
|
302
|
+
cstate.currentOutput = 0;
|
|
303
|
+
cstate.status = 128;
|
|
304
|
+
resolve(false);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
cstate.currentOutput = cstate.targetOutput;
|
|
308
|
+
this.setSuperChlor(cstate);
|
|
309
|
+
resolve(true);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
// #338
|
|
314
|
+
if (setpoint === 16) { out.appendPayloadByte(0); }
|
|
315
|
+
conn.queueSendMessage(out);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
cstate.currentOutput = cstate.targetOutput;
|
|
319
|
+
this.setSuperChlor(cstate);
|
|
320
|
+
resolve(true);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
cstate.emitEquipmentChange();
|
|
324
|
+
return success;
|
|
325
|
+
} catch (err) { logger.error(`Communication error with Chlorinator ${this.chlor.name} : ${err.message}`); return Promise.reject(err);}
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
public async getModel() {
|
|
329
|
+
try {
|
|
330
|
+
// We only need to ask for this if we can communicate with the chlorinator. IntelliCenter
|
|
331
|
+
// asks for this anyway but it really is gratuitous. If the setOutput and takeControl fail
|
|
332
|
+
// then this will too.
|
|
333
|
+
let cstate = state.chlorinators.getItemById(this.chlor.id, true);
|
|
334
|
+
if (cstate.status !== 128) {
|
|
335
|
+
// Ask the chlorinator for its model.
|
|
336
|
+
//[16, 2, 80, 20][0][118, 16, 3]
|
|
337
|
+
let success = await new Promise<boolean>((resolve, reject) => {
|
|
338
|
+
if (conn.isPortEnabled(this.chlor.portId || 0)) {
|
|
339
|
+
let out = Outbound.create({
|
|
340
|
+
portId: this.chlor.portId || 0,
|
|
341
|
+
protocol: Protocol.Chlorinator,
|
|
342
|
+
//dest: this.chlor.id,
|
|
343
|
+
dest: 1,
|
|
344
|
+
action: 20,
|
|
345
|
+
payload: [0],
|
|
346
|
+
retries: 3, // IntelliCenter tries 4 times to get a response.
|
|
347
|
+
response: Response.create({ protocol: Protocol.Chlorinator, action: 3 }),
|
|
348
|
+
onAbort: () => { },
|
|
349
|
+
onComplete: (err) => {
|
|
350
|
+
if (err) resolve(false);
|
|
351
|
+
else resolve(true);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
conn.queueSendMessage(out);
|
|
355
|
+
}
|
|
356
|
+
else { resolve(true); }
|
|
357
|
+
});
|
|
358
|
+
return success;
|
|
359
|
+
}
|
|
360
|
+
else return false;
|
|
361
|
+
} catch (err) { logger.error(`Communication error with Chlorinator ${this.chlor.name} : ${err.message}`); return Promise.reject(err);}
|
|
362
|
+
}
|
|
363
|
+
}
|