nodejs-poolcontroller 7.5.1 → 7.7.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 (64) hide show
  1. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
  2. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
  3. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. package/Changelog +19 -0
  6. package/Dockerfile +3 -3
  7. package/README.md +13 -8
  8. package/app.ts +1 -1
  9. package/config/Config.ts +38 -2
  10. package/config/VersionCheck.ts +27 -12
  11. package/controller/Constants.ts +2 -1
  12. package/controller/Equipment.ts +193 -9
  13. package/controller/Errors.ts +10 -0
  14. package/controller/Lockouts.ts +503 -0
  15. package/controller/State.ts +269 -64
  16. package/controller/boards/AquaLinkBoard.ts +1000 -0
  17. package/controller/boards/BoardFactory.ts +4 -0
  18. package/controller/boards/EasyTouchBoard.ts +468 -144
  19. package/controller/boards/IntelliCenterBoard.ts +466 -307
  20. package/controller/boards/IntelliTouchBoard.ts +37 -5
  21. package/controller/boards/NixieBoard.ts +671 -141
  22. package/controller/boards/SystemBoard.ts +1397 -641
  23. package/controller/comms/Comms.ts +462 -362
  24. package/controller/comms/messages/Messages.ts +174 -30
  25. package/controller/comms/messages/config/ChlorinatorMessage.ts +6 -3
  26. package/controller/comms/messages/config/CircuitMessage.ts +1 -0
  27. package/controller/comms/messages/config/ExternalMessage.ts +10 -8
  28. package/controller/comms/messages/config/HeaterMessage.ts +141 -29
  29. package/controller/comms/messages/config/OptionsMessage.ts +9 -2
  30. package/controller/comms/messages/config/PumpMessage.ts +53 -35
  31. package/controller/comms/messages/config/ScheduleMessage.ts +33 -25
  32. package/controller/comms/messages/config/ValveMessage.ts +2 -2
  33. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +38 -86
  34. package/controller/comms/messages/status/EquipmentStateMessage.ts +59 -23
  35. package/controller/comms/messages/status/HeaterStateMessage.ts +57 -3
  36. package/controller/comms/messages/status/IntelliChemStateMessage.ts +56 -8
  37. package/controller/comms/messages/status/PumpStateMessage.ts +23 -1
  38. package/controller/nixie/Nixie.ts +1 -1
  39. package/controller/nixie/bodies/Body.ts +3 -0
  40. package/controller/nixie/chemistry/ChemController.ts +164 -51
  41. package/controller/nixie/chemistry/Chlorinator.ts +137 -88
  42. package/controller/nixie/circuits/Circuit.ts +51 -19
  43. package/controller/nixie/heaters/Heater.ts +241 -31
  44. package/controller/nixie/pumps/Pump.ts +488 -206
  45. package/controller/nixie/schedules/Schedule.ts +91 -35
  46. package/controller/nixie/valves/Valve.ts +1 -1
  47. package/defaultConfig.json +20 -0
  48. package/package.json +21 -21
  49. package/web/Server.ts +94 -49
  50. package/web/bindings/aqualinkD.json +505 -0
  51. package/web/bindings/influxDB.json +71 -1
  52. package/web/bindings/mqtt.json +98 -39
  53. package/web/bindings/mqttAlt.json +59 -1
  54. package/web/interfaces/baseInterface.ts +1 -0
  55. package/web/interfaces/httpInterface.ts +23 -2
  56. package/web/interfaces/influxInterface.ts +45 -10
  57. package/web/interfaces/mqttInterface.ts +114 -54
  58. package/web/services/config/Config.ts +55 -132
  59. package/web/services/state/State.ts +81 -4
  60. package/web/services/state/StateSocket.ts +4 -4
  61. package/web/services/utilities/Utilities.ts +8 -6
  62. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
  63. package/config copy.json +0 -300
  64. package/issue_template.md +0 -52
@@ -8,7 +8,7 @@ import { CircuitState, PumpState, state, } from "../../State";
8
8
  import { setTimeout, clearTimeout } from 'timers';
9
9
  import { NixieControlPanel } from '../Nixie';
10
10
  import { webApp, InterfaceServerResponse } from "../../../web/Server";
11
- import { Outbound, Protocol } from '../../comms/messages/Messages';
11
+ import { Outbound, Protocol, Response } from '../../comms/messages/Messages';
12
12
  import { conn } from '../../comms/Comms';
13
13
 
14
14
  export class NixiePumpCollection extends NixieEquipmentCollection<NixiePump> {
@@ -117,6 +117,10 @@ export class NixiePumpCollection extends NixieEquipmentCollection<NixiePump> {
117
117
  return new NixiePumpSF(this.controlPanel, pump);
118
118
  case 'vs':
119
119
  return new NixiePumpVS(this.controlPanel, pump);
120
+ case 'hwvs':
121
+ return new NixiePumpHWVS(this.controlPanel, pump);
122
+ case 'hwrly':
123
+ return new NixiePumpHWRLY(this.controlPanel, pump);
120
124
  default:
121
125
  throw new EquipmentNotFoundError(`NCP: Cannot create pump ${pump.name}.`, type);
122
126
  }
@@ -180,13 +184,34 @@ export class NixiePump extends NixieEquipment {
180
184
  if (typeof type.maxCircuits !== 'undefined' && type.maxCircuits > 0 && typeof data.circuits !== 'undefined') { // This pump type supports circuits
181
185
  for (let i = 1; i <= data.circuits.length && i <= type.maxCircuits; i++) {
182
186
  let c = data.circuits[i - 1];
187
+ let circuit = parseInt(c.circuit, 10);
188
+ let cd = this.pump.circuits.find(elem => elem.circuit === circuit);
183
189
  let speed = parseInt(c.speed, 10);
184
190
  let relay = parseInt(c.relay, 10);
185
191
  let flow = parseInt(c.flow, 10);
192
+ let units = typeof c.units !== 'undefined' ? sys.board.valueMaps.pumpUnits.encode(c.units) : undefined;
193
+ switch (type.name) {
194
+ case 'vf':
195
+ units = sys.board.valueMaps.pumpUnits.getValue('gpm');
196
+ break;
197
+ case 'hwvs':
198
+ case 'vssvrs':
199
+ case 'vs':
200
+ c.units = sys.board.valueMaps.pumpUnits.getValue('rpm');
201
+ break;
202
+ case 'ss':
203
+ case 'ds':
204
+ case 'sf':
205
+ case 'hwrly':
206
+ c.units = 'undefined';
207
+ break;
208
+ }
209
+ if (isNaN(units)) units = typeof cd !== 'undefined' ? cd.units : sys.board.valueMaps.pumpUnits.getValue('rpm');
186
210
  if (isNaN(speed)) speed = type.minSpeed;
187
211
  if (isNaN(flow)) flow = type.minFlow;
188
212
  if (isNaN(relay)) relay = 1;
189
- c.units = parseInt(c.units, 10) || type.name === 'vf' ? sys.board.valueMaps.pumpUnits.getValue('gpm') : sys.board.valueMaps.pumpUnits.getValue('rpm');
213
+ c.units = units;
214
+ //c.units = parseInt(c.units, 10) || type.name === 'vf' ? sys.board.valueMaps.pumpUnits.getValue('gpm') : sys.board.valueMaps.pumpUnits.getValue('rpm');
190
215
  if (typeof type.minSpeed !== 'undefined' && c.units === sys.board.valueMaps.pumpUnits.getValue('rpm')) {
191
216
  c.speed = speed;
192
217
  }
@@ -215,8 +240,8 @@ export class NixiePump extends NixieEquipment {
215
240
  if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
216
241
  this._pollTimer = null;
217
242
  // let success = false;
218
- this.setTargetSpeed();
219
243
  let pstate = state.pumps.getItemById(this.pump.id);
244
+ this.setTargetSpeed(pstate);
220
245
  await this.setPumpStateAsync(pstate);
221
246
  }
222
247
  catch (err) { logger.error(`Nixie Error running pump sequence - ${err}`); }
@@ -239,7 +264,10 @@ export class NixiePump extends NixieEquipment {
239
264
  this._pollTimer = null;
240
265
  this._targetSpeed = 0;
241
266
  let pstate = state.pumps.getItemById(this.pump.id);
242
- await this.setPumpStateAsync(pstate);
267
+ try {
268
+ await this.setPumpStateAsync(pstate);
269
+ // Since we are closing we need to not reject.
270
+ } catch (err) { logger.error(`Nixie Closing pump closeAsync: ${err.message}`); }
243
271
  // This will make sure the timer is dead and we are completely closed.
244
272
  this.closing = true;
245
273
  if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
@@ -248,19 +276,43 @@ export class NixiePump extends NixieEquipment {
248
276
  catch (err) { logger.error(`Nixie Pump closeAsync: ${err.message}`); return Promise.reject(err); }
249
277
  }
250
278
  public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
251
- protected setTargetSpeed() { };
279
+ protected setTargetSpeed(pstate: PumpState) { };
280
+ protected isBodyOn(bodyCode: number) {
281
+ let assoc = sys.board.valueMaps.pumpBodies.transform(bodyCode);
282
+ switch (assoc.name) {
283
+ case 'body1':
284
+ case 'pool':
285
+ return state.temps.bodies.getItemById(1).isOn;
286
+ case 'body2':
287
+ case 'spa':
288
+ return state.temps.bodies.getItemById(2).isOn;
289
+ case 'body3':
290
+ return state.temps.bodies.getItemById(3).isOn;
291
+ case 'body4':
292
+ return state.temps.bodies.getItemById(4).isOn;
293
+ case 'poolspa':
294
+ if (sys.equipment.shared && sys.equipment.maxBodies >= 2) {
295
+ return state.temps.bodies.getItemById(1).isOn === true || state.temps.bodies.getItemById(2).isOn === true;
296
+ }
297
+ else
298
+ return state.temps.bodies.getItemById(1).isOn;
299
+ }
300
+ return false;
301
+ }
252
302
  }
253
303
  export class NixiePumpSS extends NixiePump {
254
- public setTargetSpeed() {
304
+ public setTargetSpeed(pState: PumpState) {
255
305
  // Turn on ss pumps.
256
306
  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'}.`);
307
+ if (!pState.pumpOnDelay) {
308
+ let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
309
+ if (pt.hasBody) _newSpeed = this.isBodyOn(this.pump.body) ? 1 : 0;
310
+ //console.log(`BODY: ${sys.board.bodies.isBodyOn(this.pump.body)} CODE: ${this.pump.body}`);
311
+ }
312
+ 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
313
  if (isNaN(_newSpeed)) _newSpeed = 0;
261
314
  this._targetSpeed = _newSpeed;
262
315
  }
263
-
264
316
  public async setPumpStateAsync(pstate: PumpState) {
265
317
  let relays: PumpRelay[] = this.pump.relays.get();
266
318
  let relayState = 0;
@@ -301,16 +353,20 @@ export class NixiePumpSS extends NixiePump {
301
353
  }
302
354
  }
303
355
  export class NixiePumpDS extends NixiePumpSS {
304
- public setTargetSpeed() {
356
+ public setTargetSpeed(pState: PumpState) {
305
357
  // 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
358
  // wash it should engage all the relays for all speeds associated with the pump. The pump logic will determine which program is
307
359
  // the one to engage.
308
360
  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);
361
+ if (!pState.pumpOnDelay) {
362
+ let pumpCircuits: PumpCircuit[] = this.pump.circuits.get();
363
+ if (!pState.pumpOnDelay) {
364
+ for (let i = 0; i < pumpCircuits.length; i++) {
365
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
366
+ // relay speeds are bit-shifted 'or' based on 1,2,4,8
367
+ if (circ.isOn) _newSpeed |= (1 << pumpCircuits[i].relay - 1);
368
+ }
369
+ }
314
370
  }
315
371
  if (isNaN(_newSpeed)) _newSpeed = 0;
316
372
  this.logSpeed(_newSpeed);
@@ -327,6 +383,100 @@ export class NixiePumpSF extends NixiePumpDS {
327
383
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} relays to Relay 1: ${_newSpeed & 1 ? 'on' : 'off'}, Relay 2: ${_newSpeed & 2 ? 'on' : 'off'}, Relay 3: ${_newSpeed & 4 ? 'on' : 'off'}, and Relay 4: ${_newSpeed & 8 ? 'on' : 'off'}.`);
328
384
  }
329
385
  }
386
+ export class NixiePumpHWRLY extends NixiePumpDS {
387
+ // This operates as a relay pump with up to 8 speeds. The speeds are defined as follows. The override
388
+ // relay should be defined as being normally closed. When it opens then the pump will turn on to the speed.
389
+ // +-------+---------+---------+---------+---------+
390
+ // + Speed | Relay 1 | Relay 2 | Relay 3 | OVRD |
391
+ // +-------+---------+---------+---------+---------+
392
+ // | OFF | OFF | OFF | OFF | OFF |
393
+ // +-------+---------+---------+---------+---------+
394
+ // | 1 | OFF | OFF | OFF | ON |
395
+ // +-------+---------+---------+---------+---------+
396
+ // | 2 | ON | OFF | OFF | ON |
397
+ // +-------+---------+---------+---------+---------+
398
+ // | 3 | OFF | ON | OFF | ON |
399
+ // +-------+---------+---------+---------+---------+
400
+ // | 4 | ON | ON | OFF | ON |
401
+ // +-------+---------+---------+---------+---------+
402
+ // | 5 | OFF | OFF | ON | ON |
403
+ // +-------+---------+---------+---------+---------+
404
+ // | 6 | ON | OFF | ON | ON |
405
+ // +-------+---------+---------+---------+---------+
406
+ // | 7 | OFF | ON | ON | ON |
407
+ // +-------+---------+---------+---------+---------+
408
+ // | 8 | ON | ON | ON | ON |
409
+ // +-------+---------+---------+---------+---------+
410
+
411
+ public setTargetSpeed(pState: PumpState) {
412
+ let _newSpeed = 0;
413
+ if (!pState.pumpOnDelay) {
414
+ let pumpCircuits = this.pump.circuits.get();
415
+ for (let i = 0; i < pumpCircuits.length; i++) {
416
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
417
+ let pc = pumpCircuits[i];
418
+ if (circ.isOn) {
419
+ _newSpeed = Math.max(_newSpeed, pc.relay);
420
+ }
421
+ }
422
+ }
423
+ if (isNaN(_newSpeed)) _newSpeed = 0;
424
+ this._targetSpeed = _newSpeed;
425
+ if (this._targetSpeed !== 0) Math.min(Math.max(this.pump.minSpeed, this._targetSpeed), this.pump.maxSpeed);
426
+ if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed}.`);
427
+ }
428
+ public async setPumpStateAsync(pstate: PumpState) {
429
+ // Don't poll while we are seting the state.
430
+ this.suspendPolling = true;
431
+ try {
432
+ let relays: PumpRelay[] = this.pump.relays.get();
433
+ let relayState = 0;
434
+ let targetState = 0;
435
+ for (let i = 0; i < relays.length; i++) {
436
+ let pr = relays[i];
437
+ if (typeof pr.id === 'undefined') pr.id = i + 1; // remove when id is added to dP relays upon save.
438
+ // If we are turning on the pump relay #4 needs to be on. NOTE: It is expected that the OVRD relay is hooked up in a normally closed
439
+ // configuration so that whenever the pump is off the relay terminals are closed.
440
+ let isOn = this._targetSpeed > 0 ? i === 3 ? true : (this._targetSpeed - 1 & (1 << i)) > 0 : false;
441
+ let bit = isOn ? (1 << i) : 0;
442
+ targetState |= bit;
443
+ if (utils.isNullOrEmpty(pr.connectionId) || utils.isNullOrEmpty(pr.deviceBinding)) {
444
+ // Determine whether the relay should be on.
445
+ relayState |= bit;
446
+ }
447
+ else {
448
+ try {
449
+ let res = await NixieEquipment.putDeviceService(pr.connectionId, `/state/device/${pr.deviceBinding}`, { isOn, latch: isOn ? 5000 : undefined });
450
+ if (res.status.code === 200) {
451
+ relayState |= bit;
452
+ }
453
+ else pstate.status = 16;
454
+ }
455
+ catch (err) {
456
+ logger.error(`NCP: Error setting pump ${this.pump.name} relay ${pr.id} to ${isOn ? 'on' : 'off'}. Error ${err.message}}`);
457
+ pstate.status = 16;
458
+ }
459
+ }
460
+ }
461
+ pstate.command = this._targetSpeed;
462
+ if (targetState === relayState) {
463
+ pstate.status = relayState > 0 ? 1 : 0;
464
+ pstate.driveState = relayState > 0 ? 2 : 0;
465
+ pstate.relay = relayState;
466
+ }
467
+ else {
468
+ pstate.driveState = 0;
469
+ }
470
+ return new InterfaceServerResponse(200, 'Success');
471
+ }
472
+ catch (err) {
473
+ logger.error(`Error running pump sequence for ${this.pump.name}: ${err.message}`);
474
+ return Promise.reject(err);
475
+ }
476
+ finally { this.suspendPolling = false; }
477
+ };
478
+
479
+ }
330
480
  export class NixiePumpRS485 extends NixiePump {
331
481
  public async setPumpStateAsync(pstate: PumpState) {
332
482
  // Don't poll while we are seting the state.
@@ -336,15 +486,16 @@ export class NixiePumpRS485 extends NixiePump {
336
486
  // Since these process are async the closing flag can be set
337
487
  // between calls. We need to check it in between each call.
338
488
  try { if (!this.closing) await this.setDriveStateAsync(); } catch (err) {}
339
- try { if (!this.closing) {
340
- if (this._targetSpeed >= pt.minFlow && this._targetSpeed <= pt.maxFlow) await this.setPumpGPMAsync();
341
- else if (this._targetSpeed >= pt.minSpeed && this._targetSpeed <= pt.maxSpeed) await this.setPumpRPMAsync();
342
- } } catch (err) {}
343
-
344
- try { if(!this.closing) await this.setPumpFeature(6); } catch (err) {};
345
- try { if(!this.closing) await utils.sleep(1000); } catch (err) {};
346
- try { if(!this.closing) await this.requestPumpStatus(); } catch (err) {};
347
- try { if(!this.closing) await this.setPumpToRemoteControl(); } catch (err) {};
489
+ try {
490
+ if (!this.closing) {
491
+ if (this._targetSpeed >= pt.minFlow && this._targetSpeed <= pt.maxFlow) await this.setPumpGPMAsync();
492
+ else if (this._targetSpeed >= pt.minSpeed && this._targetSpeed <= pt.maxSpeed) await this.setPumpRPMAsync();
493
+ }
494
+ } catch (err) { };
495
+ try { if (!this.closing && pt.name !== 'vsf') await this.setPumpFeature(6); } catch (err) { };
496
+ try { if(!this.closing) await utils.sleep(1000); } catch (err) { };
497
+ try { if (!this.closing) await this.requestPumpStatus(); } catch (err) { };
498
+ try { if (!this.closing) await this.setPumpToRemoteControl(); } catch (err) { };
348
499
  return new InterfaceServerResponse(200, 'Success');
349
500
  }
350
501
  catch (err) {
@@ -352,132 +503,160 @@ export class NixiePumpRS485 extends NixiePump {
352
503
  return Promise.reject(err);
353
504
  }
354
505
  finally { this.suspendPolling = false; }
355
-
356
506
  };
357
507
  protected async setDriveStateAsync(running: boolean = true) {
358
508
  return new Promise<void>((resolve, reject) => {
359
- let out = Outbound.create({
360
- protocol: Protocol.Pump,
361
- dest: this.pump.address,
362
- action: 6,
363
- payload: running && this._targetSpeed > 0 ? [10] : [4],
364
- retries: 1,
365
- response: true,
366
- onComplete: (err, msg: Outbound) => {
367
- if (err) {
368
- logger.error(`Error sending setDriveState for ${this.pump.name} : ${err.message}`);
369
- reject(err);
509
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
510
+ let out = Outbound.create({
511
+ portId: this.pump.portId || 0,
512
+ protocol: Protocol.Pump,
513
+ dest: this.pump.address,
514
+ action: 6,
515
+ payload: running && this._targetSpeed > 0 ? [10] : [4],
516
+ retries: 1,
517
+ response: true,
518
+ onComplete: (err, msg: Outbound) => {
519
+ if (err) {
520
+ logger.error(`Error sending setDriveState for ${this.pump.name} : ${err.message}`);
521
+ reject(err);
522
+ }
523
+ else resolve();
370
524
  }
371
- else resolve();
372
- }
373
- });
374
- conn.queueSendMessage(out);
525
+ });
526
+ conn.queueSendMessage(out);
527
+ }
528
+ else {
529
+ let pstate = state.pumps.getItemById(this.pump.id);
530
+ pstate.command = pstate.rpm > 0 || pstate.flow > 0 ? 10 : 0;
531
+ resolve();
532
+ }
375
533
  });
376
534
  };
377
535
  protected async requestPumpStatus() {
378
- return new Promise<void>((resolve, reject) => {
379
- let out = Outbound.create({
380
- protocol: Protocol.Pump,
381
- dest: this.pump.address,
382
- action: 7,
383
- payload: [],
384
- retries: 2,
385
- response: true,
386
- onComplete: (err, msg) => {
387
- if (err) {
388
- logger.error(`Error sending requestPumpStatus for ${this.pump.name}: ${err.message}`);
389
- reject(err);
536
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
537
+ return new Promise<void>((resolve, reject) => {
538
+ let out = Outbound.create({
539
+ portId: this.pump.portId || 0,
540
+ protocol: Protocol.Pump,
541
+ dest: this.pump.address,
542
+ action: 7,
543
+ payload: [],
544
+ retries: 2,
545
+ response: true,
546
+ onComplete: (err, msg) => {
547
+ if (err) {
548
+ logger.error(`Error sending requestPumpStatus for ${this.pump.name}: ${err.message}`);
549
+ reject(err);
550
+ }
551
+ else resolve();
390
552
  }
391
- else resolve();
392
- }
553
+ });
554
+ conn.queueSendMessage(out);
393
555
  });
394
- conn.queueSendMessage(out);
395
- })
556
+ }
396
557
  };
397
558
  protected setPumpToRemoteControl(running: boolean = true) {
398
- return new Promise<void>((resolve, reject) => {
399
- let out = Outbound.create({
400
- protocol: Protocol.Pump,
401
- dest: this.pump.address,
402
- action: 4,
403
- payload: running ? [255] : [0], // when stopAsync is called, pass false to return control to pump panel
404
- // payload: spump.virtualControllerStatus === sys.board.valueMaps.virtualControllerStatus.getValue('running') ? [255] : [0],
405
- retries: 1,
406
- response: true,
407
- onComplete: (err) => {
408
- if (err) {
409
- logger.error(`Error sending setPumpToRemoteControl for ${this.pump.name}: ${err.message}`);
410
- reject(err);
559
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
560
+ return new Promise<void>((resolve, reject) => {
561
+ let out = Outbound.create({
562
+ portId: this.pump.portId || 0,
563
+ protocol: Protocol.Pump,
564
+ dest: this.pump.address,
565
+ action: 4,
566
+ payload: running ? [255] : [0], // when stopAsync is called, pass false to return control to pump panel
567
+ // payload: spump.virtualControllerStatus === sys.board.valueMaps.virtualControllerStatus.getValue('running') ? [255] : [0],
568
+ retries: 1,
569
+ response: true,
570
+ onComplete: (err) => {
571
+ if (err) {
572
+ logger.error(`Error sending setPumpToRemoteControl for ${this.pump.name}: ${err.message}`);
573
+ reject(err);
574
+ }
575
+ else resolve();
411
576
  }
412
- else resolve();
413
- }
577
+ });
578
+ conn.queueSendMessage(out);
414
579
  });
415
- conn.queueSendMessage(out);
416
- });
580
+ }
417
581
  }
418
582
  protected setPumpFeature(feature?: number) {
419
- // empty payload (possibly 0?, too) is no feature
420
- // 6: Feature 1
421
- return new Promise<void>((resolve, reject) => {
422
- let out = Outbound.create({
423
- protocol: Protocol.Pump,
424
- dest: this.pump.address,
425
- action: 5,
426
- payload: typeof feature === 'undefined' ? [] : [ feature ],
427
- retries: 2,
428
- repsonse: true,
429
- onComplete: (err, msg: Outbound) => {
430
- if (err) {
431
- logger.error(`Error sending setPumpManual for ${this.pump.name}: ${err.message}`);
432
- reject(err);
583
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
584
+ // empty payload (possibly 0?, too) is no feature
585
+ // 6: Feature 1
586
+ return new Promise<void>((resolve, reject) => {
587
+ let out = Outbound.create({
588
+ portId: this.pump.portId || 0,
589
+ protocol: Protocol.Pump,
590
+ dest: this.pump.address,
591
+ action: 5,
592
+ payload: typeof feature === 'undefined' ? [] : [feature],
593
+ retries: 2,
594
+ response: true,
595
+ onComplete: (err, msg: Outbound) => {
596
+ if (err) {
597
+ logger.error(`Error sending setPumpManual for ${this.pump.name}: ${err.message}`);
598
+ reject(err);
599
+ }
600
+ else resolve();
433
601
  }
434
- else resolve();
435
- }
602
+ });
603
+ conn.queueSendMessage(out);
436
604
  });
437
- conn.queueSendMessage(out);
438
- });
605
+ }
606
+ else {
607
+
608
+ }
439
609
  };
440
610
  protected async setPumpRPMAsync() {
441
- return new Promise<void>((resolve, reject) => {
442
- let out = Outbound.create({
443
- protocol: Protocol.Pump,
444
- dest: this.pump.address,
445
- action: 1,
446
- payload: [2, 196, Math.floor(this._targetSpeed / 256), this._targetSpeed % 256],
447
- retries: 1,
448
- // timeout: 250,
449
- response: true,
450
- onComplete: (err, msg) => {
451
- if (err) {
452
- logger.error(`Error sending setPumpRPMAsync for ${this.pump.name}: ${err.message}`);
453
- reject(err);
611
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
612
+ return new Promise<void>((resolve, reject) => {
613
+ let out = Outbound.create({
614
+ portId: this.pump.portId || 0,
615
+ protocol: Protocol.Pump,
616
+ dest: this.pump.address,
617
+ action: 1,
618
+ payload: [2, 196, Math.floor(this._targetSpeed / 256), this._targetSpeed % 256],
619
+ retries: 1,
620
+ // timeout: 250,
621
+ response: true,
622
+ onComplete: (err, msg) => {
623
+ if (err) {
624
+ logger.error(`Error sending setPumpRPMAsync for ${this.pump.name}: ${err.message}`);
625
+ reject(err);
626
+ }
627
+ else resolve();
454
628
  }
455
- else resolve();
456
- }
629
+ });
630
+ conn.queueSendMessage(out);
457
631
  });
458
- conn.queueSendMessage(out);
459
- });
632
+ }
633
+ else {
634
+
635
+ }
460
636
  };
461
637
  protected async setPumpGPMAsync() {
462
- // packet for vf; vsf will override
463
- return new Promise<void>((resolve, reject) => {
464
- let out = Outbound.create({
465
- protocol: Protocol.Pump,
466
- dest: this.pump.address,
467
- action: 1,
468
- payload: [2, 228, 0, this._targetSpeed],
469
- retries: 1,
470
- response: true,
471
- onComplete: (err, msg) => {
472
- if (err) {
473
- logger.error(`Error sending setPumpGPMAsync for ${this.pump.name}: ${err.message}`);
474
- reject(err);
638
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
639
+ // packet for vf; vsf will override
640
+ return new Promise<void>((resolve, reject) => {
641
+ let out = Outbound.create({
642
+ portId: this.pump.portId || 0,
643
+ protocol: Protocol.Pump,
644
+ dest: this.pump.address,
645
+ action: 1,
646
+ payload: [2, 228, 0, this._targetSpeed],
647
+ retries: 1,
648
+ response: true,
649
+ onComplete: (err, msg) => {
650
+ if (err) {
651
+ logger.error(`Error sending setPumpGPMAsync for ${this.pump.name}: ${err.message}`);
652
+ reject(err);
653
+ }
654
+ else resolve();
475
655
  }
476
- else resolve();
477
- }
656
+ });
657
+ conn.queueSendMessage(out);
478
658
  });
479
- conn.queueSendMessage(out);
480
- });
659
+ }
481
660
  };
482
661
  public async closeAsync() {
483
662
  try {
@@ -503,75 +682,82 @@ export class NixiePumpRS485 extends NixiePump {
503
682
  }
504
683
  }
505
684
  export class NixiePumpVS extends NixiePumpRS485 {
506
- public setTargetSpeed() {
685
+ public setTargetSpeed(pState: PumpState) {
507
686
  let _newSpeed = 0;
508
- let pumpCircuits = this.pump.circuits.get();
509
- for (let i = 0; i < pumpCircuits.length; i++) {
510
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
511
- let pc = pumpCircuits[i];
512
- if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.speed);
687
+ if (!pState.pumpOnDelay) {
688
+ let pumpCircuits = this.pump.circuits.get();
689
+
690
+ for (let i = 0; i < pumpCircuits.length; i++) {
691
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
692
+ let pc = pumpCircuits[i];
693
+ if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.speed);
694
+ }
513
695
  }
514
696
  if (isNaN(_newSpeed)) _newSpeed = 0;
697
+ this._targetSpeed = _newSpeed;
515
698
  if (this._targetSpeed !== 0) Math.min(Math.max(this.pump.minSpeed, this._targetSpeed), this.pump.maxSpeed);
516
699
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} RPM.`);
517
- this._targetSpeed = _newSpeed;
518
700
  }
519
701
  }
520
702
  export class NixiePumpVF extends NixiePumpRS485 {
521
- public setTargetSpeed() {
703
+ public setTargetSpeed(pState: PumpState) {
522
704
  let _newSpeed = 0;
523
- let pumpCircuits = this.pump.circuits.get();
524
- for (let i = 0; i < pumpCircuits.length; i++) {
525
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
526
- let pc = pumpCircuits[i];
527
- if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.flow);
705
+ if (!pState.pumpOnDelay) {
706
+ let pumpCircuits = this.pump.circuits.get();
707
+ for (let i = 0; i < pumpCircuits.length; i++) {
708
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
709
+ let pc = pumpCircuits[i];
710
+ if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.flow);
711
+ }
528
712
  }
529
713
  if (isNaN(_newSpeed)) _newSpeed = 0;
714
+ this._targetSpeed = _newSpeed;
530
715
  if (this._targetSpeed !== 0) Math.min(Math.max(this.pump.minFlow, this._targetSpeed), this.pump.maxFlow);
531
716
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} GPM.`);
532
- this._targetSpeed = _newSpeed;
533
717
  }
534
718
  }
535
719
  export class NixiePumpVSF extends NixiePumpRS485 {
536
- public setTargetSpeed() {
720
+ public setTargetSpeed(pState: PumpState) {
537
721
  let _newSpeed = 0;
538
- let pumpCircuits = this.pump.circuits.get();
539
- let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
540
- // 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
541
- // 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
542
- // the GPM to RPM but if there is none then it will use GPM.
543
722
  let maxRPM = 0;
544
723
  let maxGPM = 0;
545
724
  let flows = 0;
546
725
  let speeds = 0;
547
- let toRPM = (flowRate: number, minSpeed: number = 450, maxSpeed: number = 3450) => {
548
- let eff = .03317 * maxSpeed;
549
- let rpm = Math.min((flowRate * maxSpeed) / eff, maxSpeed);
550
- return rpm > 0 ? Math.max(rpm, minSpeed) : 0;
551
- };
552
- let toGPM = (speed: number, maxSpeed: number = 3450, minFlow: number = 15, maxFlow: number = 140) => {
553
- let eff = .03317 * maxSpeed;
554
- let gpm = Math.min((eff * speed) / maxSpeed, maxFlow);
555
- return gpm > 0 ? Math.max(gpm, minFlow) : 0;
556
- }
557
- for (let i = 0; i < pumpCircuits.length; i++) {
558
- let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
559
- let pc = pumpCircuits[i];
560
- if (circ.isOn) {
561
- if (pc.units > 0) {
562
- maxGPM = Math.max(maxGPM, pc.flow);
563
- // Calculate an RPM from this flow.
564
- maxRPM = Math.max(maxGPM, toRPM(pc.flow, pt.minSpeed, pt.maxSpeed));
565
- flows++;
566
- }
567
- else {
568
- maxRPM = Math.max(maxRPM, pc.speed);
569
- maxGPM = Math.max(maxGPM, toGPM(pc.speed, pt.maxSpeed, pt.minFlow, pt.maxFlow));
570
- speeds++;
726
+ if (!pState.pumpOnDelay) {
727
+ let pumpCircuits = this.pump.circuits.get();
728
+ let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
729
+ // 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
730
+ // 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
731
+ // the GPM to RPM but if there is none then it will use GPM.
732
+ let toRPM = (flowRate: number, minSpeed: number = 450, maxSpeed: number = 3450) => {
733
+ let eff = .03317 * maxSpeed;
734
+ let rpm = Math.min((flowRate * maxSpeed) / eff, maxSpeed);
735
+ return rpm > 0 ? Math.max(rpm, minSpeed) : 0;
736
+ };
737
+ let toGPM = (speed: number, maxSpeed: number = 3450, minFlow: number = 15, maxFlow: number = 140) => {
738
+ let eff = .03317 * maxSpeed;
739
+ let gpm = Math.min((eff * speed) / maxSpeed, maxFlow);
740
+ return gpm > 0 ? Math.max(gpm, minFlow) : 0;
741
+ }
742
+ for (let i = 0; i < pumpCircuits.length; i++) {
743
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
744
+ let pc = pumpCircuits[i];
745
+ if (circ.isOn) {
746
+ if (pc.units > 0) {
747
+ maxGPM = Math.max(maxGPM, pc.flow);
748
+ // Calculate an RPM from this flow.
749
+ maxRPM = Math.max(maxGPM, toRPM(pc.flow, pt.minSpeed, pt.maxSpeed));
750
+ flows++;
751
+ }
752
+ else {
753
+ maxRPM = Math.max(maxRPM, pc.speed);
754
+ maxGPM = Math.max(maxGPM, toGPM(pc.speed, pt.maxSpeed, pt.minFlow, pt.maxFlow));
755
+ speeds++;
756
+ }
571
757
  }
572
758
  }
759
+ _newSpeed = speeds > 0 || flows === 0 ? maxRPM : maxGPM;
573
760
  }
574
- _newSpeed = speeds > 0 || flows === 0 ? maxRPM : maxGPM;
575
761
  if (isNaN(_newSpeed)) _newSpeed = 0;
576
762
  // Send the flow message if it is flow and the rpm message if it is rpm.
577
763
  if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} ${flows > 0 ? 'GPM' : 'RPM'}.`);
@@ -579,46 +765,142 @@ export class NixiePumpVSF extends NixiePumpRS485 {
579
765
  }
580
766
  protected async setPumpRPMAsync() {
581
767
  // vsf action is 10 for rpm
582
- return new Promise<void>((resolve, reject) => {
583
- let out = Outbound.create({
584
- protocol: Protocol.Pump,
585
- dest: this.pump.address,
586
- action: 10,
587
- payload: [2, 196, Math.floor(this._targetSpeed / 256), this._targetSpeed % 256],
588
- retries: 1,
589
- // timeout: 250,
590
- response: true,
591
- onComplete: (err, msg) => {
592
- if (err) {
593
- logger.error(`Error sending setPumpRPMAsync for ${this.pump.name}: ${err.message}`);
594
- reject(err);
768
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
769
+ return new Promise<void>((resolve, reject) => {
770
+ let out = Outbound.create({
771
+ portId: this.pump.portId || 0,
772
+ protocol: Protocol.Pump,
773
+ dest: this.pump.address,
774
+ action: 10,
775
+ payload: [2, 196, Math.floor(this._targetSpeed / 256), this._targetSpeed % 256],
776
+ retries: 1,
777
+ // timeout: 250,
778
+ response: true,
779
+ onComplete: (err, msg) => {
780
+ if (err) {
781
+ logger.error(`Error sending setPumpRPMAsync for ${this.pump.name}: ${err.message}`);
782
+ reject(err);
783
+ }
784
+ else resolve();
595
785
  }
596
- else resolve();
597
- }
786
+ });
787
+ conn.queueSendMessage(out);
598
788
  });
599
- conn.queueSendMessage(out);
600
- });
789
+ }
601
790
  };
602
791
  protected async setPumpGPMAsync() {
603
- // vsf payload; different from vf payload
604
- return new Promise<void>((resolve, reject) => {
605
- let out = Outbound.create({
606
- protocol: Protocol.Pump,
607
- dest: this.pump.address,
608
- action: 9,
609
- payload: [2, 196, 0, this._targetSpeed],
610
- retries: 1,
611
- response: true,
612
- onComplete: (err, msg) => {
613
- if (err) {
614
- logger.error(`Error sending setPumpGPMAsync for ${this.pump.name}: ${err.message}`);
615
- reject(err);
792
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
793
+ // vsf payload; different from vf payload
794
+ return new Promise<void>((resolve, reject) => {
795
+ let out = Outbound.create({
796
+ portId: this.pump.portId || 0,
797
+ protocol: Protocol.Pump,
798
+ dest: this.pump.address,
799
+ action: 9,
800
+ payload: [2, 196, 0, this._targetSpeed],
801
+ retries: 1,
802
+ response: true,
803
+ onComplete: (err, msg) => {
804
+ if (err) {
805
+ logger.error(`Error sending setPumpGPMAsync for ${this.pump.name}: ${err.message}`);
806
+ reject(err);
807
+ }
808
+ else resolve();
809
+ return
616
810
  }
617
- else resolve();
618
- return
619
- }
811
+ });
812
+ conn.queueSendMessage(out);
620
813
  });
621
- conn.queueSendMessage(out);
622
- });
814
+ }
623
815
  };
624
816
  };
817
+ export class NixiePumpHWVS extends NixiePumpRS485 {
818
+ public setTargetSpeed(pState: PumpState) {
819
+ let _newSpeed = 0;
820
+ if (!pState.pumpOnDelay) {
821
+ let pumpCircuits = this.pump.circuits.get();
822
+
823
+ for (let i = 0; i < pumpCircuits.length; i++) {
824
+ let circ = state.circuits.getInterfaceById(pumpCircuits[i].circuit);
825
+ let pc = pumpCircuits[i];
826
+ if (circ.isOn) _newSpeed = Math.max(_newSpeed, pc.speed);
827
+ }
828
+ }
829
+ if (isNaN(_newSpeed)) _newSpeed = 0;
830
+ this._targetSpeed = _newSpeed;
831
+ if (this._targetSpeed !== 0) Math.min(Math.max(this.pump.minSpeed, this._targetSpeed), this.pump.maxSpeed);
832
+ if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} RPM.`);
833
+ }
834
+ public async setPumpStateAsync(pstate: PumpState) {
835
+ // Don't poll while we are seting the state.
836
+ this.suspendPolling = true;
837
+ try {
838
+ let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
839
+ // Since these process are async the closing flag can be set
840
+ // between calls. We need to check it in between each call.
841
+ try { if (!this.closing) { await this.setPumpRPMAsync(); } } catch (err) { }
842
+ return new InterfaceServerResponse(200, 'Success');
843
+ }
844
+ catch (err) {
845
+ logger.error(`Error running pump sequence for ${this.pump.name}: ${err.message}`);
846
+ return Promise.reject(err);
847
+ }
848
+ finally { this.suspendPolling = false; }
849
+ };
850
+ protected async requestPumpStatus() { return Promise.resolve(); };
851
+ protected setPumpFeature(feature?: number) { return Promise.resolve(); }
852
+ protected setPumpToRemoteControl(running: boolean = true) {
853
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
854
+ // We do nothing on this pump to set it to remote control. That is unless we are turning it off.
855
+ return new Promise<void>((resolve, reject) => {
856
+ if (!running) {
857
+ let out = Outbound.create({
858
+ portId: this.pump.portId || 0,
859
+ protocol: Protocol.Hayward,
860
+ source: 12, // Use the broadcast address
861
+ dest: this.pump.address,
862
+ action: 1,
863
+ payload: [0], // when stopAsync is called, pass false to return control to pump panel
864
+ // payload: spump.virtualControllerStatus === sys.board.valueMaps.virtualControllerStatus.getValue('running') ? [255] : [0],
865
+ retries: 1,
866
+ response: Response.create({ protocol: Protocol.Hayward, action: 12, source: this.pump.address }),
867
+ onComplete: (err) => {
868
+ if (err) {
869
+ logger.error(`Error sending setPumpToRemoteControl for ${this.pump.name}: ${err.message}`);
870
+ reject(err);
871
+ }
872
+ else resolve();
873
+ }
874
+ });
875
+ conn.queueSendMessage(out);
876
+ }
877
+ else resolve();
878
+ });
879
+ }
880
+ }
881
+ protected async setPumpRPMAsync() {
882
+ if (conn.isPortEnabled(this.pump.portId || 0)) {
883
+ return new Promise<void>((resolve, reject) => {
884
+ let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
885
+ let out = Outbound.create({
886
+ portId: this.pump.portId || 0,
887
+ protocol: Protocol.Hayward,
888
+ source: 12, // Use the broadcast address
889
+ dest: this.pump.address - 96,
890
+ action: 1,
891
+ payload: [Math.min(Math.round((this._targetSpeed / pt.maxSpeed) * 100), 100)], // when stopAsync is called, pass false to return control to pump panel
892
+ retries: 1,
893
+ response: Response.create({ protocol: Protocol.Hayward, action: 12, source: this.pump.address }),
894
+ onComplete: (err) => {
895
+ if (err) {
896
+ logger.error(`Error sending setPumpRPM for ${this.pump.name}: ${err.message}`);
897
+ reject(err);
898
+ }
899
+ else resolve();
900
+ }
901
+ });
902
+ conn.queueSendMessage(out);
903
+ });
904
+ }
905
+ };
906
+ }