nodejs-poolcontroller 7.3.0 → 7.6.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 (60) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/Changelog +23 -0
  3. package/README.md +5 -5
  4. package/app.ts +2 -0
  5. package/config/Config.ts +3 -0
  6. package/config/VersionCheck.ts +8 -4
  7. package/controller/Constants.ts +88 -0
  8. package/controller/Equipment.ts +246 -66
  9. package/controller/Errors.ts +24 -1
  10. package/controller/Lockouts.ts +423 -0
  11. package/controller/State.ts +314 -54
  12. package/controller/boards/EasyTouchBoard.ts +107 -59
  13. package/controller/boards/IntelliCenterBoard.ts +186 -125
  14. package/controller/boards/IntelliTouchBoard.ts +104 -30
  15. package/controller/boards/NixieBoard.ts +721 -159
  16. package/controller/boards/SystemBoard.ts +2370 -1108
  17. package/controller/comms/Comms.ts +85 -10
  18. package/controller/comms/messages/Messages.ts +10 -4
  19. package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
  20. package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
  21. package/controller/comms/messages/config/CoverMessage.ts +1 -0
  22. package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
  23. package/controller/comms/messages/config/ExternalMessage.ts +44 -26
  24. package/controller/comms/messages/config/FeatureMessage.ts +8 -1
  25. package/controller/comms/messages/config/GeneralMessage.ts +8 -0
  26. package/controller/comms/messages/config/HeaterMessage.ts +15 -9
  27. package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
  28. package/controller/comms/messages/config/OptionsMessage.ts +13 -1
  29. package/controller/comms/messages/config/PumpMessage.ts +4 -20
  30. package/controller/comms/messages/config/RemoteMessage.ts +4 -0
  31. package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
  32. package/controller/comms/messages/config/SecurityMessage.ts +1 -0
  33. package/controller/comms/messages/config/ValveMessage.ts +13 -3
  34. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
  35. package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
  36. package/controller/comms/messages/status/HeaterStateMessage.ts +42 -9
  37. package/controller/comms/messages/status/IntelliChemStateMessage.ts +37 -26
  38. package/controller/nixie/Nixie.ts +18 -16
  39. package/controller/nixie/bodies/Body.ts +4 -1
  40. package/controller/nixie/chemistry/ChemController.ts +80 -77
  41. package/controller/nixie/chemistry/Chlorinator.ts +9 -8
  42. package/controller/nixie/circuits/Circuit.ts +55 -6
  43. package/controller/nixie/heaters/Heater.ts +192 -32
  44. package/controller/nixie/pumps/Pump.ts +146 -84
  45. package/controller/nixie/schedules/Schedule.ts +3 -2
  46. package/controller/nixie/valves/Valve.ts +1 -1
  47. package/defaultConfig.json +32 -1
  48. package/issue_template.md +1 -1
  49. package/logger/DataLogger.ts +37 -22
  50. package/package.json +20 -18
  51. package/web/Server.ts +520 -29
  52. package/web/bindings/influxDB.json +96 -8
  53. package/web/bindings/mqtt.json +151 -40
  54. package/web/bindings/mqttAlt.json +114 -4
  55. package/web/interfaces/httpInterface.ts +2 -0
  56. package/web/interfaces/influxInterface.ts +36 -19
  57. package/web/interfaces/mqttInterface.ts +14 -3
  58. package/web/services/config/Config.ts +171 -44
  59. package/web/services/state/State.ts +49 -5
  60. package/web/services/state/StateSocket.ts +18 -1
@@ -107,6 +107,7 @@ export class HeaterMessage {
107
107
  catch (err) { logger.error(`Error with OCP reclaiming control over gas heater: ${err}`) }
108
108
  })();
109
109
  }
110
+ heater.master = 0;
110
111
  sys.heaters.getItemById(2).isActive = false;
111
112
  sys.heaters.getItemById(3).isActive = false;
112
113
  sys.heaters.getItemById(4).isActive = false;
@@ -133,6 +134,7 @@ export class HeaterMessage {
133
134
  catch (err) { logger.error(`Error with OCP reclaiming control over Ultratemp: ${err}`) }
134
135
  })();
135
136
  }
137
+ heatPump.master = 0;
136
138
  heatPump.name = 'Ultratemp';
137
139
  heatPump.body = 32;
138
140
  heatPump.type = 4;
@@ -158,6 +160,7 @@ export class HeaterMessage {
158
160
  catch (err) { logger.error(`Error with OCP reclaiming control over heat pump: ${err}`) }
159
161
  })();
160
162
  }
163
+ heatPump.master = 0;
161
164
  heatPump.name = 'Heat Pump';
162
165
  heatPump.body = 32;
163
166
  heatPump.type = 3;
@@ -184,6 +187,7 @@ export class HeaterMessage {
184
187
  catch (err) { logger.error(`Error with OCP reclaiming control over solar heater: ${err}`) }
185
188
  })();
186
189
  }
190
+ solar.master = 0;
187
191
  solar.name = 'Solar Heater';
188
192
  solar.type = 2;
189
193
  solar.isActive = true;
@@ -215,13 +219,7 @@ export class HeaterMessage {
215
219
  }
216
220
  sys.board.heaters.syncHeaterStates();
217
221
  sys.equipment.setEquipmentIds();
218
- break;
219
- case 114:
220
- // something to do with heat pumps... need equipment or other packets to decipher
221
- // [ 255, 0, 255], [165, 0, 112, 16, 114, 10], [144, 2, 0, 0, 0, 0, 0, 0, 0, 0], [2, 51 ]heat + cool
222
- // [165,0,112,16,114,10][144,0,0,0,0,0,0,0,0,0][2,49] == no Heater, no cool
223
- // [165,0,112,16,114,10][144,2,0,0,0,0,0,0,0,0][2,51] == no heat, cooling
224
- // this might be heatStatus not heatMode?
222
+ msg.isProcessed = true;
225
223
  break;
226
224
  case 168:
227
225
  {
@@ -241,7 +239,7 @@ export class HeaterMessage {
241
239
  }
242
240
  // Spa Manual Heat on/off
243
241
  sys.general.options.manualHeat = msg.extractPayloadByte(4) === 1 ? true : false;
244
-
242
+ msg.isProcessed = true;
245
243
  }
246
244
  }
247
245
  }
@@ -261,6 +259,7 @@ export class HeaterMessage {
261
259
  })();
262
260
  }
263
261
  }
262
+ msg.isProcessed = true;
264
263
  }
265
264
 
266
265
  //private static processBody(msg: Inbound) {
@@ -293,6 +292,7 @@ export class HeaterMessage {
293
292
  catch (err) { logger.error(`Error with OCP reclaiming control over heater: ${err}`) }
294
293
  })();
295
294
  }
295
+ heater.master = 0;
296
296
  let hstate = state.heaters.getItemById(i);
297
297
  // hstate.isVirtual = false;
298
298
  hstate.name = heater.name;
@@ -300,12 +300,14 @@ export class HeaterMessage {
300
300
 
301
301
  }
302
302
  sys.board.heaters.updateHeaterServices();
303
+ msg.isProcessed = true;
303
304
  }
304
305
  private static processMaxBoostTemp(msg: Inbound) {
305
306
  for (let i = 0; i < msg.payload.length - 1 && i < sys.equipment.maxHeaters; i++) {
306
307
  var heater: Heater = sys.heaters.getItemById(i + 1);
307
308
  heater.maxBoostTemp = msg.extractPayloadByte(i + 2);
308
309
  }
310
+ msg.isProcessed = true;
309
311
  }
310
312
  private static processStartStopDelta(msg: Inbound) {
311
313
  for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxHeaters; i++) {
@@ -313,6 +315,7 @@ export class HeaterMessage {
313
315
  heater.startTempDelta = msg.extractPayloadByte(i + 1);
314
316
  heater.stopTempDelta = msg.extractPayloadByte(i + 18);
315
317
  }
318
+ msg.isProcessed = true;
316
319
  }
317
320
  private static processCoolingSetTemp(msg: Inbound) {
318
321
  for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxHeaters; i++) {
@@ -320,20 +323,22 @@ export class HeaterMessage {
320
323
  heater.coolingEnabled = msg.extractPayloadByte(i + 1) > 0;
321
324
  heater.differentialTemp = msg.extractPayloadByte(i + 18);
322
325
  }
326
+ msg.isProcessed = true;
323
327
  }
324
328
  private static processAddress(msg: Inbound) {
325
329
  for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxHeaters; i++) {
326
330
  var heater: Heater = sys.heaters.getItemById(i);
327
331
  heater.address = msg.extractPayloadByte(i + 1);
328
332
  }
333
+ msg.isProcessed = true;
329
334
  }
330
335
  private static processEfficiencyMode(msg: Inbound) {
331
336
  for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxHeaters; i++) {
332
337
  var heater: Heater = sys.heaters.getItemById(i);
333
338
  heater.efficiencyMode = msg.extractPayloadByte(i + 1);
334
339
  }
340
+ msg.isProcessed = true;
335
341
  }
336
-
337
342
  private static processHeaterNames(msg: Inbound) {
338
343
  var heaterId = ((msg.extractPayloadByte(1) - 5) * 2) + 1;
339
344
  if (heaterId <= sys.equipment.maxHeaters) {
@@ -344,5 +349,6 @@ export class HeaterMessage {
344
349
  let hstate = state.heaters.getItemById(heaterId);
345
350
  hstate.name = sys.heaters.getItemById(heaterId++).name = msg.extractPayloadString(18, 16);
346
351
  }
352
+ msg.isProcessed = true;
347
353
  }
348
354
  }
@@ -138,7 +138,8 @@ export class IntellichemMessage {
138
138
  let controller = sys.chemControllers.getItemById(id, isActive, { id: i + 1, type: 1 });
139
139
  let scontroller = state.chemControllers.getItemById(controller.id, isActive);
140
140
  scontroller.isActive = controller.isActive = true;
141
- controller.isVirtual = false;
141
+ // controller.isVirtual = false;
142
+ controller.master = 0;
142
143
  if (!controller.isActive) {
143
144
  sys.chemControllers.removeItemById(controller.id);
144
145
  state.chemControllers.removeItemById(controller.id);
@@ -163,6 +164,7 @@ export class IntellichemMessage {
163
164
  }
164
165
  }
165
166
  }
167
+ msg.isProcessed = true;
166
168
  break;
167
169
  case 1:
168
170
  for (let i = 0; i < 4; i++) {
@@ -175,6 +177,7 @@ export class IntellichemMessage {
175
177
  controller.alkalinity = msg.extractPayloadInt((i * 2) + 26);
176
178
  }
177
179
  }
180
+ msg.isProcessed = true;
178
181
  break;
179
182
  }
180
183
  }
@@ -92,9 +92,21 @@ export class OptionsMessage {
92
92
  //body = sys.bodies.getItemById(4, sys.equipment.maxBodies > 3);
93
93
  //body.heatMode = msg.extractPayloadByte(27);
94
94
  //body.heatSetpoint = msg.extractPayloadByte(23);
95
+ msg.isProcessed = true;
95
96
  break;
96
97
  }
97
- case 1: // Unknown
98
+ case 1: // Vacation mode
99
+ let yy = msg.extractPayloadByte(4) + 2000;
100
+ let mm = msg.extractPayloadByte(5);
101
+ let dd = msg.extractPayloadByte(6);
102
+ sys.general.options.vacation.startDate = new Date(yy, mm - 1, dd);
103
+ yy = msg.extractPayloadByte(7) + 2000;
104
+ mm = msg.extractPayloadByte(8);
105
+ dd = msg.extractPayloadByte(9);
106
+ sys.general.options.vacation.endDate = new Date(yy, mm - 1, dd);
107
+ sys.general.options.vacation.enabled = msg.extractPayloadByte(2) > 0;
108
+ sys.general.options.vacation.useTimeframe = msg.extractPayloadByte(3) > 0;
109
+ msg.isProcessed = true;
98
110
  break;
99
111
  }
100
112
  msg.isProcessed = true;
@@ -132,8 +132,10 @@ export class PumpMessage {
132
132
  }
133
133
  }
134
134
  }
135
+ msg.isProcessed = true;
135
136
  switch (msgId) {
136
137
  case 0:
138
+ msg.isProcessed = true;
137
139
  break;
138
140
  case 1:
139
141
  PumpMessage.processFlowStepSize(msg);
@@ -347,38 +349,20 @@ export class PumpMessage {
347
349
  circuit.circuit = _circuit;
348
350
  circuit.flow = msg.extractPayloadByte(circuitId * 2 + 4);
349
351
  circuit.units = 1;
350
- switch (circuit.circuit) {
351
- case 1:
352
- {
353
- let body = sys.bodies.getItemById(2, sys.equipment.maxBodies >= 2);
354
- let sbody = state.temps.bodies.getItemById(2, sys.equipment.maxBodies >= 2);
355
- sbody.type = body.type = 1; // spa
356
- body.isActive = true;
357
- break;
358
- }
359
- case 6:
360
- {
361
- let body = sys.bodies.getItemById(1, sys.equipment.maxBodies >= 1);
362
- let sbody = state.temps.bodies.getItemById(1, sys.equipment.maxBodies >= 1);
363
- sbody.type = body.type = 0; // pool
364
- body.isActive = true;
365
- body.capacity = msg.extractPayloadByte(6) * 1000;
366
- break;
367
- }
368
- }
369
352
  }
370
353
  else {
371
354
  pump.circuits.removeItemById(_circuit);
372
355
  }
373
356
  }
374
357
  pump.backgroundCircuit = msg.extractPayloadByte(1);
358
+ pump.filterSize = msg.extractPayloadByte(2) * 1000;
375
359
  pump.turnovers = msg.extractPayloadByte(3);
360
+ pump.manualFilterGPM = msg.extractPayloadByte(21);
376
361
  pump.primingSpeed = msg.extractPayloadByte(22);
377
362
  pump.primingTime = (msg.extractPayloadByte(23) & 0xf);
378
363
  pump.minFlow = sys.board.valueMaps.pumpTypes.get(pump.type).minFlow;
379
364
  pump.maxFlow = sys.board.valueMaps.pumpTypes.get(pump.type).maxFlow;
380
365
  pump.flowStepSize = sys.board.valueMaps.pumpTypes.get(pump.type).flowStepSize;
381
- pump.manualFilterGPM = msg.extractPayloadByte(21);
382
366
  pump.maxSystemTime = msg.extractPayloadByte(23) >> 4;
383
367
  pump.maxPressureIncrease = msg.extractPayloadByte(24);
384
368
  pump.backwashFlow = msg.extractPayloadByte(25);
@@ -93,6 +93,7 @@ export class RemoteMessage {
93
93
  if (!remote.button1 && !remote.button2 && !remote.button3 && !remote.button4) remote.isActive = false;
94
94
  else remote.isActive = true;
95
95
  remote.name = "QuickTouch";
96
+ msg.isProcessed = true;
96
97
  break;
97
98
  }
98
99
  case 32: // is4/is10
@@ -120,6 +121,7 @@ export class RemoteMessage {
120
121
  remote.type = 1;
121
122
  remote.name = "is4";
122
123
  }
124
+ msg.isProcessed = true;
123
125
  break;
124
126
  }
125
127
  case 22: // Spa Command spa side remote additional config
@@ -132,6 +134,7 @@ export class RemoteMessage {
132
134
  remote.pumpId = msg.extractPayloadByte(5);
133
135
  remote.stepSize = msg.extractPayloadByte(6);
134
136
  remote.type = 7;
137
+ msg.isProcessed = true;
135
138
  break;
136
139
  }
137
140
  }
@@ -187,5 +190,6 @@ export class RemoteMessage {
187
190
  }
188
191
  remote["button" + (i + 1)] = msg.extractPayloadByte(i + 2);
189
192
  }
193
+ msg.isProcessed = true;
190
194
  }
191
195
  }
@@ -187,6 +187,7 @@ export class ScheduleMessage {
187
187
  const schedule: Schedule = sys.schedules.getItemById(schedId++, false, { isActive: false });
188
188
  if (schedule.isActive !== false) schedule.startMonth = msg.extractPayloadByte(i + 1);
189
189
  }
190
+ msg.isProcessed = true;
190
191
  }
191
192
  private static processStartDay(msg: Inbound) {
192
193
  let schedId = (msg.extractPayloadByte(1) - 17) * 40 + 1;
@@ -198,6 +199,7 @@ export class ScheduleMessage {
198
199
  csched.startTime = schedule.startTime;
199
200
  }
200
201
  }
202
+ msg.isProcessed = true;
201
203
  }
202
204
  private static processStartYear(msg: Inbound) {
203
205
  let schedId = (msg.extractPayloadByte(1) - 20) * 40 + 1;
@@ -207,6 +209,7 @@ export class ScheduleMessage {
207
209
  schedule.startYear = msg.extractPayloadByte(i + 1);
208
210
  }
209
211
  }
212
+ msg.isProcessed = true;
210
213
  }
211
214
  private static processStartTimes(msg: Inbound) {
212
215
  let schedId = msg.extractPayloadByte(1) * 20 + 1;
@@ -226,6 +229,7 @@ export class ScheduleMessage {
226
229
  i += 2;
227
230
  }
228
231
  ScheduleMessage._maxSchedId = sys.schedules.getMaxId(true, 0);
232
+ msg.isProcessed = true;
229
233
  }
230
234
  private static processEndTimes(msg: Inbound) {
231
235
  let schedId = (msg.extractPayloadByte(1) - 23) * 20 + 1;
@@ -239,6 +243,7 @@ export class ScheduleMessage {
239
243
  }
240
244
  i += 2;
241
245
  }
246
+ msg.isProcessed = true;
242
247
  }
243
248
  private static processCircuit(msg: Inbound) {
244
249
  let schedId = (msg.extractPayloadByte(1) - 5) * 40 + 1;
@@ -258,6 +263,7 @@ export class ScheduleMessage {
258
263
  csched.circuit = schedule.circuit;
259
264
  }
260
265
  }
266
+ msg.isProcessed = true;
261
267
  }
262
268
  private static processRunOnce(msg: Inbound) {
263
269
  let schedId = (msg.extractPayloadByte(1) - 8) * 40 + 1;
@@ -280,6 +286,7 @@ export class ScheduleMessage {
280
286
  csched.scheduleType = schedule.scheduleType;
281
287
  }
282
288
  }
289
+ msg.isProcessed = true;
283
290
  }
284
291
  private static processDays(msg: Inbound) {
285
292
  let schedId = (msg.extractPayloadByte(1) - 11) * 40 + 1;
@@ -291,6 +298,7 @@ export class ScheduleMessage {
291
298
  csched.scheduleDays = csched.scheduleType === 128 ? schedule.scheduleDays : 0;
292
299
  }
293
300
  }
301
+ msg.isProcessed = true;
294
302
  }
295
303
  private static processHeatSource(msg: Inbound) {
296
304
  let schedId = (msg.extractPayloadByte(1) - 28) * 40 + 1;
@@ -305,6 +313,7 @@ export class ScheduleMessage {
305
313
  csched.heatSource = schedule.heatSource;
306
314
  }
307
315
  }
316
+ msg.isProcessed = true;
308
317
  }
309
318
  private static processHeatSetpoint(msg: Inbound) {
310
319
  let schedId = (msg.extractPayloadByte(1) - 31) * 40 + 1;
@@ -316,6 +325,7 @@ export class ScheduleMessage {
316
325
  csched.heatSetpoint = schedule.heatSetpoint;
317
326
  }
318
327
  }
328
+ msg.isProcessed = true;
319
329
  }
320
330
  private static processCoolSetpoint(msg: Inbound) {
321
331
  let schedId = (msg.extractPayloadByte(1) - 34) * 40 + 1;
@@ -327,5 +337,6 @@ export class ScheduleMessage {
327
337
  csched.coolSetpoint = schedule.coolSetpoint;
328
338
  }
329
339
  }
340
+ msg.isProcessed = true;
330
341
  }
331
342
  }
@@ -34,5 +34,6 @@ export class SecurityMessage {
34
34
  role.flag2 = msg.extractPayloadByte(3);
35
35
  role.pin = msg.extractPayloadByte(21).toString() + msg.extractPayloadByte(22).toString() + msg.extractPayloadByte(23).toString() + msg.extractPayloadByte(24).toString();
36
36
  }
37
+ msg.isProcessed = true;
37
38
  }
38
39
  }
@@ -32,6 +32,7 @@ export class ValveMessage {
32
32
  ValveMessage.processValveNames(msg);
33
33
  break;
34
34
  case 3: // Skip the secondary intake/return
35
+ msg.isProcessed = true;
35
36
  break;
36
37
  case 4:
37
38
  case 5:
@@ -70,6 +71,7 @@ export class ValveMessage {
70
71
  // [165,33,15,16,35,2],[132,0],[1,142]
71
72
  // ^^^ 128 = Pump off during valve operation
72
73
  sys.general.options.pumpDelay = msg.extractPayloadByte(0) >> 7 === 1;
74
+ msg.isProcessed = true;
73
75
  }
74
76
  private static process_ValveAssignment_IT(msg: Inbound) {
75
77
  // sample packet
@@ -91,6 +93,7 @@ export class ValveMessage {
91
93
  let svalve = state.valves.getItemById(id, true);
92
94
  svalve.name = valve.name;
93
95
  svalve.type = valve.type;
96
+ valve.master = 0;
94
97
  }
95
98
  else {
96
99
  sys.valves.removeItemById(id);
@@ -110,6 +113,7 @@ export class ValveMessage {
110
113
  let svalve = state.valves.getItemById(id, true);
111
114
  svalve.name = valve.name;
112
115
  svalve.type = valve.type;
116
+ valve.master = 0;
113
117
  }
114
118
  else {
115
119
  sys.valves.removeItemById(id);
@@ -136,17 +140,20 @@ export class ValveMessage {
136
140
  let svalve = state.valves.getItemById(id, true);
137
141
  svalve.name = valve.name;
138
142
  svalve.type = valve.type;
143
+ valve.master = 0;
139
144
  }
140
145
  if (!valve.isActive) {
141
146
  sys.valves.removeItemById(id);
142
147
  state.valves.removeItemById(id);
143
148
  }
144
149
  else {
145
- valve.isVirtual = false;
150
+ valve.master = 0;
151
+ // valve.isVirtual = false;
146
152
  valve.type = 0;
147
153
  }
148
154
  id++;
149
155
  }
156
+ msg.isProcessed = true;
150
157
  }
151
158
  private static getName(id: number, cir: number) {
152
159
  if (cir <= 0 || cir >= 255 || cir === 6) {
@@ -167,7 +174,7 @@ export class ValveMessage {
167
174
  // for i10d.
168
175
  let ndx: number = 2;
169
176
  let id = 1;
170
- for (let i = 0; i < sys.equipment.maxValves - 1; i++) {
177
+ for (let i = 0; i < sys.equipment.maxValves; i++) {
171
178
  if (id === 3 && !sys.equipment.shared) {
172
179
  // The intake/return valves are skipped for non-shared systems.
173
180
  sys.valves.removeItemById(3);
@@ -187,7 +194,8 @@ export class ValveMessage {
187
194
  ndx += 2;
188
195
  }
189
196
  let valve: Valve = sys.valves.getItemById(id, true);
190
- valve.isVirtual = false;
197
+ valve.master = 0;
198
+ // valve.isVirtual = false;
191
199
  if (id === 3 || id === 5) {
192
200
  valve.circuit = 247; // Hardcode the intake/return to pool/spa;
193
201
  valve.isIntake = true;
@@ -214,6 +222,7 @@ export class ValveMessage {
214
222
  }
215
223
  // Sort them so they are in valve id order. This will ensure any OCP valves come first in the list. Valves ids > 50 are virtual valves.
216
224
  sys.valves.sortById();
225
+ msg.isProcessed = true;
217
226
  }
218
227
  private static processValveNames(msg: Inbound) {
219
228
  let byte = msg.extractPayloadByte(1);
@@ -228,5 +237,6 @@ export class ValveMessage {
228
237
  if (typeof sys.valves.find(elem => elem.id === valveId) !== 'undefined') {
229
238
  state.valves.getItemById(valveId).name = sys.valves.getItemById(valveId++).name = msg.extractPayloadString(18, 16);
230
239
  }
240
+ msg.isProcessed = true;
231
241
  }
232
242
  }
@@ -64,9 +64,8 @@ export class ChlorinatorStateMessage {
64
64
  //[16, 2, 0, 3][0, 73, 110, 116, 101, 108, 108, 105, 99, 104, 108, 111, 114, 45, 45, 52, 48][188, 16, 3]
65
65
  // This is the model number of the chlorinator and the address is actually the second byte.
66
66
  let name = msg.extractPayloadString(1, 16);
67
- chlor.model = name;
68
- if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = name;
69
- cstate.name = chlor.name;
67
+ if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = cstate.name = name;
68
+ if (typeof chlor.model === 'undefined') chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
70
69
  cstate.isActive = chlor.isActive;
71
70
  state.emitEquipmentChanges();
72
71
  break;
@@ -101,7 +101,9 @@ export class EquipmentStateMessage {
101
101
  Message.headerSubByte = msg.header[1];
102
102
  //console.log(process.memoryUsage());
103
103
  if (msg.action === 2 && state.isInitialized && sys.controllerType === ControllerType.Nixie) {
104
- // Start over because we didn't have communication before but we now do.
104
+ // Start over because we didn't have communication before but we now do. This will fall into the if
105
+ // below so that it goes through the intialization process. In this case we didn't see an OCP when we started
106
+ // but there clearly is one now.
105
107
  sys.controllerType = ControllerType.Unknown;
106
108
  state.status = 0;
107
109
  }
@@ -139,19 +141,24 @@ export class EquipmentStateMessage {
139
141
 
140
142
  // Shared
141
143
  let dt = new Date();
142
- if (state.chemControllers.length > 0) {
143
- // TODO: move this to chemController when we understand the packets better
144
- for (let i = 0; i < state.chemControllers.length; i++) {
145
- let ccontroller = state.chemControllers.getItemByIndex(i);
146
- if (sys.board.valueMaps.chemControllerTypes.getName(ccontroller.type) === 'intellichem') {
147
- if (dt.getTime() - ccontroller.lastComm > 60000) ccontroller.status = 1;
148
- }
149
- }
150
- }
144
+ // RKS: This was moved to the ChemControllerState message. This is flawed in that it incorrectly sets IntelliChem to no comms.
145
+ //if (state.chemControllers.length > 0) {
146
+ // // TODO: move this to chemController when we understand the packets better
147
+ // for (let i = 0; i < state.chemControllers.length; i++) {
148
+ // let ccontroller = state.chemControllers.getItemByIndex(i);
149
+ // if (sys.board.valueMaps.chemControllerTypes.getName(ccontroller.type) === 'intellichem') {
150
+ // if (dt.getTime() - ccontroller.lastComm > 60000) ccontroller.status = 1;
151
+ // }
152
+ // }
153
+ //}
151
154
  state.time.hours = msg.extractPayloadByte(0);
152
155
  state.time.minutes = msg.extractPayloadByte(1);
153
156
  state.time.seconds = dt.getSeconds();
154
- state.mode = msg.extractPayloadByte(9) & 0x81;
157
+ state.mode = sys.controllerType !== ControllerType.IntelliCenter ? (msg.extractPayloadByte(9) & 0x81) : (msg.extractPayloadByte(9) & 0x01);
158
+
159
+ // RKS: The units have been normalized for English and Metric for the overall panel. It is important that the val numbers match for at least the temp units since
160
+ // the only unit of measure native to the Touch controllers is temperature they chose to name these C or F. However, with the njsPC extensions this is non-semantic
161
+ // since pressure, volume, and length have been introduced.
155
162
  sys.general.options.units = state.temps.units = msg.extractPayloadByte(9) & 0x04;
156
163
  state.valve = msg.extractPayloadByte(10);
157
164
 
@@ -163,8 +170,8 @@ export class EquipmentStateMessage {
163
170
  // 1. IntelliCenter has "manual" time set (Internet will automatically adjust) and autoAdjustDST is enabled
164
171
  // 2. *Touch is "manual" (only option) and autoAdjustDST is enabled - (same as #1)
165
172
  // 3. clock source is "server" isn't an OCP option but can be enabled on the clients
166
- if (dt.getMinutes() % 5 === 0 && sys.general.options.clockSource === 'server') {
167
- if ((Math.abs(dt.getTime() - state.time.getTime()) > 60 * 5 * 1000) && !state.time.isUpdating) {
173
+ if (dt.getMinutes() % 5 === 0 && dt.getSeconds() <= 10 && sys.general.options.clockSource === 'server') {
174
+ if ((Math.abs(dt.getTime() - state.time.getTime()) > 60 * 2 * 1000) && !state.time.isUpdating) {
168
175
  state.time.isUpdating = true;
169
176
  sys.board.system.setDateTimeAsync({ dt, dst: sys.general.options.adjustDST || 0, })
170
177
  .then(() => {
@@ -197,10 +204,19 @@ export class EquipmentStateMessage {
197
204
  tbody.name = cbody.name;
198
205
  tbody.circuit = cbody.circuit = 6;
199
206
  tbody.heatStatus = msg.extractPayloadByte(11) & 0x0F;
200
- if ((msg.extractPayloadByte(2) & 0x20) === 32) {
207
+ // With the IntelliCenter i10D, bit 6 is not reliable. It is not set properly and requires the 204 message
208
+ // to process the data.
209
+ if (!sys.equipment.dual) {
210
+ if ((msg.extractPayloadByte(2) & 0x20) === 32) {
211
+ tbody.temp = state.temps.waterSensor1;
212
+ tbody.isOn = true;
213
+ } else tbody.isOn = false;
214
+ }
215
+ else if (state.circuits.getItemById(6).isOn === true) {
201
216
  tbody.temp = state.temps.waterSensor1;
202
217
  tbody.isOn = true;
203
- } else tbody.isOn = false;
218
+ }
219
+ else tbody.isOn = false;
204
220
  }
205
221
  if (sys.bodies.length > 1) {
206
222
  const tbody: BodyTempState = state.temps.bodies.getItemById(2, true);
@@ -210,10 +226,16 @@ export class EquipmentStateMessage {
210
226
  tbody.name = cbody.name;
211
227
  tbody.circuit = cbody.circuit = 1;
212
228
  tbody.heatStatus = (msg.extractPayloadByte(11) & 0xF0) >> 4;
213
- if ((msg.extractPayloadByte(2) & 0x01) === 1) {
229
+ if (!sys.equipment.dual) {
230
+ if ((msg.extractPayloadByte(2) & 0x01) === 1) {
231
+ tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
232
+ tbody.isOn = true;
233
+ } else tbody.isOn = false;
234
+ } else if (state.circuits.getItemById(1).isOn === true) {
214
235
  tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
215
236
  tbody.isOn = true;
216
- } else tbody.isOn = false;
237
+ }
238
+ else tbody.isOn = false;
217
239
  }
218
240
  if (sys.bodies.length > 2) {
219
241
  state.temps.waterSensor3 = fnTempFromByte(msg.extractPayloadByte(20));
@@ -390,13 +412,14 @@ export class EquipmentStateMessage {
390
412
  sys.board.circuits.syncCircuitRelayStates();
391
413
  sys.board.circuits.syncVirtualCircuitStates();
392
414
  sys.board.valves.syncValveStates();
415
+ sys.board.filters.syncFilterStates();
393
416
  state.emitControllerChange();
394
417
  state.emitEquipmentChanges();
395
418
  sys.board.heaters.syncHeaterStates();
396
419
  break;
397
420
  }
398
- case ControllerType.IntelliCom:
399
421
  case ControllerType.EasyTouch:
422
+ case ControllerType.IntelliCom:
400
423
  case ControllerType.IntelliTouch:
401
424
  {
402
425
  EquipmentStateMessage.processTouchCircuits(msg);
@@ -405,6 +428,7 @@ export class EquipmentStateMessage {
405
428
  sys.board.features.syncGroupStates();
406
429
  sys.board.circuits.syncVirtualCircuitStates();
407
430
  sys.board.valves.syncValveStates();
431
+ sys.board.filters.syncFilterStates();
408
432
  state.emitControllerChange();
409
433
  state.emitEquipmentChanges();
410
434
  sys.board.heaters.syncHeaterStates();
@@ -495,8 +519,7 @@ export class EquipmentStateMessage {
495
519
  state.time.date = msg.extractPayloadByte(6);
496
520
  state.time.month = msg.extractPayloadByte(7);
497
521
  state.time.year = msg.extractPayloadByte(8);
498
- sys.equipment.controllerFirmware = (msg.extractPayloadByte(42)
499
- + (msg.extractPayloadByte(43) / 1000)).toString();
522
+ sys.equipment.controllerFirmware = (msg.extractPayloadByte(42) + (msg.extractPayloadByte(43) / 1000)).toString();
500
523
  if (sys.chlorinators.length > 0) {
501
524
  if (msg.extractPayloadByte(37, 255) !== 255) {
502
525
  const chlor = state.chlorinators.getItemById(1);
@@ -508,9 +531,38 @@ export class EquipmentStateMessage {
508
531
  }
509
532
  }
510
533
  ExternalMessage.processFeatureState(9, msg);
534
+ //if (sys.equipment.dual === true) {
535
+ // // For IntelliCenter i10D the body state is on byte 26 of the 204. This impacts circuit 6.
536
+ // let byte = msg.extractPayloadByte(26);
537
+ // let pstate = state.circuits.getItemById(6, true);
538
+ // let oldstate = pstate.isOn;
539
+ // pstate.isOn = ((byte & 0x0010) === 0x0010);
540
+ // logger.info(`Checking i10D pool state ${byte} old:${oldstate} new: ${pstate.isOn}`);
541
+ // //if (oldstate !== pstate.isOn) {
542
+ // state.temps.bodies.getItemById(1, true).isOn = pstate.isOn;
543
+ // sys.board.circuits.syncCircuitRelayStates();
544
+ // sys.board.circuits.syncVirtualCircuitStates();
545
+ // sys.board.valves.syncValveStates();
546
+ // sys.board.filters.syncFilterStates();
547
+ // sys.board.heaters.syncHeaterStates();
548
+ // //}
549
+ // if (oldstate !== pstate.isOn) pstate.emitEquipmentChange();
550
+ //}
551
+ // At this point normally on is ignored. Not sure what this does.
552
+ let cover1 = sys.covers.getItemById(1);
553
+ let cover2 = sys.covers.getItemById(2);
554
+ if (cover1.isActive) {
555
+ let scover1 = state.covers.getItemById(1, true);
556
+ scover1.name = cover1.name;
557
+ state.temps.bodies.getItemById(cover1.body + 1).isCovered = scover1.isClosed = (msg.extractPayloadByte(30) & 0x0001) > 0;
558
+ }
559
+ if (cover2.isActive) {
560
+ let scover2 = state.covers.getItemById(2, true);
561
+ scover2.name = cover2.name;
562
+ state.temps.bodies.getItemById(cover2.body + 1).isCovered = scover2.isClosed = (msg.extractPayloadByte(30) & 0x0002) > 0;
563
+ }
511
564
  msg.isProcessed = true;
512
- // state.emitControllerChange();
513
- // state.emitEquipmentChanges();
565
+ state.emitEquipmentChanges();
514
566
  break;
515
567
  }
516
568
  }
@@ -528,13 +580,15 @@ export class EquipmentStateMessage {
528
580
  let circuit = sys.circuits.getItemById(circuitId, false, { isActive: false });
529
581
  if (circuit.isActive !== false) {
530
582
  let cstate = state.circuits.getItemById(circuitId, circuit.isActive);
531
- let isOn = (byte & (1 << j)) > 0;
532
- sys.board.circuits.setEndTime(circuit, cstate, isOn);
583
+ // For IntelliCenter i10D body circuits are not reported here.
584
+ let isOn = ((circuitId === 6 || circuitId === 1) && sys.equipment.dual === true) ? cstate.isOn : (byte & (1 << j)) > 0;
585
+ //let isOn = (byte & (1 << j)) > 0;
533
586
  cstate.isOn = isOn;
534
587
  cstate.name = circuit.name;
535
588
  cstate.nameId = circuit.nameId;
536
589
  cstate.showInFeatures = circuit.showInFeatures;
537
590
  cstate.type = circuit.type;
591
+ sys.board.circuits.setEndTime(circuit, cstate, isOn);
538
592
  if (sys.controllerType === ControllerType.IntelliCenter) {
539
593
  // intellitouch sends a separate msg with themes
540
594
  switch (circuit.type) {