nodejs-poolcontroller 7.7.0 → 8.0.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.
Files changed (82) hide show
  1. package/.eslintrc.json +26 -35
  2. package/Changelog +22 -0
  3. package/README.md +7 -3
  4. package/anslq25/MessagesMock.ts +218 -0
  5. package/anslq25/boards/MockBoardFactory.ts +50 -0
  6. package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
  7. package/anslq25/boards/MockSystemBoard.ts +217 -0
  8. package/anslq25/chemistry/MockChlorinator.ts +75 -0
  9. package/anslq25/pumps/MockPump.ts +84 -0
  10. package/app.ts +10 -14
  11. package/config/Config.ts +13 -9
  12. package/config/VersionCheck.ts +6 -2
  13. package/controller/Constants.ts +58 -25
  14. package/controller/Equipment.ts +225 -41
  15. package/controller/Errors.ts +2 -1
  16. package/controller/Lockouts.ts +34 -2
  17. package/controller/State.ts +491 -48
  18. package/controller/boards/AquaLinkBoard.ts +6 -3
  19. package/controller/boards/BoardFactory.ts +5 -1
  20. package/controller/boards/EasyTouchBoard.ts +1971 -1751
  21. package/controller/boards/IntelliCenterBoard.ts +1311 -1688
  22. package/controller/boards/IntelliComBoard.ts +7 -1
  23. package/controller/boards/IntelliTouchBoard.ts +153 -42
  24. package/controller/boards/NixieBoard.ts +209 -66
  25. package/controller/boards/SunTouchBoard.ts +393 -0
  26. package/controller/boards/SystemBoard.ts +1862 -1543
  27. package/controller/comms/Comms.ts +539 -138
  28. package/controller/comms/ScreenLogic.ts +1663 -0
  29. package/controller/comms/messages/Messages.ts +242 -60
  30. package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
  31. package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
  32. package/controller/comms/messages/config/CircuitMessage.ts +81 -13
  33. package/controller/comms/messages/config/ConfigMessage.ts +3 -1
  34. package/controller/comms/messages/config/CoverMessage.ts +2 -1
  35. package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
  36. package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
  37. package/controller/comms/messages/config/ExternalMessage.ts +33 -3
  38. package/controller/comms/messages/config/FeatureMessage.ts +2 -1
  39. package/controller/comms/messages/config/GeneralMessage.ts +2 -1
  40. package/controller/comms/messages/config/HeaterMessage.ts +3 -1
  41. package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
  42. package/controller/comms/messages/config/OptionsMessage.ts +12 -6
  43. package/controller/comms/messages/config/PumpMessage.ts +9 -12
  44. package/controller/comms/messages/config/RemoteMessage.ts +80 -13
  45. package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
  46. package/controller/comms/messages/config/SecurityMessage.ts +2 -1
  47. package/controller/comms/messages/config/ValveMessage.ts +43 -26
  48. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
  49. package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
  50. package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
  51. package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
  52. package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
  53. package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
  54. package/controller/comms/messages/status/VersionMessage.ts +2 -1
  55. package/controller/nixie/Nixie.ts +15 -4
  56. package/controller/nixie/NixieEquipment.ts +1 -0
  57. package/controller/nixie/chemistry/ChemController.ts +300 -129
  58. package/controller/nixie/chemistry/ChemDoser.ts +806 -0
  59. package/controller/nixie/chemistry/Chlorinator.ts +133 -129
  60. package/controller/nixie/circuits/Circuit.ts +171 -30
  61. package/controller/nixie/heaters/Heater.ts +337 -173
  62. package/controller/nixie/pumps/Pump.ts +264 -236
  63. package/controller/nixie/schedules/Schedule.ts +9 -3
  64. package/defaultConfig.json +46 -5
  65. package/logger/Logger.ts +38 -9
  66. package/package.json +13 -9
  67. package/web/Server.ts +235 -122
  68. package/web/bindings/aqualinkD.json +114 -59
  69. package/web/bindings/homeassistant.json +437 -0
  70. package/web/bindings/influxDB.json +15 -0
  71. package/web/bindings/mqtt.json +28 -9
  72. package/web/bindings/mqttAlt.json +15 -0
  73. package/web/interfaces/baseInterface.ts +58 -7
  74. package/web/interfaces/httpInterface.ts +5 -2
  75. package/web/interfaces/influxInterface.ts +9 -2
  76. package/web/interfaces/mqttInterface.ts +234 -74
  77. package/web/interfaces/ruleInterface.ts +87 -0
  78. package/web/services/config/Config.ts +140 -33
  79. package/web/services/config/ConfigSocket.ts +2 -1
  80. package/web/services/state/State.ts +144 -3
  81. package/web/services/state/StateSocket.ts +65 -14
  82. 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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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
- circuit.type = circuitId - 1 !== 6 ? msg.extractPayloadByte(i) : 12;
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
- circuit.showInFeatures = msg.extractPayloadByte(i) > 0;
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(1, sys.equipment.maxBodies > 1);
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.features.removeItemById(id);
307
- state.features.removeItemById(id);
308
- sys.circuits.removeItemById(id);
309
- state.circuits.removeItemById(id);
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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 <= 3; 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
- let chem = sys.chemControllers.getItemByAddress(144);
161
- state.chemControllers.removeItemById(chem.id);
162
- sys.chemControllers.removeItemById(chem.id);
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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 eniand speed for the priming speed (1000 rpm with byte(30))
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
- // [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]
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(4) >> circuitId - 1 & 1) === 0 ? 1 : 0;
405
- if (circuit.units)
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. Russell Goldin, tagyoureit. russ.goldin@gmail.com
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,323,11],[id,button1,button2,button3,button4,btn5,btn1bot,btn2bot,btn3bot,btn4bot,btn5bot],[chkh,chkl]
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
- 0-3 = is10
79
- 4-5 = is4
80
- 6 = QuickTouch
81
- 7 = Spa Command (can multiple be present? are these tied to is10?)
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 = 6; // what determines 2nd is4?
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 = 7;
133
- const remote: Remote = sys.remotes.getItemById(remoteId, true);
134
- remote.pumpId = msg.extractPayloadByte(5);
135
- remote.stepSize = msg.extractPayloadByte(6);
136
- remote.type = 7;
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++) {