nodejs-poolcontroller 7.7.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +26 -35
- package/Changelog +22 -0
- package/README.md +7 -3
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +13 -9
- package/config/VersionCheck.ts +6 -2
- package/controller/Constants.ts +58 -25
- package/controller/Equipment.ts +225 -41
- package/controller/Errors.ts +2 -1
- package/controller/Lockouts.ts +34 -2
- package/controller/State.ts +491 -48
- package/controller/boards/AquaLinkBoard.ts +6 -3
- package/controller/boards/BoardFactory.ts +5 -1
- package/controller/boards/EasyTouchBoard.ts +1971 -1751
- package/controller/boards/IntelliCenterBoard.ts +1311 -1688
- package/controller/boards/IntelliComBoard.ts +7 -1
- package/controller/boards/IntelliTouchBoard.ts +153 -42
- package/controller/boards/NixieBoard.ts +209 -66
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +1862 -1543
- package/controller/comms/Comms.ts +539 -138
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +242 -60
- package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +81 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +3 -1
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +12 -6
- package/controller/comms/messages/config/PumpMessage.ts +9 -12
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +43 -26
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
- package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
- package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
- package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +15 -4
- package/controller/nixie/NixieEquipment.ts +1 -0
- package/controller/nixie/chemistry/ChemController.ts +300 -129
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +133 -129
- package/controller/nixie/circuits/Circuit.ts +171 -30
- package/controller/nixie/heaters/Heater.ts +337 -173
- package/controller/nixie/pumps/Pump.ts +264 -236
- package/controller/nixie/schedules/Schedule.ts +9 -3
- package/defaultConfig.json +46 -5
- package/logger/Logger.ts +38 -9
- package/package.json +13 -9
- package/web/Server.ts +235 -122
- package/web/bindings/aqualinkD.json +114 -59
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +15 -0
- package/web/bindings/mqtt.json +28 -9
- package/web/bindings/mqttAlt.json +15 -0
- package/web/interfaces/baseInterface.ts +58 -7
- package/web/interfaces/httpInterface.ts +5 -2
- package/web/interfaces/influxInterface.ts +9 -2
- package/web/interfaces/mqttInterface.ts +234 -74
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +140 -33
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +144 -3
- package/web/services/state/StateSocket.ts +65 -14
- package/web/services/utilities/Utilities.ts +189 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -42,8 +43,10 @@ import { TouchScheduleCommands } from "controller/boards/EasyTouchBoard";
|
|
|
42
43
|
import { IntelliValveStateMessage } from "./status/IntelliValveStateMessage";
|
|
43
44
|
import { IntelliChemStateMessage } from "./status/IntelliChemStateMessage";
|
|
44
45
|
import { OutboundMessageError } from "../../Errors";
|
|
45
|
-
import {
|
|
46
|
+
import { conn } from "../Comms"
|
|
46
47
|
import extend = require("extend");
|
|
48
|
+
import { MessagesMock } from "../../../anslq25/MessagesMock";
|
|
49
|
+
|
|
47
50
|
export enum Direction {
|
|
48
51
|
In = 'in',
|
|
49
52
|
Out = 'out'
|
|
@@ -70,7 +73,10 @@ export class Message {
|
|
|
70
73
|
private _id: number = -1;
|
|
71
74
|
// Fields
|
|
72
75
|
private static _messageId: number = 0;
|
|
73
|
-
public static get nextMessageId(): number {
|
|
76
|
+
public static get nextMessageId(): number {
|
|
77
|
+
let i = this._messageId < 80000 ? ++this._messageId : this._messageId = 0;
|
|
78
|
+
logger.debug(`Assigning message id ${i}`)
|
|
79
|
+
return i; }
|
|
74
80
|
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.
|
|
75
81
|
public timestamp: Date = new Date();
|
|
76
82
|
public direction: Direction = Direction.In;
|
|
@@ -85,18 +91,19 @@ export class Message {
|
|
|
85
91
|
public set id(val: number) { this._id = val; }
|
|
86
92
|
public isValid: boolean = true;
|
|
87
93
|
public scope: string;
|
|
94
|
+
public isClone: boolean;
|
|
88
95
|
// Properties
|
|
89
96
|
public get isComplete(): boolean { return this._complete; }
|
|
90
97
|
public get sub(): number { return this.header.length > 1 ? this.header[1] : -1; }
|
|
91
98
|
public get dest(): number {
|
|
92
99
|
if (this.header.length > 2) {
|
|
93
100
|
if (this.protocol === Protocol.Chlorinator || this.protocol === Protocol.AquaLink) {
|
|
94
|
-
return this.header.length > 2 ? (this.header[2] >= 80 ? this.header[2]
|
|
101
|
+
return this.header.length > 2 ? (this.header[2] >= 80 ? this.header[2] : 0) : -1;
|
|
95
102
|
}
|
|
96
103
|
else if (this.protocol === Protocol.Hayward) {
|
|
97
104
|
// src act dest
|
|
98
105
|
//0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
|
|
99
|
-
return this.header.length > 4 ? this.header[
|
|
106
|
+
return this.header.length > 4 ? this.header[2] : -1;
|
|
100
107
|
}
|
|
101
108
|
else return this.header.length > 2 ? this.header[2] : -1;
|
|
102
109
|
}
|
|
@@ -104,7 +111,7 @@ export class Message {
|
|
|
104
111
|
}
|
|
105
112
|
public get source(): number {
|
|
106
113
|
if (this.protocol === Protocol.Chlorinator) {
|
|
107
|
-
return this.header.length > 2 ? (this.header[2] >= 80 ? 0 :
|
|
114
|
+
return this.header.length > 2 ? (this.header[2] >= 80 ? 0 : this.header[2]) : -1;
|
|
108
115
|
// have to assume incoming packets with header[2] >= 80 (sent to a chlorinator)
|
|
109
116
|
// are from controller (0);
|
|
110
117
|
// likewise, if the destination is 0 (controller) we
|
|
@@ -119,19 +126,20 @@ export class Message {
|
|
|
119
126
|
// src act dest
|
|
120
127
|
//0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
|
|
121
128
|
//0x10, 0x02, 0x0C, 0x01, 0x02, 0x2D, 0x00, 0x4E, 0x10, 0x03 -- Command to AUX2 Pump
|
|
122
|
-
return this.header.length >
|
|
129
|
+
return this.header.length > 4 ? this.header[4] : -1;
|
|
123
130
|
}
|
|
124
131
|
if (this.header.length > 3) return this.header[3];
|
|
125
132
|
else return -1;
|
|
126
133
|
}
|
|
127
134
|
public get action(): number {
|
|
128
135
|
// The action byte is actually the 4th byte in the header the destination address is the 5th byte.
|
|
129
|
-
if (this.protocol === Protocol.Chlorinator ||
|
|
136
|
+
if (this.protocol === Protocol.Chlorinator ||
|
|
137
|
+
this.protocol === Protocol.AquaLink) return this.header.length > 3 ? this.header[3] : -1;
|
|
130
138
|
else if (this.protocol === Protocol.Hayward) {
|
|
131
139
|
// src act dest
|
|
132
140
|
//0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
|
|
133
141
|
//0x10, 0x02, 0x0C, 0x01, 0x02, 0x2D, 0x00, 0x4E, 0x10, 0x03 -- Command to AUX2 Pump
|
|
134
|
-
this.header.length > 3 ? this.header[3] : -1;
|
|
142
|
+
return this.header.length > 3 ? this.header[3] || this.header[2] : -1;
|
|
135
143
|
}
|
|
136
144
|
if (this.header.length > 4) return this.header[4];
|
|
137
145
|
else return -1;
|
|
@@ -164,7 +172,59 @@ export class Message {
|
|
|
164
172
|
return pkt;
|
|
165
173
|
}
|
|
166
174
|
public toLog(): string {
|
|
167
|
-
return `{"port":${this.portId},"id":${
|
|
175
|
+
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)}"}`;
|
|
176
|
+
}
|
|
177
|
+
public static convertOutboundToInbound(out: Outbound): Inbound {
|
|
178
|
+
let inbound = new Inbound();
|
|
179
|
+
inbound.portId = out.portId;
|
|
180
|
+
// inbound.id = Message.nextMessageId;
|
|
181
|
+
inbound.protocol = out.protocol;
|
|
182
|
+
inbound.scope = out.scope;
|
|
183
|
+
inbound.preamble = out.preamble;
|
|
184
|
+
inbound.padding = out.padding;
|
|
185
|
+
inbound.header = out.header;
|
|
186
|
+
inbound.payload = [...out.payload];
|
|
187
|
+
inbound.term = out.term;
|
|
188
|
+
inbound.portId = out.portId;
|
|
189
|
+
return inbound;
|
|
190
|
+
}
|
|
191
|
+
public static convertInboundToOutbound(inbound: Inbound): Outbound {
|
|
192
|
+
let out = new Outbound(
|
|
193
|
+
inbound.protocol,
|
|
194
|
+
inbound.source,
|
|
195
|
+
inbound.dest,
|
|
196
|
+
inbound.action,
|
|
197
|
+
inbound.payload,
|
|
198
|
+
);
|
|
199
|
+
out.scope = inbound.scope;
|
|
200
|
+
out.preamble = inbound.preamble;
|
|
201
|
+
out.padding = inbound.padding;
|
|
202
|
+
out.header = inbound.header;
|
|
203
|
+
out.term = inbound.term;
|
|
204
|
+
out.portId = inbound.portId;
|
|
205
|
+
return out;
|
|
206
|
+
}
|
|
207
|
+
public clone(): Inbound | Outbound {
|
|
208
|
+
let msg;
|
|
209
|
+
if (this instanceof Inbound) {
|
|
210
|
+
msg = new Inbound();
|
|
211
|
+
msg.id = Message.nextMessageId;
|
|
212
|
+
msg.scope = this.scope;
|
|
213
|
+
msg.preamble = this.preamble;
|
|
214
|
+
msg.padding = this.padding;
|
|
215
|
+
msg.payload = [...this.payload];
|
|
216
|
+
msg.header = this.header;
|
|
217
|
+
msg.term = this.term;
|
|
218
|
+
msg.portId = this.portId;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
msg = new Outbound(
|
|
222
|
+
this.protocol, this.source, this.dest, this.action, [...this.payload],
|
|
223
|
+
);
|
|
224
|
+
msg.portId = this.portId;
|
|
225
|
+
msg.scope = this.scope;
|
|
226
|
+
}
|
|
227
|
+
return msg;
|
|
168
228
|
}
|
|
169
229
|
}
|
|
170
230
|
export class Inbound extends Message {
|
|
@@ -194,7 +254,7 @@ export class Inbound extends Message {
|
|
|
194
254
|
}
|
|
195
255
|
public toLog() {
|
|
196
256
|
if (this.responseFor.length > 0)
|
|
197
|
-
return `{"port":${this.portId || 0},"id":${this.id},"valid":${this.isValid},"dir":"${
|
|
257
|
+
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
258
|
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)}"}`;
|
|
199
259
|
}
|
|
200
260
|
private testChlorHeader(bytes: number[], ndx: number): boolean {
|
|
@@ -202,22 +262,74 @@ export class Inbound extends Message {
|
|
|
202
262
|
// prev been detected as chlor packets;
|
|
203
263
|
// valid chlor packets should have 16,2,0 or 16,2,[80-96];
|
|
204
264
|
// this should reduce the number of false chlor packets
|
|
205
|
-
|
|
265
|
+
// For any of these 16,2 type headers we need at least 5 bytes to determine the routing.
|
|
266
|
+
//63,15,16,2,29,9,36,0,0,0,0,0,16,0,32,0,0,2,0,75,75,32,241,80,85,24,241,16,16,48,245,69,45,100,186,16,2,80,17,0,115,16,3
|
|
267
|
+
if (bytes.length > ndx + 4) {
|
|
268
|
+
if (bytes[ndx] === 16 && bytes[ndx + 1] === 2) {
|
|
269
|
+
let dst = bytes[ndx + 2];
|
|
270
|
+
let act = bytes[ndx + 3];
|
|
271
|
+
// For now the dst byte will always be 0 or 80.
|
|
272
|
+
if (![0, 16, 80, 81, 82, 83].includes(dst)) {
|
|
273
|
+
//logger.info(`Sensed chlorinator header but the dst byte is ${dst}`);
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
else if (dst === 0 && [1, 18, 3].includes(act))
|
|
277
|
+
return true;
|
|
278
|
+
else if (![0, 17, 19, 20, 21, 22].includes(act)) {
|
|
279
|
+
//logger.info(`Sensed out chlorinator header but the dst byte is ${dst} ${act} ${JSON.stringify(bytes)}`);
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
206
286
|
}
|
|
207
287
|
private testAquaLinkHeader(bytes: number[], ndx: number): boolean {
|
|
208
|
-
|
|
288
|
+
if (bytes.length > ndx + 4 && sys.controllerType === 'aqualink') {
|
|
289
|
+
if (bytes[ndx] === 16 && bytes[ndx + 1] === 2) {
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
209
294
|
}
|
|
210
295
|
private testHaywardHeader(bytes: number[], ndx: number): boolean {
|
|
211
296
|
//0x10, 0x02, 0x0C, 0x01, 0x00, 0x2D, 0x00, 0x4C, 0x10, 0x03 -- Command to pump
|
|
297
|
+
//[16,2,12,1,0]
|
|
212
298
|
//0x10, 0x02, 0x0C, 0x01, 0x00, 0x2D, 0x00, 0x4C, 0x10, 0x03 -- Command to Filter Pump
|
|
299
|
+
//[16,2,12,1,0]
|
|
213
300
|
//0x10, 0x02, 0x0C, 0x01, 0x02, 0x2D, 0x00, 0x4E, 0x10, 0x03 -- Command to AUX2 Pump
|
|
301
|
+
//[16,2,12,1,2]
|
|
214
302
|
// src act dest
|
|
215
303
|
//0x10, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x2D, 0x02, 0x36, 0x00, 0x83, 0x10, 0x03 -- Response from pump
|
|
216
|
-
|
|
304
|
+
//[16,2,0,12,0] --> Response
|
|
305
|
+
//[16,2,0,12,0]
|
|
306
|
+
if (bytes.length > ndx + 4) {
|
|
307
|
+
if (sys.controllerType === 'aqualink') return false;
|
|
308
|
+
if (bytes[ndx] === 16 && bytes[ndx + 1] === 2) {
|
|
309
|
+
let dst = bytes[ndx + 3];
|
|
310
|
+
let src = bytes[ndx + 2];
|
|
311
|
+
if (dst === 12 || src === 12) return true;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
private testBroadcastHeader(bytes: number[], ndx: number): boolean {
|
|
317
|
+
// We are looking for [255,0,255,165]
|
|
318
|
+
if (bytes.length > ndx + 3) {
|
|
319
|
+
if (bytes[ndx] === 255 && bytes[ndx + 1] === 0 && bytes[ndx + 2] === 255 && bytes[ndx + 3] === 165) return true;
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
//return ndx < bytes.length - 3 && bytes[ndx] === 255 && bytes[ndx + 1] === 0 && bytes[ndx + 2] === 255 && bytes[ndx + 3] === 165;
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
private testUnidentifiedHeader(bytes: number[], ndx: number): boolean {
|
|
326
|
+
if (bytes.length > ndx + 3) {
|
|
327
|
+
if (bytes[ndx] === 255 && bytes[ndx + 1] === 0 && bytes[ndx + 2] === 255 && bytes[ndx + 3] !== 165) return true;
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
//return ndx < bytes.length - 3 && bytes[ndx] === 255 && bytes[ndx + 1] === 0 && bytes[ndx + 2] === 255 && bytes[ndx + 3] !== 165;
|
|
331
|
+
return false;
|
|
217
332
|
}
|
|
218
|
-
|
|
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; }
|
|
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; }
|
|
221
333
|
private testChlorTerm(bytes: number[], ndx: number): boolean { return ndx + 2 < bytes.length && bytes[ndx + 1] === 16 && bytes[ndx + 2] === 3; }
|
|
222
334
|
private testAquaLinkTerm(bytes: number[], ndx: number): boolean { return ndx + 2 < bytes.length && bytes[ndx + 1] === 16 && bytes[ndx + 2] === 3; }
|
|
223
335
|
private testHaywardTerm(bytes: number[], ndx: number): boolean { return ndx + 3 < bytes.length && bytes[ndx + 2] === 16 && bytes[ndx + 3] === 3; }
|
|
@@ -255,10 +367,11 @@ export class Inbound extends Message {
|
|
|
255
367
|
//return this.padding.length + this.preamble.length;
|
|
256
368
|
}
|
|
257
369
|
public readPacket(bytes: number[]): number {
|
|
370
|
+
//logger.info(`BYTES: ${JSON.stringify(bytes)}`);
|
|
258
371
|
var ndx = this.readHeader(bytes, 0);
|
|
259
372
|
if (this.isValid && this.header.length > 0) ndx = this.readPayload(bytes, ndx);
|
|
260
373
|
if (this.isValid && this.header.length > 0) ndx = this.readChecksum(bytes, ndx);
|
|
261
|
-
|
|
374
|
+
if (this.isComplete && !this.isValid) return this.rewind(bytes, ndx);
|
|
262
375
|
return ndx;
|
|
263
376
|
}
|
|
264
377
|
public mergeBytes(bytes) {
|
|
@@ -271,30 +384,40 @@ export class Inbound extends Message {
|
|
|
271
384
|
}
|
|
272
385
|
public readHeader(bytes: number[], ndx: number): number {
|
|
273
386
|
// start over to include the padding bytes.
|
|
387
|
+
//if (this.protocol !== Protocol.Unknown) {
|
|
388
|
+
// logger.warn(`${this.protocol} resulted in an empty message header ${JSON.stringify(this.header)}`);
|
|
389
|
+
//}
|
|
274
390
|
let ndxStart = ndx;
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
391
|
+
// RKS: 05-30-22 -- OMG we have not been dealing with short headers. As a result it was restarting
|
|
392
|
+
// the header process even after it had identified it.
|
|
393
|
+
if (this.protocol === Protocol.Unknown) {
|
|
394
|
+
while (ndx < bytes.length) {
|
|
395
|
+
if (this.testBroadcastHeader(bytes, ndx)) {
|
|
396
|
+
this.protocol = Protocol.Broadcast;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
if (this.testUnidentifiedHeader(bytes, ndx)) {
|
|
400
|
+
this.protocol = Protocol.Unidentified;
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
if (this.testChlorHeader(bytes, ndx)) {
|
|
404
|
+
this.protocol = Protocol.Chlorinator;
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
if (this.testAquaLinkHeader(bytes, ndx)) {
|
|
408
|
+
this.protocol = Protocol.AquaLink;
|
|
409
|
+
break;
|
|
410
|
+
}
|
|
411
|
+
if (this.testHaywardHeader(bytes, ndx)) {
|
|
412
|
+
this.protocol = Protocol.Hayward;
|
|
413
|
+
break;
|
|
414
|
+
}
|
|
415
|
+
this.padding.push(bytes[ndx++]);
|
|
295
416
|
}
|
|
296
|
-
this.padding.push(bytes[ndx++]);
|
|
297
417
|
}
|
|
418
|
+
// When the code above finds a protocol, ndx will be at the start of that
|
|
419
|
+
// header. If it is not identified then it will rewind to the initial
|
|
420
|
+
// start position until we get more bytes. This is the default case below.
|
|
298
421
|
let ndxHeader = ndx;
|
|
299
422
|
switch (this.protocol) {
|
|
300
423
|
case Protocol.Pump:
|
|
@@ -309,11 +432,11 @@ export class Inbound extends Message {
|
|
|
309
432
|
// We actually don't have a complete header yet so just return.
|
|
310
433
|
// we will pick it up next go around.
|
|
311
434
|
// logger.debug(`We have an incoming message but the serial port hasn't given a complete header. [${this.padding}][${this.preamble}][${this.header}]`);
|
|
435
|
+
//logger.info(`We don't have a complete header ${JSON.stringify(this.header)}`);
|
|
312
436
|
this.preamble = [];
|
|
313
437
|
this.header = [];
|
|
314
438
|
return ndxHeader;
|
|
315
439
|
}
|
|
316
|
-
|
|
317
440
|
if (this.source >= 96 && this.source <= 111) this.protocol = Protocol.Pump;
|
|
318
441
|
else if (this.dest >= 96 && this.dest <= 111) this.protocol = Protocol.Pump;
|
|
319
442
|
else if (this.source >= 112 && this.source <= 127) this.protocol = Protocol.Heater;
|
|
@@ -323,7 +446,7 @@ export class Inbound extends Message {
|
|
|
323
446
|
else if (this.source == 12 || this.dest == 12) this.protocol = Protocol.IntelliValve;
|
|
324
447
|
if (this.datalen > 75) {
|
|
325
448
|
//this.isValid = false;
|
|
326
|
-
logger.debug(`Broadcast length ${this.datalen} exceeded
|
|
449
|
+
logger.debug(`Broadcast length ${this.datalen} exceeded 75 bytes for ${this.protocol} message. Message rewound ${this.header}`);
|
|
327
450
|
this.padding.push(...this.preamble);
|
|
328
451
|
this.padding.push(...this.header.slice(0, 1));
|
|
329
452
|
this.preamble = [];
|
|
@@ -352,11 +475,11 @@ export class Inbound extends Message {
|
|
|
352
475
|
}
|
|
353
476
|
break;
|
|
354
477
|
case Protocol.Hayward:
|
|
355
|
-
ndx = this.pushBytes(this.header, bytes, ndx,
|
|
478
|
+
ndx = this.pushBytes(this.header, bytes, ndx, 5);
|
|
356
479
|
if (this.header.length < 4) {
|
|
357
480
|
// We actually don't have a complete header yet so just return.
|
|
358
481
|
// we will pick it up next go around.
|
|
359
|
-
logger.debug(`We have an incoming
|
|
482
|
+
logger.debug(`We have an incoming Hayward message but the serial port hasn't given a complete header. [${this.padding}][${this.preamble}][${this.header}]`);
|
|
360
483
|
this.preamble = [];
|
|
361
484
|
this.header = [];
|
|
362
485
|
return ndxHeader;
|
|
@@ -401,7 +524,11 @@ export class Inbound extends Message {
|
|
|
401
524
|
case Protocol.IntelliValve:
|
|
402
525
|
case Protocol.Heater:
|
|
403
526
|
case Protocol.Unidentified:
|
|
404
|
-
if (this.datalen - this.payload.length <= 0)
|
|
527
|
+
if (this.datalen - this.payload.length <= 0) {
|
|
528
|
+
let buff = bytes.slice(ndx - 1);
|
|
529
|
+
//logger.info(`We don't need any more payload ${this.datalen - this.payload.length} ${ndx} ${JSON.stringify(buff)};`);
|
|
530
|
+
return ndx; // We don't need any more payload.
|
|
531
|
+
}
|
|
405
532
|
ndx = this.pushBytes(this.payload, bytes, ndx, this.datalen - this.payload.length);
|
|
406
533
|
break;
|
|
407
534
|
case Protocol.Chlorinator:
|
|
@@ -586,7 +713,16 @@ export class Inbound extends Message {
|
|
|
586
713
|
PumpMessage.process(this);
|
|
587
714
|
break;
|
|
588
715
|
case 30:
|
|
589
|
-
|
|
716
|
+
switch (sys.controllerType) {
|
|
717
|
+
case ControllerType.Unknown:
|
|
718
|
+
break;
|
|
719
|
+
case ControllerType.SunTouch:
|
|
720
|
+
ScheduleMessage.processSunTouch(this);
|
|
721
|
+
break;
|
|
722
|
+
default:
|
|
723
|
+
OptionsMessage.process(this);
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
590
726
|
break;
|
|
591
727
|
case 22:
|
|
592
728
|
case 32:
|
|
@@ -629,6 +765,9 @@ export class Inbound extends Message {
|
|
|
629
765
|
case 147:
|
|
630
766
|
IntellichemMessage.process(this);
|
|
631
767
|
break;
|
|
768
|
+
case 136:
|
|
769
|
+
ExternalMessage.processTouchSetHeatMode(this);
|
|
770
|
+
break;
|
|
632
771
|
default:
|
|
633
772
|
if (this.action === 109 && this.payload[1] === 3) break;
|
|
634
773
|
if (this.source === 17 && this.payload[0] === 109) break;
|
|
@@ -639,6 +778,13 @@ export class Inbound extends Message {
|
|
|
639
778
|
}
|
|
640
779
|
}
|
|
641
780
|
public process() {
|
|
781
|
+
let port = conn.findPortById(this.portId);
|
|
782
|
+
if (this.portId === sys.anslq25.portId) {
|
|
783
|
+
return MessagesMock.process(this);
|
|
784
|
+
}
|
|
785
|
+
if (port.mock && port.hasAssignedEquipment()){
|
|
786
|
+
return MessagesMock.process(this);
|
|
787
|
+
}
|
|
642
788
|
switch (this.protocol) {
|
|
643
789
|
case Protocol.Broadcast:
|
|
644
790
|
this.processBroadcast();
|
|
@@ -674,18 +820,38 @@ class OutboundCommon extends Message {
|
|
|
674
820
|
public set sub(val: number) { if (this.protocol !== Protocol.Chlorinator && this.protocol !== Protocol.AquaLink) this.header[1] = val; }
|
|
675
821
|
public get sub() { return super.sub; }
|
|
676
822
|
public set dest(val: number) {
|
|
677
|
-
if (this.protocol === Protocol.Chlorinator) this.header[2] = val
|
|
823
|
+
if (this.protocol === Protocol.Chlorinator) this.header[2] = val;
|
|
678
824
|
else if (this.protocol === Protocol.Hayward) this.header[4] = val;
|
|
679
825
|
else this.header[2] = val;
|
|
680
826
|
}
|
|
681
827
|
public get dest() { return super.dest; }
|
|
682
828
|
public set source(val: number) {
|
|
683
|
-
|
|
684
|
-
|
|
829
|
+
switch (this.protocol) {
|
|
830
|
+
case Protocol.Chlorinator:
|
|
831
|
+
break;
|
|
832
|
+
case Protocol.Hayward:
|
|
833
|
+
this.header[3] = val;
|
|
834
|
+
break;
|
|
835
|
+
default:
|
|
836
|
+
this.header[3] = val;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
//if (this.protocol === Protocol.Hayward) this.header[2] = val;
|
|
840
|
+
//else if (this.protocol !== Protocol.Chlorinator) this.header[3] = val;
|
|
685
841
|
}
|
|
686
842
|
public get source() { return super.source; }
|
|
687
843
|
public set action(val: number) {
|
|
688
|
-
(this.protocol
|
|
844
|
+
switch (this.protocol) {
|
|
845
|
+
case Protocol.Chlorinator:
|
|
846
|
+
this.header[3] = val;
|
|
847
|
+
break;
|
|
848
|
+
case Protocol.Hayward:
|
|
849
|
+
this.header[2] = val;
|
|
850
|
+
break;
|
|
851
|
+
default:
|
|
852
|
+
this.header[4] = val;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
689
855
|
}
|
|
690
856
|
public get action() { return super.action; }
|
|
691
857
|
public set datalen(val: number) { if (this.protocol !== Protocol.Chlorinator && this.protocol !== Protocol.Hayward) this.header[5] = val; }
|
|
@@ -705,16 +871,13 @@ class OutboundCommon extends Message {
|
|
|
705
871
|
case Protocol.Unidentified:
|
|
706
872
|
case Protocol.IntelliChem:
|
|
707
873
|
case Protocol.Heater:
|
|
874
|
+
case Protocol.Hayward:
|
|
708
875
|
this.chkHi = Math.floor(sum / 256);
|
|
709
876
|
this.chkLo = (sum - (super.chkHi * 256));
|
|
710
877
|
break;
|
|
711
878
|
case Protocol.AquaLink:
|
|
712
879
|
case Protocol.Chlorinator:
|
|
713
|
-
this.term[0] = sum;
|
|
714
|
-
break;
|
|
715
|
-
case Protocol.Hayward:
|
|
716
|
-
this.chkHi = Math.floor(sum / 256);
|
|
717
|
-
this.chkLo = (sum - (super.chkHi * 256));
|
|
880
|
+
this.term[0] = sum % 256;
|
|
718
881
|
break;
|
|
719
882
|
}
|
|
720
883
|
}
|
|
@@ -790,7 +953,9 @@ export class Outbound extends OutboundCommon {
|
|
|
790
953
|
public static createMessage(action: number, payload: number[], retries?: number, response?: Response | boolean): Outbound {
|
|
791
954
|
return new Outbound(Protocol.Broadcast, sys.board.commandSourceAddress || Message.pluginAddress, sys.board.commandDestAddress || 16, action, payload, retries, response);
|
|
792
955
|
}
|
|
793
|
-
|
|
956
|
+
public async sendAsync() {
|
|
957
|
+
return conn.queueSendMessageAsync(this);
|
|
958
|
+
}
|
|
794
959
|
// Fields
|
|
795
960
|
public retries: number = 0;
|
|
796
961
|
public tries: number = 0;
|
|
@@ -883,11 +1048,11 @@ export class Outbound extends OutboundCommon {
|
|
|
883
1048
|
if (typeof s === 'undefined') s = def;
|
|
884
1049
|
let l = typeof len === 'undefined' ? s.length : len;
|
|
885
1050
|
let buf = [];
|
|
886
|
-
for (let i = 0; i < l;
|
|
887
|
-
if (i <
|
|
888
|
-
else buf.push(
|
|
1051
|
+
for (let i = 0; i < l; i++) {
|
|
1052
|
+
if (i < s.length) buf.push(s.charCodeAt(i));
|
|
1053
|
+
else buf.push(0);
|
|
889
1054
|
}
|
|
890
|
-
this.payload.splice(start,
|
|
1055
|
+
this.payload.splice(start, l, ...buf);
|
|
891
1056
|
return this;
|
|
892
1057
|
}
|
|
893
1058
|
public toPacket(): number[] {
|
|
@@ -900,6 +1065,20 @@ export class Outbound extends OutboundCommon {
|
|
|
900
1065
|
pkt.push.apply(pkt, this.term);
|
|
901
1066
|
return pkt;
|
|
902
1067
|
}
|
|
1068
|
+
public processMock(){
|
|
1069
|
+
// When the port is a mock port, we are no longer sending an
|
|
1070
|
+
// outbound message but converting it to an inbound and
|
|
1071
|
+
// skipping the actual send/receive part of the comms.
|
|
1072
|
+
let inbound = Message.convertOutboundToInbound(this);
|
|
1073
|
+
let port = conn.findPortById(this.portId);
|
|
1074
|
+
if (port.hasAssignedEquipment() || this.portId === sys.anslq25.portId){
|
|
1075
|
+
MessagesMock.process(inbound);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
inbound.process();
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
}
|
|
903
1082
|
}
|
|
904
1083
|
export class Ack extends Outbound {
|
|
905
1084
|
constructor(byte: number) {
|
|
@@ -951,7 +1130,10 @@ export class Response extends OutboundCommon {
|
|
|
951
1130
|
try {
|
|
952
1131
|
if (typeof this.responseBool === 'boolean' && this.responseBool) bresp = this.evalResponse(msgIn, msgOut);
|
|
953
1132
|
else return bresp;
|
|
954
|
-
if (bresp === true && typeof msgOut !== 'undefined')
|
|
1133
|
+
if (bresp === true && typeof msgOut !== 'undefined') {
|
|
1134
|
+
msgIn.responseFor.push(msgOut.id);
|
|
1135
|
+
logger.silly(`Message in ${msgIn.id} is a response for message out ${msgOut.id}`);
|
|
1136
|
+
}
|
|
955
1137
|
return bresp;
|
|
956
1138
|
}
|
|
957
1139
|
catch (err) { }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -28,7 +29,7 @@ export class ChlorinatorMessage {
|
|
|
28
29
|
for (let i = 0; i < 4 && i + 30 < msg.payload.length; i++) {
|
|
29
30
|
let isActive = msg.extractPayloadByte(i + 22) === 1;
|
|
30
31
|
chlor = sys.chlorinators.getItemById(chlorId);
|
|
31
|
-
|
|
32
|
+
if (chlor.master !== 0) continue; // RSG: probably never need this. See Touch chlor below.
|
|
32
33
|
if (isActive) {
|
|
33
34
|
chlor = sys.chlorinators.getItemById(chlorId, true);
|
|
34
35
|
let schlor = state.chlorinators.getItemById(chlor.id, true);
|
|
@@ -85,7 +86,7 @@ export class ChlorinatorMessage {
|
|
|
85
86
|
// chlorinator. These should be 0 anyway.
|
|
86
87
|
schlor.spaSetpoint = chlor.spaSetpoint = msg.extractPayloadByte(0) >> 1;
|
|
87
88
|
schlor.poolSetpoint = chlor.poolSetpoint = msg.extractPayloadByte(1);
|
|
88
|
-
chlor.address =
|
|
89
|
+
chlor.address = msg.dest;
|
|
89
90
|
schlor.body = chlor.body = sys.equipment.maxBodies >= 1 || sys.equipment.shared === true ? 32 : 0;
|
|
90
91
|
}
|
|
91
92
|
if (typeof chlor.name === 'undefined') schlor.name = chlor.name = msg.extractPayloadString(6, 16);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -113,7 +114,8 @@ export class CircuitGroupMessage {
|
|
|
113
114
|
group.nameId = sgroup.nameId = feature.nameId;
|
|
114
115
|
group.type = sgroup.type = sys.board.valueMaps.circuitGroupTypes.getValue('circuit');
|
|
115
116
|
group.isActive = _isActive;
|
|
116
|
-
if (typeof group.showInFeatures === 'undefined')
|
|
117
|
+
if (typeof group.showInFeatures === 'undefined') group.showInFeatures = true;
|
|
118
|
+
sgroup.showInFeatures = group.showInFeatures;
|
|
117
119
|
let circuits: CircuitGroupCircuitCollection = group.circuits;
|
|
118
120
|
for (let byte = 1; byte <= 7; byte++){
|
|
119
121
|
let offByte = msg.extractPayloadByte(byte);
|
|
@@ -189,6 +191,7 @@ export class CircuitGroupMessage {
|
|
|
189
191
|
group.isActive = sgroup.isActive = true;
|
|
190
192
|
if (typeof group.showInFeatures === 'undefined') group.showInFeatures = true;
|
|
191
193
|
sgroup.type = group.type;
|
|
194
|
+
sgroup.showInFeatures = group.showInFeatures;
|
|
192
195
|
}
|
|
193
196
|
state.emitEquipmentChanges();
|
|
194
197
|
msg.isProcessed = true;
|