nodejs-poolcontroller 7.6.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/.eslintrc.json +44 -44
- 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/CONTRIBUTING.md +74 -74
- package/Changelog +220 -215
- package/Dockerfile +17 -17
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +191 -191
- package/app.ts +1 -1
- package/config/Config.ts +14 -0
- package/config/VersionCheck.ts +2 -2
- package/controller/Constants.ts +2 -1
- package/controller/Equipment.ts +2484 -2459
- package/controller/Errors.ts +180 -180
- package/controller/Lockouts.ts +502 -436
- package/controller/State.ts +106 -30
- package/controller/boards/AquaLinkBoard.ts +1000 -0
- package/controller/boards/BoardFactory.ts +49 -45
- package/controller/boards/EasyTouchBoard.ts +2859 -2653
- package/controller/boards/IntelliCenterBoard.ts +4198 -4230
- package/controller/boards/IntelliComBoard.ts +63 -63
- package/controller/boards/IntelliTouchBoard.ts +273 -241
- package/controller/boards/NixieBoard.ts +1728 -1675
- package/controller/boards/SystemBoard.ts +4925 -4697
- package/controller/comms/Comms.ts +442 -479
- package/controller/comms/messages/Messages.ts +171 -25
- package/controller/comms/messages/config/ChlorinatorMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitGroupMessage.ts +0 -0
- package/controller/comms/messages/config/CircuitMessage.ts +1 -0
- package/controller/comms/messages/config/ConfigMessage.ts +0 -0
- package/controller/comms/messages/config/CoverMessage.ts +0 -0
- package/controller/comms/messages/config/CustomNameMessage.ts +30 -30
- package/controller/comms/messages/config/EquipmentMessage.ts +0 -0
- package/controller/comms/messages/config/ExternalMessage.ts +0 -0
- package/controller/comms/messages/config/FeatureMessage.ts +0 -0
- package/controller/comms/messages/config/GeneralMessage.ts +0 -0
- package/controller/comms/messages/config/HeaterMessage.ts +142 -10
- package/controller/comms/messages/config/IntellichemMessage.ts +0 -0
- package/controller/comms/messages/config/OptionsMessage.ts +4 -21
- package/controller/comms/messages/config/PumpMessage.ts +53 -35
- package/controller/comms/messages/config/RemoteMessage.ts +0 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +350 -347
- package/controller/comms/messages/config/SecurityMessage.ts +0 -0
- package/controller/comms/messages/config/ValveMessage.ts +1 -1
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +38 -86
- package/controller/comms/messages/status/EquipmentStateMessage.ts +58 -22
- package/controller/comms/messages/status/HeaterStateMessage.ts +116 -86
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +445 -445
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +35 -35
- package/controller/comms/messages/status/PumpStateMessage.ts +23 -1
- package/controller/comms/messages/status/VersionMessage.ts +0 -0
- package/controller/nixie/Nixie.ts +162 -162
- package/controller/nixie/NixieEquipment.ts +103 -103
- package/controller/nixie/bodies/Body.ts +120 -120
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2511 -2498
- package/controller/nixie/chemistry/Chlorinator.ts +363 -314
- package/controller/nixie/circuits/Circuit.ts +261 -248
- package/controller/nixie/heaters/Heater.ts +650 -648
- package/controller/nixie/pumps/Pump.ts +906 -661
- package/controller/nixie/schedules/Schedule.ts +313 -257
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +306 -286
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +0 -0
- package/package.json +56 -56
- package/tsconfig.json +25 -25
- package/web/Server.ts +92 -47
- package/web/bindings/aqualinkD.json +505 -0
- package/web/bindings/influxDB.json +1051 -1021
- package/web/bindings/mqtt.json +702 -654
- package/web/bindings/mqttAlt.json +731 -684
- package/web/bindings/rulesManager.json +54 -54
- package/web/bindings/smartThings-Hubitat.json +31 -31
- package/web/bindings/valveRelays.json +20 -20
- package/web/bindings/vera.json +25 -25
- package/web/interfaces/baseInterface.ts +137 -136
- package/web/interfaces/httpInterface.ts +145 -124
- package/web/interfaces/influxInterface.ts +276 -245
- package/web/interfaces/mqttInterface.ts +535 -475
- package/web/services/config/Config.ts +39 -18
- package/web/services/config/ConfigSocket.ts +0 -0
- package/web/services/state/State.ts +10 -0
- package/web/services/state/StateSocket.ts +4 -4
- package/web/services/utilities/Utilities.ts +44 -42
- 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
|
}
|
|
@@ -546,6 +661,9 @@ export class Inbound extends Message {
|
|
|
546
661
|
case Protocol.Chlorinator:
|
|
547
662
|
ChlorinatorStateMessage.process(this);
|
|
548
663
|
break;
|
|
664
|
+
case Protocol.Hayward:
|
|
665
|
+
PumpStateMessage.processHayward(this);
|
|
666
|
+
break;
|
|
549
667
|
default:
|
|
550
668
|
logger.debug(`Unprocessed Message ${this.toPacket()}`)
|
|
551
669
|
break;
|
|
@@ -553,15 +671,24 @@ export class Inbound extends Message {
|
|
|
553
671
|
}
|
|
554
672
|
}
|
|
555
673
|
class OutboundCommon extends Message {
|
|
556
|
-
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; }
|
|
557
675
|
public get sub() { return super.sub; }
|
|
558
|
-
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
|
+
}
|
|
559
681
|
public get dest() { return super.dest; }
|
|
560
|
-
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
|
+
}
|
|
561
686
|
public get source() { return super.source; }
|
|
562
|
-
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
|
+
}
|
|
563
690
|
public get action() { return super.action; }
|
|
564
|
-
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; }
|
|
565
692
|
public get datalen() { return super.datalen; }
|
|
566
693
|
public set chkHi(val: number) { if (this.protocol !== Protocol.Chlorinator) this.term[0] = val; }
|
|
567
694
|
public get chkHi() { return super.chkHi; }
|
|
@@ -581,9 +708,14 @@ class OutboundCommon extends Message {
|
|
|
581
708
|
this.chkHi = Math.floor(sum / 256);
|
|
582
709
|
this.chkLo = (sum - (super.chkHi * 256));
|
|
583
710
|
break;
|
|
711
|
+
case Protocol.AquaLink:
|
|
584
712
|
case Protocol.Chlorinator:
|
|
585
713
|
this.term[0] = sum;
|
|
586
714
|
break;
|
|
715
|
+
case Protocol.Hayward:
|
|
716
|
+
this.chkHi = Math.floor(sum / 256);
|
|
717
|
+
this.chkLo = (sum - (super.chkHi * 256));
|
|
718
|
+
break;
|
|
587
719
|
}
|
|
588
720
|
}
|
|
589
721
|
}
|
|
@@ -598,7 +730,7 @@ export class Outbound extends OutboundCommon {
|
|
|
598
730
|
this.header.length = 0;
|
|
599
731
|
this.term.length = 0;
|
|
600
732
|
this.payload.length = 0;
|
|
601
|
-
if (proto === Protocol.Chlorinator) {
|
|
733
|
+
if (proto === Protocol.Chlorinator || proto === Protocol.AquaLink) {
|
|
602
734
|
this.header.push.apply(this.header, [16, 2, 0, 0]);
|
|
603
735
|
this.term.push.apply(this.term, [0, 16, 3]);
|
|
604
736
|
}
|
|
@@ -612,6 +744,10 @@ export class Outbound extends OutboundCommon {
|
|
|
612
744
|
this.header.push.apply(this.header, [165, 0, 15, Message.pluginAddress, 0, 0]);
|
|
613
745
|
this.term.push.apply(this.term, [0, 0]);
|
|
614
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
|
+
}
|
|
615
751
|
this.scope = scope;
|
|
616
752
|
this.source = source;
|
|
617
753
|
this.dest = dest;
|
|
@@ -625,8 +761,19 @@ export class Outbound extends OutboundCommon {
|
|
|
625
761
|
}
|
|
626
762
|
// Factory
|
|
627
763
|
public static create(obj?: any) {
|
|
628
|
-
let
|
|
629
|
-
|
|
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;
|
|
630
777
|
out.onComplete = obj.onComplete;
|
|
631
778
|
out.onAbort = obj.onAbort;
|
|
632
779
|
out.timeout = obj.timeout;
|
|
@@ -659,7 +806,6 @@ export class Outbound extends OutboundCommon {
|
|
|
659
806
|
return false;
|
|
660
807
|
}
|
|
661
808
|
public get remainingTries(): number { return this.retries - this.tries + 1; } // Always allow 1 try.
|
|
662
|
-
|
|
663
809
|
public setPayloadByte(ndx: number, value: number, def?: number) {
|
|
664
810
|
if (typeof value === 'undefined' || isNaN(value)) value = def;
|
|
665
811
|
if (ndx < this.payload.length) this.payload[ndx] = value;
|
|
@@ -28,11 +28,12 @@ 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) {
|
|
@@ -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);
|
|
File without changes
|
|
@@ -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;
|
|
File without changes
|
|
File without changes
|
|
@@ -1,31 +1,31 @@
|
|
|
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 } from "../Messages";
|
|
18
|
-
import { sys } from "../../../Equipment";
|
|
19
|
-
export class CustomNameMessage
|
|
20
|
-
{
|
|
21
|
-
public static process ( msg: Inbound ): void
|
|
22
|
-
{
|
|
23
|
-
let customNameId = msg.extractPayloadByte( 0 );
|
|
24
|
-
let customName = sys.customNames.getItemById( customNameId, customNameId <= sys.equipment.maxCustomNames );
|
|
25
|
-
customName.name = msg.extractPayloadString( 1, 11 );
|
|
26
|
-
// customName.isActive = customNameId <= sys.equipment.maxCustomNames && !customName.name.includes('USERNAME-')
|
|
27
|
-
if (customNameId >= sys.equipment.maxCustomNames) sys.equipment.maxCustomNames = customNameId + 1;
|
|
28
|
-
sys.board.system.syncCustomNamesValueMap();
|
|
29
|
-
msg.isProcessed = true;
|
|
30
|
-
}
|
|
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 } from "../Messages";
|
|
18
|
+
import { sys } from "../../../Equipment";
|
|
19
|
+
export class CustomNameMessage
|
|
20
|
+
{
|
|
21
|
+
public static process ( msg: Inbound ): void
|
|
22
|
+
{
|
|
23
|
+
let customNameId = msg.extractPayloadByte( 0 );
|
|
24
|
+
let customName = sys.customNames.getItemById( customNameId, customNameId <= sys.equipment.maxCustomNames );
|
|
25
|
+
customName.name = msg.extractPayloadString( 1, 11 );
|
|
26
|
+
// customName.isActive = customNameId <= sys.equipment.maxCustomNames && !customName.name.includes('USERNAME-')
|
|
27
|
+
if (customNameId >= sys.equipment.maxCustomNames) sys.equipment.maxCustomNames = customNameId + 1;
|
|
28
|
+
sys.board.system.syncCustomNamesValueMap();
|
|
29
|
+
msg.isProcessed = true;
|
|
30
|
+
}
|
|
31
31
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|