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
@@ -62,12 +62,21 @@ export class ConfigRoute {
62
62
  };
63
63
  return res.status(200).send(opts);
64
64
  });
65
- app.get('/config/options/rs485', (req, res) => {
66
- let opts = {
67
- port: config.getSection('controller.comms', { enabled: false, netConnect: false }),
68
- stats: conn.buffer.counter
69
- };
70
- return res.status(200).send(opts);
65
+ app.get('/config/options/rs485', async (req, res, next) => {
66
+ try {
67
+ let opts = { ports: [], local: [] }
68
+ let cfg = config.getSection('controller');
69
+ for (let section in cfg) {
70
+ if (section.startsWith('comms')) {
71
+ let cport = extend(true, { enabled: false, netConnect: false }, cfg[section]);
72
+ let port = conn.findPortById(cport.portId || 0);
73
+ if (typeof port !== 'undefined') cport.stats = port.stats;
74
+ opts.ports.push(cport);
75
+ }
76
+ }
77
+ opts.local = await conn.getLocalPortsAsync() || [];
78
+ return res.status(200).send(opts);
79
+ } catch (err) { next(err); }
71
80
  });
72
81
  app.get('/config/options/circuits', async (req, res, next) => {
73
82
  try {
@@ -112,7 +121,7 @@ export class ConfigRoute {
112
121
  invalidIds: sys.board.equipmentIds.invalidIds.get(),
113
122
  equipmentIds: sys.equipment.equipmentIds.features,
114
123
  equipmentNames: sys.board.circuits.getCircuitNames(),
115
- functions: sys.board.valueMaps.featureFunctions.toArray(),
124
+ functions: sys.board.features.getFeatureFunctions(),
116
125
  features: sys.features.get()
117
126
  };
118
127
  return res.status(200).send(opts);
@@ -156,7 +165,8 @@ export class ConfigRoute {
156
165
  circuits: sys.board.circuits.getCircuitReferences(true, true, true, true),
157
166
  bodies: sys.board.valueMaps.pumpBodies.toArray(),
158
167
  pumps: sys.pumps.get(),
159
- servers: await sys.ncp.getREMServers()
168
+ servers: await sys.ncp.getREMServers(),
169
+ rs485ports: await conn.listInstalledPorts()
160
170
  };
161
171
  // RKS: Why do we need the circuit names? We have the circuits. Is this so
162
172
  // that we can name the pump. I thought that *Touch uses the pump type as the name
@@ -203,7 +213,8 @@ export class ConfigRoute {
203
213
  heaterTypes: sys.board.valueMaps.heaterTypes.toArray(),
204
214
  heatModes: sys.board.valueMaps.heatModes.toArray(),
205
215
  coolDownDelay: sys.general.options.cooldownDelay,
206
- servers: []
216
+ servers: [],
217
+ rs485ports: await conn.listInstalledPorts()
207
218
  };
208
219
  // We only need the servers data when the controller is a Nixie controller. We don't need to
209
220
  // wait for this information if we are dealing with an OCP.
@@ -283,16 +294,19 @@ export class ConfigRoute {
283
294
  return res.status(200).send(opts);
284
295
  } catch (err) { next(err); }
285
296
  });
286
- app.get('/config/options/chlorinators', (req, res) => {
287
- let opts = {
288
- types: sys.board.valueMaps.chlorinatorType.toArray(),
289
- bodies: sys.board.bodies.getBodyAssociations(),
290
- chlorinators: sys.chlorinators.get(),
291
- maxChlorinators: sys.equipment.maxChlorinators,
292
- models: sys.board.valueMaps.chlorinatorModel.toArray(),
293
- equipmentMasters: sys.board.valueMaps.equipmentMaster.toArray()
294
- };
295
- return res.status(200).send(opts);
297
+ app.get('/config/options/chlorinators', async (req, res, next) => {
298
+ try {
299
+ let opts = {
300
+ types: sys.board.valueMaps.chlorinatorType.toArray(),
301
+ bodies: sys.board.bodies.getBodyAssociations(),
302
+ chlorinators: sys.chlorinators.get(),
303
+ maxChlorinators: sys.equipment.maxChlorinators,
304
+ models: sys.board.valueMaps.chlorinatorModel.toArray(),
305
+ equipmentMasters: sys.board.valueMaps.equipmentMaster.toArray(),
306
+ rs485ports: await conn.listInstalledPorts()
307
+ };
308
+ return res.status(200).send(opts);
309
+ } catch (err) { next(err); }
296
310
  });
297
311
  app.get('/config/options/dateTime', (req, res) => {
298
312
  let opts = {
@@ -592,8 +606,8 @@ export class ConfigRoute {
592
606
  return res.status(200).send(circuitFunctions);
593
607
  });
594
608
  app.get('/config/features/functions', (req, res) => {
595
- let circuitFunctions = sys.board.circuits.getCircuitFunctions();
596
- return res.status(200).send(circuitFunctions);
609
+ let featureFunctions = sys.board.features.getFeatureFunctions();
610
+ return res.status(200).send(featureFunctions);
597
611
  });
598
612
  app.get('/config/circuit/:id', (req, res) => {
599
613
  // todo: need getInterfaceById.get() in case features are requested here
@@ -605,15 +619,15 @@ export class ConfigRoute {
605
619
  let themes = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightThemes(circuit.type) : [];
606
620
  return res.status(200).send(themes);
607
621
  });
622
+ app.get('/config/circuit/:id/lightCommands', (req, res) => {
623
+ let circuit = sys.circuits.getInterfaceById(parseInt(req.params.id, 10));
624
+ let commands = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightCommands(circuit.type) : [];
625
+ return res.status(200).send(commands);
626
+ });
627
+
608
628
  app.get('/config/chlorinator/:id', (req, res) => {
609
629
  return res.status(200).send(sys.chlorinators.getItemById(parseInt(req.params.id, 10)).get());
610
630
  });
611
- //app.put('/config/chlorinator', (req, res) => {
612
- // let chlor = sys.chlorinators.getItemById(parseInt(req.body.id, 10), true);
613
- // sys.board.chlorinator.setChlorProps(chlor, req.body);
614
- // // if (chlor.isVirtual) { sys.board.virtualChlorinatorController.start(); }
615
- // return res.status(200).send(sys.chlorinators.getItemById(parseInt(req.params.id, 10)).get());
616
- //});
617
631
  app.get('/config/chlorinators/search', async (req, res, next) => {
618
632
  // Change the options for the pool.
619
633
  try {
@@ -624,103 +638,6 @@ export class ConfigRoute {
624
638
  next(err);
625
639
  }
626
640
  });
627
- /* app.get('/config/pump/:id/circuits', (req, res) => {
628
- return res.status(200).send(sys.pumps.getItemById(parseInt(req.params.id, 10)).get().circuits);
629
- });
630
- app.get('/config/pump/availableCircuits', (req, res) => {
631
- return res.status(200).send(sys.board.pumps.availableCircuits());
632
- });
633
- app.get('/config/pump/:id/circuit/:circuitid', (req, res) => {
634
- return res.status(200).send(sys.pumps.getItemById(parseInt(req.params.id, 10)).get().circuits[parseInt(req.params.circuitid, 10)]);
635
- });
636
- app.get('/config/pump/:id/nextAvailablePumpCircuit', (req, res) => {
637
- // if no pumpCircuitId is available, 0 will be returned
638
- let _id = sys.pumps.getItemById(parseInt(req.params.id, 10)).nextAvailablePumpCircuit();
639
- return res.status(200).send(_id.toString());
640
- }); */
641
- /*
642
- app.put('/config/pump/:id/pumpCircuit', (req, res) => {
643
- // if no pumpCircuitId is specified, set it as 0 and take the next available one
644
- req.url = `${ req.url }/0`;
645
- req.next();
646
- });
647
- app.put('/config/pump/:id/pumpCircuit/:pumpCircuitId', (req, res) => {
648
- // RSG - do we want a /config/pump/:id/pumpCircuit/ that will just assume the next circuit?
649
- let pump = sys.pumps.getItemById(parseInt(req.params.id, 10));
650
- let _pumpCircuitId = parseInt(req.params.pumpCircuitId, 10);
651
- let _circuit = parseInt(req.body.circuit, 10);
652
- let _rate = parseInt(req.body.rate, 10);
653
- let _units = parseInt(req.body.units, 10) || pump.defaultUnits;
654
- let pumpCircuit = {
655
- pump: parseInt(req.params.id, 10),
656
- pumpCircuitId: isNaN(_pumpCircuitId) ? undefined : _pumpCircuitId,
657
- circuit: isNaN(_circuit) ? undefined : _circuit,
658
- rate: isNaN(_rate) ? undefined : _rate,
659
- units: isNaN(_units) ? undefined : _units
660
- };
661
- let { result, reason } = pump.setPumpCircuit(pumpCircuit);
662
- if (result === 'OK')
663
- return res.status(200).send({ result: result, reason: reason });
664
- else
665
- return res.status(500).send({ result: result, reason: reason });
666
- }); */
667
- /* app.delete('/config/pump/:id/pumpCircuit/:pumpCircuitId', (req, res) => {
668
- let pump = sys.pumps.getItemById(parseInt(req.params.id, 10));
669
- // pump.circuits.removeItemById(parseInt(req.params.pumpCircuitId, 10));
670
- pump.deletePumpCircuit(parseInt(req.params.pumpCircuitId, 10));
671
- return res.status(200).send('OK');
672
- }); */
673
- /* app.get('/config/pump/types', (req, res) => {
674
- let pumpTypes = sys.board.pumps.getPumpTypes();
675
- return res.status(200).send(pumpTypes);
676
- });
677
- app.get('/config/pump/units', (req, res) => {
678
- // get all units for all system board
679
- let pumpTypes = sys.board.pumps.getCircuitUnits();
680
- return res.status(200).send(pumpTypes);
681
- });
682
- app.get('/config/pump/:id/units', (req, res) => {
683
- // get units for all specific pump types
684
- // need to coorerce into array if only a single unit is returned; by default getExtended will return an array
685
- // if there is 1+ object so this creates parity
686
- let pump = sys.pumps.getItemById(parseInt(req.params.id, 10));
687
- let pumpTypes = sys.board.pumps.getCircuitUnits(pump);
688
- if (!Array.isArray(pumpTypes)) pumpTypes = [pumpTypes];
689
- return res.status(200).send(pumpTypes);
690
- });
691
- app.put('/config/pump/:pumpId/type', (req, res) => {
692
- const _type = parseInt(req.body.pumpType, 10);
693
- const _pumpId = parseInt(req.params.pumpId, 10);
694
- // RG - this was left as it's own end point because trying to combine changing the pump type (which requires resetting the pump values) while simultaneously setting new pump values was tricky.
695
- let pump = sys.pumps.getItemById(_pumpId);
696
- if (sys.controllerType === ControllerType.Virtual) {
697
- pump.isVirtual = true;
698
- }
699
- if (_type !== pump.type) {
700
- pump.setType(_type);
701
- }
702
- return res.status(200).send('OK');
703
- }); */
704
- /* app.get('/config/pump/:pumpId', (req, res) => {
705
- let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10)).get(true);
706
- return res.status(200).send(pump);
707
- });
708
- app.put('/config/pump/:pumpId', (req, res) => {
709
- // this will change the pump type
710
- let _type = parseInt(req.body.pumpType, 10);
711
- let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10));
712
- if (sys.controllerType === ControllerType.Virtual) {
713
- // if virtualController, add the virtual pump
714
- pump.isVirtual = true;
715
- }
716
-
717
- if (_type !== pump.type && typeof _type !== 'undefined') {
718
- pump.setType(_type);
719
- }
720
- // get a new instance of the pump here because setType will remove/add a new instance
721
- if (Object.keys(req.body).length) sys.pumps.getItemById(parseInt(req.params.pumpId, 10)).setPump(req.body);
722
- return res.status(200).send('OK');
723
- }); */
724
641
  app.delete('/config/pump/:pumpId', (req, res) => {
725
642
  let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10));
726
643
  if (pump.type === 0) {
@@ -738,13 +655,12 @@ export class ConfigRoute {
738
655
  });
739
656
  app.get('/config/lightGroups/themes', (req, res) => {
740
657
  // RSG: is this and /config/circuit/:id/lightThemes both needed?
741
-
742
- // if (sys.controllerType === ControllerType.IntelliCenter) {
743
658
  let grp = sys.lightGroups.getItemById(parseInt(req.body.id, 10));
744
659
  return res.status(200).send(grp.getLightThemes());
745
- // }
746
- // else
747
- // return res.status(200).send(sys.intellibrite.getLightThemes());
660
+ });
661
+ app.get('/config/lightGroups/commands', (req, res) => {
662
+ let grp = sys.lightGroups.getItemById(parseInt(req.body.id, 10));
663
+ return res.status(200).send(grp.getLightCommands());
748
664
  });
749
665
  app.get('/config/lightGroup/:id', (req, res) => {
750
666
  // if (sys.controllerType === ControllerType.IntelliCenter) {
@@ -854,6 +770,13 @@ export class ConfigRoute {
854
770
  }
855
771
  catch (err) { next(err); }
856
772
  });
773
+ app.delete('/app/rs485Port', async (req, res, next) => {
774
+ try {
775
+ let port = await conn.deleteAuxPort(req.body);
776
+ return res.status(200).send(port);
777
+ }
778
+ catch (err) { next(err); }
779
+ });
857
780
  app.get('/app/config/startPacketCapture', (req, res) => {
858
781
  startPacketCapture(true);
859
782
  return res.status(200).send('OK');
@@ -874,7 +797,7 @@ export class ConfigRoute {
874
797
  });
875
798
  app.get('/app/config/options/backup', async (req, res, next) => {
876
799
  try {
877
- let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0, keepCount: 5, servers: [] } });
800
+ let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [] });
878
801
  let servers = await sys.ncp.getREMServers();
879
802
  if (typeof servers !== 'undefined') {
880
803
  // Just in case somebody deletes the backup section and doesn't put it back properly.
@@ -893,7 +816,7 @@ export class ConfigRoute {
893
816
  });
894
817
  app.get('/app/config/options/restore', async (req, res, next) => {
895
818
  try {
896
- let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0, keepCount: 5, servers: [], backupFiles: [] } });
819
+ let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [], backupFiles: [] });
897
820
  let servers = await sys.ncp.getREMServers();
898
821
  if (typeof servers !== 'undefined') {
899
822
  for (let i = 0; i < servers.length; i++) {
@@ -914,7 +837,7 @@ export class ConfigRoute {
914
837
  app.put('/app/config/options/backup', async (req, res, next) => {
915
838
  try {
916
839
  config.setSection('controller.backups', req.body);
917
- let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0, keepCount: 5, servers: [] } });
840
+ let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [] });
918
841
  webApp.autoBackup = utils.makeBool(opts.automatic);
919
842
  await webApp.checkAutoBackup();
920
843
  return res.status(200).send(opts);
@@ -161,6 +161,9 @@ export class StateRoute {
161
161
  app.get('/state/feature/:id', (req, res) => {
162
162
  res.status(200).send(state.features.getItemById(parseInt(req.params.id, 10)).get());
163
163
  });
164
+ app.get('/state/schedule/:id', (req, res) => {
165
+ res.status(200).send(state.schedules.getItemById(parseInt(req.params.id, 10)).get());
166
+ });
164
167
  app.get('/state/circuitGroup/:id', (req, res) => {
165
168
  res.status(200).send(state.circuitGroups.getItemById(parseInt(req.params.id, 10)).get());
166
169
  });
@@ -214,7 +217,44 @@ export class StateRoute {
214
217
  return res.status(200).send(theme.get(true));
215
218
  }
216
219
  catch (err) { next(err); }
217
- });
220
+ });
221
+ app.put('/state/light/setTheme', async (req, res, next) => {
222
+ try {
223
+ let theme = await state.circuits.setLightThemeAsync(parseInt(req.body.id, 10), sys.board.valueMaps.lightThemes.encode(req.body.theme));
224
+ return res.status(200).send(theme.get(true));
225
+ }
226
+ catch (err) { next(err); }
227
+ });
228
+
229
+ app.put('/state/light/runCommand', async (req, res, next) => {
230
+ try {
231
+ let slight = await sys.board.circuits.runLightCommandAsync(req.body);
232
+ return res.status(200).send(slight.get(true));
233
+ }
234
+ catch (err) { next(err); }
235
+ });
236
+ app.put('/state/light/:id/colorHold', async (req, res, next) => {
237
+ try {
238
+ let slight = await sys.board.circuits.runLightCommandAsync({ id: parseInt(req.params.id, 10), command: 'colorhold' });
239
+ return res.status(200).send(slight.get(true));
240
+ }
241
+ catch (err) { next(err); }
242
+ });
243
+ app.put('/state/light/:id/colorRecall', async (req, res, next) => {
244
+ try {
245
+ let slight = await sys.board.circuits.runLightCommandAsync({ id: parseInt(req.params.id, 10), command: 'colorecall' });
246
+ return res.status(200).send(slight.get(true));
247
+ }
248
+ catch (err) { next(err); }
249
+ });
250
+ app.put('/state/light/:id/lightThumper', async (req, res, next) => {
251
+ try {
252
+ let slight = await sys.board.circuits.runLightCommandAsync({ id: parseInt(req.params.id, 10), command: 'lightthumper' });
253
+ return res.status(200).send(slight.get(true));
254
+ }
255
+ catch (err) { next(err); }
256
+ });
257
+
218
258
  /* app.put('/state/intellibrite/setTheme', (req, res) => {
219
259
  let id = sys.board.equipmentIds.circuitGroups.start;
220
260
  if (typeof req.body.theme !== 'undefined') id = parseInt(req.body.id, 10);
@@ -330,27 +370,64 @@ export class StateRoute {
330
370
  }
331
371
  catch (err) { next(err); }
332
372
  });
373
+ app.put('/state/manualOperationPriority', async (req, res, next) => {
374
+ try {
375
+ let cstate = await sys.board.system.setManualOperationPriority(parseInt(req.body.id, 10));
376
+ return res.status(200).send(cstate.get(true));
377
+ }
378
+ catch (err) { next(err); }
379
+ });
380
+ app.put('/state/lightGroup/runCommand', async (req, res, next) => {
381
+ try {
382
+ let sgroup = await sys.board.circuits.runLightGroupCommandAsync(req.body);
383
+ return res.status(200).send(sgroup.get(true));
384
+ }
385
+ catch (err) { next(err); }
386
+ });
333
387
  app.put('/state/lightGroup/:id/colorSync', async (req, res, next) => {
334
388
  try {
335
- let sgroup = await sys.board.circuits.sequenceLightGroupAsync(parseInt(req.params.id, 10), 'sync');
389
+ let sgroup = await sys.board.circuits.sequenceLightGroupAsync(parseInt(req.params.id, 10), 'colorsync');
336
390
  return res.status(200).send(sgroup.get(true));
337
391
  }
338
392
  catch (err) { next(err); }
339
393
  });
340
394
  app.put('/state/lightGroup/:id/colorSet', async (req, res, next) => {
341
395
  try {
342
- let sgroup = await sys.board.circuits.sequenceLightGroupAsync(parseInt(req.params.id, 10), 'set');
396
+ let sgroup = await sys.board.circuits.sequenceLightGroupAsync(parseInt(req.params.id, 10), 'colorset');
343
397
  return res.status(200).send(sgroup.get(true));
344
398
  }
345
399
  catch (err) { next(err); }
346
400
  });
347
401
  app.put('/state/lightGroup/:id/colorSwim', async (req, res, next) => {
348
402
  try {
349
- let sgroup = await sys.board.circuits.sequenceLightGroupAsync(parseInt(req.params.id, 10), 'swim');
403
+ let sgroup = await sys.board.circuits.sequenceLightGroupAsync(parseInt(req.params.id, 10), 'colorswim');
404
+ return res.status(200).send(sgroup.get(true));
405
+ }
406
+ catch (err) { next(err); }
407
+ });
408
+ app.put('/state/lightGroup/:id/colorHold', async (req, res, next) => {
409
+ try {
410
+ let sgroup = await sys.board.circuits.runLightGroupCommandAsync({ id: parseInt(req.params.id, 10), command: 'colorhold' });
350
411
  return res.status(200).send(sgroup.get(true));
351
412
  }
352
413
  catch (err) { next(err); }
353
414
  });
415
+ app.put('/state/lightGroup/:id/colorRecall', async (req, res, next) => {
416
+ try {
417
+ let sgroup = await sys.board.circuits.runLightGroupCommandAsync({ id: parseInt(req.params.id, 10), command: 'colorrecall' });
418
+ return res.status(200).send(sgroup.get(true));
419
+ }
420
+ catch (err) { next(err); }
421
+ });
422
+ app.put('/state/lightGroup/:id/lightThumper', async (req, res, next) => {
423
+ try {
424
+ let sgroup = await sys.board.circuits.runLightGroupCommandAsync({ id: parseInt(req.params.id, 10), command: 'lightthumper' });
425
+ return res.status(200).send(sgroup.get(true));
426
+ }
427
+ catch (err) { next(err); }
428
+ });
429
+
430
+
354
431
  app.get('/state/emitAll', (req, res) => {
355
432
  res.status(200).send(state.emitAllEquipmentChanges());
356
433
  });
@@ -174,7 +174,7 @@ export class StateSocket {
174
174
  sock.on('/circuit', async (data: any) => {
175
175
  try {
176
176
  data = JSON.parse(data);
177
- let id = data.parseInt(data.id, 10);
177
+ let id = parseInt(data.id, 10);
178
178
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
179
179
  await sys.board.circuits.setCircuitStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
180
180
  }
@@ -184,7 +184,7 @@ export class StateSocket {
184
184
  sock.on('/feature', async (data: any) => {
185
185
  try {
186
186
  data = JSON.parse(data);
187
- let id = data.parseInt(data.id, 10);
187
+ let id = parseInt(data.id, 10);
188
188
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
189
189
  await sys.board.features.setFeatureStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
190
190
  }
@@ -194,7 +194,7 @@ export class StateSocket {
194
194
  sock.on('/circuitGroup', async (data: any) => {
195
195
  try {
196
196
  data = JSON.parse(data);
197
- let id = data.parseInt(data.id, 10);
197
+ let id = parseInt(data.id, 10);
198
198
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
199
199
  await sys.board.circuits.setCircuitGroupStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
200
200
  }
@@ -204,7 +204,7 @@ export class StateSocket {
204
204
  sock.on('/lightGroup', async (data: any) => {
205
205
  try {
206
206
  data = JSON.parse(data);
207
- let id = data.parseInt(data.id, 10);
207
+ let id = parseInt(data.id, 10);
208
208
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
209
209
  await sys.board.circuits.setLightGroupStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
210
210
  }
@@ -18,16 +18,18 @@ import * as express from 'express';
18
18
  import { SsdpServer} from '../../Server';
19
19
  import { state } from "../../../controller/State";
20
20
  import { sys } from "../../../controller/Equipment";
21
-
21
+ import { webApp } from "../../Server";
22
22
  const extend = require("extend");
23
23
  export class UtilitiesRoute {
24
24
 
25
25
  public static initRoutes(app: express.Application) {
26
- app.get('/device', function(req, res) {
27
- // there's got to be a better way to get this than instantiating SsdpServer() again.
28
- // RKS: There was I made the function static.
29
- let xml = SsdpServer.deviceXML();
30
- res.status(200).set('Content-Type', 'text/xml').send(xml);
26
+ app.use('/upnp.xml', async (req, res, next) => {
27
+ try {
28
+ // Put together the upnp device description.
29
+ let ssdp = webApp.findServer('ssdp') as SsdpServer;
30
+ if (typeof ssdp === 'undefined') throw new Error(`SSDP Server not initialized. No upnp information available.`);
31
+ res.status(200).set('Content-Type', 'text/xml').send(ssdp.deviceXML());
32
+ } catch (err) { next(err); }
31
33
  });
32
34
  app.get('/extended/:section', (req, res) => {
33
35
  let cfg = sys.getSection(req.params.section);
@@ -1,52 +0,0 @@
1
- ---
2
- name: Bug report
3
- about: Create a report to help us improve
4
- title: ''
5
- labels: ''
6
- assignees: ''
7
-
8
- ---
9
-
10
- **Describe the bug**
11
- A clear and concise description of what the bug is.
12
-
13
- **To Reproduce**
14
- Steps to reproduce the behavior:
15
- 1. Go to '...'
16
- 2. Click on '....'
17
- 3. Scroll down to '....'
18
- 4. See error
19
-
20
- **Expected behavior**
21
- A clear and concise description of what you expected to happen.
22
-
23
- **Screenshots**
24
- If applicable, add screenshots to help explain your problem.
25
-
26
- **Packet Capture**
27
- Follow the instructions to complete a [packet capture](https://github.com/tagyoureit/nodejs-poolController/wiki/How-to-capture-all-packets-for-issue-resolution) and attach the resulting zip/log files.
28
-
29
- **Pool Equipment**
30
- - Controller: [e.g. IntelliCenter, EasyTouch, IntelliTouch]
31
- - Controller Model: [e.g. IntelliCenter i5PS, EasyTouch2 8P]
32
- - Controller Firmware Version: [e.g. 1.047]
33
- - Pump(s) manufacturer and model: [e.g. IntelliFlow 2 VST 011056]
34
- - Chlorinator: [e.g. iChlor, IntelliChlor-40]
35
- - Heater(s): [e.g. gas, solar, heatpump, ultratemp]
36
- - Chemical controller: [e.g. IntelliChem, Relay Equipment Manager (REM)]
37
- - Valves: [e.g. Intellivalve]
38
- - Any other relevant equipment:
39
-
40
- **Desktop (please complete the following information):**
41
- - OS: [e.g. iOS]
42
- - Browser [e.g. chrome, safari]
43
- - Version [e.g. 22]
44
-
45
- **Smartphone (please complete the following information):**
46
- - Device: [e.g. iPhone6]
47
- - OS: [e.g. iOS8.1]
48
- - Browser [e.g. stock browser, safari]
49
- - Version [e.g. 22]
50
-
51
- **Additional context**
52
- Add any other context about the problem here.