nodejs-poolcontroller 8.0.0 → 8.0.2

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 (47) hide show
  1. package/.docker/Dockerfile.armv6 +29 -0
  2. package/.docker/Dockerfile.armv7 +29 -0
  3. package/.docker/Dockerfile.linux +62 -0
  4. package/.docker/Dockerfile.windows +43 -0
  5. package/.docker/docker-compose.yml +47 -0
  6. package/.docker/ecosystem.config.js +35 -0
  7. package/.github/workflows/docker-publish-njsPC-linux.yml +81 -0
  8. package/.github/workflows/docker-publish-njsPC-windows.yml +41 -0
  9. package/Dockerfile +4 -3
  10. package/README.md +4 -1
  11. package/config/Config.ts +1 -1
  12. package/controller/Constants.ts +164 -67
  13. package/controller/Equipment.ts +79 -18
  14. package/controller/Lockouts.ts +15 -0
  15. package/controller/State.ts +280 -7
  16. package/controller/boards/EasyTouchBoard.ts +226 -102
  17. package/controller/boards/IntelliCenterBoard.ts +67 -18
  18. package/controller/boards/IntelliTouchBoard.ts +2 -4
  19. package/controller/boards/NixieBoard.ts +84 -27
  20. package/controller/boards/SunTouchBoard.ts +8 -2
  21. package/controller/boards/SystemBoard.ts +3259 -3242
  22. package/controller/comms/ScreenLogic.ts +60 -57
  23. package/controller/comms/messages/Messages.ts +4 -4
  24. package/controller/comms/messages/config/ChlorinatorMessage.ts +10 -3
  25. package/controller/comms/messages/config/ExternalMessage.ts +4 -1
  26. package/controller/comms/messages/config/PumpMessage.ts +8 -7
  27. package/controller/comms/messages/config/RemoteMessage.ts +48 -43
  28. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -2
  29. package/controller/comms/messages/status/EquipmentStateMessage.ts +9 -4
  30. package/controller/nixie/NixieEquipment.ts +1 -1
  31. package/controller/nixie/bodies/Body.ts +1 -1
  32. package/controller/nixie/chemistry/ChemController.ts +37 -28
  33. package/controller/nixie/circuits/Circuit.ts +36 -0
  34. package/controller/nixie/heaters/Heater.ts +24 -5
  35. package/controller/nixie/pumps/Pump.ts +155 -97
  36. package/controller/nixie/schedules/Schedule.ts +207 -126
  37. package/defaultConfig.json +4 -3
  38. package/logger/DataLogger.ts +7 -7
  39. package/package.json +3 -3
  40. package/sendSocket.js +32 -0
  41. package/web/Server.ts +17 -11
  42. package/web/bindings/homeassistant.json +2 -2
  43. package/web/interfaces/mqttInterface.ts +18 -18
  44. package/web/services/config/Config.ts +34 -1
  45. package/web/services/state/State.ts +10 -3
  46. package/web/services/state/StateSocket.ts +7 -3
  47. package/web/services/utilities/Utilities.ts +3 -3
@@ -68,12 +68,12 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
68
68
  // make sure status is up to date immediately
69
69
  // especially in the case of a re-connect
70
70
  this.bindEvent("controller", state.controllerState);
71
- } catch (err) { logger.error(err); }
71
+ } catch (err) { logger.error(`Error connecting to MQTT Broker ${this.cfg.name} ${err.message}`); }
72
72
  });
73
73
  this.client.on('reconnect', () => {
74
74
  try {
75
75
  logger.info(`Re-connecting to MQTT broker ${this.cfg.name}`);
76
- } catch (err) { logger.error(err); }
76
+ } catch (err) { logger.error(`Error reconnecting to MQTT Brokder ${this.cfg.name} ${err.message}`); }
77
77
 
78
78
  });
79
79
  this.client.on('error', (error) => {
@@ -374,7 +374,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
374
374
  }
375
375
  }
376
376
  catch (err) {
377
- logger.error(err);
377
+ logger.error(`Error binding MQTT event ${evt}: ${err.message}`);
378
378
  }
379
379
  }
380
380
  // This needed to be refactored so we could extract it from an anonymous function. We want to be able to unbind
@@ -415,7 +415,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
415
415
  try {
416
416
  if(typeof isOn !== 'undefined') await sys.board.circuits.setCircuitStateAsync(id, isOn);
417
417
  }
418
- catch (err) { logger.error(err); }
418
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
419
419
  break;
420
420
  }
421
421
  case 'features':
@@ -423,7 +423,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
423
423
  try {
424
424
  if (typeof isOn !== 'undefined') await sys.board.features.setFeatureStateAsync(id, isOn);
425
425
  }
426
- catch (err) { logger.error(err); }
426
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
427
427
  break;
428
428
  }
429
429
  case 'lightgroups':
@@ -431,7 +431,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
431
431
  try {
432
432
  if (typeof isOn !== 'undefined') await sys.board.circuits.setLightGroupStateAsync(id, isOn);
433
433
  }
434
- catch (err) { logger.error(err); }
434
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
435
435
  break;
436
436
  }
437
437
  case 'circuitgroups':
@@ -439,7 +439,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
439
439
  try {
440
440
  if (typeof isOn !== 'undefined') await sys.board.circuits.setCircuitGroupStateAsync(id, isOn);
441
441
  }
442
- catch (err) { logger.error(err); }
442
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
443
443
  break;
444
444
  }
445
445
  default:
@@ -459,14 +459,14 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
459
459
  try {
460
460
  await sys.board.circuits.toggleCircuitStateAsync(id);
461
461
  }
462
- catch (err) { logger.error(err); }
462
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
463
463
  break;
464
464
  case 'features':
465
465
  case 'feature':
466
466
  try {
467
467
  await sys.board.features.toggleFeatureStateAsync(id);
468
468
  }
469
- catch (err) { logger.error(err); }
469
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
470
470
  break;
471
471
  default:
472
472
  logger.warn(`MQTT: Inbound topic ${topics[topics.length - 1]} not matched to event ${topics[topics.length - 2].toLowerCase()}. Message ${msg} `)
@@ -489,7 +489,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
489
489
  }
490
490
  }
491
491
  }
492
- catch (err) { logger.error(err); }
492
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
493
493
  break;
494
494
  case 'coolsetpoint':
495
495
  try {
@@ -506,7 +506,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
506
506
  }
507
507
  }
508
508
  }
509
- } catch (err) { logger.error(err); }
509
+ } catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
510
510
  break;
511
511
  case 'setpoint':
512
512
  try {
@@ -530,7 +530,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
530
530
  }
531
531
  }
532
532
  }
533
- catch (err) { logger.error(err); }
533
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
534
534
  break;
535
535
  case 'heatmode':
536
536
  try {
@@ -553,39 +553,39 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
553
553
  }
554
554
  let tbody = await sys.board.bodies.setHeatModeAsync(body, mode);
555
555
  }
556
- catch (err) { logger.error(err); }
556
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
557
557
  break;
558
558
  case 'chlorinator':
559
559
  try {
560
560
  let schlor = await sys.board.chlorinator.setChlorAsync(msg);
561
561
  }
562
- catch (err) { logger.error(err); }
562
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
563
563
  break;
564
564
  case 'chemcontroller':
565
565
  try {
566
566
  await sys.board.chemControllers.setChemControllerAsync(msg);
567
567
  }
568
- catch (err) { logger.error(err); }
568
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
569
569
  break;
570
570
  case 'settheme':
571
571
  try {
572
572
  let theme = await state.circuits.setLightThemeAsync(parseInt(msg.id, 10), sys.board.valueMaps.lightThemes.encode(msg.theme));
573
573
  }
574
- catch (err) { logger.error(err); }
574
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
575
575
  break;
576
576
  case 'temp':
577
577
  case 'temps':
578
578
  try {
579
579
  await sys.board.system.setTempsAsync(msg);
580
580
  }
581
- catch (err) { logger.error(err); }
581
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
582
582
  break;
583
583
  case 'tempsensor':
584
584
  case 'tempsensors':
585
585
  try {
586
586
  await sys.board.system.setTempSensorsAsync(msg);
587
587
  }
588
- catch (err) { logger.error(err); }
588
+ catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
589
589
  break;
590
590
  default:
591
591
  logger.silly(`MQTT: Inbound MQTT topic not matched: ${topic}: ${message.toString()}`)
@@ -575,6 +575,39 @@ export class ConfigRoute {
575
575
  }
576
576
  catch (err) { next(err); }
577
577
  });
578
+ app.put('/config/pumpCircuit', async (req, res, next) => {
579
+ try {
580
+ let pmpId = parseInt(req.body.pumpId, 10);
581
+ let circId = parseInt(req.body.circuitId, 10);
582
+ let pmp: Pump;
583
+ if (isNaN(pmpId)) {
584
+ let pmpAddress = parseInt(req.body.address, 10);
585
+ if (!isNaN(pmpAddress)) pmp = sys.pumps.find(x => x.address === pmpAddress);
586
+ }
587
+ else
588
+ pmp = sys.pumps.find(x => x.id === pmpId);
589
+ if (typeof pmp === 'undefined') throw new ServiceProcessError(`Pump not found`, '/config/pumpCircuit', 'Set circuit speed');
590
+ let data = pmp.get(true);
591
+ let c = typeof data.circuits !== 'undefined' && typeof data.circuits.find !== 'undefined' ? data.circuits.find(x => x.circuit === circId) : undefined;
592
+ if (typeof c === 'undefined') throw new ServiceProcessError(`Circuit not found`, '/config/pumpCircuit', 'Set circuit speed');
593
+ if (typeof req.body.speed !== 'undefined') {
594
+ let speed = parseInt(req.body.speed, 10);
595
+ if (isNaN(speed)) throw new ServiceProcessError(`Invalid circuit speed supplied`, '/config/pumpCircuit', 'Set circuit speed');
596
+ c.speed = speed;
597
+ }
598
+ else if (typeof req.body.flow !== 'undefined') {
599
+ let flow = parseInt(req.body.flow, 10);
600
+ if (isNaN(flow)) throw new ServiceProcessError(`Invalid circuit flow supplied`, '/config/pumpCircuit', 'Set circuit flow');
601
+ c.flow = flow;
602
+ }
603
+ else {
604
+ throw new ServiceProcessError(`You must supply a target flow or speed`, '/config/pumpCircuit', 'Set circuit flow');
605
+ }
606
+ await sys.board.pumps.setPumpAsync(data);
607
+ return res.status(200).send((pmp).get(true));
608
+ } catch (err) { next(err); }
609
+
610
+ });
578
611
  // RKS: 05-20-22 This is a remnant of the old web ui. It is not called and the setType method needed to go away.
579
612
  //app.delete('/config/pump/:pumpId', async (req, res, next) => {
580
613
  // try {
@@ -984,7 +1017,7 @@ export class ConfigRoute {
984
1017
  } catch (e) {
985
1018
  err = new ServiceProcessError(`Error uploading file: ${e.message}`, 'POST: app/backup/file', 'uploadFile');
986
1019
  next(err);
987
- logger.error(e);
1020
+ logger.error(`Error uploading file ${e.message}`);
988
1021
  }
989
1022
  });
990
1023
  } catch (err) { next(err); }
@@ -415,9 +415,16 @@ export class StateRoute {
415
415
  let val;
416
416
  if (isNaN(mode)) mode = parseInt(req.body.heatMode, 10);
417
417
  if (!isNaN(mode)) val = sys.board.valueMaps.heatModes.transform(mode);
418
- else val = sys.board.valueMaps.heatModes.transformByName(req.body.mode || req.body.heatMode);
419
- if (typeof val.val === 'undefined') {
420
- return next(new ServiceParameterError(`Invalid value for heatMode: ${req.body.mode}`, 'body', 'heatMode', mode));
418
+ else {
419
+ let smode = req.body.mode || req.body.heatMode;
420
+ if (typeof smode === 'string') smode = smode.toLowerCase();
421
+ else {
422
+ return next(new ServiceParameterError(`Invalid mode supplied ${req.body.mode || req.body.heatMode}.`, 'body', 'heatmode', smode));
423
+ }
424
+ val = sys.board.valueMaps.heatModes.transformByName(smode);
425
+ if (typeof val.val === 'undefined') {
426
+ return next(new ServiceParameterError(`Invalid value for heatMode: ${req.body.mode}`, 'body', 'heatMode', mode));
427
+ }
421
428
  }
422
429
  mode = val.val;
423
430
  let body = sys.bodies.findByObject(req.body);
@@ -72,7 +72,7 @@ export class StateSocket {
72
72
  sock.on('/temps', async (data: any) => {
73
73
  try {
74
74
  data = JSON.parse(data);
75
- await sys.board.system.setTempsAsync(data).catch(err => logger.error(err));
75
+ await sys.board.system.setTempsAsync(data).catch(err => logger.error(`setTempsAsync: ${err.message}`));
76
76
  }
77
77
  catch (err) { logger.error(`Socket processing error /temps: ${err.message}`); }
78
78
  });
@@ -193,16 +193,20 @@ export class StateSocket {
193
193
  try {
194
194
  data = JSON.parse(data);
195
195
  let id = parseInt(data.id, 10);
196
- if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
196
+ if (!isNaN(id) && typeof data.toggle !== 'undefined')
197
+ if (utils.makeBool(data.toggle)) await sys.board.circuits.toggleCircuitStateAsync(id);
198
+ else if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
197
199
  await sys.board.circuits.setCircuitStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
198
200
  }
199
201
  }
200
- catch (err) { logger.error(err); }
202
+ catch (err) { logger.error(`Socket /circuit error: ${err.message}`); }
201
203
  });
202
204
  sock.on('/feature', async (data: any) => {
203
205
  try {
204
206
  data = JSON.parse(data);
205
207
  let id = parseInt(data.id, 10);
208
+ if (!isNaN(id) && typeof data.toggle !== 'undefined')
209
+ if (utils.makeBool(data.toggle)) await sys.board.features.toggleFeatureStateAsync(id);
206
210
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
207
211
  await sys.board.features.setFeatureStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
208
212
  }
@@ -103,7 +103,7 @@ export class UtilitiesRoute {
103
103
  options: {
104
104
  protocol: 'mqtt://', host: '', port: 1883, username: '', password: '',
105
105
  selfSignedCertificate: false,
106
- rootTopic: "pool/@bind=(state.equipment.model).replace(' ','-').replace(' / ','').toLowerCase();",
106
+ rootTopic: "pool/@bind=(state.equipment.model).replace(/ /g,'-').replace(' / ','').toLowerCase();",
107
107
  retain: true, qos: 0, changesOnly: true
108
108
  }
109
109
  }
@@ -223,11 +223,11 @@ export class UtilitiesRoute {
223
223
  } catch (e) {
224
224
  err = new ServiceProcessError(`Error uploading file: ${e.message}`, 'POST: app/backup/file', 'uploadFile');
225
225
  next(err);
226
- logger.error(e);
226
+ logger.error(`File upload error: ${e.message}`);
227
227
  }
228
228
  });
229
229
  } catch (err) { next(err); }
230
230
  });
231
231
 
232
232
  }
233
- }
233
+ }