nodejs-poolcontroller 7.6.1 → 8.0.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 (102) hide show
  1. package/.eslintrc.json +36 -45
  2. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
  3. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
  4. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  6. package/CONTRIBUTING.md +74 -74
  7. package/Changelog +242 -215
  8. package/Dockerfile +17 -17
  9. package/Gruntfile.js +40 -40
  10. package/LICENSE +661 -661
  11. package/README.md +195 -191
  12. package/anslq25/MessagesMock.ts +218 -0
  13. package/anslq25/boards/MockBoardFactory.ts +50 -0
  14. package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
  15. package/anslq25/boards/MockSystemBoard.ts +217 -0
  16. package/anslq25/chemistry/MockChlorinator.ts +75 -0
  17. package/anslq25/pumps/MockPump.ts +84 -0
  18. package/app.ts +10 -14
  19. package/config/Config.ts +26 -8
  20. package/config/VersionCheck.ts +8 -4
  21. package/controller/Constants.ts +59 -25
  22. package/controller/Equipment.ts +2667 -2459
  23. package/controller/Errors.ts +181 -180
  24. package/controller/Lockouts.ts +534 -436
  25. package/controller/State.ts +596 -77
  26. package/controller/boards/AquaLinkBoard.ts +1003 -0
  27. package/controller/boards/BoardFactory.ts +53 -45
  28. package/controller/boards/EasyTouchBoard.ts +3079 -2653
  29. package/controller/boards/IntelliCenterBoard.ts +3821 -4230
  30. package/controller/boards/IntelliComBoard.ts +69 -63
  31. package/controller/boards/IntelliTouchBoard.ts +384 -241
  32. package/controller/boards/NixieBoard.ts +1871 -1675
  33. package/controller/boards/SunTouchBoard.ts +393 -0
  34. package/controller/boards/SystemBoard.ts +5244 -4697
  35. package/controller/comms/Comms.ts +905 -541
  36. package/controller/comms/ScreenLogic.ts +1663 -0
  37. package/controller/comms/messages/Messages.ts +382 -54
  38. package/controller/comms/messages/config/ChlorinatorMessage.ts +8 -4
  39. package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
  40. package/controller/comms/messages/config/CircuitMessage.ts +82 -13
  41. package/controller/comms/messages/config/ConfigMessage.ts +3 -1
  42. package/controller/comms/messages/config/CoverMessage.ts +2 -1
  43. package/controller/comms/messages/config/CustomNameMessage.ts +31 -30
  44. package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
  45. package/controller/comms/messages/config/ExternalMessage.ts +33 -3
  46. package/controller/comms/messages/config/FeatureMessage.ts +2 -1
  47. package/controller/comms/messages/config/GeneralMessage.ts +2 -1
  48. package/controller/comms/messages/config/HeaterMessage.ts +145 -11
  49. package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
  50. package/controller/comms/messages/config/OptionsMessage.ts +16 -27
  51. package/controller/comms/messages/config/PumpMessage.ts +62 -47
  52. package/controller/comms/messages/config/RemoteMessage.ts +80 -13
  53. package/controller/comms/messages/config/ScheduleMessage.ts +390 -347
  54. package/controller/comms/messages/config/SecurityMessage.ts +2 -1
  55. package/controller/comms/messages/config/ValveMessage.ts +44 -27
  56. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +44 -91
  57. package/controller/comms/messages/status/EquipmentStateMessage.ts +139 -30
  58. package/controller/comms/messages/status/HeaterStateMessage.ts +135 -86
  59. package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -445
  60. package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -35
  61. package/controller/comms/messages/status/PumpStateMessage.ts +92 -2
  62. package/controller/comms/messages/status/VersionMessage.ts +2 -1
  63. package/controller/nixie/Nixie.ts +173 -162
  64. package/controller/nixie/NixieEquipment.ts +104 -103
  65. package/controller/nixie/bodies/Body.ts +120 -120
  66. package/controller/nixie/bodies/Filter.ts +135 -135
  67. package/controller/nixie/chemistry/ChemController.ts +2682 -2498
  68. package/controller/nixie/chemistry/ChemDoser.ts +806 -0
  69. package/controller/nixie/chemistry/Chlorinator.ts +367 -314
  70. package/controller/nixie/circuits/Circuit.ts +402 -248
  71. package/controller/nixie/heaters/Heater.ts +815 -649
  72. package/controller/nixie/pumps/Pump.ts +934 -661
  73. package/controller/nixie/schedules/Schedule.ts +319 -257
  74. package/controller/nixie/valves/Valve.ts +170 -170
  75. package/defaultConfig.json +346 -286
  76. package/logger/DataLogger.ts +448 -448
  77. package/logger/Logger.ts +38 -9
  78. package/package.json +60 -56
  79. package/tsconfig.json +25 -25
  80. package/web/Server.ts +275 -117
  81. package/web/bindings/aqualinkD.json +560 -0
  82. package/web/bindings/homeassistant.json +437 -0
  83. package/web/bindings/influxDB.json +1066 -1021
  84. package/web/bindings/mqtt.json +721 -654
  85. package/web/bindings/mqttAlt.json +746 -684
  86. package/web/bindings/rulesManager.json +54 -54
  87. package/web/bindings/smartThings-Hubitat.json +31 -31
  88. package/web/bindings/valveRelays.json +20 -20
  89. package/web/bindings/vera.json +25 -25
  90. package/web/interfaces/baseInterface.ts +188 -136
  91. package/web/interfaces/httpInterface.ts +148 -124
  92. package/web/interfaces/influxInterface.ts +283 -245
  93. package/web/interfaces/mqttInterface.ts +695 -475
  94. package/web/interfaces/ruleInterface.ts +87 -0
  95. package/web/services/config/Config.ts +177 -49
  96. package/web/services/config/ConfigSocket.ts +2 -1
  97. package/web/services/state/State.ts +154 -3
  98. package/web/services/state/StateSocket.ts +69 -18
  99. package/web/services/utilities/Utilities.ts +232 -42
  100. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
  101. package/config copy.json +0 -300
  102. package/issue_template.md +0 -52
@@ -1,5 +1,6 @@
1
1
  /* nodejs-poolController. An application to control pool equipment.
2
- Copyright (C) 2016, 2017, 2018, 2019, 2020. Russell Goldin, tagyoureit. russ.goldin@gmail.com
2
+ Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
3
+ Russell Goldin, tagyoureit. russ.goldin@gmail.com
3
4
 
4
5
  This program is free software: you can redistribute it and/or modify
5
6
  it under the terms of the GNU Affero General Public License as
@@ -22,14 +23,64 @@ import { sys } from "../../../controller/Equipment";
22
23
  import { utils } from '../../../controller/Constants';
23
24
  import { logger } from "../../../logger/Logger";
24
25
  import { DataLogger } from "../../../logger/DataLogger";
26
+ import { conn } from "../../../controller/comms/Comms";
27
+ import { config } from "../../../config/Config";
25
28
 
26
29
  import { ServiceParameterError } from "../../../controller/Errors";
27
30
 
28
31
  export class StateRoute {
29
32
  public static initRoutes(app: express.Application) {
33
+ app.get('/state/rs485Port/:id', async (req, res, next) => {
34
+ try {
35
+ let portId = parseInt(req.params.id, 10);
36
+ if (isNaN(portId)) throw new ServiceParameterError(`RS485 port id not supplied`, '/state/rs485Port/:id', 'portId', req.params.id);
37
+ let cfg = config.getSection(portId === 0 ? 'controller.comms' : `controller.comms${portId}`);
38
+ if (typeof cfg === 'undefined') throw new ServiceParameterError(`RS485 port id not found`, '/state/rs485Port/:id', 'portId', req.params.id);
39
+ let port = conn.findPortById(portId);
40
+ let sport: any = {
41
+ portId: portId,
42
+ enabled: cfg.enabled || false,
43
+ netConnect: cfg.netConnect,
44
+ reconnects: 0,
45
+ inactivityRetry: cfg.inactivityRetry,
46
+ isOpen: false,
47
+ mock: cfg.mock || false
48
+ }
49
+ if (cfg.netConnect) sport.netConnect = { host: cfg.netHost, port: cfg.netPort }
50
+ else if (typeof cfg.type !== 'undefined' && cfg.type === 'screenlogic'){
51
+ sport.screenlogic = cfg.screenlogic;
52
+ }
53
+ else sport.settings = extend(true, { name: cfg.rs485Port }, cfg.portSettings);
54
+ if (typeof port !== 'undefined' && port.type !== 'screenlogic') {
55
+ let stats = port.stats;
56
+ sport.reconnects = port.reconnects;
57
+ sport.isOpen = port.isOpen;
58
+ sport.received = {
59
+ bytes: stats.bytesReceived,
60
+ success: stats.recSuccess,
61
+ failed: stats.recFailed,
62
+ collisions: stats.recCollisions,
63
+ rewinds: stats.recFRewinds,
64
+ failureRate: stats.recFailureRate
65
+ };
66
+ sport.sent = {
67
+ bytes: stats.bytesSent,
68
+ success: stats.sndSuccess,
69
+ aborted: stats.sndAborted,
70
+ retries: stats.sndRetries,
71
+ failureRate: stats.sndFailureRate
72
+ }
73
+ }
74
+ res.status(200).send(sport);
75
+ }
76
+ catch (err) { next(err); }
77
+ });
30
78
  app.get('/state/chemController/:id', (req, res) => {
31
79
  res.status(200).send(state.chemControllers.getItemById(parseInt(req.params.id, 10)).getExtended());
32
80
  });
81
+ app.get('/state/chemDoser/:id', (req, res) => {
82
+ res.status(200).send(state.chemDosers.getItemById(parseInt(req.params.id, 10)).getExtended());
83
+ });
33
84
  app.put('/state/chemController', async (req, res, next) => {
34
85
  try {
35
86
  let schem = await sys.board.chemControllers.setChemControllerStateAsync(req.body);
@@ -37,6 +88,13 @@ export class StateRoute {
37
88
  }
38
89
  catch (err) { next(err); }
39
90
  });
91
+ app.put('/state/chemDoser', async (req, res, next) => {
92
+ try {
93
+ let schem = await sys.board.chemDosers.setChemDoserStateAsync(req.body);
94
+ return res.status(200).send(schem.getExtended());
95
+ }
96
+ catch (err) { next(err); }
97
+ });
40
98
  app.put('/state/chemController/manualDose', async (req, res, next) => {
41
99
  try {
42
100
  let schem = await sys.board.chemControllers.manualDoseAsync(req.body);
@@ -44,6 +102,14 @@ export class StateRoute {
44
102
  }
45
103
  catch (err) { next(err); }
46
104
  });
105
+ app.put('/state/chemDoser/manualDose', async (req, res, next) => {
106
+ try {
107
+ let schem = await sys.board.chemDosers.manualDoseAsync(req.body);
108
+ return res.status(200).send(schem.getExtended());
109
+ }
110
+ catch (err) { next(err); }
111
+ });
112
+
47
113
  app.put('/state/chemController/manualMix', async (req, res, next) => {
48
114
  try {
49
115
  logger.debug(`Starting manual mix`);
@@ -53,6 +119,13 @@ export class StateRoute {
53
119
  }
54
120
  catch (err) { next(err); }
55
121
  });
122
+ app.put('/state/chemDoser/manualMix', async (req, res, next) => {
123
+ try {
124
+ let schem = await sys.board.chemDosers.manualMixAsync(req.body);
125
+ return res.status(200).send(schem.getExtended());
126
+ }
127
+ catch (err) { next(err); }
128
+ });
56
129
  app.get('/state/chemController/:id/doseHistory', (req, res) => {
57
130
  let schem = state.chemControllers.getItemById(parseInt(req.params.id));
58
131
  let hist = { ph: [], orp: [] };
@@ -62,6 +135,11 @@ export class StateRoute {
62
135
  hist.orp.push(schem.orp.doseHistory[i]);
63
136
  return res.status(200).send(hist);
64
137
  });
138
+ app.get('/state/chemDoser/:id/doseHistory', (req, res) => {
139
+ let schem = state.chemDosers.getItemById(parseInt(req.params.id));
140
+ return res.status(200).send(schem.doseHistory);
141
+ });
142
+
65
143
  app.put('/state/chemController/:id/doseHistory/orp/clear', async (req, res, next) => {
66
144
  try {
67
145
  let schem = state.chemControllers.getItemById(parseInt(req.params.id));
@@ -71,6 +149,16 @@ export class StateRoute {
71
149
  }
72
150
  catch (err) { next(err); }
73
151
  });
152
+ app.put('/state/chemDoser/:id/doseHistory/clear', async (req, res, next) => {
153
+ try {
154
+ let schem = state.chemDosers.getItemById(parseInt(req.params.id));
155
+ schem.doseHistory = [];
156
+ schem.calcDoseHistory();
157
+ return res.status(200).send(schem.doseHistory);
158
+ }
159
+ catch (err) { next(err); }
160
+ });
161
+
74
162
  app.put('/state/chemController/:id/doseHistory/ph/clear', async (req, res, next) => {
75
163
  try {
76
164
  let schem = state.chemControllers.getItemById(parseInt(req.params.id));
@@ -95,6 +183,21 @@ export class StateRoute {
95
183
  }
96
184
  catch (err) { next(err); }
97
185
  });
186
+ app.get('/state/chemDoser/:id/doseLog', async (req, res, next) => {
187
+ try {
188
+ let schem = state.chemDosers.getItemById(parseInt(req.params.id));
189
+ let filter = req.body || {};
190
+ let dh = await DataLogger.readFromEndAsync(`chemDosage_Peristalic.log`, ChemicalDoseState, (lineNumber: number, entry: ChemicalDoseState, arr: ChemicalDoseState[]): boolean => {
191
+ if (entry.id !== schem.id) return false;
192
+ if (typeof filter.lines !== 'undefined' && filter.lines <= arr.length) return false;
193
+ if (typeof filter.date !== 'undefined' && entry.end < filter.date) return false;
194
+ return true;
195
+ });
196
+ return res.status(200).send(dh);
197
+ }
198
+ catch (err) { next(err); }
199
+ });
200
+
98
201
  app.search('/state/chemController/:id/doseLog/ph', async (req, res, next) => {
99
202
  try {
100
203
  let schem = state.chemControllers.getItemById(parseInt(req.params.id));
@@ -144,6 +247,13 @@ export class StateRoute {
144
247
  }
145
248
  catch (err) { next(err); }
146
249
  });
250
+ app.put('/state/chemDoser/cancelDosing', async (req, res, next) => {
251
+ try {
252
+ let schem = await sys.board.chemDosers.cancelDosingAsync(req.body);
253
+ return res.status(200).send(schem.getExtended());
254
+ }
255
+ catch (err) { next(err); }
256
+ });
147
257
  app.put('/state/chemController/cancelMixing', async (req, res, next) => {
148
258
  try {
149
259
  let schem = await sys.board.chemControllers.cancelMixingAsync(req.body);
@@ -151,6 +261,13 @@ export class StateRoute {
151
261
  }
152
262
  catch (err) { next(err); }
153
263
  });
264
+ app.put('/state/chemDoser/cancelMixing', async (req, res, next) => {
265
+ try {
266
+ let schem = await sys.board.chemDosers.cancelMixingAsync(req.body);
267
+ return res.status(200).send(schem.getExtended());
268
+ }
269
+ catch (err) { next(err); }
270
+ });
154
271
 
155
272
  app.get('/state/chlorinator/:id', (req, res) => {
156
273
  res.status(200).send(state.chlorinators.getItemById(parseInt(req.params.id, 10), false).getExtended());
@@ -161,6 +278,9 @@ export class StateRoute {
161
278
  app.get('/state/feature/:id', (req, res) => {
162
279
  res.status(200).send(state.features.getItemById(parseInt(req.params.id, 10)).get());
163
280
  });
281
+ app.get('/state/schedule/:id', (req, res) => {
282
+ res.status(200).send(state.schedules.getItemById(parseInt(req.params.id, 10)).get());
283
+ });
164
284
  app.get('/state/circuitGroup/:id', (req, res) => {
165
285
  res.status(200).send(state.circuitGroups.getItemById(parseInt(req.params.id, 10)).get());
166
286
  });
@@ -230,6 +350,13 @@ export class StateRoute {
230
350
  }
231
351
  catch (err) { next(err); }
232
352
  });
353
+ app.put('/state/light/:id/colorSync', async (req, res, next) => {
354
+ try {
355
+ let slight = await sys.board.circuits.runLightCommandAsync({ id: parseInt(req.params.id, 10), command: 'colorsync' });
356
+ return res.status(200).send(slight.get(true));
357
+ }
358
+ catch (err) { next(err); }
359
+ });
233
360
  app.put('/state/light/:id/colorHold', async (req, res, next) => {
234
361
  try {
235
362
  let slight = await sys.board.circuits.runLightCommandAsync({ id: parseInt(req.params.id, 10), command: 'colorhold' });
@@ -303,6 +430,7 @@ export class StateRoute {
303
430
  // RKS: 06-24-20 -- Changed this so that users can send in the body id, circuit id, or the name.
304
431
  // RKS: 05-14-21 -- Added cooling setpoints for the body.
305
432
  try {
433
+
306
434
  let body = sys.bodies.findByObject(req.body);
307
435
  if (typeof body === 'undefined') return next(new ServiceParameterError(`Cannot set body setPoint. You must supply a valid id, circuit, name, or type for the body`, 'body', 'id', req.body.id));
308
436
  if (typeof req.body.coolSetpoint !== 'undefined' && !isNaN(parseInt(req.body.coolSetpoint, 10)))
@@ -367,6 +495,13 @@ export class StateRoute {
367
495
  }
368
496
  catch (err) { next(err); }
369
497
  });
498
+ app.put('/state/manualOperationPriority', async (req, res, next) => {
499
+ try {
500
+ let cstate = await sys.board.system.setManualOperationPriority(parseInt(req.body.id, 10));
501
+ return res.status(200).send(cstate.get(true));
502
+ }
503
+ catch (err) { next(err); }
504
+ });
370
505
  app.put('/state/lightGroup/runCommand', async (req, res, next) => {
371
506
  try {
372
507
  let sgroup = await sys.board.circuits.runLightGroupCommandAsync(req.body);
@@ -416,8 +551,24 @@ export class StateRoute {
416
551
  }
417
552
  catch (err) { next(err); }
418
553
  });
419
-
420
-
554
+ app.put('/state/panelMode', async (req, res, next) => {
555
+ try {
556
+ await sys.board.system.setPanelModeAsync(req.body);
557
+ return res.status(200).send(state.controllerState);
558
+ } catch (err) { next(err); }
559
+ });
560
+ app.put('/state/toggleServiceMode', async (req, res, next) => {
561
+ try {
562
+ let data = extend({}, req.body);
563
+ if (state.mode === 0) {
564
+ if (typeof data.timeout !== 'undefined' && !isNaN(data.timeout)) data.mode = 'timeout';
565
+ else data.mode = 'service';
566
+ await sys.board.system.setPanelModeAsync(req.body);
567
+ }
568
+ else sys.board.system.setPanelModeAsync({ mode: 'auto' });
569
+ return res.status(200).send(state.controllerState);
570
+ } catch (err) { next(err); }
571
+ });
421
572
  app.get('/state/emitAll', (req, res) => {
422
573
  res.status(200).send(state.emitAllEquipmentChanges());
423
574
  });
@@ -1,5 +1,6 @@
1
1
  /* nodejs-poolController. An application to control pool equipment.
2
- Copyright (C) 2016, 2017, 2018, 2019, 2020. Russell Goldin, tagyoureit. russ.goldin@gmail.com
2
+ Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
3
+ Russell Goldin, tagyoureit. russ.goldin@gmail.com
3
4
 
4
5
  This program is free software: you can redistribute it and/or modify
5
6
  it under the terms of the GNU Affero General Public License as
@@ -28,7 +29,7 @@ export class StateSocket {
28
29
  await state.circuits.toggleCircuitStateAsync(parseInt(data.id, 10));
29
30
  // return res.status(200).send(cstate);
30
31
  }
31
- catch (err) {logger.error(err);}
32
+ catch (err) {logger.error(`Socket processing error /state/circuit/toggleState: ${err.message}`);}
32
33
  });
33
34
  sock.on('/state/body/heatMode', async (data: any) => {
34
35
  // RKS: 06-24-20 -- Changed this so that users can send in the body id, circuit id, or the name.
@@ -52,7 +53,8 @@ export class StateSocket {
52
53
  }
53
54
  await sys.board.bodies.setHeatModeAsync(body, mode);
54
55
  // return res.status(200).send(tbody);
55
- } catch (err) { logger.error(err); }
56
+ }
57
+ catch (err) { logger.error(`Socket processing error /state/body/heatmode: ${err.message}`); }
56
58
  });
57
59
  sock.on('/state/body/setPoint', async (data: any) => {
58
60
  // RKS: 06-24-20 -- Changed this so that users can send in the body id, circuit id, or the name.
@@ -65,14 +67,14 @@ export class StateSocket {
65
67
  }
66
68
  await sys.board.bodies.setHeatSetpointAsync(body, parseInt(data.setPoint, 10));
67
69
  // return res.status(200).send(tbody);
68
- } catch (err) { logger.error(err); }
70
+ } catch (err) { logger.error(`Socket processing error /state/body/setPoint: ${err.message}`); }
69
71
  });
70
72
  sock.on('/temps', async (data: any) => {
71
73
  try {
72
74
  data = JSON.parse(data);
73
75
  await sys.board.system.setTempsAsync(data).catch(err => logger.error(err));
74
76
  }
75
- catch (err) { logger.error(err); }
77
+ catch (err) { logger.error(`Socket processing error /temps: ${err.message}`); }
76
78
  });
77
79
 
78
80
  sock.on('/chlorinator', async (data: any) => {
@@ -98,7 +100,7 @@ export class StateSocket {
98
100
  schlor.emitEquipmentChange();
99
101
  }
100
102
  }
101
- catch (err) { logger.error(err); }
103
+ catch (err) { logger.error(`Socket processing error /chlorinator: ${err.message}`); }
102
104
  });
103
105
  sock.on('/filter', async (data: any) => {
104
106
  try {
@@ -112,9 +114,7 @@ export class StateSocket {
112
114
  await sys.board.filters.setFilterPressure(filter.id, data.pressure, data.pressureUnits || pu.name);
113
115
  sfilter.emitEquipmentChange();
114
116
  }
115
-
116
-
117
- } catch (err) { logger.error(err); }
117
+ } catch (err) { logger.error(`Socket processing error /filter: ${err.message}`); }
118
118
  });
119
119
  sock.on('/chemController', async (data: any) => {
120
120
  try {
@@ -160,7 +160,25 @@ export class StateSocket {
160
160
  if (!isNaN(parseFloat(data.orpTank.capacity))) scontroller.orp.tank.capacity = controller.orp.tank.capacity = parseFloat(data.orpTank.capacity);
161
161
  if (typeof data.orpTank.units === 'string') scontroller.orp.tank.units = controller.orp.tank.units = data.orpTank.units;
162
162
  }
163
+ if (typeof data.acidPump !== 'undefined' || typeof data.orpPump !== 'undefined') {
164
+ let chem = typeof data.acidPump !== 'undefined' ? controller.ph : controller.orp;
165
+ let schem = typeof data.acidPump !== 'undefined' ? scontroller.ph : scontroller.orp;
166
+ let vals = typeof data.acidPump !== 'undefined' ? data.acidPump : data.orpPump;
167
+ let pump = chem.pump;
168
+ switch (sys.board.valueMaps.chemPumpTypes.getName(pump.type)) {
169
+ case 'ezo-pmp':
170
+ if (typeof vals.dispense !== 'undefined') {
171
+ pump.ratedFlow = typeof vals.dispense.ratedFlow === 'number' ? vals.dispense.ratedFlow : pump.ratedFlow;
172
+ }
173
+ if (typeof vals.tank !== 'undefined') {
174
+ if (!isNaN(parseFloat(vals.tank.level))) schem.tank.level = parseFloat(vals.tank.level);
175
+ if (!isNaN(parseFloat(vals.tank.capacity))) schem.tank.capacity = chem.tank.capacity = parseFloat(vals.tank.capacity);
176
+ if (typeof vals.tank.units === 'string') schem.tank.units = chem.tank.units = vals.tank.units;
177
+ }
178
+ break;
179
+ }
163
180
 
181
+ }
164
182
  // Need to build this out to include the type of controller. If this is REM Chem we
165
183
  // will send the whole rest of the nut over to it. Intellichem will only let us
166
184
  // set specific values.
@@ -169,12 +187,12 @@ export class StateSocket {
169
187
  }
170
188
  }
171
189
  }
172
- catch (err) { logger.error(err); }
190
+ catch (err) { logger.error(`Socket processing error /chemController: ${err.message}`); }
173
191
  });
174
192
  sock.on('/circuit', async (data: any) => {
175
193
  try {
176
194
  data = JSON.parse(data);
177
- let id = data.parseInt(data.id, 10);
195
+ let id = parseInt(data.id, 10);
178
196
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
179
197
  await sys.board.circuits.setCircuitStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
180
198
  }
@@ -184,35 +202,68 @@ export class StateSocket {
184
202
  sock.on('/feature', async (data: any) => {
185
203
  try {
186
204
  data = JSON.parse(data);
187
- let id = data.parseInt(data.id, 10);
205
+ let id = parseInt(data.id, 10);
188
206
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
189
207
  await sys.board.features.setFeatureStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
190
208
  }
191
209
  }
192
- catch (err) { logger.error(err); }
210
+ catch (err) { logger.error(`Socket processing error /feature: ${err.message}`); }
193
211
  });
194
212
  sock.on('/circuitGroup', async (data: any) => {
195
213
  try {
196
214
  data = JSON.parse(data);
197
- let id = data.parseInt(data.id, 10);
215
+ let id = parseInt(data.id, 10);
198
216
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
199
217
  await sys.board.circuits.setCircuitGroupStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
200
218
  }
201
219
  }
202
- catch (err) { logger.error(err); }
220
+ catch (err) { logger.error(`Socket processing error /circuitGroup: ${err.message}`); }
203
221
  });
204
222
  sock.on('/lightGroup', async (data: any) => {
205
223
  try {
206
224
  data = JSON.parse(data);
207
- let id = data.parseInt(data.id, 10);
225
+ let id = parseInt(data.id, 10);
208
226
  if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
209
227
  await sys.board.circuits.setLightGroupStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
210
228
  }
211
229
  if (!isNaN(id) && typeof data.theme !== 'undefined') await sys.board.circuits.setLightGroupThemeAsync(id, data.theme);
212
230
  }
213
- catch (err) { logger.error(err); }
231
+ catch (err) { logger.error(`Socket processing error /lightGroup: ${err.message}`); }
232
+ });
233
+ sock.on('/panelMode', async (data: any) => {
234
+ try {
235
+ data = JSON.parse(data);
236
+ let obj = {};
237
+ if (typeof data.isOn !== 'undefined' && !utils.makeBool(data.isOn)) return; // This is just in case it is sent by REM for a toggle button.
238
+ if (typeof data.timeout !== 'undefined' && !isNaN(data.timeout) && data.timeout) {
239
+ switch (data.timeUnits.toLowerCase()) {
240
+ case 'min':
241
+ case 'mins':
242
+ case 'm':
243
+ case 'minute':
244
+ case 'minutes':
245
+ data.timeout = data.timeout * 60;
246
+ break;
247
+ case 'hr':
248
+ case 'hrs':
249
+ case 'h':
250
+ case 'hour':
251
+ data.timeout = data.timeout * 3600;
252
+ break;
253
+ }
254
+ }
255
+ if (typeof data.mode === 'undefined' || data.mode === 'toggle') {
256
+ if (state.mode === 0) {
257
+ if (typeof data.timeout !== 'undefined' && !isNaN(data.timeout) && data.timeout)
258
+ data.mode = 'timeout';
259
+ else data.mode = 'service';
260
+ await sys.board.system.setPanelModeAsync(data);
261
+ }
262
+ else sys.board.system.setPanelModeAsync({ mode: 'auto' });
263
+ }
264
+ else await sys.board.system.setPanelModeAsync(data);
265
+ } catch (err) { logger.error(`Socket processing error /panelMode: ${err.message}`); }
214
266
  });
215
-
216
267
  /*
217
268
  app.get('/state/chemController/:id', (req, res) => {
218
269
  res.status(200).send(state.chemControllers.getItemById(parseInt(req.params.id, 10)).getExtended());