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
@@ -42,6 +42,8 @@ import { TouchScheduleCommands } from "controller/boards/EasyTouchBoard";
42
42
  import { IntelliValveStateMessage } from "./status/IntelliValveStateMessage";
43
43
  import { IntelliChemStateMessage } from "./status/IntelliChemStateMessage";
44
44
  import { OutboundMessageError } from "../../Errors";
45
+ import { prototype } from "events";
46
+ import extend = require("extend");
45
47
  export enum Direction {
46
48
  In = 'in',
47
49
  Out = 'out'
@@ -54,6 +56,8 @@ export enum Protocol {
54
56
  IntelliChem = 'intellichem',
55
57
  IntelliValve = 'intellivalve',
56
58
  Heater = 'heater',
59
+ AquaLink = 'aqualink',
60
+ Hayward = 'hayward',
57
61
  Unidentified = 'unidentified'
58
62
  }
59
63
  export class Message {
@@ -67,7 +71,7 @@ export class Message {
67
71
  // Fields
68
72
  private static _messageId: number = 0;
69
73
  public static get nextMessageId(): number { return this._messageId < 80000 ? ++this._messageId : this._messageId = 0; }
70
-
74
+ public portId = 0; // This will be the target or source port for the message. If this is from or to an Aux RS485 port the value will be > 0.
71
75
  public timestamp: Date = new Date();
72
76
  public direction: Direction = Direction.In;
73
77
  public protocol: Protocol = Protocol.Unknown;
@@ -85,32 +89,56 @@ export class Message {
85
89
  public get isComplete(): boolean { return this._complete; }
86
90
  public get sub(): number { return this.header.length > 1 ? this.header[1] : -1; }
87
91
  public get dest(): number {
88
- if (this.protocol === Protocol.Chlorinator) {
89
- return this.header[2] >= 80 ? this.header[2] - 79 : 0;
92
+ if (this.header.length > 2) {
93
+ if (this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink) {
94
+ return this.header.length > 2 ? (this.header[2] >= 80 ? this.header[2] - 79 : 0) : -1;
95
+ }
96
+ else if (this.protocol === Protocol.Hayward) {
97
+ // src act dest
98
+ //0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
99
+ return this.header.length > 4 ? this.header[4] : -1;
100
+ }
101
+ else return this.header.length > 2 ? this.header[2] : -1;
90
102
  }
91
- if (this.header.length > 2) return this.header[2];
92
103
  else return -1;
93
104
  }
94
105
  public get source(): number {
95
106
  if (this.protocol === Protocol.Chlorinator) {
96
- return this.header[2] >= 80 ? 0 : 1;
107
+ return this.header.length > 2 ? (this.header[2] >= 80 ? 0 : 1) : -1;
97
108
  // have to assume incoming packets with header[2] >= 80 (sent to a chlorinator)
98
109
  // are from controller (0);
99
110
  // likewise, if the destination is 0 (controller) we
100
111
  // have to assume it was sent from the 1st chlorinator (1)
101
112
  // until we learn otherwise.
102
113
  }
114
+ else if (this.protocol === Protocol.AquaLink) {
115
+ // Once we decode the devices we will be able to tell where it came from based upon the commands.
116
+ return 0;
117
+ }
118
+ else if (this.protocol === Protocol.Hayward) {
119
+ // src act dest
120
+ //0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
121
+ //0x10, 0x02, 0x0C, 0x01, 0x02, 0x2D, 0x00, 0x4E, 0x10, 0x03 -- Command to AUX2 Pump
122
+ return this.header.length > 2 ? this.header[2] : -1;
123
+ }
103
124
  if (this.header.length > 3) return this.header[3];
104
125
  else return -1;
105
126
  }
106
127
  public get action(): number {
107
- if (this.protocol === Protocol.Chlorinator) return this.header[3];
108
- if (this.header.length > 5) return this.header[4];
128
+ // The action byte is actually the 4th byte in the header the destination address is the 5th byte.
129
+ if (this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink) return this.header.length > 3 ? this.header[3] : -1;
130
+ else if (this.protocol === Protocol.Hayward) {
131
+ // src act dest
132
+ //0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
133
+ //0x10, 0x02, 0x0C, 0x01, 0x02, 0x2D, 0x00, 0x4E, 0x10, 0x03 -- Command to AUX2 Pump
134
+ this.header.length > 3 ? this.header[3] : -1;
135
+ }
136
+ if (this.header.length > 4) return this.header[4];
109
137
  else return -1;
110
138
  }
111
- public get datalen(): number { return this.protocol === Protocol.Chlorinator ? this.payload.length : this.header.length > 5 ? this.header[5] : -1; }
112
- public get chkHi(): number { return this.protocol === Protocol.Chlorinator ? 0 : this.term.length > 0 ? this.term[0] : -1; }
113
- public get chkLo(): number { return this.protocol === Protocol.Chlorinator ? this.term[0] : this.term[1]; }
139
+ public get datalen(): number { return this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink || this.protocol === Protocol.Hayward ? this.payload.length : this.header.length > 5 ? this.header[5] : -1; }
140
+ public get chkHi(): number { return this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink ? 0 : this.term.length > 0 ? this.term[0] : -1; }
141
+ public get chkLo(): number { return this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink ? this.term[0] : this.term[1]; }
114
142
  public get checksum(): number {
115
143
  var sum = 0;
116
144
  for (let i = 0; i < this.header.length; i++) sum += this.header[i];
@@ -136,7 +164,7 @@ export class Message {
136
164
  return pkt;
137
165
  }
138
166
  public toLog(): string {
139
- return `{"id":${this.id},"valid":${this.isValid},"dir":"${this.direction}","proto":"${this.protocol}","pkt":[${JSON.stringify(this.padding)},${JSON.stringify(this.preamble)},${JSON.stringify(this.header)},${JSON.stringify(this.payload)},${JSON.stringify(this.term)}],"ts":"${Timestamp.toISOLocal(this.timestamp)}"}`;
167
+ return `{"port":${this.portId},"id":${ this.id },"valid":${ this.isValid },"dir":"${this.direction}","proto":"${this.protocol}","pkt":[${JSON.stringify(this.padding)},${JSON.stringify(this.preamble)}, ${JSON.stringify(this.header)}, ${JSON.stringify(this.payload)},${JSON.stringify(this.term)}],"ts":"${Timestamp.toISOLocal(this.timestamp)}"}`;
140
168
  }
141
169
  }
142
170
  export class Inbound extends Message {
@@ -158,26 +186,41 @@ export class Inbound extends Message {
158
186
  public responseFor: number[] = [];
159
187
  public isProcessed: boolean = false;
160
188
  public collisions: number = 0;
189
+ public rewinds: number = 0;
161
190
  // Private methods
162
191
  private isValidChecksum(): boolean {
163
- if (this.protocol === Protocol.Chlorinator) return this.checksum % 256 === this.chkLo;
192
+ if (this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink) return this.checksum % 256 === this.chkLo;
164
193
  return (this.chkHi * 256) + this.chkLo === this.checksum;
165
194
  }
166
195
  public toLog() {
167
196
  if (this.responseFor.length > 0)
168
- return `{"id":${this.id},"valid":${this.isValid},"dir":"${this.direction}","proto":"${this.protocol}","for":${JSON.stringify(this.responseFor)},"pkt":[${JSON.stringify(this.padding)},${JSON.stringify(this.preamble)},${JSON.stringify(this.header)},${JSON.stringify(this.payload)},${JSON.stringify(this.term)}],"ts": "${Timestamp.toISOLocal(this.timestamp)}"}`;
169
- return `{"id":${this.id},"valid":${this.isValid},"dir":"${this.direction}","proto":"${this.protocol}","pkt":[${JSON.stringify(this.padding)},${JSON.stringify(this.preamble)},${JSON.stringify(this.header)},${JSON.stringify(this.payload)},${JSON.stringify(this.term)}],"ts": "${Timestamp.toISOLocal(this.timestamp)}"}`;
197
+ return `{"port":${this.portId || 0},"id":${this.id},"valid":${this.isValid},"dir":"${ this.direction }","proto":"${ this.protocol }","for":${JSON.stringify(this.responseFor)},"pkt":[${JSON.stringify(this.padding)},${JSON.stringify(this.preamble)},${JSON.stringify(this.header)},${JSON.stringify(this.payload)},${JSON.stringify(this.term)}],"ts": "${ Timestamp.toISOLocal(this.timestamp) }"}`;
198
+ return `{"port":${this.portId || 0},"id":${this.id},"valid":${this.isValid},"dir":"${this.direction}","proto":"${this.protocol}","pkt":[${JSON.stringify(this.padding)},${JSON.stringify(this.preamble)},${JSON.stringify(this.header)},${JSON.stringify(this.payload)},${JSON.stringify(this.term)}],"ts": "${Timestamp.toISOLocal(this.timestamp)}"}`;
170
199
  }
171
200
  private testChlorHeader(bytes: number[], ndx: number): boolean {
172
201
  // if packets have 16,2 (eg status=16,2,29) in them and they come as partial packets, they would have
173
202
  // prev been detected as chlor packets;
174
203
  // valid chlor packets should have 16,2,0 or 16,2,[80-96];
175
204
  // this should reduce the number of false chlor packets
176
- return (ndx + 2 < bytes.length && bytes[ndx] === 16 && bytes[ndx + 1] === 2 && (bytes[ndx + 2] === 0 || (bytes[ndx + 2] >= 80 && bytes[ndx + 2] <= 96)))
205
+ return (ndx + 3 < bytes.length && bytes[ndx] === 16 && bytes[ndx + 1] === 2 && (bytes[ndx + 2] === 0 || (bytes[ndx + 2] >= 80 && bytes[ndx + 2] <= 96)))
206
+ }
207
+ private testAquaLinkHeader(bytes: number[], ndx: number): boolean {
208
+ return (sys.controllerType === 'aqualink' && ndx + 3 < bytes.length && bytes[ndx] === 16 && bytes[ndx + 1] === 2);
177
209
  }
210
+ private testHaywardHeader(bytes: number[], ndx: number): boolean {
211
+ //0x10, 0x02, 0x0C, 0x01, 0x00, 0x2D, 0x00, 0x4C, 0x10, 0x03 -- Command to pump
212
+ //0x10, 0x02, 0x0C, 0x01, 0x00, 0x2D, 0x00, 0x4C, 0x10, 0x03 -- Command to Filter Pump
213
+ //0x10, 0x02, 0x0C, 0x01, 0x02, 0x2D, 0x00, 0x4E, 0x10, 0x03 -- Command to AUX2 Pump
214
+ // src act dest
215
+ //0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
216
+ return (sys.controllerType === 'nixie' && ndx + 4 < bytes.length && bytes[ndx] === 16 && bytes[ndx + 1] === 2 && (bytes[ndx + 2] === 12 && bytes[ndx + 3] === 12));
217
+ }
218
+
178
219
  private testBroadcastHeader(bytes: number[], ndx: number): boolean { return ndx < bytes.length - 3 && bytes[ndx] === 255 && bytes[ndx + 1] === 0 && bytes[ndx + 2] === 255 && bytes[ndx + 3] === 165; }
179
220
  private testUnidentifiedHeader(bytes: number[], ndx: number): boolean { return ndx < bytes.length - 3 && bytes[ndx] === 255 && bytes[ndx + 1] === 0 && bytes[ndx + 2] === 255 && bytes[ndx + 3] !== 165; }
180
- private testChlorTerm(bytes: number[], ndx: number): boolean { return ndx < bytes.length - 2 && bytes[ndx + 1] === 16 && bytes[ndx + 2] === 3; }
221
+ private testChlorTerm(bytes: number[], ndx: number): boolean { return ndx + 2 < bytes.length && bytes[ndx + 1] === 16 && bytes[ndx + 2] === 3; }
222
+ private testAquaLinkTerm(bytes: number[], ndx: number): boolean { return ndx + 2 < bytes.length && bytes[ndx + 1] === 16 && bytes[ndx + 2] === 3; }
223
+ private testHaywardTerm(bytes: number[], ndx: number): boolean { return ndx + 3 < bytes.length && bytes[ndx + 2] === 16 && bytes[ndx + 3] === 3; }
181
224
  private pushBytes(target: number[], bytes: number[], ndx: number, length: number): number {
182
225
  let end = ndx + length;
183
226
  while (ndx < bytes.length && ndx < end)
@@ -205,6 +248,7 @@ export class Inbound extends Message {
205
248
  this.isValid = true;
206
249
 
207
250
  this.collisions++;
251
+ this.rewinds++;
208
252
  logger.info(`rewinding message collision ${this.collisions} ${ndx} ${bytes.length} ${JSON.stringify(buff)}`);
209
253
  this.readPacket(buff);
210
254
  return ndx;
@@ -233,6 +277,14 @@ export class Inbound extends Message {
233
277
  this.protocol = Protocol.Chlorinator;
234
278
  break;
235
279
  }
280
+ if (this.testAquaLinkHeader(bytes, ndx)) {
281
+ this.protocol = Protocol.AquaLink;
282
+ break;
283
+ }
284
+ if (this.testHaywardHeader(bytes, ndx)) {
285
+ this.protocol = Protocol.Hayward;
286
+ break;
287
+ }
236
288
  if (this.testBroadcastHeader(bytes, ndx)) {
237
289
  this.protocol = Protocol.Broadcast;
238
290
  break;
@@ -277,6 +329,7 @@ export class Inbound extends Message {
277
329
  this.preamble = [];
278
330
  this.header = [];
279
331
  this.collisions++;
332
+ this.rewinds++;
280
333
  return ndxHeader + 1;
281
334
  }
282
335
  break;
@@ -298,6 +351,28 @@ export class Inbound extends Message {
298
351
  return ndxHeader;
299
352
  }
300
353
  break;
354
+ case Protocol.Hayward:
355
+ ndx = this.pushBytes(this.header, bytes, ndx, 4);
356
+ if (this.header.length < 4) {
357
+ // We actually don't have a complete header yet so just return.
358
+ // we will pick it up next go around.
359
+ logger.debug(`We have an incoming AquaLink message but the serial port hasn't given a complete header. [${this.padding}][${this.preamble}][${this.header}]`);
360
+ this.preamble = [];
361
+ this.header = [];
362
+ return ndxHeader;
363
+ }
364
+ break;
365
+ case Protocol.AquaLink:
366
+ ndx = this.pushBytes(this.header, bytes, ndx, 5);
367
+ if (this.header.length < 5) {
368
+ // We actually don't have a complete header yet so just return.
369
+ // we will pick it up next go around.
370
+ logger.debug(`We have an incoming AquaLink message but the serial port hasn't given a complete header. [${this.padding}][${this.preamble}][${this.header}]`);
371
+ this.preamble = [];
372
+ this.header = [];
373
+ return ndxHeader;
374
+ }
375
+ break;
301
376
  default:
302
377
  // We didn't get a message signature. don't do anything with it.
303
378
  ndx = ndxStart;
@@ -341,6 +416,31 @@ export class Inbound extends Message {
341
416
  }
342
417
  }
343
418
  break;
419
+ case Protocol.AquaLink:
420
+ // We need to deal with AquaLink packets where the terminator is actually split meaning only the first byte or
421
+ // two of the total payload is provided for the term. We need at least 3 bytes to make this determination.
422
+ while (ndx + 3 <= bytes.length && !this.testAquaLinkTerm(bytes, ndx)) {
423
+ this.payload.push(bytes[ndx++]);
424
+ if (this.payload.length > 25) {
425
+ this.isValid = false; // We have a runaway packet. Some collision occurred so lets preserve future packets.
426
+ logger.debug(`AquaLink message marked as invalid after not finding 16,3 in payload after ${this.payload.length} bytes`);
427
+ break;
428
+ }
429
+ }
430
+ break;
431
+ case Protocol.Hayward:
432
+ // We need to deal with AquaLink packets where the terminator is actually split meaning only the first byte or
433
+ // two of the total payload is provided for the term. We need at least 3 bytes to make this determination.
434
+ while (ndx + 4 <= bytes.length && !this.testHaywardTerm(bytes, ndx)) {
435
+ this.payload.push(bytes[ndx++]);
436
+ if (this.payload.length > 25) {
437
+ this.isValid = false; // We have a runaway packet. Some collision occurred so lets preserve future packets.
438
+ logger.debug(`Hayward message marked as invalid after not finding 16,3 in payload after ${this.payload.length} bytes`);
439
+ break;
440
+ }
441
+ }
442
+ break;
443
+
344
444
  }
345
445
  return ndx;
346
446
  }
@@ -369,6 +469,21 @@ export class Inbound extends Message {
369
469
  this.isValid = this.isValidChecksum();
370
470
  }
371
471
  break;
472
+ case Protocol.AquaLink:
473
+ if (ndx + 3 <= bytes.length && this.testAquaLinkTerm(bytes, ndx)) {
474
+ this._complete = true;
475
+ ndx = this.pushBytes(this.term, bytes, ndx, 3);
476
+ this.isValid = this.isValidChecksum();
477
+ }
478
+ break;
479
+ case Protocol.Hayward:
480
+ if (ndx + 4 <= bytes.length && this.testHaywardTerm(bytes, ndx)) {
481
+ this._complete = true;
482
+ ndx = this.pushBytes(this.term, bytes, ndx, 4);
483
+ this.isValid = this.isValidChecksum();
484
+ }
485
+ break;
486
+
372
487
  }
373
488
  return ndx;
374
489
  }
@@ -487,14 +602,12 @@ export class Inbound extends Message {
487
602
  CircuitMessage.processTouch(this);
488
603
  break;
489
604
  case 40:
605
+ case 168:
490
606
  OptionsMessage.process(this);
491
607
  break;
492
608
  case 41:
493
609
  CircuitGroupMessage.process(this);
494
610
  break;
495
- case 168:
496
- if (sys.controllerType !== ControllerType.Unknown) HeaterMessage.process(this);
497
- break;
498
611
  case 197:
499
612
  EquipmentStateMessage.process(this); // Date/Time request
500
613
  break;
@@ -548,6 +661,9 @@ export class Inbound extends Message {
548
661
  case Protocol.Chlorinator:
549
662
  ChlorinatorStateMessage.process(this);
550
663
  break;
664
+ case Protocol.Hayward:
665
+ PumpStateMessage.processHayward(this);
666
+ break;
551
667
  default:
552
668
  logger.debug(`Unprocessed Message ${this.toPacket()}`)
553
669
  break;
@@ -555,15 +671,24 @@ export class Inbound extends Message {
555
671
  }
556
672
  }
557
673
  class OutboundCommon extends Message {
558
- public set sub(val: number) { if (this.protocol !== Protocol.Chlorinator) this.header[1] = val; }
674
+ public set sub(val: number) { if (this.protocol !== Protocol.Chlorinator && this.protocol !== Protocol.AquaLink) this.header[1] = val; }
559
675
  public get sub() { return super.sub; }
560
- public set dest(val: number) { this.protocol !== Protocol.Chlorinator ? this.header[2] = val : this.header[2] = val + 79; }
676
+ public set dest(val: number) {
677
+ if (this.protocol === Protocol.Chlorinator) this.header[2] = val + 79;
678
+ else if (this.protocol === Protocol.Hayward) this.header[4] = val;
679
+ else this.header[2] = val;
680
+ }
561
681
  public get dest() { return super.dest; }
562
- public set source(val: number) { if (this.protocol !== Protocol.Chlorinator) this.header[3] = val; }
682
+ public set source(val: number) {
683
+ if (this.protocol === Protocol.Hayward) this.header[2] = val;
684
+ else if (this.protocol !== Protocol.Chlorinator) this.header[3] = val;
685
+ }
563
686
  public get source() { return super.source; }
564
- public set action(val: number) { (this.protocol !== Protocol.Chlorinator) ? this.header[4] = val : this.header[3] = val; }
687
+ public set action(val: number) {
688
+ (this.protocol !== Protocol.Chlorinator && this.protocol !== Protocol.Hayward) ? this.header[4] = val : this.header[3] = val;
689
+ }
565
690
  public get action() { return super.action; }
566
- public set datalen(val: number) { if (this.protocol !== Protocol.Chlorinator) this.header[5] = val; }
691
+ public set datalen(val: number) { if (this.protocol !== Protocol.Chlorinator && this.protocol !== Protocol.Hayward) this.header[5] = val; }
567
692
  public get datalen() { return super.datalen; }
568
693
  public set chkHi(val: number) { if (this.protocol !== Protocol.Chlorinator) this.term[0] = val; }
569
694
  public get chkHi() { return super.chkHi; }
@@ -583,9 +708,14 @@ class OutboundCommon extends Message {
583
708
  this.chkHi = Math.floor(sum / 256);
584
709
  this.chkLo = (sum - (super.chkHi * 256));
585
710
  break;
711
+ case Protocol.AquaLink:
586
712
  case Protocol.Chlorinator:
587
713
  this.term[0] = sum;
588
714
  break;
715
+ case Protocol.Hayward:
716
+ this.chkHi = Math.floor(sum / 256);
717
+ this.chkLo = (sum - (super.chkHi * 256));
718
+ break;
589
719
  }
590
720
  }
591
721
  }
@@ -600,7 +730,7 @@ export class Outbound extends OutboundCommon {
600
730
  this.header.length = 0;
601
731
  this.term.length = 0;
602
732
  this.payload.length = 0;
603
- if (proto === Protocol.Chlorinator) {
733
+ if (proto === Protocol.Chlorinator || proto === Protocol.AquaLink) {
604
734
  this.header.push.apply(this.header, [16, 2, 0, 0]);
605
735
  this.term.push.apply(this.term, [0, 16, 3]);
606
736
  }
@@ -614,6 +744,10 @@ export class Outbound extends OutboundCommon {
614
744
  this.header.push.apply(this.header, [165, 0, 15, Message.pluginAddress, 0, 0]);
615
745
  this.term.push.apply(this.term, [0, 0]);
616
746
  }
747
+ else if (proto === Protocol.Hayward) {
748
+ this.header.push.apply(this.header, [16, 2, 0, 0, 0]);
749
+ this.term.push.apply(this.term, [0, 0, 16, 3]);
750
+ }
617
751
  this.scope = scope;
618
752
  this.source = source;
619
753
  this.dest = dest;
@@ -627,16 +761,27 @@ export class Outbound extends OutboundCommon {
627
761
  }
628
762
  // Factory
629
763
  public static create(obj?: any) {
630
- let out = new Outbound(obj.protocol || Protocol.Broadcast,
631
- obj.source || sys.board.commandSourceAddress || Message.pluginAddress, obj.dest || sys.board.commandDestAddress || 16, obj.action || 0, obj.payload || [], obj.retries || 0, obj.response || false, obj.scope || undefined);
764
+ let o = extend({
765
+ protocol: Protocol.Broadcast,
766
+ source: sys.board.commandSourceAddress || Message.pluginAddress,
767
+ dest: sys.board.commandDestAddress || 16,
768
+ action: 0,
769
+ payload: [],
770
+ retries: 0,
771
+ response: false,
772
+ }, obj, true);
773
+ let out = new Outbound(o.protocol, o.source, o.dest, o.action, o.payload, o.retries, o.response, o.scope);
774
+ //let out = new Outbound(obj.protocol || Protocol.Broadcast,
775
+ // obj.source || sys.board.commandSourceAddress || Message.pluginAddress, obj.dest || sys.board.commandDestAddress || 16, obj.action || 0, obj.payload || [], obj.retries || 0, obj.response || false, obj.scope || undefined);
776
+ out.portId = obj.portId || 0;
632
777
  out.onComplete = obj.onComplete;
633
778
  out.onAbort = obj.onAbort;
634
779
  out.timeout = obj.timeout;
635
- for (let i = 0; i < out.header.length; i++){
780
+ for (let i = 0; i < out.header.length; i++) {
636
781
  if (out.header[i] >= 0 && out.header[i] <= 255 && out.header[i] !== null && typeof out.header[i] !== 'undefined') continue;
637
782
  throw new OutboundMessageError(out, `Invalid header detected: ${out.toShortPacket()}`);
638
783
  }
639
- for (let i = 0; i < out.payload.length; i++){
784
+ for (let i = 0; i < out.payload.length; i++) {
640
785
  if (out.payload[i] >= 0 && out.payload[i] <= 255 && out.payload[i] !== null && typeof out.payload[i] !== 'undefined') continue;
641
786
  throw new OutboundMessageError(out, `Invalid payload detected: ${out.toShortPacket()}`);
642
787
  }
@@ -661,7 +806,6 @@ export class Outbound extends OutboundCommon {
661
806
  return false;
662
807
  }
663
808
  public get remainingTries(): number { return this.retries - this.tries + 1; } // Always allow 1 try.
664
-
665
809
  public setPayloadByte(ndx: number, value: number, def?: number) {
666
810
  if (typeof value === 'undefined' || isNaN(value)) value = def;
667
811
  if (ndx < this.payload.length) this.payload[ndx] = value;
@@ -28,17 +28,18 @@ export class ChlorinatorMessage {
28
28
  for (let i = 0; i < 4 && i + 30 < msg.payload.length; i++) {
29
29
  let isActive = msg.extractPayloadByte(i + 22) === 1;
30
30
  chlor = sys.chlorinators.getItemById(chlorId);
31
- if (chlor.master === 1) continue; // RSG: probably never need this. See Touch chlor below.
31
+ //if (chlor.master !== 0) continue; // RSG: probably never need this. See Touch chlor below.
32
32
  if (isActive) {
33
33
  chlor = sys.chlorinators.getItemById(chlorId, true);
34
34
  let schlor = state.chlorinators.getItemById(chlor.id, true);
35
35
  chlor.isActive = schlor.isActive = true;
36
+ chlor.master = 0;
36
37
  chlor.body = msg.extractPayloadByte(i + 2);
37
38
  chlor.type = msg.extractPayloadByte(i + 6);
38
39
  if (!chlor.disabled && !chlor.isDosing) {
39
40
  // RKS: We don't want to change the setpoints if our chem controller disabled
40
41
  // the chlorinator. These should be 0.
41
- if (msg.extractPayloadByte(i + 10) === 0) logger.info(`Changing pool setpoint to 0 ${msg.extractPayloadByte(i + 10)}`);
42
+ if (msg.extractPayloadByte(i + 10) === 0 && chlor.poolSetpoint > 0) logger.info(`Changing pool setpoint to 0 ${msg.extractPayloadByte(i + 10)}`);
42
43
 
43
44
  chlor.poolSetpoint = msg.extractPayloadByte(i + 10);
44
45
  chlor.spaSetpoint = msg.extractPayloadByte(i + 14);
@@ -51,6 +52,7 @@ export class ChlorinatorMessage {
51
52
  schlor.poolSetpoint = chlor.poolSetpoint;
52
53
  schlor.spaSetpoint = chlor.spaSetpoint;
53
54
  schlor.type = chlor.type;
55
+ schlor.model = chlor.model;
54
56
  schlor.isActive = chlor.isActive;
55
57
  schlor.superChlorHours = chlor.superChlorHours;
56
58
  state.emitEquipmentChanges();
@@ -69,9 +71,10 @@ export class ChlorinatorMessage {
69
71
  }
70
72
  }
71
73
  public static processTouch(msg: Inbound) {
74
+ //[255, 0, 255][165, 1, 15, 16, 25, 22][1, 90, 128, 58, 128, 0, 73, 110, 116, 101, 108, 108, 105, 99, 104, 108, 111, 114, 45, 45, 54, 48][8, 50]
72
75
  // This is for the 25 message that is broadcast from the OCP.
73
76
  let chlor = sys.chlorinators.getItemById(1);
74
- if (chlor.master === 1) return; // Some Aquarite chlors need more frequent control (via Nixie) but will be disabled via Touch. https://github.com/tagyoureit/nodejs-poolController/issues/349
77
+ if (chlor.master !== 0 && typeof chlor.master !== 'undefined') return; // Some Aquarite chlors need more frequent control (via Nixie) but will be disabled via Touch. https://github.com/tagyoureit/nodejs-poolController/issues/349
75
78
  let isActive = (msg.extractPayloadByte(0) & 0x01) === 1;
76
79
  if (isActive) {
77
80
  let chlor = sys.chlorinators.getItemById(1, true);
@@ -243,6 +243,7 @@ export class CircuitMessage {
243
243
  if (_isActive) {
244
244
  const type = functionId & 63;
245
245
  let circuit: ICircuit = sys.circuits.getInterfaceById(id, _isActive);
246
+ circuit.master = 0;
246
247
  circuit.name = sys.board.circuits.getNameById(nameId);
247
248
  circuit.nameId = nameId;
248
249
  circuit.type = type;
@@ -217,7 +217,7 @@ export class ExternalMessage {
217
217
  if (group.isActive) {
218
218
  for (let i = 0; i < 16; i++) {
219
219
  let circuitId = msg.extractPayloadByte(i + 6);
220
- let circuit = group.circuits.getItemById(i + 1, circuitId !== 255);
220
+ let circuit = group.circuits.getItemById(i + 1, circuitId < 255);
221
221
  if (circuitId === 255) group.circuits.removeItemById(i + 1);
222
222
  circuit.circuit = circuitId + 1;
223
223
 
@@ -429,13 +429,13 @@ export class ExternalMessage {
429
429
  // [11] = No sequencing underway.
430
430
  switch (byte) {
431
431
  case 0: // Sync
432
- lg.action = 1;
432
+ lg.action = sys.board.valueMaps.circuitActions.getValue('colorsync');
433
433
  break;
434
434
  case 1: // Color swim
435
- lg.action = 3;
435
+ lg.action = sys.board.valueMaps.circuitActions.getValue('colorswim');
436
436
  break;
437
437
  case 2: // Color set
438
- lg.action = 2;
438
+ lg.action = sys.board.valueMaps.circuitActions.getValue('colorset');
439
439
  break;
440
440
  default:
441
441
  lg.action = 0;
@@ -500,11 +500,14 @@ export class ExternalMessage {
500
500
  let startTime = msg.extractPayloadInt(3);
501
501
  let endTime = msg.extractPayloadInt(5);
502
502
  let circuit = msg.extractPayloadByte(7) + 1;
503
- let cfg = sys.schedules.getItemById(schedId, circuit !== 256 && startTime !== 0 && endTime !== 0);
504
- cfg.isActive = (circuit !== 256 && startTime !== 0 && endTime !== 0);
503
+ let isActive = (msg.extractPayloadByte(8) & 128) === 128; // Inactive schedules do not have bit 8 set.
504
+ let cfg = sys.schedules.getItemById(schedId, isActive);
505
+ let s = state.schedules.getItemById(schedId, cfg.isActive);
506
+ //cfg.isActive = (circuit !== 256);
505
507
  cfg.startTime = startTime;
506
508
  cfg.endTime = endTime;
507
509
  cfg.circuit = circuit;
510
+ cfg.isActive = isActive;
508
511
  let byte = msg.extractPayloadByte(8);
509
512
  cfg.scheduleType = (byte & 1 & 0xFF) === 1 ? 0 : 128;
510
513
  if ((byte & 4 & 0xFF) === 4) cfg.startTimeType = 1;
@@ -525,7 +528,6 @@ export class ExternalMessage {
525
528
  cfg.heatSource = hs;
526
529
  cfg.heatSetpoint = msg.extractPayloadByte(14);
527
530
  cfg.coolSetpoint = msg.extractPayloadByte(15);
528
- let s = state.schedules.getItemById(schedId, cfg.isActive);
529
531
  if (cfg.isActive) {
530
532
  let s = state.schedules.getItemById(schedId, cfg.isActive);
531
533
  s.isActive = cfg.isActive = true;
@@ -773,7 +775,7 @@ export class ExternalMessage {
773
775
  break;
774
776
  case 21: // Body 2 Cool Setpoint
775
777
  body = sys.bodies.getItemById(2, false);
776
- body.setPoint = msg.extractPayloadByte(24);
778
+ body.coolSetpoint = msg.extractPayloadByte(24);
777
779
  state.temps.bodies.getItemById(2).coolSetpoint = body.coolSetpoint;
778
780
  state.emitEquipmentChanges();
779
781
  msg.isProcessed = true;