nodejs-poolcontroller 7.3.1 → 7.6.1

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 (85) hide show
  1. package/.eslintrc.json +44 -44
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +52 -52
  3. package/CONTRIBUTING.md +74 -74
  4. package/Changelog +215 -195
  5. package/Dockerfile +17 -17
  6. package/Gruntfile.js +40 -40
  7. package/LICENSE +661 -661
  8. package/README.md +191 -186
  9. package/app.ts +2 -0
  10. package/config/Config.ts +27 -2
  11. package/config/VersionCheck.ts +33 -14
  12. package/config copy.json +299 -299
  13. package/controller/Constants.ts +88 -0
  14. package/controller/Equipment.ts +2459 -2225
  15. package/controller/Errors.ts +180 -157
  16. package/controller/Lockouts.ts +437 -0
  17. package/controller/State.ts +364 -79
  18. package/controller/boards/BoardFactory.ts +45 -45
  19. package/controller/boards/EasyTouchBoard.ts +2653 -2489
  20. package/controller/boards/IntelliCenterBoard.ts +4230 -3973
  21. package/controller/boards/IntelliComBoard.ts +63 -63
  22. package/controller/boards/IntelliTouchBoard.ts +241 -167
  23. package/controller/boards/NixieBoard.ts +1675 -1105
  24. package/controller/boards/SystemBoard.ts +4697 -3201
  25. package/controller/comms/Comms.ts +222 -10
  26. package/controller/comms/messages/Messages.ts +13 -9
  27. package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
  28. package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
  29. package/controller/comms/messages/config/CircuitMessage.ts +0 -0
  30. package/controller/comms/messages/config/ConfigMessage.ts +0 -0
  31. package/controller/comms/messages/config/CoverMessage.ts +1 -0
  32. package/controller/comms/messages/config/CustomNameMessage.ts +30 -30
  33. package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
  34. package/controller/comms/messages/config/ExternalMessage.ts +53 -33
  35. package/controller/comms/messages/config/FeatureMessage.ts +8 -1
  36. package/controller/comms/messages/config/GeneralMessage.ts +8 -0
  37. package/controller/comms/messages/config/HeaterMessage.ts +14 -28
  38. package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
  39. package/controller/comms/messages/config/OptionsMessage.ts +38 -2
  40. package/controller/comms/messages/config/PumpMessage.ts +4 -20
  41. package/controller/comms/messages/config/RemoteMessage.ts +4 -0
  42. package/controller/comms/messages/config/ScheduleMessage.ts +347 -331
  43. package/controller/comms/messages/config/SecurityMessage.ts +1 -0
  44. package/controller/comms/messages/config/ValveMessage.ts +13 -3
  45. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
  46. package/controller/comms/messages/status/EquipmentStateMessage.ts +79 -25
  47. package/controller/comms/messages/status/HeaterStateMessage.ts +86 -53
  48. package/controller/comms/messages/status/IntelliChemStateMessage.ts +445 -386
  49. package/controller/comms/messages/status/IntelliValveStateMessage.ts +35 -35
  50. package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
  51. package/controller/comms/messages/status/VersionMessage.ts +0 -0
  52. package/controller/nixie/Nixie.ts +162 -160
  53. package/controller/nixie/NixieEquipment.ts +103 -103
  54. package/controller/nixie/bodies/Body.ts +120 -117
  55. package/controller/nixie/bodies/Filter.ts +135 -135
  56. package/controller/nixie/chemistry/ChemController.ts +2498 -2395
  57. package/controller/nixie/chemistry/Chlorinator.ts +314 -313
  58. package/controller/nixie/circuits/Circuit.ts +248 -210
  59. package/controller/nixie/heaters/Heater.ts +649 -441
  60. package/controller/nixie/pumps/Pump.ts +661 -599
  61. package/controller/nixie/schedules/Schedule.ts +257 -256
  62. package/controller/nixie/valves/Valve.ts +170 -170
  63. package/defaultConfig.json +286 -271
  64. package/issue_template.md +51 -51
  65. package/logger/DataLogger.ts +448 -433
  66. package/logger/Logger.ts +0 -0
  67. package/package.json +56 -54
  68. package/tsconfig.json +25 -25
  69. package/web/Server.ts +522 -31
  70. package/web/bindings/influxDB.json +1022 -894
  71. package/web/bindings/mqtt.json +654 -543
  72. package/web/bindings/mqttAlt.json +684 -574
  73. package/web/bindings/rulesManager.json +54 -54
  74. package/web/bindings/smartThings-Hubitat.json +31 -31
  75. package/web/bindings/valveRelays.json +20 -20
  76. package/web/bindings/vera.json +25 -25
  77. package/web/interfaces/baseInterface.ts +136 -136
  78. package/web/interfaces/httpInterface.ts +124 -122
  79. package/web/interfaces/influxInterface.ts +245 -240
  80. package/web/interfaces/mqttInterface.ts +475 -464
  81. package/web/services/config/Config.ts +181 -152
  82. package/web/services/config/ConfigSocket.ts +0 -0
  83. package/web/services/state/State.ts +118 -7
  84. package/web/services/state/StateSocket.ts +18 -1
  85. package/web/services/utilities/Utilities.ts +42 -42
@@ -34,5 +34,6 @@ export class SecurityMessage {
34
34
  role.flag2 = msg.extractPayloadByte(3);
35
35
  role.pin = msg.extractPayloadByte(21).toString() + msg.extractPayloadByte(22).toString() + msg.extractPayloadByte(23).toString() + msg.extractPayloadByte(24).toString();
36
36
  }
37
+ msg.isProcessed = true;
37
38
  }
38
39
  }
@@ -32,6 +32,7 @@ export class ValveMessage {
32
32
  ValveMessage.processValveNames(msg);
33
33
  break;
34
34
  case 3: // Skip the secondary intake/return
35
+ msg.isProcessed = true;
35
36
  break;
36
37
  case 4:
37
38
  case 5:
@@ -70,6 +71,7 @@ export class ValveMessage {
70
71
  // [165,33,15,16,35,2],[132,0],[1,142]
71
72
  // ^^^ 128 = Pump off during valve operation
72
73
  sys.general.options.pumpDelay = msg.extractPayloadByte(0) >> 7 === 1;
74
+ msg.isProcessed = true;
73
75
  }
74
76
  private static process_ValveAssignment_IT(msg: Inbound) {
75
77
  // sample packet
@@ -91,6 +93,7 @@ export class ValveMessage {
91
93
  let svalve = state.valves.getItemById(id, true);
92
94
  svalve.name = valve.name;
93
95
  svalve.type = valve.type;
96
+ valve.master = 0;
94
97
  }
95
98
  else {
96
99
  sys.valves.removeItemById(id);
@@ -110,6 +113,7 @@ export class ValveMessage {
110
113
  let svalve = state.valves.getItemById(id, true);
111
114
  svalve.name = valve.name;
112
115
  svalve.type = valve.type;
116
+ valve.master = 0;
113
117
  }
114
118
  else {
115
119
  sys.valves.removeItemById(id);
@@ -136,17 +140,20 @@ export class ValveMessage {
136
140
  let svalve = state.valves.getItemById(id, true);
137
141
  svalve.name = valve.name;
138
142
  svalve.type = valve.type;
143
+ valve.master = 0;
139
144
  }
140
145
  if (!valve.isActive) {
141
146
  sys.valves.removeItemById(id);
142
147
  state.valves.removeItemById(id);
143
148
  }
144
149
  else {
145
- valve.isVirtual = false;
150
+ valve.master = 0;
151
+ // valve.isVirtual = false;
146
152
  valve.type = 0;
147
153
  }
148
154
  id++;
149
155
  }
156
+ msg.isProcessed = true;
150
157
  }
151
158
  private static getName(id: number, cir: number) {
152
159
  if (cir <= 0 || cir >= 255 || cir === 6) {
@@ -167,7 +174,7 @@ export class ValveMessage {
167
174
  // for i10d.
168
175
  let ndx: number = 2;
169
176
  let id = 1;
170
- for (let i = 0; i < sys.equipment.maxValves - 1; i++) {
177
+ for (let i = 0; i < sys.equipment.maxValves; i++) {
171
178
  if (id === 3 && !sys.equipment.shared) {
172
179
  // The intake/return valves are skipped for non-shared systems.
173
180
  sys.valves.removeItemById(3);
@@ -187,7 +194,8 @@ export class ValveMessage {
187
194
  ndx += 2;
188
195
  }
189
196
  let valve: Valve = sys.valves.getItemById(id, true);
190
- valve.isVirtual = false;
197
+ valve.master = 0;
198
+ // valve.isVirtual = false;
191
199
  if (id === 3 || id === 5) {
192
200
  valve.circuit = 247; // Hardcode the intake/return to pool/spa;
193
201
  valve.isIntake = true;
@@ -214,6 +222,7 @@ export class ValveMessage {
214
222
  }
215
223
  // Sort them so they are in valve id order. This will ensure any OCP valves come first in the list. Valves ids > 50 are virtual valves.
216
224
  sys.valves.sortById();
225
+ msg.isProcessed = true;
217
226
  }
218
227
  private static processValveNames(msg: Inbound) {
219
228
  let byte = msg.extractPayloadByte(1);
@@ -228,5 +237,6 @@ export class ValveMessage {
228
237
  if (typeof sys.valves.find(elem => elem.id === valveId) !== 'undefined') {
229
238
  state.valves.getItemById(valveId).name = sys.valves.getItemById(valveId++).name = msg.extractPayloadString(18, 16);
230
239
  }
240
+ msg.isProcessed = true;
231
241
  }
232
242
  }
@@ -64,9 +64,8 @@ export class ChlorinatorStateMessage {
64
64
  //[16, 2, 0, 3][0, 73, 110, 116, 101, 108, 108, 105, 99, 104, 108, 111, 114, 45, 45, 52, 48][188, 16, 3]
65
65
  // This is the model number of the chlorinator and the address is actually the second byte.
66
66
  let name = msg.extractPayloadString(1, 16);
67
- chlor.model = name;
68
- if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = name;
69
- cstate.name = chlor.name;
67
+ if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = cstate.name = name;
68
+ if (typeof chlor.model === 'undefined') chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
70
69
  cstate.isActive = chlor.isActive;
71
70
  state.emitEquipmentChanges();
72
71
  break;
@@ -101,7 +101,9 @@ export class EquipmentStateMessage {
101
101
  Message.headerSubByte = msg.header[1];
102
102
  //console.log(process.memoryUsage());
103
103
  if (msg.action === 2 && state.isInitialized && sys.controllerType === ControllerType.Nixie) {
104
- // Start over because we didn't have communication before but we now do.
104
+ // Start over because we didn't have communication before but we now do. This will fall into the if
105
+ // below so that it goes through the intialization process. In this case we didn't see an OCP when we started
106
+ // but there clearly is one now.
105
107
  sys.controllerType = ControllerType.Unknown;
106
108
  state.status = 0;
107
109
  }
@@ -139,19 +141,24 @@ export class EquipmentStateMessage {
139
141
 
140
142
  // Shared
141
143
  let dt = new Date();
142
- if (state.chemControllers.length > 0) {
143
- // TODO: move this to chemController when we understand the packets better
144
- for (let i = 0; i < state.chemControllers.length; i++) {
145
- let ccontroller = state.chemControllers.getItemByIndex(i);
146
- if (sys.board.valueMaps.chemControllerTypes.getName(ccontroller.type) === 'intellichem') {
147
- if (dt.getTime() - ccontroller.lastComm > 60000) ccontroller.status = 1;
148
- }
149
- }
150
- }
144
+ // RKS: This was moved to the ChemControllerState message. This is flawed in that it incorrectly sets IntelliChem to no comms.
145
+ //if (state.chemControllers.length > 0) {
146
+ // // TODO: move this to chemController when we understand the packets better
147
+ // for (let i = 0; i < state.chemControllers.length; i++) {
148
+ // let ccontroller = state.chemControllers.getItemByIndex(i);
149
+ // if (sys.board.valueMaps.chemControllerTypes.getName(ccontroller.type) === 'intellichem') {
150
+ // if (dt.getTime() - ccontroller.lastComm > 60000) ccontroller.status = 1;
151
+ // }
152
+ // }
153
+ //}
151
154
  state.time.hours = msg.extractPayloadByte(0);
152
155
  state.time.minutes = msg.extractPayloadByte(1);
153
156
  state.time.seconds = dt.getSeconds();
154
- state.mode = msg.extractPayloadByte(9) & 0x81;
157
+ state.mode = sys.controllerType !== ControllerType.IntelliCenter ? (msg.extractPayloadByte(9) & 0x81) : (msg.extractPayloadByte(9) & 0x01);
158
+
159
+ // RKS: The units have been normalized for English and Metric for the overall panel. It is important that the val numbers match for at least the temp units since
160
+ // the only unit of measure native to the Touch controllers is temperature they chose to name these C or F. However, with the njsPC extensions this is non-semantic
161
+ // since pressure, volume, and length have been introduced.
155
162
  sys.general.options.units = state.temps.units = msg.extractPayloadByte(9) & 0x04;
156
163
  state.valve = msg.extractPayloadByte(10);
157
164
 
@@ -163,8 +170,8 @@ export class EquipmentStateMessage {
163
170
  // 1. IntelliCenter has "manual" time set (Internet will automatically adjust) and autoAdjustDST is enabled
164
171
  // 2. *Touch is "manual" (only option) and autoAdjustDST is enabled - (same as #1)
165
172
  // 3. clock source is "server" isn't an OCP option but can be enabled on the clients
166
- if (dt.getMinutes() % 5 === 0 && sys.general.options.clockSource === 'server') {
167
- if ((Math.abs(dt.getTime() - state.time.getTime()) > 60 * 5 * 1000) && !state.time.isUpdating) {
173
+ if (dt.getMinutes() % 5 === 0 && dt.getSeconds() <= 10 && sys.general.options.clockSource === 'server') {
174
+ if ((Math.abs(dt.getTime() - state.time.getTime()) > 60 * 2 * 1000) && !state.time.isUpdating) {
168
175
  state.time.isUpdating = true;
169
176
  sys.board.system.setDateTimeAsync({ dt, dst: sys.general.options.adjustDST || 0, })
170
177
  .then(() => {
@@ -197,10 +204,19 @@ export class EquipmentStateMessage {
197
204
  tbody.name = cbody.name;
198
205
  tbody.circuit = cbody.circuit = 6;
199
206
  tbody.heatStatus = msg.extractPayloadByte(11) & 0x0F;
200
- if ((msg.extractPayloadByte(2) & 0x20) === 32) {
207
+ // With the IntelliCenter i10D, bit 6 is not reliable. It is not set properly and requires the 204 message
208
+ // to process the data.
209
+ if (!sys.equipment.dual) {
210
+ if ((msg.extractPayloadByte(2) & 0x20) === 32) {
211
+ tbody.temp = state.temps.waterSensor1;
212
+ tbody.isOn = true;
213
+ } else tbody.isOn = false;
214
+ }
215
+ else if (state.circuits.getItemById(6).isOn === true) {
201
216
  tbody.temp = state.temps.waterSensor1;
202
217
  tbody.isOn = true;
203
- } else tbody.isOn = false;
218
+ }
219
+ else tbody.isOn = false;
204
220
  }
205
221
  if (sys.bodies.length > 1) {
206
222
  const tbody: BodyTempState = state.temps.bodies.getItemById(2, true);
@@ -210,10 +226,16 @@ export class EquipmentStateMessage {
210
226
  tbody.name = cbody.name;
211
227
  tbody.circuit = cbody.circuit = 1;
212
228
  tbody.heatStatus = (msg.extractPayloadByte(11) & 0xF0) >> 4;
213
- if ((msg.extractPayloadByte(2) & 0x01) === 1) {
229
+ if (!sys.equipment.dual) {
230
+ if ((msg.extractPayloadByte(2) & 0x01) === 1) {
231
+ tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
232
+ tbody.isOn = true;
233
+ } else tbody.isOn = false;
234
+ } else if (state.circuits.getItemById(1).isOn === true) {
214
235
  tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
215
236
  tbody.isOn = true;
216
- } else tbody.isOn = false;
237
+ }
238
+ else tbody.isOn = false;
217
239
  }
218
240
  if (sys.bodies.length > 2) {
219
241
  state.temps.waterSensor3 = fnTempFromByte(msg.extractPayloadByte(20));
@@ -390,13 +412,14 @@ export class EquipmentStateMessage {
390
412
  sys.board.circuits.syncCircuitRelayStates();
391
413
  sys.board.circuits.syncVirtualCircuitStates();
392
414
  sys.board.valves.syncValveStates();
415
+ sys.board.filters.syncFilterStates();
393
416
  state.emitControllerChange();
394
417
  state.emitEquipmentChanges();
395
418
  sys.board.heaters.syncHeaterStates();
396
419
  break;
397
420
  }
398
- case ControllerType.IntelliCom:
399
421
  case ControllerType.EasyTouch:
422
+ case ControllerType.IntelliCom:
400
423
  case ControllerType.IntelliTouch:
401
424
  {
402
425
  EquipmentStateMessage.processTouchCircuits(msg);
@@ -405,6 +428,7 @@ export class EquipmentStateMessage {
405
428
  sys.board.features.syncGroupStates();
406
429
  sys.board.circuits.syncVirtualCircuitStates();
407
430
  sys.board.valves.syncValveStates();
431
+ sys.board.filters.syncFilterStates();
408
432
  state.emitControllerChange();
409
433
  state.emitEquipmentChanges();
410
434
  sys.board.heaters.syncHeaterStates();
@@ -495,8 +519,7 @@ export class EquipmentStateMessage {
495
519
  state.time.date = msg.extractPayloadByte(6);
496
520
  state.time.month = msg.extractPayloadByte(7);
497
521
  state.time.year = msg.extractPayloadByte(8);
498
- sys.equipment.controllerFirmware = (msg.extractPayloadByte(42)
499
- + (msg.extractPayloadByte(43) / 1000)).toString();
522
+ sys.equipment.controllerFirmware = (msg.extractPayloadByte(42) + (msg.extractPayloadByte(43) / 1000)).toString();
500
523
  if (sys.chlorinators.length > 0) {
501
524
  if (msg.extractPayloadByte(37, 255) !== 255) {
502
525
  const chlor = state.chlorinators.getItemById(1);
@@ -508,9 +531,38 @@ export class EquipmentStateMessage {
508
531
  }
509
532
  }
510
533
  ExternalMessage.processFeatureState(9, msg);
534
+ //if (sys.equipment.dual === true) {
535
+ // // For IntelliCenter i10D the body state is on byte 26 of the 204. This impacts circuit 6.
536
+ // let byte = msg.extractPayloadByte(26);
537
+ // let pstate = state.circuits.getItemById(6, true);
538
+ // let oldstate = pstate.isOn;
539
+ // pstate.isOn = ((byte & 0x0010) === 0x0010);
540
+ // logger.info(`Checking i10D pool state ${byte} old:${oldstate} new: ${pstate.isOn}`);
541
+ // //if (oldstate !== pstate.isOn) {
542
+ // state.temps.bodies.getItemById(1, true).isOn = pstate.isOn;
543
+ // sys.board.circuits.syncCircuitRelayStates();
544
+ // sys.board.circuits.syncVirtualCircuitStates();
545
+ // sys.board.valves.syncValveStates();
546
+ // sys.board.filters.syncFilterStates();
547
+ // sys.board.heaters.syncHeaterStates();
548
+ // //}
549
+ // if (oldstate !== pstate.isOn) pstate.emitEquipmentChange();
550
+ //}
551
+ // At this point normally on is ignored. Not sure what this does.
552
+ let cover1 = sys.covers.getItemById(1);
553
+ let cover2 = sys.covers.getItemById(2);
554
+ if (cover1.isActive) {
555
+ let scover1 = state.covers.getItemById(1, true);
556
+ scover1.name = cover1.name;
557
+ state.temps.bodies.getItemById(cover1.body + 1).isCovered = scover1.isClosed = (msg.extractPayloadByte(30) & 0x0001) > 0;
558
+ }
559
+ if (cover2.isActive) {
560
+ let scover2 = state.covers.getItemById(2, true);
561
+ scover2.name = cover2.name;
562
+ state.temps.bodies.getItemById(cover2.body + 1).isCovered = scover2.isClosed = (msg.extractPayloadByte(30) & 0x0002) > 0;
563
+ }
511
564
  msg.isProcessed = true;
512
- // state.emitControllerChange();
513
- // state.emitEquipmentChanges();
565
+ state.emitEquipmentChanges();
514
566
  break;
515
567
  }
516
568
  }
@@ -528,13 +580,15 @@ export class EquipmentStateMessage {
528
580
  let circuit = sys.circuits.getItemById(circuitId, false, { isActive: false });
529
581
  if (circuit.isActive !== false) {
530
582
  let cstate = state.circuits.getItemById(circuitId, circuit.isActive);
531
- let isOn = (byte & (1 << j)) > 0;
532
- sys.board.circuits.setEndTime(circuit, cstate, isOn);
583
+ // For IntelliCenter i10D body circuits are not reported here.
584
+ let isOn = ((circuitId === 6 || circuitId === 1) && sys.equipment.dual === true) ? cstate.isOn : (byte & (1 << j)) > 0;
585
+ //let isOn = (byte & (1 << j)) > 0;
533
586
  cstate.isOn = isOn;
534
587
  cstate.name = circuit.name;
535
588
  cstate.nameId = circuit.nameId;
536
589
  cstate.showInFeatures = circuit.showInFeatures;
537
590
  cstate.type = circuit.type;
591
+ sys.board.circuits.setEndTime(circuit, cstate, isOn);
538
592
  if (sys.controllerType === ControllerType.IntelliCenter) {
539
593
  // intellitouch sends a separate msg with themes
540
594
  switch (circuit.type) {
@@ -635,7 +689,7 @@ export class EquipmentStateMessage {
635
689
  case 144: // swim
636
690
  sys.board.circuits.sequenceLightGroupAsync(grp.id, 'swim');
637
691
  break;
638
- case 160: // swim
692
+ case 160: // set
639
693
  sys.board.circuits.sequenceLightGroupAsync(grp.id, 'set');
640
694
  break;
641
695
  case 190: // save
@@ -1,54 +1,87 @@
1
- /* nodejs-poolController. An application to control pool equipment.
2
- Copyright (C) 2016, 2017, 2018, 2019, 2020. Russell Goldin, tagyoureit. russ.goldin@gmail.com
3
-
4
- This program is free software: you can redistribute it and/or modify
5
- it under the terms of the GNU Affero General Public License as
6
- published by the Free Software Foundation, either version 3 of the
7
- License, or (at your option) any later version.
8
-
9
- This program is distributed in the hope that it will be useful,
10
- but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- GNU Affero General Public License for more details.
13
-
14
- You should have received a copy of the GNU Affero General Public License
15
- along with this program. If not, see <http://www.gnu.org/licenses/>.
16
- */
17
- import { Inbound, Protocol } from "../Messages";
18
- import { state, BodyTempState, HeaterState } from "../../../State";
19
- import { sys, ControllerType, Heater } from "../../../Equipment";
20
-
21
- export class HeaterStateMessage {
22
- public static process(msg: Inbound) {
23
- if (msg.protocol === Protocol.Heater) {
24
- switch (msg.action) {
25
- case 114: // This is a message from a master controlling the heater
26
- break;
27
- case 115:
28
- HeaterStateMessage.processHeaterStatus(msg);
29
- break;
30
- }
31
- }
32
- }
33
- public static processHeaterStatus(msg: Inbound) {
34
- // RKS: 07-03-21 - We only know byte 2 at this point for Ultratemp for the 115 message we are processing here. The
35
- // byte description
36
- // ------------------------------------------------
37
- // 0 Unknown
38
- // 1 Unknown
39
- // 2 Current heater status 0=off, 1=heat, 2=cool
40
- // 3-9 Unknown
41
- let heater: Heater = sys.heaters.getItemByAddress(msg.source);
42
- let sheater = state.heaters.getItemById(heater.id);
43
- // We need to decode the message. For a 2 of
44
- //[165, 1, 15, 16, 2, 29][16, 42, 3, 0, 0, 0, 0, 0, 0, 32, 0, 0, 2, 0, 88, 88, 0, 241, 95, 100, 24, 246, 0, 0, 0, 0, 0, 40, 0][4, 221]
45
- //[165, 0, 112, 16, 114, 10][144, 0, 0, 0, 0, 0, 0, 0, 0, 0][2, 49] // OCP to Heater
46
- //[165, 0, 16, 112, 115, 10][160, 1, 0, 3, 0, 0, 0, 0, 0, 0][2, 70] // Heater Reply
47
- let byte = msg.extractPayloadByte(2);
48
- sheater.isOn = byte >= 1;
49
- sheater.isCooling = byte === 2;
50
- sheater.commStatus = 0;
51
- state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
52
- msg.isProcessed = true;
53
- }
1
+ /* nodejs-poolController. An application to control pool equipment.
2
+ Copyright (C) 2016, 2017, 2018, 2019, 2020. Russell Goldin, tagyoureit. russ.goldin@gmail.com
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU Affero General Public License as
6
+ published by the Free Software Foundation, either version 3 of the
7
+ License, or (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU Affero General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Affero General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ */
17
+ import { Inbound, Protocol } from "../Messages";
18
+ import { state, BodyTempState, HeaterState } from "../../../State";
19
+ import { sys, ControllerType, Heater } from "../../../Equipment";
20
+
21
+ export class HeaterStateMessage {
22
+ public static process(msg: Inbound) {
23
+ if (msg.protocol === Protocol.Heater) {
24
+ switch (msg.action) {
25
+ case 112: // This is a message from a master controlling MasterTemp
26
+ case 114: // This is a message from a master controlling UltraTemp
27
+ msg.isProcessed = true;
28
+ break;
29
+ case 116:
30
+ HeaterStateMessage.processMasterTempStatus(msg);
31
+ break;
32
+ case 115:
33
+ HeaterStateMessage.processUltraTempStatus(msg);
34
+ break;
35
+ }
36
+ }
37
+ }
38
+ public static processUltraTempStatus(msg: Inbound) {
39
+ // RKS: 07-03-21 - We only know byte 2 at this point for Ultratemp for the 115 message we are processing here. The
40
+ // byte description
41
+ // ------------------------------------------------
42
+ // 0 Unknown (always seems to be 160 for response)
43
+ // 1 Unknown (always 1)
44
+ // 2 Current heater status 0=off, 1=heat, 2=cool
45
+ // 3-9 Unknown
46
+
47
+ // 114 message - outbound response
48
+ //[165, 0, 112, 16, 114, 10][144, 0, 0, 0, 0, 0, 0, 0, 0, 0][2, 49] // OCP to Heater
49
+ // byte description
50
+ // ------------------------------------------------
51
+ // 0 Unknown (always seems to be 144 for request)
52
+ // 1 Current heater status 0=off, 1=heat, 2=cool
53
+ // 3 Believed to be ofset temp
54
+ // 4-9 Unknown
55
+
56
+ // byto 0: always seems to be 144 for outbound
57
+ // byte 1: Sets heater mode to 0 = Off 1 = Heat 2 = Cool
58
+ //[165, 0, 16, 112, 115, 10][160, 1, 0, 3, 0, 0, 0, 0, 0, 0][2, 70] // Heater Reply
59
+ let heater: Heater = sys.heaters.getItemByAddress(msg.source);
60
+ let sheater = state.heaters.getItemById(heater.id);
61
+ let byte = msg.extractPayloadByte(2);
62
+ sheater.isOn = byte >= 1;
63
+ sheater.isCooling = byte === 2;
64
+ sheater.commStatus = 0;
65
+ state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
66
+ msg.isProcessed = true;
67
+ }
68
+ public static processMasterTempStatus(msg: Inbound) {
69
+ //[255, 0, 255][165, 0, 16, 112, 116, 23][67, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0][2, 66]
70
+ // Byte 1 is the indicator to which setpoint it is heating to.
71
+ // Byte 8 increments over time when the heater is on.
72
+ // Byte 13 looks like the mode the heater is in for instance it is in cooldown mode.
73
+ // 0 = Normal
74
+ // 2 = ??????
75
+ // 6 = Cooldown
76
+ // Byte 14 looks like the cooldown delay in minutes.
77
+ let heater: Heater = sys.heaters.getItemByAddress(msg.source);
78
+ let sheater = state.heaters.getItemById(heater.id);
79
+ let byte = msg.extractPayloadByte(1);
80
+ sheater.isOn = byte >= 1;
81
+ sheater.isCooling = false;
82
+ sheater.commStatus = 0;
83
+ state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
84
+ msg.isProcessed = true;
85
+ }
86
+
54
87
  }