nodejs-poolcontroller 7.6.1 → 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.
Files changed (102) hide show
  1. package/.eslintrc.json +36 -45
  2. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
  3. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
  4. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  6. package/CONTRIBUTING.md +74 -74
  7. package/Changelog +242 -215
  8. package/Dockerfile +17 -17
  9. package/Gruntfile.js +40 -40
  10. package/LICENSE +661 -661
  11. package/README.md +195 -191
  12. package/anslq25/MessagesMock.ts +218 -0
  13. package/anslq25/boards/MockBoardFactory.ts +50 -0
  14. package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
  15. package/anslq25/boards/MockSystemBoard.ts +217 -0
  16. package/anslq25/chemistry/MockChlorinator.ts +75 -0
  17. package/anslq25/pumps/MockPump.ts +84 -0
  18. package/app.ts +10 -14
  19. package/config/Config.ts +26 -8
  20. package/config/VersionCheck.ts +8 -4
  21. package/controller/Constants.ts +59 -25
  22. package/controller/Equipment.ts +2667 -2459
  23. package/controller/Errors.ts +181 -180
  24. package/controller/Lockouts.ts +534 -436
  25. package/controller/State.ts +596 -77
  26. package/controller/boards/AquaLinkBoard.ts +1003 -0
  27. package/controller/boards/BoardFactory.ts +53 -45
  28. package/controller/boards/EasyTouchBoard.ts +3079 -2653
  29. package/controller/boards/IntelliCenterBoard.ts +3821 -4230
  30. package/controller/boards/IntelliComBoard.ts +69 -63
  31. package/controller/boards/IntelliTouchBoard.ts +384 -241
  32. package/controller/boards/NixieBoard.ts +1871 -1675
  33. package/controller/boards/SunTouchBoard.ts +393 -0
  34. package/controller/boards/SystemBoard.ts +5244 -4697
  35. package/controller/comms/Comms.ts +905 -541
  36. package/controller/comms/ScreenLogic.ts +1663 -0
  37. package/controller/comms/messages/Messages.ts +382 -54
  38. package/controller/comms/messages/config/ChlorinatorMessage.ts +8 -4
  39. package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
  40. package/controller/comms/messages/config/CircuitMessage.ts +82 -13
  41. package/controller/comms/messages/config/ConfigMessage.ts +3 -1
  42. package/controller/comms/messages/config/CoverMessage.ts +2 -1
  43. package/controller/comms/messages/config/CustomNameMessage.ts +31 -30
  44. package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
  45. package/controller/comms/messages/config/ExternalMessage.ts +33 -3
  46. package/controller/comms/messages/config/FeatureMessage.ts +2 -1
  47. package/controller/comms/messages/config/GeneralMessage.ts +2 -1
  48. package/controller/comms/messages/config/HeaterMessage.ts +145 -11
  49. package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
  50. package/controller/comms/messages/config/OptionsMessage.ts +16 -27
  51. package/controller/comms/messages/config/PumpMessage.ts +62 -47
  52. package/controller/comms/messages/config/RemoteMessage.ts +80 -13
  53. package/controller/comms/messages/config/ScheduleMessage.ts +390 -347
  54. package/controller/comms/messages/config/SecurityMessage.ts +2 -1
  55. package/controller/comms/messages/config/ValveMessage.ts +44 -27
  56. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +44 -91
  57. package/controller/comms/messages/status/EquipmentStateMessage.ts +139 -30
  58. package/controller/comms/messages/status/HeaterStateMessage.ts +135 -86
  59. package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -445
  60. package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -35
  61. package/controller/comms/messages/status/PumpStateMessage.ts +92 -2
  62. package/controller/comms/messages/status/VersionMessage.ts +2 -1
  63. package/controller/nixie/Nixie.ts +173 -162
  64. package/controller/nixie/NixieEquipment.ts +104 -103
  65. package/controller/nixie/bodies/Body.ts +120 -120
  66. package/controller/nixie/bodies/Filter.ts +135 -135
  67. package/controller/nixie/chemistry/ChemController.ts +2682 -2498
  68. package/controller/nixie/chemistry/ChemDoser.ts +806 -0
  69. package/controller/nixie/chemistry/Chlorinator.ts +367 -314
  70. package/controller/nixie/circuits/Circuit.ts +402 -248
  71. package/controller/nixie/heaters/Heater.ts +815 -649
  72. package/controller/nixie/pumps/Pump.ts +934 -661
  73. package/controller/nixie/schedules/Schedule.ts +319 -257
  74. package/controller/nixie/valves/Valve.ts +170 -170
  75. package/defaultConfig.json +346 -286
  76. package/logger/DataLogger.ts +448 -448
  77. package/logger/Logger.ts +38 -9
  78. package/package.json +60 -56
  79. package/tsconfig.json +25 -25
  80. package/web/Server.ts +275 -117
  81. package/web/bindings/aqualinkD.json +560 -0
  82. package/web/bindings/homeassistant.json +437 -0
  83. package/web/bindings/influxDB.json +1066 -1021
  84. package/web/bindings/mqtt.json +721 -654
  85. package/web/bindings/mqttAlt.json +746 -684
  86. package/web/bindings/rulesManager.json +54 -54
  87. package/web/bindings/smartThings-Hubitat.json +31 -31
  88. package/web/bindings/valveRelays.json +20 -20
  89. package/web/bindings/vera.json +25 -25
  90. package/web/interfaces/baseInterface.ts +188 -136
  91. package/web/interfaces/httpInterface.ts +148 -124
  92. package/web/interfaces/influxInterface.ts +283 -245
  93. package/web/interfaces/mqttInterface.ts +695 -475
  94. package/web/interfaces/ruleInterface.ts +87 -0
  95. package/web/services/config/Config.ts +177 -49
  96. package/web/services/config/ConfigSocket.ts +2 -1
  97. package/web/services/state/State.ts +154 -3
  98. package/web/services/state/StateSocket.ts +69 -18
  99. package/web/services/utilities/Utilities.ts +232 -42
  100. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
  101. package/config copy.json +0 -300
  102. package/issue_template.md +0 -52
@@ -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,7 +64,9 @@ 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);
69
+ break;
67
70
  }
68
71
  }
69
72
  private static processIntelliTouch(msg: Inbound) {
@@ -71,8 +74,22 @@ export class HeaterMessage {
71
74
  // Gas heater setup in EquipmentStateMessage upon *Touch discovery
72
75
  switch (msg.action) {
73
76
  // 1 = gas heater, 2 = solar, 3 = heat pump
74
- case 34:
75
- case 162:
77
+ case 34: // Somebody requested the configuration
78
+ case 162: // Somebody set the configuration
79
+ // Release any heaters that are tied to Nixie that should not be.
80
+ for (let i = sys.heaters.length - 1; i >= 0; i--) {
81
+ let heater = sys.heaters.getItemByIndex(i);
82
+ if (heater.id <= 4 && heater.master === 1) {
83
+ // Return the heater control back to Touch
84
+ (async function () {
85
+ try {
86
+ await ncp.heaters.deleteHeaterAsync(heater.id);
87
+ logger.debug(`${heater.name} control returned to OCP.`);
88
+ }
89
+ catch (err) { logger.error(`Error with OCP reclaiming control over gas ${heater.name}: ${err}`) }
90
+ })();
91
+ }
92
+ }
76
93
  // byte 0
77
94
  // 21 = solar or heat pump disabled
78
95
  // 23 = solar or heat pump enabled
@@ -94,6 +111,121 @@ export class HeaterMessage {
94
111
  // on/off (16) = solar as a heat pump
95
112
  // bits 7,8 = stop temp delta
96
113
 
114
+
115
+
116
+ // Determine what type of heater should be in id number 1. If this is a hybrid heater
117
+ // we will be installing it in id 1 although it will perform dual purpose.
118
+ let hasHeater = (msg.extractPayloadByte(0) & 0x01) === 0x01 || true;
119
+ let hasSolar = (msg.extractPayloadByte(0) & 0x02) === 0x02;
120
+ let hasHeatpump = (msg.extractPayloadByte(1) & 0x20) === 0x20;
121
+ let hasHybrid = !hasHeatpump && (msg.extractPayloadByte(1) & 0x10) === 0x10;
122
+
123
+ // Ok so it appears that the heater ids are as follows.
124
+ // 1 = Gas Heater
125
+ // 2 = Solar
126
+ // 3 = UltraTemp (HEATPUMPCOM)
127
+ // 4 = UltraTemp ETi (Hybrid)
128
+ // If an UltraTemp ETi is installed, no other heaters can be installed so they should be removed. This is not
129
+ // an available option for IntelliTouch i10D so it does not apply to it.
130
+ if (!hasHeater) {
131
+ sys.heaters.removeItemById(1);
132
+ state.heaters.removeItemById(1);
133
+ sys.heaters.removeItemById(2);
134
+ state.heaters.removeItemById(2);
135
+ sys.heaters.removeItemById(3);
136
+ state.heaters.removeItemById(3);
137
+ sys.heaters.removeItemById(4);
138
+ state.heaters.removeItemById(4);
139
+ }
140
+ else {
141
+ if (hasHybrid) {
142
+ sys.heaters.removeItemById(1);
143
+ state.heaters.removeItemById(1);
144
+ sys.heaters.removeItemById(2);
145
+ state.heaters.removeItemById(2);
146
+ sys.heaters.removeItemById(3);
147
+ state.heaters.removeItemById(3);
148
+ let hybrid = sys.heaters.getItemById(4, true);
149
+ let shybrid = state.heaters.getItemById(4, true);
150
+ // [5, { name: 'hybrid', desc: 'Hybrid', hasAddress: true }],
151
+ shybrid.type = hybrid.type = 5;
152
+ hybrid.address = 112; // Touch only supports address 1.
153
+ hybrid.isActive = true;
154
+ hybrid.master = 0;
155
+ hybrid.body = sys.equipment.shared ? 32 : 0;
156
+ if (typeof hybrid.name === 'undefined') shybrid.name = hybrid.name = 'UltraTemp ETi';
157
+ // The following 2 values need to come from somewhere.
158
+ if (typeof hybrid.economyTime === 'undefined') hybrid.economyTime = 1;
159
+ if (typeof hybrid.maxBoostTemp === 'undefined') hybrid.maxBoostTemp = 5;
160
+ hasHeatpump = false; // You cannot have a heatpump and a hybrid heater.
161
+ }
162
+ else {
163
+ // Hybrid heaters and gas heaters cannot co-exist but it appears you cannot disable the gas
164
+ // heater on the touch panels.
165
+ sys.heaters.removeItemById(4);
166
+ state.heaters.removeItemById(4);
167
+ let heater = sys.heaters.getItemById(1, true);
168
+ let hstate = state.heaters.getItemById(1, true);
169
+ heater.body = sys.equipment.shared ? 32 : 0;
170
+ // [1, { name: 'gas', desc: 'Gas Heater', hasAddress: false }],
171
+ heater.type = hstate.type = 1;
172
+ heater.isActive = true;
173
+ heater.master = 0;
174
+ if (typeof heater.name === 'undefined') heater.name = hstate.name = 'Gas Heater';
175
+ if (typeof heater.cooldownDelay === 'undefined') heater.cooldownDelay = 5;
176
+ }
177
+ // Check to see if a heatpump is installed. This will replace the solar heater so they cannot coexist.
178
+ if (hasHeatpump) {
179
+ // Remove the solar heater. This will be replaced with the heatpump.
180
+ sys.heaters.removeItemById(2);
181
+ state.heaters.removeItemById(2);
182
+ let heatpump = sys.heaters.getItemById(3, true);
183
+ let sheatpump = state.heaters.getItemById(3, true);
184
+ // [3, { name: 'heatpump', desc: 'Heat Pump', hasAddress: true, hasPreference: true }],
185
+ heatpump.type = sheatpump.type = 3;
186
+ heatpump.body = sys.equipment.shared ? 32 : 0;
187
+ heatpump.isActive = true;
188
+ heatpump.master = 0;
189
+
190
+ if (typeof heatpump.name === 'undefined') sheatpump.name = heatpump.name = 'UltraTemp';
191
+ heatpump.heatingEnabled = (msg.extractPayloadByte(1) & 0x01) === 1;
192
+ heatpump.coolingEnabled = (msg.extractPayloadByte(1) & 0x02) === 2;
193
+ sys.board.equipmentIds.invalidIds.add(20); // exclude Aux Extra
194
+ }
195
+ else if (hasSolar) {
196
+ sys.heaters.removeItemById(3);
197
+ state.heaters.removeItemById(3);
198
+ let solar = sys.heaters.getItemById(2, true);
199
+ let ssolar = sys.heaters.getItemById(2, true);
200
+ // [2, { name: 'solar', desc: 'Solar Heater', hasAddress: false, hasPreference: true }],
201
+ solar.type = ssolar.type = 2;
202
+ solar.body = sys.equipment.shared ? 32 : 0;
203
+ solar.isActive = true;
204
+ solar.master = 0;
205
+ if (typeof solar.name === 'undefined') solar.name = ssolar.name = 'Solar Heater';
206
+ solar.freeze = (msg.extractPayloadByte(1) & 0x80) >> 7 === 1;
207
+ solar.coolingEnabled = (msg.extractPayloadByte(1) & 0x20) >> 5 === 1;
208
+ solar.startTempDelta = ((msg.extractPayloadByte(2) & 0xE) >> 1) + 3;
209
+ solar.stopTempDelta = ((msg.extractPayloadByte(2) & 0xC0) >> 6) + 2;
210
+ sys.board.equipmentIds.invalidIds.add(20); // exclude Aux Extra
211
+ }
212
+ else {
213
+ sys.board.equipmentIds.invalidIds.remove(20); // Allow access to Aux Extra
214
+ }
215
+ }
216
+ sys.board.heaters.updateHeaterServices();
217
+ for (let i = 0; i < sys.bodies.length; i++) {
218
+ let body = sys.bodies.getItemByIndex(i);
219
+ let btemp = state.temps.bodies.getItemById(body.id, body.isActive !== false);
220
+ let opts = sys.board.heaters.getInstalledHeaterTypes(body.id);
221
+ btemp.heaterOptions = opts;
222
+ }
223
+ sys.board.heaters.syncHeaterStates();
224
+ sys.equipment.setEquipmentIds();
225
+ msg.isProcessed = true;
226
+
227
+
228
+ /*
97
229
  // gas heater only; solar/heatpump/ultratemp disabled
98
230
  if ((msg.extractPayloadByte(0) & 0x2) === 0) {
99
231
  let heater = sys.heaters.getItemById(1);
@@ -112,14 +244,14 @@ export class HeaterMessage {
112
244
  sys.heaters.getItemById(3).isActive = false;
113
245
  sys.heaters.getItemById(4).isActive = false;
114
246
  sys.board.equipmentIds.invalidIds.remove(20); // include Aux Extra
115
- /* sys.equipment.setEquipmentIds();
116
- for (let i = 0; i < sys.bodies.length; i++) {
117
- let body = sys.bodies.getItemByIndex(i);
118
- let btemp = state.temps.bodies.getItemById(body.id, body.isActive !== false);
119
- let opts = sys.board.heaters.getInstalledHeaterTypes(body.id);
120
- btemp.heaterOptions = opts;
121
- }
122
- return; */
247
+ //sys.equipment.setEquipmentIds();
248
+ //for (let i = 0; i < sys.bodies.length; i++) {
249
+ // let body = sys.bodies.getItemByIndex(i);
250
+ // let btemp = state.temps.bodies.getItemById(body.id, body.isActive !== false);
251
+ // let opts = sys.board.heaters.getInstalledHeaterTypes(body.id);
252
+ // btemp.heaterOptions = opts;
253
+ //}
254
+ return;
123
255
  }
124
256
  // Ultratemp (+ cooling?);
125
257
  else if ((msg.extractPayloadByte(2) & 0x30) === 0x30) {
@@ -220,7 +352,9 @@ export class HeaterMessage {
220
352
  sys.board.heaters.syncHeaterStates();
221
353
  sys.equipment.setEquipmentIds();
222
354
  msg.isProcessed = true;
355
+ */
223
356
  break;
357
+
224
358
  }
225
359
  }
226
360
  private static processCooldownDelay(msg: Inbound) {
@@ -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;
@@ -145,42 +149,27 @@ export class OptionsMessage {
145
149
  // [165,33,16,34,168,10],[0,0,0,254,0,0,0,0,0,0],[2,168 = manual heat mode off
146
150
  // [165,33,16,34,168,10],[0,0,0,254,1,0,0,0,0,0],[2,169] = manual heat mode on
147
151
  sys.general.options.manualHeat = msg.extractPayloadByte(4) === 1;
152
+ // From https://github.com/tagyoureit/nodejs-poolController/issues/362 = Intellitouch
153
+ // [0,0,0,0,1,x,0,0,0,0] x=0 Manual OP heat Off; x=1 Manual OP heat On
154
+ sys.general.options.manualPriority = msg.extractPayloadByte(5) === 1;
148
155
  if ((msg.extractPayloadByte(3) & 0x01) === 1) {
149
156
  // only support for 1 ic with EasyTouch
150
157
  let chem = sys.chemControllers.getItemByAddress(144, true);
151
- let schem = state.chemControllers.getItemById(chem.id, true);
158
+ //let schem = state.chemControllers.getItemById(chem.id, true);
152
159
  chem.ph.tank.capacity = chem.orp.tank.capacity = 6;
153
160
  chem.ph.tank.units = chem.orp.tank.units = '';
154
161
 
155
162
  }
156
163
  else {
157
- let chem = sys.chemControllers.getItemByAddress(144);
158
- state.chemControllers.removeItemById(chem.id);
159
- sys.chemControllers.removeItemById(chem.id);
160
- }
161
- msg.isProcessed = true;
162
- break;
163
- }
164
- /* case 168:
165
- {
166
- // IntelliChem Installed
167
- if ((msg.extractPayloadByte(3) & 0x01) === 1) {
168
- // only support for 1 ic with EasyTouch
169
- let chem = sys.chemControllers.getItemByAddress(144, true);
170
- let schem = state.chemControllers.getItemById(chem.id, true);
171
- chem.ph.tank.capacity = chem.orp.tank.capacity = 6;
172
- chem.ph.tank.units = chem.orp.tank.units = '';
173
-
174
- }
175
- else {
164
+ if (sys.controllerType !== ControllerType.SunTouch) {
176
165
  let chem = sys.chemControllers.getItemByAddress(144);
177
166
  state.chemControllers.removeItemById(chem.id);
178
167
  sys.chemControllers.removeItemById(chem.id);
179
168
  }
180
- // Spa Manual Heat on/off
181
- sys.general.options.manualHeat = msg.extractPayloadByte(4) === 1 ? true : false;
182
- msg.isProcessed = true;
183
- } */
169
+ }
170
+ msg.isProcessed = true;
171
+ break;
172
+ }
184
173
  }
185
174
  }
186
175
  }
@@ -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:
@@ -36,43 +38,60 @@ export class PumpMessage {
36
38
  // packet 24/27/152/155 - Pump Config: IntelliTouch
37
39
  const pumpId = msg.extractPayloadByte(0);
38
40
  let type = msg.extractPayloadByte(1); // Avoid setting this then setting it back if we are mapping to a different value.
41
+ let isActive = type !== 0 && pumpId <= sys.equipment.maxPumps;
39
42
  // RKS: 04-14-21 - Only create the pump if it is available. If the pump was previously defined as another type
40
43
  // then it will be removed and recreated.
41
- let pump: Pump = sys.pumps.getItemById(pumpId, pumpId <= sys.equipment.maxPumps && type !== 0);
42
- if (pump.type !== type && type !== 0) {
43
- sys.pumps.removeItemById(pumpId);
44
- pump = sys.pumps.getItemById(pumpId, true);
45
- }
46
- pump.address = pumpId + 95;
47
- switch (type) {
48
- case 0: // none
49
- pump.type = 0;
50
- pump.isActive = false;
51
- break;
52
- case 64: // vsf
53
- pump.type = type;
54
- pump.isActive = true;
55
- PumpMessage.processVSF_IT(msg);
56
- break;
57
- case 255: // vs 3050 on old panels.
58
- case 128: // vs
59
- case 134: // vs Ultra Efficiency
60
- pump.type = 128;
61
- pump.isActive = true;
62
- PumpMessage.processVS_IT(msg);
63
- break;
64
- case 169: // vs+svrs
65
- pump.type = 169;
66
- pump.isActive = true;
67
- PumpMessage.processVS_IT(msg);
68
- break;
69
- default: // vf - type is the background circuit
70
- pump.type = 1; // force to type 1?
71
- pump.isActive = true;
72
- PumpMessage.processVF_IT(msg);
73
- break;
74
- }
75
- if (pump.isActive) {
44
+ let pump: Pump = sys.pumps.getItemById(pumpId, isActive);
45
+ if(isActive) {
46
+ // Remap the combination pump types.
47
+ switch (type) {
48
+ case 0:
49
+ case 64:
50
+ case 169:
51
+ break;
52
+ case 255:
53
+ case 128:
54
+ case 134:
55
+ type = 128;
56
+ break;
57
+ default:
58
+ type = 1;
59
+ break;
60
+ }
61
+ if (pump.type !== type) {
62
+ sys.pumps.removeItemById(pumpId);
63
+ pump = sys.pumps.getItemById(pumpId, isActive);
64
+ }
65
+ pump.address = pumpId + 95;
66
+ pump.master = 0;
67
+ switch (type) {
68
+ case 0: // none
69
+ pump.type = 0;
70
+ pump.isActive = false;
71
+ break;
72
+ case 64: // vsf
73
+ pump.type = type;
74
+ pump.isActive = true;
75
+ PumpMessage.processVSF_IT(msg);
76
+ break;
77
+ case 255: // vs 3050 on old panels.
78
+ case 128: // vs
79
+ case 134: // vs Ultra Efficiency
80
+ pump.type = 128;
81
+ pump.isActive = true;
82
+ PumpMessage.processVS_IT(msg);
83
+ break;
84
+ case 169: // vs+svrs
85
+ pump.type = 169;
86
+ pump.isActive = true;
87
+ PumpMessage.processVS_IT(msg);
88
+ break;
89
+ default: // vf - type is the background circuit
90
+ pump.type = 1; // force to type 1?
91
+ pump.isActive = true;
92
+ PumpMessage.processVF_IT(msg);
93
+ break;
94
+ }
76
95
  if (typeof pump.name === 'undefined') pump.name = sys.board.valueMaps.pumpTypes.get(pump.type).desc;
77
96
  const spump = state.pumps.getItemById(pump.id, true);
78
97
  spump.name = pump.name;
@@ -224,6 +243,7 @@ export class PumpMessage {
224
243
  }
225
244
  if (typeof pump.model === 'undefined') pump.model = 0;
226
245
  pump.type = type;
246
+ pump.master = 0;
227
247
  let spump = state.pumps.getItemById(pump.id, true);
228
248
  spump.type = pump.type;
229
249
  spump.isActive = pump.isActive = true;
@@ -312,9 +332,8 @@ export class PumpMessage {
312
332
  // 18 | 3 | Big endian speed for the speed (1000 rpm with byte(28))
313
333
  // 19 | 0 | Circuit speed #8 = No circuit
314
334
  // 20 | 3 | Big endian speed for the speed (1000 rpm with byte(29))
315
- // 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))
316
336
  // All 30 bytes on this message are accounted for except for byte 3 & 4.
317
-
318
337
  if (typeof pump.model === 'undefined') pump.model = 0;
319
338
  for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
320
339
  let _circuit = msg.extractPayloadByte(circuitId * 2 + 3);
@@ -373,7 +392,8 @@ export class PumpMessage {
373
392
  }
374
393
  private static processVSF_IT(msg: Inbound) {
375
394
  // Sample packet
376
- // [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]
377
397
  const pumpId = msg.extractPayloadByte(0);
378
398
  const pump = sys.pumps.getItemById(pumpId);
379
399
  if (typeof pump.model === 'undefined') pump.model = 0;
@@ -382,14 +402,9 @@ export class PumpMessage {
382
402
  if (_circuit !== 0){
383
403
  const circuit: PumpCircuit = pump.circuits.getItemById(circuitId, true);
384
404
  circuit.circuit = _circuit;
385
- circuit.units =
386
- (msg.extractPayloadByte(4) >> circuitId - 1 & 1) === 0 ? 1 : 0;
387
- if (circuit.units)
388
- circuit.flow = msg.extractPayloadByte(circuitId * 2 + 4);
389
- else
390
- circuit.speed =
391
- msg.extractPayloadByte(circuitId * 2 + 4) * 256 +
392
- 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);
393
408
  }
394
409
  else {
395
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++) {