nodejs-poolcontroller 7.7.0 → 8.0.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 +26 -35
- package/Changelog +22 -0
- package/README.md +7 -3
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +13 -9
- package/config/VersionCheck.ts +6 -2
- package/controller/Constants.ts +58 -25
- package/controller/Equipment.ts +224 -41
- package/controller/Errors.ts +2 -1
- package/controller/Lockouts.ts +34 -2
- package/controller/State.ts +491 -48
- package/controller/boards/AquaLinkBoard.ts +6 -3
- package/controller/boards/BoardFactory.ts +5 -1
- package/controller/boards/EasyTouchBoard.ts +1971 -1751
- package/controller/boards/IntelliCenterBoard.ts +1311 -1688
- package/controller/boards/IntelliComBoard.ts +7 -1
- package/controller/boards/IntelliTouchBoard.ts +153 -42
- package/controller/boards/NixieBoard.ts +209 -66
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +1862 -1543
- package/controller/comms/Comms.ts +539 -138
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +242 -60
- package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +81 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +3 -1
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +12 -6
- package/controller/comms/messages/config/PumpMessage.ts +9 -12
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +43 -26
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
- package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
- package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
- package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +15 -4
- package/controller/nixie/NixieEquipment.ts +1 -0
- package/controller/nixie/chemistry/ChemController.ts +300 -129
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +133 -129
- package/controller/nixie/circuits/Circuit.ts +171 -30
- package/controller/nixie/heaters/Heater.ts +337 -173
- package/controller/nixie/pumps/Pump.ts +264 -236
- package/controller/nixie/schedules/Schedule.ts +9 -3
- package/defaultConfig.json +45 -5
- package/logger/Logger.ts +38 -9
- package/package.json +13 -9
- package/web/Server.ts +235 -122
- package/web/bindings/aqualinkD.json +114 -59
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +15 -0
- package/web/bindings/mqtt.json +28 -9
- package/web/bindings/mqttAlt.json +15 -0
- package/web/interfaces/baseInterface.ts +58 -7
- package/web/interfaces/httpInterface.ts +5 -2
- package/web/interfaces/influxInterface.ts +9 -2
- package/web/interfaces/mqttInterface.ts +234 -74
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +140 -33
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +144 -3
- package/web/services/state/StateSocket.ts +65 -14
- package/web/services/utilities/Utilities.ts +189 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -18,12 +19,13 @@ import { Inbound } from "../Messages";
|
|
|
18
19
|
import { sys, Body, Circuit, ICircuit } from "../../../Equipment";
|
|
19
20
|
import { state, BodyTempState } from "../../../State";
|
|
20
21
|
import { logger } from "../../../../logger/Logger";
|
|
22
|
+
import { ControllerType } from "../../../Constants";
|
|
21
23
|
|
|
22
24
|
export class CircuitMessage {
|
|
23
25
|
public static processTouch(msg: Inbound): void {
|
|
24
26
|
switch (msg.action) {
|
|
25
27
|
case 11: // IntelliTouch Circuits
|
|
26
|
-
CircuitMessage.processCircuitAttributes(msg);
|
|
28
|
+
sys.controllerType === ControllerType.SunTouch ? CircuitMessage.processSunTouchCircuit(msg) : CircuitMessage.processCircuitAttributes(msg);
|
|
27
29
|
break;
|
|
28
30
|
case 39: // IntelliTouch Light Groups
|
|
29
31
|
case 167:
|
|
@@ -153,7 +155,13 @@ export class CircuitMessage {
|
|
|
153
155
|
let circuit: Circuit = sys.circuits.getItemById(circuitId++, true);
|
|
154
156
|
|
|
155
157
|
// For some odd reason the circuit type for circuit 6 does not equal pool while circuit 1 does equal spa.
|
|
156
|
-
|
|
158
|
+
// Apparently in later versions, spa does not do this either
|
|
159
|
+
if (circuit.id === 1)
|
|
160
|
+
circuit.type = 13
|
|
161
|
+
else if (circuit.id == 6)
|
|
162
|
+
circuit.type = 12
|
|
163
|
+
else
|
|
164
|
+
circuit.type = msg.extractPayloadByte(i);
|
|
157
165
|
circuit.isActive = true;
|
|
158
166
|
circuit.master = 0;
|
|
159
167
|
}
|
|
@@ -175,7 +183,8 @@ export class CircuitMessage {
|
|
|
175
183
|
let maxCircuitId = sys.board.equipmentIds.circuits.end;
|
|
176
184
|
for (let i = circuitId + 1; i < msg.payload.length && circuitId <= maxCircuitId; i++) {
|
|
177
185
|
let circuit: Circuit = sys.circuits.getItemById(circuitId++, true);
|
|
178
|
-
|
|
186
|
+
let cstate = state.circuits.getItemById(circuit.id, true);
|
|
187
|
+
cstate.showInFeatures = circuit.showInFeatures = msg.extractPayloadByte(i) > 0;
|
|
179
188
|
}
|
|
180
189
|
msg.isProcessed = true;
|
|
181
190
|
}
|
|
@@ -230,7 +239,49 @@ export class CircuitMessage {
|
|
|
230
239
|
}
|
|
231
240
|
msg.isProcessed = true;
|
|
232
241
|
}
|
|
233
|
-
|
|
242
|
+
// SunTouch
|
|
243
|
+
private static processSunTouchCircuit(msg: Inbound) {
|
|
244
|
+
let id = msg.extractPayloadByte(0);
|
|
245
|
+
// We need to remap the SunTouch circuits because the features start with 5.
|
|
246
|
+
// SunTouch bit mapping for circuits and features
|
|
247
|
+
// Bit Mask Circuit/Feature id msg
|
|
248
|
+
// 1 = 0x01 Spa 1 1
|
|
249
|
+
// 2 = 0x02 Aux 1 2 2
|
|
250
|
+
// 3 = 0x04 Aux 2 3 3
|
|
251
|
+
// 4 = 0x08 Aux 3 4 4
|
|
252
|
+
// 5 = 0x10 Feature 1 7 5
|
|
253
|
+
// 6 = 0x20 Pool 6 6
|
|
254
|
+
// 7 = 0x40 Feature 2 8 7
|
|
255
|
+
// 8 = 0x80 Feature 3 9 8
|
|
256
|
+
// 9 = 0x01 Feature 4 10 9
|
|
257
|
+
if ([5, 7, 8, 9].includes(id)) {
|
|
258
|
+
id = id === 5 ? 7 : id + 1;
|
|
259
|
+
let feat = sys.features.getItemById(id, true);
|
|
260
|
+
let fstate = state.features.getItemById(id, true);
|
|
261
|
+
fstate.isActive = feat.isActive = true;
|
|
262
|
+
feat.master = 0;
|
|
263
|
+
fstate.nameId = feat.nameId = msg.extractPayloadByte(2);
|
|
264
|
+
fstate.type = feat.type = msg.extractPayloadByte(1) & 63;
|
|
265
|
+
feat.freeze = (msg.extractPayloadByte(1) & 64) > 0;
|
|
266
|
+
feat.showInFeatures = fstate.showInFeatures = typeof feat.showInFeatures === 'undefined' ? true : feat.showInFeatures;
|
|
267
|
+
if (typeof feat.eggTimer === 'undefined' || feat.eggTimer === 0) feat.eggTimer = 720;
|
|
268
|
+
if (typeof feat.dontStop === 'undefined') feat.dontStop = feat.eggTimer === 1620;
|
|
269
|
+
if (typeof feat.name === 'undefined') feat.name = fstate.name = sys.board.valueMaps.circuitNames.transform(feat.nameId).desc;
|
|
270
|
+
}
|
|
271
|
+
else if ([1, 2, 3, 4, 6].includes(id)) {
|
|
272
|
+
let circ = sys.circuits.getItemById(id, true);
|
|
273
|
+
let cstate = state.circuits.getItemById(id, true);
|
|
274
|
+
cstate.isActive = circ.isActive = true;
|
|
275
|
+
circ.master = 0;
|
|
276
|
+
cstate.nameId = circ.nameId = msg.extractPayloadByte(2);
|
|
277
|
+
cstate.type = circ.type = msg.extractPayloadByte(1) & 63;
|
|
278
|
+
circ.freeze = (msg.extractPayloadByte(1) & 64) > 0;
|
|
279
|
+
circ.showInFeatures = cstate.showInFeatures = typeof circ.showInFeatures === 'undefined' ? true : circ.showInFeatures;
|
|
280
|
+
if (typeof circ.eggTimer === 'undefined' || circ.eggTimer === 0) circ.eggTimer = 720;
|
|
281
|
+
if (typeof circ.dontStop === 'undefined') circ.dontStop = circ.eggTimer === 1620;
|
|
282
|
+
if (typeof circ.name === 'undefined') circ.name = cstate.name = sys.board.valueMaps.circuitNames.transform(circ.nameId).desc;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
234
285
|
// Intellitouch
|
|
235
286
|
private static processCircuitAttributes(msg: Inbound) {
|
|
236
287
|
// Sample packet
|
|
@@ -238,7 +289,7 @@ export class CircuitMessage {
|
|
|
238
289
|
const id = msg.extractPayloadByte(0);
|
|
239
290
|
const functionId = msg.extractPayloadByte(1);
|
|
240
291
|
const nameId = msg.extractPayloadByte(2);
|
|
241
|
-
let _isActive = functionId !== sys.board.valueMaps.circuitFunctions.getValue('notused') && nameId !== 0;
|
|
292
|
+
let _isActive = functionId !== sys.board.valueMaps.circuitFunctions.getValue('notused') && (nameId !== 0 || sys.controllerType === ControllerType.SunTouch);
|
|
242
293
|
if (!sys.board.equipmentIds.invalidIds.isValidId(id)) { _isActive = false; }
|
|
243
294
|
if (_isActive) {
|
|
244
295
|
const type = functionId & 63;
|
|
@@ -272,14 +323,14 @@ export class CircuitMessage {
|
|
|
272
323
|
case 6: // pool
|
|
273
324
|
body = sys.bodies.getItemById(1, sys.equipment.maxBodies > 0);
|
|
274
325
|
sbody = state.temps.bodies.getItemById(1, sys.equipment.maxBodies > 0);
|
|
275
|
-
sbody.name = body.name = "Pool";
|
|
326
|
+
if (typeof body.name === 'undefined') sbody.name = body.name = "Pool";
|
|
276
327
|
sbody.type = body.type = 0; // RKS: The body types were backwards here but correct everywhere else e.g. PumpMessage.
|
|
277
328
|
circuit.type === 2 ? body.isActive = true : body.isActive = false;
|
|
278
329
|
break;
|
|
279
330
|
case 1: // spa
|
|
280
331
|
body = sys.bodies.getItemById(2, sys.equipment.maxBodies > 1);
|
|
281
|
-
sbody = state.temps.bodies.getItemById(
|
|
282
|
-
sbody.name = body.name = "Spa";
|
|
332
|
+
sbody = state.temps.bodies.getItemById(2, sys.equipment.maxBodies > 1);
|
|
333
|
+
if(typeof body.name === 'undefined') sbody.name = body.name = "Spa";
|
|
283
334
|
sbody.type = body.type = 1;
|
|
284
335
|
// process bodies - there might be a better place to do this but without other comparison packets from pools with expansion packs it is hard to determine
|
|
285
336
|
// also, if we get this far spa should always be active. not sure if would ever not be active if we are here.
|
|
@@ -303,11 +354,28 @@ export class CircuitMessage {
|
|
|
303
354
|
sys.lightGroups.removeItemById(sys.board.equipmentIds.circuitGroups.start);
|
|
304
355
|
state.lightGroups.removeItemById(sys.board.equipmentIds.circuitGroups.start);
|
|
305
356
|
}
|
|
306
|
-
sys.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
357
|
+
if (!sys.board.equipmentIds.circuits.isInRange(id)) {
|
|
358
|
+
sys.circuits.removeItemById(id);
|
|
359
|
+
state.circuits.removeItemById(id);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
let circuit = sys.circuits.getItemById(id, true);
|
|
363
|
+
let cstate = sys.circuits.getItemById(id, true);
|
|
364
|
+
cstate.showInFeatures = circuit.showInFeatures = false;
|
|
365
|
+
cstate.type = circuit.type = functionId & 63;
|
|
366
|
+
cstate.name = circuit.name = sys.board.circuits.getNameById(nameId || id);
|
|
367
|
+
cstate.nameId = circuit.nameId = nameId || id;
|
|
368
|
+
cstate.isActive = circuit.isActive = true;
|
|
369
|
+
}
|
|
370
|
+
if (!sys.board.equipmentIds.features.isInRange(id)) {
|
|
371
|
+
sys.features.removeItemById(id);
|
|
372
|
+
state.features.removeItemById(id);
|
|
373
|
+
}
|
|
310
374
|
sys.circuitGroups.removeItemById(id);
|
|
375
|
+
//sys.features.removeItemById(id);
|
|
376
|
+
//state.features.removeItemById(id);
|
|
377
|
+
//sys.circuits.removeItemById(id);
|
|
378
|
+
//state.circuits.removeItemById(id);
|
|
311
379
|
}
|
|
312
380
|
msg.isProcessed = true;
|
|
313
381
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -103,6 +104,7 @@ export class ConfigMessage {
|
|
|
103
104
|
}
|
|
104
105
|
break;
|
|
105
106
|
case ControllerType.EasyTouch:
|
|
107
|
+
case ControllerType.SunTouch:
|
|
106
108
|
case ControllerType.IntelliCom:
|
|
107
109
|
case ControllerType.IntelliTouch:
|
|
108
110
|
// switch (msg.action) { }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -131,7 +132,9 @@ export class EquipmentMessage {
|
|
|
131
132
|
sys.bodies.removeItemById(bodyId);
|
|
132
133
|
state.temps.bodies.removeItemById(bodyId);
|
|
133
134
|
}
|
|
135
|
+
state.equipment.single = sys.equipment.single = sys.equipment.shared == false && sys.equipment.dual === false;
|
|
134
136
|
state.equipment.shared = sys.equipment.shared;
|
|
137
|
+
state.equipment.dual = sys.equipment.dual;
|
|
135
138
|
state.equipment.model = sys.equipment.model;
|
|
136
139
|
state.equipment.controllerType = sys.controllerType;
|
|
137
140
|
state.equipment.maxBodies = sys.equipment.maxBodies;
|
|
@@ -149,6 +152,7 @@ export class EquipmentMessage {
|
|
|
149
152
|
case ControllerType.IntelliCom:
|
|
150
153
|
case ControllerType.EasyTouch:
|
|
151
154
|
case ControllerType.IntelliTouch:
|
|
155
|
+
case ControllerType.SunTouch:
|
|
152
156
|
switch (msg.action) {
|
|
153
157
|
case 252:
|
|
154
158
|
EquipmentMessage.processSoftwareVersion(msg);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -16,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16
17
|
*/
|
|
17
18
|
import { Inbound } from "../Messages";
|
|
18
19
|
import { sys, Body, ICircuitGroup, LightGroup, CircuitGroup } from "../../../Equipment";
|
|
19
|
-
import { state, ICircuitGroupState, LightGroupState } from "../../../State";
|
|
20
|
+
import { state, ICircuitGroupState, LightGroupState, CircuitGroupState } from "../../../State";
|
|
20
21
|
import { Timestamp, utils } from "../../../Constants";
|
|
21
22
|
import { logger } from "../../../../logger/Logger";
|
|
22
23
|
export class ExternalMessage {
|
|
@@ -208,6 +209,7 @@ export class ExternalMessage {
|
|
|
208
209
|
sgroup = state.circuitGroups.getItemById(groupId, true);
|
|
209
210
|
sgroup.type = group.type = type;
|
|
210
211
|
if (typeof group.showInFeatures === 'undefined') group.showInFeatures = sgroup.showInFeatures = true;
|
|
212
|
+
sgroup.showInFeatures = group.showInFeatures;
|
|
211
213
|
sys.lightGroups.removeItemById(groupId);
|
|
212
214
|
state.lightGroups.removeItemById(groupId);
|
|
213
215
|
sgroup.isActive = group.isActive = true;
|
|
@@ -274,6 +276,8 @@ export class ExternalMessage {
|
|
|
274
276
|
private static processHeater(msg: Inbound) {
|
|
275
277
|
// So a user is changing the heater info. Lets
|
|
276
278
|
// hijack it and get it ourselves.
|
|
279
|
+
// Installing hybrid heater.
|
|
280
|
+
//[165, 63, 15, 16, 168, 30][10, 0, 2, 5, 32, 5, 6, 3, 0, 6, 112, 72, 121, 98, 114, 105, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 1][4, 230]
|
|
277
281
|
let isActive = msg.extractPayloadByte(3) !== 0;
|
|
278
282
|
let heaterId = msg.extractPayloadByte(2) + 1;
|
|
279
283
|
if (isActive) {
|
|
@@ -406,6 +410,7 @@ export class ExternalMessage {
|
|
|
406
410
|
for (let j = 0; j < 8; j++) {
|
|
407
411
|
let group = sys.circuitGroups.getInterfaceById(groupId);
|
|
408
412
|
let gstate = group.type === 1 ? state.lightGroups.getItemById(groupId, group.isActive) : state.circuitGroups.getItemById(groupId, group.isActive);
|
|
413
|
+
|
|
409
414
|
if (group.isActive !== false) {
|
|
410
415
|
let isOn = ((byte & (1 << (j))) >> j) > 0;
|
|
411
416
|
sys.board.circuits.setEndTime(group, gstate, isOn);
|
|
@@ -442,6 +447,9 @@ export class ExternalMessage {
|
|
|
442
447
|
break;
|
|
443
448
|
}
|
|
444
449
|
}
|
|
450
|
+
else if(gstate.dataName === 'circuitGroup') {
|
|
451
|
+
(gstate as CircuitGroupState).showInFeatures = group.showInFeatures;
|
|
452
|
+
}
|
|
445
453
|
}
|
|
446
454
|
else {
|
|
447
455
|
state.circuitGroups.removeItemById(groupId);
|
|
@@ -850,7 +858,7 @@ export class ExternalMessage {
|
|
|
850
858
|
// chlorinator. These should be 0 anyway.
|
|
851
859
|
schlor.poolSetpoint = chlor.spaSetpoint = msg.extractPayloadByte(0) >> 1;
|
|
852
860
|
schlor.spaSetpoint = chlor.poolSetpoint = msg.extractPayloadByte(1);
|
|
853
|
-
chlor.address = chlor.id + 79;
|
|
861
|
+
if (typeof chlor.address === 'undefined') chlor.address = 80; // chlor.id + 79;
|
|
854
862
|
schlor.body = chlor.body = sys.equipment.maxBodies >= 1 || sys.equipment.shared === true ? 32 : 0;
|
|
855
863
|
}
|
|
856
864
|
schlor.superChlor = chlor.superChlor = msg.extractPayloadByte(2) - 128 > 0;
|
|
@@ -868,4 +876,26 @@ export class ExternalMessage {
|
|
|
868
876
|
state.chlorinators.removeItemById(1);
|
|
869
877
|
}
|
|
870
878
|
}
|
|
879
|
+
public static processTouchSetHeatMode(msg: Inbound) {
|
|
880
|
+
// We get here because some other controller is setting the heat
|
|
881
|
+
// mode. The OCP will emit an 8 later but it can be very slow
|
|
882
|
+
// in doing this. ScreenLogic also captures this message so it
|
|
883
|
+
// doesn't get behind.
|
|
884
|
+
//[165, 1, 16, 34, 136, 4][86, 100, 3, 0][2, 33]
|
|
885
|
+
//payload: [temp1, temp2, mode2 << 2 | mode1, setPoint],
|
|
886
|
+
let bstate1 = state.temps.bodies.getItemById(1);
|
|
887
|
+
let bstate2 = state.temps.bodies.getItemById(2);
|
|
888
|
+
let body1 = sys.bodies.getItemById(1);
|
|
889
|
+
let body2 = sys.bodies.getItemById(2);
|
|
890
|
+
let mode1 = msg.extractPayloadByte(2) & 0x03;
|
|
891
|
+
let mode2 = (msg.extractPayloadByte(2) & 0x0C) >> 2;
|
|
892
|
+
bstate1.setPoint = body1.heatSetpoint = msg.extractPayloadByte(0);
|
|
893
|
+
bstate1.coolSetpoint = body1.coolSetpoint = msg.extractPayloadByte(3);
|
|
894
|
+
bstate2.setPoint = body2.heatSetpoint = msg.extractPayloadByte(1);
|
|
895
|
+
bstate1.heatMode = body1.heatMode = mode1;
|
|
896
|
+
bstate2.heatMode = body2.heatMode = mode2;
|
|
897
|
+
msg.isProcessed = true;
|
|
898
|
+
state.emitEquipmentChanges();
|
|
899
|
+
|
|
900
|
+
}
|
|
871
901
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -63,6 +64,7 @@ export class HeaterMessage {
|
|
|
63
64
|
break;
|
|
64
65
|
case ControllerType.IntelliTouch:
|
|
65
66
|
case ControllerType.EasyTouch:
|
|
67
|
+
case ControllerType.SunTouch:
|
|
66
68
|
HeaterMessage.processIntelliTouch(msg);
|
|
67
69
|
break;
|
|
68
70
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -25,6 +26,7 @@ export class OptionsMessage {
|
|
|
25
26
|
OptionsMessage.processIntelliCenter(msg);
|
|
26
27
|
break;
|
|
27
28
|
case ControllerType.IntelliCom:
|
|
29
|
+
case ControllerType.SunTouch:
|
|
28
30
|
case ControllerType.EasyTouch:
|
|
29
31
|
case ControllerType.IntelliTouch:
|
|
30
32
|
OptionsMessage.processIntelliTouch(msg);
|
|
@@ -123,16 +125,18 @@ export class OptionsMessage {
|
|
|
123
125
|
// We don't want the dual speed pump to even exist unless there are no circuit controlling it.
|
|
124
126
|
// It should not be showing up in our pumps list or emitting state unless the user has defined
|
|
125
127
|
// circuits to it on *Touch interfaces.
|
|
128
|
+
// RSG 1/5/23 - Intellitouch (and Dual Body) accept 8 high speed circuits
|
|
129
|
+
let maxCircuits = sys.controllerType === ControllerType.IntelliTouch ? 8 : 4;
|
|
126
130
|
let arrCircuits = [];
|
|
127
131
|
let pump = sys.pumps.getDualSpeed(true);
|
|
128
|
-
for (let i = 0; i
|
|
132
|
+
for (let i = 0; i < maxCircuits; i++) {
|
|
129
133
|
let val = msg.extractPayloadByte(i);
|
|
130
134
|
if (val > 0) arrCircuits.push(val);
|
|
131
135
|
else pump.circuits.removeItemById(i);
|
|
132
136
|
}
|
|
133
137
|
if (arrCircuits.length > 0) {
|
|
134
138
|
let pump = sys.pumps.getDualSpeed(true);
|
|
135
|
-
for (let j = 1; j <= arrCircuits.length; j++) pump.circuits.getItemById(j, true).circuit = arrCircuits[j];
|
|
139
|
+
for (let j = 1; j <= arrCircuits.length; j++) pump.circuits.getItemById(j, true).circuit = arrCircuits[j-1];
|
|
136
140
|
}
|
|
137
141
|
else sys.pumps.removeItemById(10);
|
|
138
142
|
msg.isProcessed = true;
|
|
@@ -157,9 +161,11 @@ export class OptionsMessage {
|
|
|
157
161
|
|
|
158
162
|
}
|
|
159
163
|
else {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
if (sys.controllerType !== ControllerType.SunTouch) {
|
|
165
|
+
let chem = sys.chemControllers.getItemByAddress(144);
|
|
166
|
+
state.chemControllers.removeItemById(chem.id);
|
|
167
|
+
sys.chemControllers.removeItemById(chem.id);
|
|
168
|
+
}
|
|
163
169
|
}
|
|
164
170
|
msg.isProcessed = true;
|
|
165
171
|
break;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -25,6 +26,7 @@ export class PumpMessage {
|
|
|
25
26
|
case ControllerType.IntelliCenter:
|
|
26
27
|
PumpMessage.processIntelliCenterPump(msg);
|
|
27
28
|
break;
|
|
29
|
+
case ControllerType.SunTouch:
|
|
28
30
|
case ControllerType.IntelliCom:
|
|
29
31
|
case ControllerType.EasyTouch:
|
|
30
32
|
case ControllerType.IntelliTouch:
|
|
@@ -330,9 +332,8 @@ export class PumpMessage {
|
|
|
330
332
|
// 18 | 3 | Big endian speed for the speed (1000 rpm with byte(28))
|
|
331
333
|
// 19 | 0 | Circuit speed #8 = No circuit
|
|
332
334
|
// 20 | 3 | Big endian speed for the speed (1000 rpm with byte(29))
|
|
333
|
-
// 21 | 3 | Big
|
|
335
|
+
// 21 | 3 | Big endian speed for the priming speed (1000 rpm with byte(30))
|
|
334
336
|
// All 30 bytes on this message are accounted for except for byte 3 & 4.
|
|
335
|
-
|
|
336
337
|
if (typeof pump.model === 'undefined') pump.model = 0;
|
|
337
338
|
for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
|
|
338
339
|
let _circuit = msg.extractPayloadByte(circuitId * 2 + 3);
|
|
@@ -391,7 +392,8 @@ export class PumpMessage {
|
|
|
391
392
|
}
|
|
392
393
|
private static processVSF_IT(msg: Inbound) {
|
|
393
394
|
// Sample packet
|
|
394
|
-
//
|
|
395
|
+
//[255, 0, 255][165, 33, 15, 16, 27, 46][2, 64, 0, 0, 2, 1, 33, 2, 4, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 94]
|
|
396
|
+
//[255, 0, 255][165, 1, 15, 16, 24, 31][1, 64, 0, 0, 0, 6, 5, 2, 8, 1, 11, 7, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 152, 184, 122, 0, 0, 0, 0, 0][4, 24]
|
|
395
397
|
const pumpId = msg.extractPayloadByte(0);
|
|
396
398
|
const pump = sys.pumps.getItemById(pumpId);
|
|
397
399
|
if (typeof pump.model === 'undefined') pump.model = 0;
|
|
@@ -400,14 +402,9 @@ export class PumpMessage {
|
|
|
400
402
|
if (_circuit !== 0){
|
|
401
403
|
const circuit: PumpCircuit = pump.circuits.getItemById(circuitId, true);
|
|
402
404
|
circuit.circuit = _circuit;
|
|
403
|
-
circuit.units =
|
|
404
|
-
(msg.extractPayloadByte(
|
|
405
|
-
|
|
406
|
-
circuit.flow = msg.extractPayloadByte(circuitId * 2 + 4);
|
|
407
|
-
else
|
|
408
|
-
circuit.speed =
|
|
409
|
-
msg.extractPayloadByte(circuitId * 2 + 4) * 256 +
|
|
410
|
-
msg.extractPayloadByte(circuitId + 21);
|
|
405
|
+
circuit.units = (msg.extractPayloadByte(4) >> circuitId - 1 & 1) === 0 ? 1 : 0;
|
|
406
|
+
if (circuit.units) circuit.flow = msg.extractPayloadByte(circuitId * 2 + 4);
|
|
407
|
+
else circuit.speed = msg.extractPayloadByte(circuitId * 2 + 4) * 256 + msg.extractPayloadByte(circuitId + 21);
|
|
411
408
|
}
|
|
412
409
|
else {
|
|
413
410
|
pump.circuits.removeItemById(_circuit);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -15,7 +16,8 @@ You should have received a copy of the GNU Affero General Public License
|
|
|
15
16
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
17
|
*/
|
|
17
18
|
import {Inbound} from "../Messages";
|
|
18
|
-
import {sys, Remote} from "../../../Equipment";
|
|
19
|
+
import { sys, Remote } from "../../../Equipment";
|
|
20
|
+
import { state } from "../../../State";
|
|
19
21
|
import {ControllerType} from "../../../Constants";
|
|
20
22
|
export class RemoteMessage {
|
|
21
23
|
private static maxCircuits: number=8;
|
|
@@ -58,7 +60,7 @@ export class RemoteMessage {
|
|
|
58
60
|
public static processRemote_IT(msg: Inbound) {
|
|
59
61
|
/* process Spa-side remotes
|
|
60
62
|
for is4 [165,33,16,34,33,11],[id,button1,button2,button3,button4,5,6,7,8,9,10],[chkh,chkl]
|
|
61
|
-
for is10:[165,33,16,34,
|
|
63
|
+
for is10:[165,33,16,34,32,11],[id,button1,button2,button3,button4,btn5,btn1bot,btn2bot,btn3bot,btn4bot,btn5bot],[chkh,chkl]
|
|
62
64
|
[255, 0, 255], [165, 33, 15, 16, 32, 11], [0, 1, 5, 18, 13, 5, 6, 7, 8, 9, 10], [1, 98]
|
|
63
65
|
[255, 0, 255], [165, 33, 15, 16, 32, 11], [1, 8, 2, 7, 7, 5, 8, 9, 8, 9, 3], [1, 83]
|
|
64
66
|
for quicktouch:
|
|
@@ -73,17 +75,29 @@ export class RemoteMessage {
|
|
|
73
75
|
Note: For systems with four iS10/SpaCommand remotes, adding one or two iS4 remotes will affect button function assignments as follows: Assigned button functions 1 - 4 on iS4 #1 are linked with the same functions to buttons 1 - 4 (top row) of iS10 #4. Also, buttons 1 - 4 on iS4 #2 are linked to buttons 6 - 10 (bottom row) of iS10 #4. For example, button 6 on the bottom row of iS10 #4 is linked to button 1 of iS4 #2, button 7 on iS10 #4 is linked to button 2 of iS4 #2, etc.
|
|
74
76
|
|
|
75
77
|
Not sure how packets will come through with 6 controllers on it.
|
|
78
|
+
|
|
79
|
+
// RSG 1.6.23 - Per Screenlogic Config, the IS10#4 shares the bytes with IS4#1/IS4#2.
|
|
80
|
+
// Byte 1 - IS10#1-1 IS4#1-1
|
|
81
|
+
// Byte 2 - IS10#1-2 IS4#1-2
|
|
82
|
+
// Byte 3 - IS10#1-3 IS4#1-3
|
|
83
|
+
// Byte 4 - IS10#1-4 IS4#1-4
|
|
84
|
+
// Byte 5 - IS10#1-5
|
|
85
|
+
// Byte 6 - IS10#1-6 IS4#2-1
|
|
86
|
+
// Byte 7 - IS10#1-7 IS4#2-2
|
|
87
|
+
// Byte 8 - IS10#1-8 IS4#2-3
|
|
88
|
+
// Byte 9 - IS10#1-9 IS4#2-4
|
|
89
|
+
// Byte 10 - IS10#1-10
|
|
76
90
|
|
|
77
91
|
Fixing ID's for lack of having better info.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
1-4 = is10
|
|
93
|
+
5-6 = is4
|
|
94
|
+
7 = QuickTouch
|
|
95
|
+
8 = Spa Command
|
|
82
96
|
*/
|
|
83
97
|
switch (msg.action) {
|
|
84
98
|
case 33: // quicktouch
|
|
85
99
|
{
|
|
86
|
-
const remoteId =
|
|
100
|
+
const remoteId = 7; // what determines 2nd is4?
|
|
87
101
|
const remote: Remote = sys.remotes.getItemById(remoteId, true);
|
|
88
102
|
remote.type = remoteId;
|
|
89
103
|
remote.button1 = msg.extractPayloadByte(0);
|
|
@@ -129,16 +143,69 @@ export class RemoteMessage {
|
|
|
129
143
|
// sample packet from EasyTouch
|
|
130
144
|
// [165,33,16,34,150,16],[0,1,7,8,0,2,250,10,1,144,13,122,15,130,0,0],[4,93]
|
|
131
145
|
// note: spa command may be tied to an already present is10. Need to clarify.
|
|
132
|
-
const remoteId =
|
|
133
|
-
const remote: Remote = sys.remotes.getItemById(remoteId, true);
|
|
134
|
-
remote.pumpId = msg.extractPayloadByte(5);
|
|
135
|
-
remote.stepSize = msg.extractPayloadByte(6);
|
|
136
|
-
remote.type =
|
|
146
|
+
//const remoteId = 8;
|
|
147
|
+
//const remote: Remote = sys.remotes.getItemById(remoteId, true);
|
|
148
|
+
//remote.pumpId = msg.extractPayloadByte(5);
|
|
149
|
+
//remote.stepSize = msg.extractPayloadByte(6);
|
|
150
|
+
//remote.type = 8;
|
|
151
|
+
RemoteMessage.processIntelliFlo4(msg);
|
|
137
152
|
msg.isProcessed = true;
|
|
138
153
|
break;
|
|
139
154
|
}
|
|
140
155
|
}
|
|
141
156
|
}
|
|
157
|
+
private static processIntelliFlo4(msg: Inbound) {
|
|
158
|
+
// RKS: 12-1-22 This message is a message that has been mis-interpreted for quite some time
|
|
159
|
+
// it appears that early versions of EasyTouch did not include the ability to add more than one pump and only 4 potential
|
|
160
|
+
// circuits could be set. This comes as 3 bytes per pump setting. If there are no circuits assigned then the pump is not installed.
|
|
161
|
+
let isActive = (msg.extractPayloadByte(1, 0) + msg.extractPayloadByte(4, 0) + msg.extractPayloadByte(7, 0) + msg.extractPayloadByte(10, 0)) > 0;
|
|
162
|
+
let pump = sys.pumps.find(x => x.address === 96 && (x.master || 0) === 0);
|
|
163
|
+
if (!isActive) {
|
|
164
|
+
if (typeof pump !== 'undefined') {
|
|
165
|
+
let spump = state.pumps.getItemById(pump.id, false);
|
|
166
|
+
spump.address = 96;
|
|
167
|
+
spump.isActive = false;
|
|
168
|
+
sys.pumps.removeItemById(pump.id);
|
|
169
|
+
state.pumps.removeItemById(pump.id);
|
|
170
|
+
spump.emitEquipmentChange();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
if (typeof pump === 'undefined') pump = sys.pumps.getPumpByAddress(96, true,
|
|
175
|
+
{
|
|
176
|
+
id: sys.pumps.getNextEquipmentId(),
|
|
177
|
+
master: 0,
|
|
178
|
+
address: 96,
|
|
179
|
+
type: 128,
|
|
180
|
+
name: `Pump${sys.pumps.length + 1}`,
|
|
181
|
+
flowStepSize: 1,
|
|
182
|
+
primingTime: 0,
|
|
183
|
+
primingSpeed: 450,
|
|
184
|
+
minSpeed: 450,
|
|
185
|
+
maxSpeed: 3450
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
let spump = state.pumps.getItemById(pump.id, true);
|
|
189
|
+
spump.name = pump.name;
|
|
190
|
+
spump.address = pump.address = 96;
|
|
191
|
+
spump.type = pump.type = 128;
|
|
192
|
+
pump.isActive = spump.isActive = true;
|
|
193
|
+
// Set the circuits on the pump.
|
|
194
|
+
let cid = 0;
|
|
195
|
+
for (let i = 1; i <= 10; i += 3) {
|
|
196
|
+
let circuitId = msg.extractPayloadByte(i, 0);
|
|
197
|
+
if (circuitId > 0) {
|
|
198
|
+
cid++;
|
|
199
|
+
let circ = pump.circuits.getItemById(cid, true);
|
|
200
|
+
circ.circuit = circuitId;
|
|
201
|
+
circ.speed = (msg.extractPayloadByte(i + 1, 0) * 256) + msg.extractPayloadByte(i + 2, 0);
|
|
202
|
+
circ.units = 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (cid < 4) for (let i = 4; i > cid && i > 0; i--) pump.circuits.removeItemById(i);
|
|
206
|
+
spump.emitEquipmentChange();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
142
209
|
private static processRemoteType(msg: Inbound) {
|
|
143
210
|
let remoteId = 1;
|
|
144
211
|
for (let i = 28; i < msg.payload.length && remoteId <= sys.equipment.maxRemotes; i++) {
|