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
@@ -215,8 +215,8 @@ export class NixiePump extends NixieEquipment {
215
215
  if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
216
216
  this._pollTimer = null;
217
217
  // let success = false;
218
- this.setTargetSpeed();
219
218
  let pstate = state.pumps.getItemById(this.pump.id);
219
+ this.setTargetSpeed(pstate);
220
220
  await this.setPumpStateAsync(pstate);
221
221
  }
222
222
  catch (err) { logger.error(`Nixie Error running pump sequence - ${err}`); }
@@ -239,7 +239,10 @@ export class NixiePump extends NixieEquipment {
239
239
  this._pollTimer = null;
240
240
  this._targetSpeed = 0;
241
241
  let pstate = state.pumps.getItemById(this.pump.id);
242
- await this.setPumpStateAsync(pstate);
242
+ try {
243
+ await this.setPumpStateAsync(pstate);
244
+ // Since we are closing we need to not reject.
245
+ } catch (err) { logger.error(`Nixie Closing pump closeAsync: ${err.message}`); }
243
246
  // This will make sure the timer is dead and we are completely closed.
244
247
  this.closing = true;
245
248
  if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
@@ -248,19 +251,43 @@ export class NixiePump extends NixieEquipment {
248
251
  catch (err) { logger.error(`Nixie Pump closeAsync: ${err.message}`); return Promise.reject(err); }
249
252
  }
250
253
  public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
251
- protected setTargetSpeed() { };
254
+ protected setTargetSpeed(pstate: PumpState) { };
255
+ protected isBodyOn(bodyCode: number) {
256
+ let assoc = sys.board.valueMaps.pumpBodies.transform(bodyCode);
257
+ switch (assoc.name) {
258
+ case 'body1':
259
+ case 'pool':
260
+ return state.temps.bodies.getItemById(1).isOn;
261
+ case 'body2':
262
+ case 'spa':
263
+ return state.temps.bodies.getItemById(2).isOn;
264
+ case 'body3':
265
+ return state.temps.bodies.getItemById(3).isOn;
266
+ case 'body4':
267
+ return state.temps.bodies.getItemById(4).isOn;
268
+ case 'poolspa':
269
+ if (sys.equipment.shared && sys.equipment.maxBodies >= 2) {
270
+ return state.temps.bodies.getItemById(1).isOn === true || state.temps.bodies.getItemById(2).isOn === true;
271
+ }
272
+ else
273
+ return state.temps.bodies.getItemById(1).isOn;
274
+ }
275
+ return false;
276
+ }
252
277
  }
253
278
  export class NixiePumpSS extends NixiePump {
254
- public setTargetSpeed() {
279
+ public setTargetSpeed(pState: PumpState) {
255
280
  // Turn on ss pumps.
256
281
  let _newSpeed = 0;
257
- let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
258
- if (pt.hasBody) _newSpeed = sys.board.bodies.isBodyOn(this.pump.body) ? 1 : 0;
259
- if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed > 0 ? 'on' : 'off'}.`);
282
+ if (!pState.pumpOnDelay) {
283
+ let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
284
+ if (pt.hasBody) _newSpeed = this.isBodyOn(this.pump.body) ? 1 : 0;
285
+ //console.log(`BODY: ${sys.board.bodies.isBodyOn(this.pump.body)} CODE: ${this.pump.body}`);
286
+ }
287
+ if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed > 0 ? 'on' : 'off'}. ${sys.board.bodies.isBodyOn(this.pump.body)}`);
260
288
  if (isNaN(_newSpeed)) _newSpeed = 0;
261
289
  this._targetSpeed = _newSpeed;
262
290
  }
263
-
264
291
  public async setPumpStateAsync(pstate: PumpState) {
265
292
  let relays: PumpRelay[] = this.pump.relays.get();
266
293
  let relayState = 0;
@@ -301,16 +328,20 @@ export class NixiePumpSS extends NixiePump {
301
328
  }
302
329
  }
303
330
  export class NixiePumpDS extends NixiePumpSS {
304
- public setTargetSpeed() {
331
+ public setTargetSpeed(pState: PumpState) {
305
332
  // Turn on sf pumps. The new speed will be the relays associated with the pump. I believe when this comes out in the final
306
333
  // wash it should engage all the relays for all speeds associated with the pump. The pump logic will determine which program is
307
334
  // the one to engage.
308
335
  let _newSpeed = 0;
309
- let pumpCircuits: PumpCircuit[] = this.pump.circuits.get();
310
- for (let i = 0; i < pumpCircuits.length; i++) {
311
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
312
- // relay speeds are bit-shifted 'or' based on 1,2,4,8
313
- if (circ.isOn) _newSpeed |= (1 << pumpCircuits[i].relay - 1);
336
+ if (!pState.pumpOnDelay) {
337
+ let pumpCircuits: PumpCircuit[] = this.pump.circuits.get();
338
+ if (!pState.pumpOnDelay) {
339
+ for (let i = 0; i < pumpCircuits.length; i++) {
340
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
341
+ // relay speeds are bit-shifted 'or' based on 1,2,4,8
342
+ if (circ.isOn) _newSpeed |= (1 << pumpCircuits[i].relay - 1);
343
+ }
344
+ }
314
345
  }
315
346
  if (isNaN(_newSpeed)) _newSpeed = 0;
316
347
  this.logSpeed(_newSpeed);
@@ -335,15 +366,16 @@ export class NixiePumpRS485 extends NixiePump {
335
366
  let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
336
367
  // Since these process are async the closing flag can be set
337
368
  // between calls. We need to check it in between each call.
338
- if (!this.closing) await this.setDriveStateAsync(pstate);
339
- if (!this.closing) {
340
- if (this._targetSpeed >= pt.minFlow && this._targetSpeed <= pt.maxFlow) await this.setPumpGPMAsync(pstate);
341
- else if (this._targetSpeed >= pt.minSpeed && this._targetSpeed <= pt.maxSpeed) await this.setPumpRPMAsync(pstate);
342
- }
369
+ try { if (!this.closing) await this.setDriveStateAsync(); } catch (err) {}
370
+ try { if (!this.closing) {
371
+ if (this._targetSpeed >= pt.minFlow && this._targetSpeed <= pt.maxFlow) await this.setPumpGPMAsync();
372
+ else if (this._targetSpeed >= pt.minSpeed && this._targetSpeed <= pt.maxSpeed) await this.setPumpRPMAsync();
373
+ } } catch (err) {}
343
374
 
344
- if(!this.closing) await utils.sleep(2000);
345
- if(!this.closing) await this.requestPumpStatus(pstate);
346
- if(!this.closing) await this.setPumpToRemoteControl(pstate);
375
+ try { if(!this.closing) await this.setPumpFeature(6); } catch (err) {};
376
+ try { if(!this.closing) await utils.sleep(1000); } catch (err) {};
377
+ try { if(!this.closing) await this.requestPumpStatus(); } catch (err) {};
378
+ try { if(!this.closing) await this.setPumpToRemoteControl(); } catch (err) {};
347
379
  return new InterfaceServerResponse(200, 'Success');
348
380
  }
349
381
  catch (err) {
@@ -351,9 +383,8 @@ export class NixiePumpRS485 extends NixiePump {
351
383
  return Promise.reject(err);
352
384
  }
353
385
  finally { this.suspendPolling = false; }
354
-
355
386
  };
356
- protected async setDriveStateAsync(pstate: PumpState, running: boolean = true) {
387
+ protected async setDriveStateAsync(running: boolean = true) {
357
388
  return new Promise<void>((resolve, reject) => {
358
389
  let out = Outbound.create({
359
390
  protocol: Protocol.Pump,
@@ -373,7 +404,7 @@ export class NixiePumpRS485 extends NixiePump {
373
404
  conn.queueSendMessage(out);
374
405
  });
375
406
  };
376
- protected async requestPumpStatus(pstate: PumpState) {
407
+ protected async requestPumpStatus() {
377
408
  return new Promise<void>((resolve, reject) => {
378
409
  let out = Outbound.create({
379
410
  protocol: Protocol.Pump,
@@ -393,7 +424,7 @@ export class NixiePumpRS485 extends NixiePump {
393
424
  conn.queueSendMessage(out);
394
425
  })
395
426
  };
396
- protected setPumpToRemoteControl(spump: PumpState, running: boolean = true) {
427
+ protected setPumpToRemoteControl(running: boolean = true) {
397
428
  return new Promise<void>((resolve, reject) => {
398
429
  let out = Outbound.create({
399
430
  protocol: Protocol.Pump,
@@ -414,13 +445,15 @@ export class NixiePumpRS485 extends NixiePump {
414
445
  conn.queueSendMessage(out);
415
446
  });
416
447
  }
417
- protected setPumpManual(pstate: PumpState) {
448
+ protected setPumpFeature(feature?: number) {
449
+ // empty payload (possibly 0?, too) is no feature
450
+ // 6: Feature 1
418
451
  return new Promise<void>((resolve, reject) => {
419
452
  let out = Outbound.create({
420
453
  protocol: Protocol.Pump,
421
454
  dest: this.pump.address,
422
455
  action: 5,
423
- payload: [],
456
+ payload: typeof feature === 'undefined' ? [] : [ feature ],
424
457
  retries: 2,
425
458
  repsonse: true,
426
459
  onComplete: (err, msg: Outbound) => {
@@ -434,7 +467,7 @@ export class NixiePumpRS485 extends NixiePump {
434
467
  conn.queueSendMessage(out);
435
468
  });
436
469
  };
437
- protected async setPumpRPMAsync(pstate: PumpState) {
470
+ protected async setPumpRPMAsync() {
438
471
  return new Promise<void>((resolve, reject) => {
439
472
  let out = Outbound.create({
440
473
  protocol: Protocol.Pump,
@@ -455,14 +488,14 @@ export class NixiePumpRS485 extends NixiePump {
455
488
  conn.queueSendMessage(out);
456
489
  });
457
490
  };
458
- protected async setPumpGPMAsync(pstate: PumpState) {
491
+ protected async setPumpGPMAsync() {
459
492
  // packet for vf; vsf will override
460
493
  return new Promise<void>((resolve, reject) => {
461
494
  let out = Outbound.create({
462
495
  protocol: Protocol.Pump,
463
496
  dest: this.pump.address,
464
- action: 10,
465
- payload: [1, 4, 2, 228, this._targetSpeed, 0],
497
+ action: 1,
498
+ payload: [2, 228, 0, this._targetSpeed],
466
499
  retries: 1,
467
500
  response: true,
468
501
  onComplete: (err, msg) => {
@@ -484,10 +517,10 @@ export class NixiePumpRS485 extends NixiePump {
484
517
  this._pollTimer = null;
485
518
  let pstate = state.pumps.getItemById(this.pump.id);
486
519
  this._targetSpeed = 0;
487
- try { await this.setDriveStateAsync(pstate, false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
488
- try { await this.setPumpManual(pstate); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
489
- try { await this.setDriveStateAsync(pstate, false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
490
- try { await this.setPumpToRemoteControl(pstate, false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
520
+ try { await this.setDriveStateAsync(false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
521
+ try { await this.setPumpFeature(); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
522
+ try { await this.setDriveStateAsync(false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
523
+ try { await this.setPumpToRemoteControl(false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
491
524
  this.closing = true;
492
525
  // Make sure the polling timer is dead after we have closted this all off. That way we do not
493
526
  // have another process that revives it from the dead.
@@ -500,88 +533,117 @@ export class NixiePumpRS485 extends NixiePump {
500
533
  }
501
534
  }
502
535
  export class NixiePumpVS extends NixiePumpRS485 {
503
- public setTargetSpeed() {
536
+ public setTargetSpeed(pState: PumpState) {
504
537
  let _newSpeed = 0;
505
- let pumpCircuits = this.pump.circuits.get();
506
- for (let i = 0; i < pumpCircuits.length; i++) {
507
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
508
- let pc = pumpCircuits[i];
509
- if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.speed);
538
+ if (!pState.pumpOnDelay) {
539
+ let pumpCircuits = this.pump.circuits.get();
540
+
541
+ for (let i = 0; i < pumpCircuits.length; i++) {
542
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
543
+ let pc = pumpCircuits[i];
544
+ if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.speed);
545
+ }
510
546
  }
511
547
  if (isNaN(_newSpeed)) _newSpeed = 0;
548
+ this._targetSpeed = _newSpeed;
512
549
  if (this._targetSpeed !== 0) Math.min(Math.max(this.pump.minSpeed, this._targetSpeed), this.pump.maxSpeed);
513
550
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} RPM.`);
514
- this._targetSpeed = _newSpeed;
515
551
  }
516
552
  }
517
553
  export class NixiePumpVF extends NixiePumpRS485 {
518
- public setTargetSpeed() {
554
+ public setTargetSpeed(pState: PumpState) {
519
555
  let _newSpeed = 0;
520
- let pumpCircuits = this.pump.circuits.get();
521
- for (let i = 0; i < pumpCircuits.length; i++) {
522
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
523
- let pc = pumpCircuits[i];
524
- if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.flow);
556
+ if (!pState.pumpOnDelay) {
557
+ let pumpCircuits = this.pump.circuits.get();
558
+ for (let i = 0; i < pumpCircuits.length; i++) {
559
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
560
+ let pc = pumpCircuits[i];
561
+ if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.flow);
562
+ }
525
563
  }
526
564
  if (isNaN(_newSpeed)) _newSpeed = 0;
565
+ this._targetSpeed = _newSpeed;
527
566
  if (this._targetSpeed !== 0) Math.min(Math.max(this.pump.minFlow, this._targetSpeed), this.pump.maxFlow);
528
567
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} GPM.`);
529
- this._targetSpeed = _newSpeed;
530
568
  }
531
569
  }
532
570
  export class NixiePumpVSF extends NixiePumpRS485 {
533
- public setTargetSpeed() {
571
+ public setTargetSpeed(pState: PumpState) {
534
572
  let _newSpeed = 0;
535
- let pumpCircuits = this.pump.circuits.get();
536
- let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
537
- // VSF pumps present a problem. In fact they do not currently operate properly on Touch panels. On touch these need to either be all in RPM or GPM
538
- // if there is a mix in the circuit array then they will not work. In IntelliCenter if there is an RPM setting in the mix it will use RPM by converting
539
- // the GPM to RPM but if there is none then it will use GPM.
540
573
  let maxRPM = 0;
541
574
  let maxGPM = 0;
542
575
  let flows = 0;
543
576
  let speeds = 0;
544
- let toRPM = (flowRate: number, minSpeed: number = 450, maxSpeed: number = 3450) => {
545
- let eff = .03317 * maxSpeed;
546
- let rpm = Math.min((flowRate * maxSpeed) / eff, maxSpeed);
547
- return rpm > 0 ? Math.max(rpm, minSpeed) : 0;
548
- };
549
- let toGPM = (speed: number, maxSpeed: number = 3450, minFlow: number = 15, maxFlow: number = 140) => {
550
- let eff = .03317 * maxSpeed;
551
- let gpm = Math.min((eff * speed) / maxSpeed, maxFlow);
552
- return gpm > 0 ? Math.max(gpm, minFlow) : 0;
553
- }
554
- for (let i = 0; i < pumpCircuits.length; i++) {
555
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
556
- let pc = pumpCircuits[i];
557
- if (circ.isOn) {
558
- if (pc.units > 0) {
559
- maxGPM = Math.max(maxGPM, pc.flow);
560
- // Calculate an RPM from this flow.
561
- maxRPM = Math.max(maxGPM, toRPM(pc.flow, pt.minSpeed, pt.maxSpeed));
562
- flows++;
563
- }
564
- else {
565
- maxRPM = Math.max(maxRPM, pc.speed);
566
- maxGPM = Math.max(maxGPM, toGPM(pc.speed, pt.maxSpeed, pt.minFlow, pt.maxFlow));
567
- speeds++;
577
+ if (!pState.pumpOnDelay) {
578
+ let pumpCircuits = this.pump.circuits.get();
579
+ let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
580
+ // VSF pumps present a problem. In fact they do not currently operate properly on Touch panels. On touch these need to either be all in RPM or GPM
581
+ // if there is a mix in the circuit array then they will not work. In IntelliCenter if there is an RPM setting in the mix it will use RPM by converting
582
+ // the GPM to RPM but if there is none then it will use GPM.
583
+ let toRPM = (flowRate: number, minSpeed: number = 450, maxSpeed: number = 3450) => {
584
+ let eff = .03317 * maxSpeed;
585
+ let rpm = Math.min((flowRate * maxSpeed) / eff, maxSpeed);
586
+ return rpm > 0 ? Math.max(rpm, minSpeed) : 0;
587
+ };
588
+ let toGPM = (speed: number, maxSpeed: number = 3450, minFlow: number = 15, maxFlow: number = 140) => {
589
+ let eff = .03317 * maxSpeed;
590
+ let gpm = Math.min((eff * speed) / maxSpeed, maxFlow);
591
+ return gpm > 0 ? Math.max(gpm, minFlow) : 0;
592
+ }
593
+ for (let i = 0; i < pumpCircuits.length; i++) {
594
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
595
+ let pc = pumpCircuits[i];
596
+ if (circ.isOn) {
597
+ if (pc.units > 0) {
598
+ maxGPM = Math.max(maxGPM, pc.flow);
599
+ // Calculate an RPM from this flow.
600
+ maxRPM = Math.max(maxGPM, toRPM(pc.flow, pt.minSpeed, pt.maxSpeed));
601
+ flows++;
602
+ }
603
+ else {
604
+ maxRPM = Math.max(maxRPM, pc.speed);
605
+ maxGPM = Math.max(maxGPM, toGPM(pc.speed, pt.maxSpeed, pt.minFlow, pt.maxFlow));
606
+ speeds++;
607
+ }
568
608
  }
569
609
  }
610
+ _newSpeed = speeds > 0 || flows === 0 ? maxRPM : maxGPM;
570
611
  }
571
- _newSpeed = speeds > 0 || flows === 0 ? maxRPM : maxGPM;
572
612
  if (isNaN(_newSpeed)) _newSpeed = 0;
573
613
  // Send the flow message if it is flow and the rpm message if it is rpm.
574
614
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} ${flows > 0 ? 'GPM' : 'RPM'}.`);
575
615
  this._targetSpeed = _newSpeed;
576
616
  }
577
- protected async setPumpGPMAsync(pstate: PumpState) {
578
- // vsf payload; different from vf payload
617
+ protected async setPumpRPMAsync() {
618
+ // vsf action is 10 for rpm
579
619
  return new Promise<void>((resolve, reject) => {
580
620
  let out = Outbound.create({
581
621
  protocol: Protocol.Pump,
582
622
  dest: this.pump.address,
583
623
  action: 10,
584
- payload: [1, 4, 2, 196, this._targetSpeed, 0],
624
+ payload: [2, 196, Math.floor(this._targetSpeed / 256), this._targetSpeed % 256],
625
+ retries: 1,
626
+ // timeout: 250,
627
+ response: true,
628
+ onComplete: (err, msg) => {
629
+ if (err) {
630
+ logger.error(`Error sending setPumpRPMAsync for ${this.pump.name}: ${err.message}`);
631
+ reject(err);
632
+ }
633
+ else resolve();
634
+ }
635
+ });
636
+ conn.queueSendMessage(out);
637
+ });
638
+ };
639
+ protected async setPumpGPMAsync() {
640
+ // vsf payload; different from vf payload
641
+ return new Promise<void>((resolve, reject) => {
642
+ let out = Outbound.create({
643
+ protocol: Protocol.Pump,
644
+ dest: this.pump.address,
645
+ action: 9,
646
+ payload: [2, 196, 0, this._targetSpeed],
585
647
  retries: 1,
586
648
  response: true,
587
649
  onComplete: (err, msg) => {
@@ -141,8 +141,9 @@ export class NixieSchedule extends NixieEquipment {
141
141
  let body = sys.bodies.find(elem => elem.circuit === circuit.id);
142
142
  if (typeof body !== 'undefined') {
143
143
  let heatSource = sys.board.valueMaps.heatSources.transform(this.schedule.heatSource);
144
- if (heatSource !== 'nochange') {
144
+ if (heatSource.name !== 'nochange') {
145
145
  switch (heatSource.name) {
146
+ case 'nochange':
146
147
  case 'dontchange':
147
148
  break;
148
149
  case 'off':
@@ -233,7 +234,7 @@ export class NixieSchedule extends NixieEquipment {
233
234
  }
234
235
  public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
235
236
  }
236
- class NixieScheduleContext {
237
+ class NixieScheduleContext {
237
238
  constructor() {
238
239
 
239
240
  }
@@ -102,7 +102,7 @@ export class NixieValve extends NixieEquipment {
102
102
  try {
103
103
  // Here we go we need to set the valve state.
104
104
  if (vstate.isDiverted !== isDiverted) {
105
- logger.info(`Nixie: Set valve ${vstate.id}-${vstate.name} to ${isDiverted}`);
105
+ logger.verbose(`Nixie: Set valve ${vstate.id}-${vstate.name} to ${isDiverted}`);
106
106
  }
107
107
  if (utils.isNullOrEmpty(this.valve.connectionId) || utils.isNullOrEmpty(this.valve.deviceBinding)) {
108
108
  vstate.isDiverted = isDiverted;
@@ -17,7 +17,18 @@
17
17
  "autoOpen": false,
18
18
  "lock": false
19
19
  }
20
+ },
21
+ "backups": {
22
+ "automatic": false,
23
+ "interval": {
24
+ "days": 30,
25
+ "hours": 0
26
+ },
27
+ "keepCount": 5,
28
+ "njsPC": true,
29
+ "servers": []
20
30
  }
31
+
21
32
  },
22
33
  "web": {
23
34
  "servers": {
@@ -52,6 +63,7 @@
52
63
  "interfaces": {
53
64
  "smartThings": {
54
65
  "name": "SmartThings",
66
+ "type": "rest",
55
67
  "enabled": false,
56
68
  "fileName": "smartThings-Hubitat.json",
57
69
  "globals": {},
@@ -62,6 +74,7 @@
62
74
  },
63
75
  "hubitat": {
64
76
  "name": "Hubitat",
77
+ "type": "rest",
65
78
  "enabled": false,
66
79
  "fileName": "smartThings-Hubitat.json",
67
80
  "globals": {},
@@ -72,6 +85,7 @@
72
85
  },
73
86
  "vera": {
74
87
  "name": "Vera",
88
+ "type": "rest",
75
89
  "enabled": false,
76
90
  "fileName": "vera.json",
77
91
  "vars": {
@@ -83,6 +97,7 @@
83
97
  }
84
98
  },
85
99
  "valveRelay": {
100
+ "type": "rest",
86
101
  "name": "Valve Relays",
87
102
  "enabled": false,
88
103
  "fileName": "valveRelays.json",
@@ -100,15 +115,31 @@
100
115
  "enabled": false,
101
116
  "fileName": "influxDB.json",
102
117
  "options": {
118
+ "version": 1,
103
119
  "protocol": "http",
104
120
  "host": "192.168.0.1",
105
- "port": 32770,
121
+ "port": 9999,
106
122
  "username": "",
107
123
  "password": "",
108
124
  "database": "pool",
109
125
  "retentionPolicy": "autogen"
110
126
  }
111
127
  },
128
+ "influxDBv2": {
129
+ "name": "InfluxDBv2",
130
+ "type": "influx",
131
+ "enabled": false,
132
+ "fileName": "influxDB.json",
133
+ "options": {
134
+ "version": 2,
135
+ "protocol": "http",
136
+ "host": "192.168.0.1",
137
+ "port": 9999,
138
+ "token": "...LuyM84JJx93Qvc7tfaXPbI_mFFjRBjaA==",
139
+ "org": "example-org",
140
+ "bucket": "57ec4eed2d90a50b"
141
+ }
142
+ },
112
143
  "mqtt": {
113
144
  "name": "MQTT",
114
145
  "type": "mqtt",
package/issue_template.md CHANGED
@@ -33,7 +33,7 @@ Follow the instructions to complete a [packet capture](https://github.com/tagyou
33
33
  - Pump(s) manufacturer and model: [e.g. IntelliFlow 2 VST 011056]
34
34
  - Chlorinator: [e.g. iChlor, IntelliChlor-40]
35
35
  - Heater(s): [e.g. gas, solar, heatpump, ultratemp]
36
- - Chemical controller: [e.g. IntelliChem, homegrown]
36
+ - Chemical controller: [e.g. IntelliChem, Relay Equipment Manager (REM)]
37
37
  - Valves: [e.g. Intellivalve]
38
38
  - Any other relevant equipment:
39
39
 
@@ -29,8 +29,10 @@ export class DataLogger {
29
29
  }
30
30
  let arr: T[] = [];
31
31
  for (let i = 0; i < lines.length; i++) {
32
- let entry = DataLogger.createEntry<T>(type, lines[i]);
33
- arr.push(entry);
32
+ try {
33
+ let entry = DataLogger.createEntry<T>(type, lines[i]);
34
+ arr.push(entry);
35
+ } catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
34
36
  }
35
37
  return arr;
36
38
  } catch (err) { logger.error(err); }
@@ -47,6 +49,7 @@ export class DataLogger {
47
49
  let newLines = ['\r', '\n'];
48
50
  let arr: T[] = [];
49
51
  if (fs.existsSync(logPath)) {
52
+ console.log(`Reading logfile ${logPath}`);
50
53
  // Alright what we have created here is a method to read the data from the end of
51
54
  // a log file in reverse order (tail) that works for all os implementations. It is
52
55
  // really dumb that this isn't part of the actual file processing.
@@ -81,15 +84,16 @@ export class DataLogger {
81
84
  // record then we shoud save off the line and read the next record.
82
85
  if (newLines.includes(char) || pos === 0) {
83
86
  if (chars.length > 0) {
84
- let entry = DataLogger.createEntry<T>(type, chars.join(''));
85
- if (typeof fn === 'function') {
86
- let rc = fn(arr.length + 1, entry, arr);
87
- console.log(rc);
88
- if (rc === true) arr.push(entry);
89
- else if (rc === false) break;
90
- }
91
- else
92
- arr.push(entry);
87
+ try {
88
+ let entry = DataLogger.createEntry<T>(type, chars.join(''));
89
+ if (typeof fn === 'function') {
90
+ let rc = fn(arr.length + 1, entry, arr);
91
+ if (rc === true) arr.push(entry);
92
+ else if (rc === false) break;
93
+ }
94
+ else
95
+ arr.push(entry);
96
+ } catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
93
97
  }
94
98
  chars = [];
95
99
  }
@@ -142,14 +146,16 @@ export class DataLogger {
142
146
  // record then we shoud save off the line and read the next record.
143
147
  if (newLines.includes(char) || pos === 0) {
144
148
  if (chars.length > 0) {
145
- let entry = DataLogger.createEntry<T>(type, chars.join(''));
146
- if (typeof fn === 'function') {
147
- let rc = fn(arr.length + 1, entry, arr);
148
- if (rc === true) arr.push(entry);
149
- else if (rc === false) break;
150
- }
151
- else
152
- arr.push(entry);
149
+ try {
150
+ let entry = DataLogger.createEntry<T>(type, chars.join(''));
151
+ if (typeof fn === 'function') {
152
+ let rc = fn(arr.length + 1, entry, arr);
153
+ if (rc === true) arr.push(entry);
154
+ else if (rc === false) break;
155
+ }
156
+ else
157
+ arr.push(entry);
158
+ } catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
153
159
  }
154
160
  chars = [];
155
161
  }
@@ -276,7 +282,6 @@ export class DataLogger {
276
282
  let entry = DataLogger.createEntry<T>(type, chars.join(''));
277
283
  if (typeof fn === 'function') {
278
284
  let rc = fn(arr.length + 1, entry, arr);
279
- console.log(rc);
280
285
  if (rc === true) arr.push(entry);
281
286
  else if (rc === false) break;
282
287
  }
@@ -399,11 +404,21 @@ export class DataLoggerEntry {
399
404
  // Parse the data from the log entry if it exists.
400
405
  if (typeof entry === 'object') entry = JSON.stringify(entry);
401
406
  if (typeof entry === 'string') this.parse(entry);
407
+ else {
408
+ //console.log(`A DATALOGGER ENTRY DOES NOT HAVE A PROPER TYPE ${typeof entry} *************************************`);
409
+ //console.log(entry);
410
+ }
402
411
  }
403
- public createInstance(entry?: string) { return new DataLoggerEntry(entry); }
412
+ public static createInstance(entry?: string) { return new DataLoggerEntry(entry); }
404
413
  public parse(entry: string) {
405
414
  let obj = typeof entry !== 'undefined' ? JSON.parse(entry, this.dateParser) : {};
406
- extend(true, this, obj);
415
+ if (typeof entry === 'undefined') {
416
+ console.log(`A DATALOGGER ENTRY WAS NOT DEFINED *************************`);
417
+ }
418
+ else if (entry === '') {
419
+ console.log(`THE INCOMING DATALOGGER ENTRY WAS EMPTY ***************************`)
420
+ }
421
+ let o = extend(true, this, obj);
407
422
  }
408
423
  protected dateParser(key, value) {
409
424
  if (typeof value === 'string') {