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.
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/Changelog +19 -0
- package/Dockerfile +3 -3
- package/README.md +13 -8
- package/app.ts +1 -1
- package/config/Config.ts +38 -2
- package/config/VersionCheck.ts +27 -12
- package/controller/Constants.ts +2 -1
- package/controller/Equipment.ts +193 -9
- package/controller/Errors.ts +10 -0
- package/controller/Lockouts.ts +503 -0
- package/controller/State.ts +269 -64
- package/controller/boards/AquaLinkBoard.ts +1000 -0
- package/controller/boards/BoardFactory.ts +4 -0
- package/controller/boards/EasyTouchBoard.ts +468 -144
- package/controller/boards/IntelliCenterBoard.ts +466 -307
- package/controller/boards/IntelliTouchBoard.ts +37 -5
- package/controller/boards/NixieBoard.ts +671 -141
- package/controller/boards/SystemBoard.ts +1397 -641
- package/controller/comms/Comms.ts +462 -362
- package/controller/comms/messages/Messages.ts +174 -30
- package/controller/comms/messages/config/ChlorinatorMessage.ts +6 -3
- package/controller/comms/messages/config/CircuitMessage.ts +1 -0
- package/controller/comms/messages/config/ExternalMessage.ts +10 -8
- package/controller/comms/messages/config/HeaterMessage.ts +141 -29
- package/controller/comms/messages/config/OptionsMessage.ts +9 -2
- package/controller/comms/messages/config/PumpMessage.ts +53 -35
- package/controller/comms/messages/config/ScheduleMessage.ts +33 -25
- package/controller/comms/messages/config/ValveMessage.ts +2 -2
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +38 -86
- package/controller/comms/messages/status/EquipmentStateMessage.ts +59 -23
- package/controller/comms/messages/status/HeaterStateMessage.ts +57 -3
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +56 -8
- package/controller/comms/messages/status/PumpStateMessage.ts +23 -1
- package/controller/nixie/Nixie.ts +1 -1
- package/controller/nixie/bodies/Body.ts +3 -0
- package/controller/nixie/chemistry/ChemController.ts +164 -51
- package/controller/nixie/chemistry/Chlorinator.ts +137 -88
- package/controller/nixie/circuits/Circuit.ts +51 -19
- package/controller/nixie/heaters/Heater.ts +241 -31
- package/controller/nixie/pumps/Pump.ts +488 -206
- package/controller/nixie/schedules/Schedule.ts +91 -35
- package/controller/nixie/valves/Valve.ts +1 -1
- package/defaultConfig.json +20 -0
- package/package.json +21 -21
- package/web/Server.ts +94 -49
- package/web/bindings/aqualinkD.json +505 -0
- package/web/bindings/influxDB.json +71 -1
- package/web/bindings/mqtt.json +98 -39
- package/web/bindings/mqttAlt.json +59 -1
- package/web/interfaces/baseInterface.ts +1 -0
- package/web/interfaces/httpInterface.ts +23 -2
- package/web/interfaces/influxInterface.ts +45 -10
- package/web/interfaces/mqttInterface.ts +114 -54
- package/web/services/config/Config.ts +55 -132
- package/web/services/state/State.ts +81 -4
- package/web/services/state/StateSocket.ts +4 -4
- package/web/services/utilities/Utilities.ts +8 -6
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
- package/config copy.json +0 -300
- 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.
|
|
89
|
-
|
|
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
|
-
|
|
108
|
-
if (this.header.length >
|
|
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)}
|
|
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 +
|
|
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
|
|
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) {
|
|
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) {
|
|
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) {
|
|
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
|
|
631
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
432
|
+
lg.action = sys.board.valueMaps.circuitActions.getValue('colorsync');
|
|
433
433
|
break;
|
|
434
434
|
case 1: // Color swim
|
|
435
|
-
lg.action =
|
|
435
|
+
lg.action = sys.board.valueMaps.circuitActions.getValue('colorswim');
|
|
436
436
|
break;
|
|
437
437
|
case 2: // Color set
|
|
438
|
-
lg.action =
|
|
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
|
|
504
|
-
cfg
|
|
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.
|
|
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;
|