nodejs-poolcontroller 7.3.0 → 7.6.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/bug_report.md +1 -1
- package/Changelog +23 -0
- package/README.md +5 -5
- package/app.ts +2 -0
- package/config/Config.ts +3 -0
- package/config/VersionCheck.ts +8 -4
- package/controller/Constants.ts +88 -0
- package/controller/Equipment.ts +246 -66
- package/controller/Errors.ts +24 -1
- package/controller/Lockouts.ts +423 -0
- package/controller/State.ts +314 -54
- package/controller/boards/EasyTouchBoard.ts +107 -59
- package/controller/boards/IntelliCenterBoard.ts +186 -125
- package/controller/boards/IntelliTouchBoard.ts +104 -30
- package/controller/boards/NixieBoard.ts +721 -159
- package/controller/boards/SystemBoard.ts +2370 -1108
- package/controller/comms/Comms.ts +85 -10
- package/controller/comms/messages/Messages.ts +10 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
- package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
- package/controller/comms/messages/config/CoverMessage.ts +1 -0
- package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
- package/controller/comms/messages/config/ExternalMessage.ts +44 -26
- package/controller/comms/messages/config/FeatureMessage.ts +8 -1
- package/controller/comms/messages/config/GeneralMessage.ts +8 -0
- package/controller/comms/messages/config/HeaterMessage.ts +15 -9
- package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
- package/controller/comms/messages/config/OptionsMessage.ts +13 -1
- package/controller/comms/messages/config/PumpMessage.ts +4 -20
- package/controller/comms/messages/config/RemoteMessage.ts +4 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
- package/controller/comms/messages/config/SecurityMessage.ts +1 -0
- package/controller/comms/messages/config/ValveMessage.ts +13 -3
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
- package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
- package/controller/comms/messages/status/HeaterStateMessage.ts +42 -9
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +37 -26
- package/controller/nixie/Nixie.ts +18 -16
- package/controller/nixie/bodies/Body.ts +4 -1
- package/controller/nixie/chemistry/ChemController.ts +80 -77
- package/controller/nixie/chemistry/Chlorinator.ts +9 -8
- package/controller/nixie/circuits/Circuit.ts +55 -6
- package/controller/nixie/heaters/Heater.ts +192 -32
- package/controller/nixie/pumps/Pump.ts +146 -84
- package/controller/nixie/schedules/Schedule.ts +3 -2
- package/controller/nixie/valves/Valve.ts +1 -1
- package/defaultConfig.json +32 -1
- package/issue_template.md +1 -1
- package/logger/DataLogger.ts +37 -22
- package/package.json +20 -18
- package/web/Server.ts +520 -29
- package/web/bindings/influxDB.json +96 -8
- package/web/bindings/mqtt.json +151 -40
- package/web/bindings/mqttAlt.json +114 -4
- package/web/interfaces/httpInterface.ts +2 -0
- package/web/interfaces/influxInterface.ts +36 -19
- package/web/interfaces/mqttInterface.ts +14 -3
- package/web/services/config/Config.ts +171 -44
- package/web/services/state/State.ts +49 -5
- package/web/services/state/StateSocket.ts +18 -1
|
@@ -43,9 +43,9 @@ export class Connection {
|
|
|
43
43
|
if (conn.connTimer !== null) clearTimeout(conn.connTimer);
|
|
44
44
|
if (!conn._cfg.mockPort && conn._cfg.inactivityRetry > 0 && !conn._closing) conn.connTimer = setTimeout(async () => {
|
|
45
45
|
try {
|
|
46
|
-
await conn.openAsync()
|
|
46
|
+
await conn.openAsync();
|
|
47
47
|
}
|
|
48
|
-
catch (err) {};
|
|
48
|
+
catch (err) { logger.error(`Error resetting RS485 port on inactivity: ${err.message}`); };
|
|
49
49
|
}, conn._cfg.inactivityRetry * 1000);
|
|
50
50
|
}
|
|
51
51
|
public isRTS: boolean = true;
|
|
@@ -83,7 +83,7 @@ export class Connection {
|
|
|
83
83
|
} catch (err) { return Promise.reject(err); }
|
|
84
84
|
}
|
|
85
85
|
public async openAsync(): Promise<boolean> {
|
|
86
|
-
if (typeof
|
|
86
|
+
if (typeof this.buffer === 'undefined') {
|
|
87
87
|
this.buffer = new SendRecieveBuffer();
|
|
88
88
|
this.emitter.on('packetread', (pkt) => { this.buffer.pushIn(pkt); });
|
|
89
89
|
this.emitter.on('messagewrite', (msg) => { this.buffer.pushOut(msg); });
|
|
@@ -97,18 +97,24 @@ export class Connection {
|
|
|
97
97
|
let nc: net.Socket = new net.Socket();
|
|
98
98
|
nc.on('connect', () => { logger.info(`Net connect (socat) connected to: ${this._cfg.netHost}:${this._cfg.netPort}`); }); // Socket is opened but not yet ready.
|
|
99
99
|
nc.on('ready', () => {
|
|
100
|
+
this.isOpen = true;
|
|
101
|
+
this.isRTS = true;
|
|
100
102
|
logger.info(`Net connect (socat) ready and communicating: ${this._cfg.netHost}:${this._cfg.netPort}`);
|
|
101
|
-
nc.on('data', (data) => {
|
|
103
|
+
nc.on('data', (data) => {
|
|
104
|
+
//this.resetConnTimer();
|
|
105
|
+
if (data.length > 0 && !this.isPaused) this.emitter.emit('packetread', data);
|
|
106
|
+
});
|
|
102
107
|
});
|
|
103
108
|
nc.on('close', (p) => {
|
|
104
109
|
this.isOpen = false;
|
|
105
110
|
if (typeof this._port !== 'undefined') this._port.destroy();
|
|
106
111
|
this._port = undefined;
|
|
112
|
+
this.buffer.clearOutbound();
|
|
107
113
|
logger.info(`Net connect (socat) closed ${p === true ? 'due to error' : ''}: ${this._cfg.netHost}:${this._cfg.netPort}`);
|
|
108
114
|
});
|
|
109
115
|
nc.on('end', () => { // Happens when the other end of the socket closes.
|
|
110
116
|
this.isOpen = false;
|
|
111
|
-
this.resetConnTimer();
|
|
117
|
+
//this.resetConnTimer();
|
|
112
118
|
logger.info(`Net connect (socat) end event was fired`);
|
|
113
119
|
});
|
|
114
120
|
//nc.on('drain', () => { logger.info(`The drain event was fired.`); });
|
|
@@ -116,15 +122,28 @@ export class Connection {
|
|
|
116
122
|
// Occurs when there is no activity. This should not reset the connection, the previous implementation did so and
|
|
117
123
|
// left the connection in a weird state where the previous connection was processing events and the new connection was
|
|
118
124
|
// doing so as well. This isn't an error it is a warning as the RS485 bus will most likely be communicating at all times.
|
|
119
|
-
nc.on('timeout', () => { logger.warn(`Net connect (socat) Connection Idle: ${this._cfg.netHost}:${this._cfg.netPort}`); });
|
|
125
|
+
//nc.on('timeout', () => { logger.warn(`Net connect (socat) Connection Idle: ${this._cfg.netHost}:${this._cfg.netPort}`); });
|
|
126
|
+
nc.setTimeout(Math.max(this._cfg.inactivityRetry, 10) * 1000, async () => {
|
|
127
|
+
logger.warn(`Net connect (socat) connection idle: ${this._cfg.netHost}:${this._cfg.netPort} retrying connection.`);
|
|
128
|
+
try {
|
|
129
|
+
await conn.endAsync();
|
|
130
|
+
await conn.openAsync();
|
|
131
|
+
} catch (err) { logger.error(`Net connect (socat) error retrying connection ${err.message}`); }
|
|
132
|
+
});
|
|
133
|
+
|
|
120
134
|
return await new Promise<boolean>((resolve, _) => {
|
|
121
135
|
// We only connect an error once as we will destroy this connection on error then recreate a new socket on failure.
|
|
122
136
|
nc.once('error', (err) => {
|
|
123
|
-
logger.error(`Net connect (socat) Connection: ${err}. ${this._cfg.inactivityRetry > 0 ? `Retry in ${this._cfg.inactivityRetry} seconds` : `Never retrying; inactivityRetry set to ${this._cfg.inactivityRetry}`}`);
|
|
124
|
-
this.resetConnTimer();
|
|
137
|
+
//logger.error(`Net connect (socat) Connection: ${err}. ${this._cfg.inactivityRetry > 0 ? `Retry in ${this._cfg.inactivityRetry} seconds` : `Never retrying; inactivityRetry set to ${this._cfg.inactivityRetry}`}`);
|
|
138
|
+
//this.resetConnTimer();
|
|
125
139
|
this.isOpen = false;
|
|
126
140
|
// if the promise has already been fulfilled, but the error happens later, we don't want to call the promise again.
|
|
127
141
|
if (typeof resolve !== 'undefined') { resolve(false); }
|
|
142
|
+
if (this._cfg.inactivityRetry > 0) {
|
|
143
|
+
logger.error(`Net connect (socat) connection error: ${err}. Retry in ${this._cfg.inactivityRetry} seconds`);
|
|
144
|
+
setTimeout(async () => { try { await conn.openAsync(); } catch (err) { } }, this._cfg.inactivityRetry * 1000);
|
|
145
|
+
}
|
|
146
|
+
else logger.error(`Net connect (socat) connection error: ${err}. Never retrying -- No retry time set`);
|
|
128
147
|
});
|
|
129
148
|
nc.connect(conn._cfg.netPort, conn._cfg.netHost, () => {
|
|
130
149
|
if (typeof this._port !== 'undefined') logger.warn('Net connect (socat) recovered from lost connection.');
|
|
@@ -249,6 +268,62 @@ export class Connection {
|
|
|
249
268
|
return true;
|
|
250
269
|
} catch (err) { logger.error(`Error closing comms connection: ${err.message}`); return Promise.resolve(false); }
|
|
251
270
|
}
|
|
271
|
+
public async endAsync(): Promise<boolean> {
|
|
272
|
+
try {
|
|
273
|
+
this._closing = true;
|
|
274
|
+
if (this.connTimer) clearTimeout(this.connTimer);
|
|
275
|
+
if (typeof this._port !== 'undefined' && this.isOpen) {
|
|
276
|
+
let success = await new Promise<boolean>((resolve, reject) => {
|
|
277
|
+
if (this._cfg.netConnect) {
|
|
278
|
+
this._port.removeAllListeners();
|
|
279
|
+
this._port.once('error', (err) => {
|
|
280
|
+
if (err) {
|
|
281
|
+
logger.error(`Error closing ${this._cfg.netHost}:${this._cfg.netPort}/${this._cfg.rs485Port}: ${err}`);
|
|
282
|
+
resolve(false);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
conn._port = undefined;
|
|
286
|
+
this.isOpen = false;
|
|
287
|
+
logger.info(`Successfully closed (socat) port ${this._cfg.netHost}:${this._cfg.netPort}/${this._cfg.rs485Port}`);
|
|
288
|
+
resolve(true);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this._port.once('close', (p) => {
|
|
292
|
+
this.isOpen = false;
|
|
293
|
+
this._port = undefined;
|
|
294
|
+
logger.info(`Net connect (socat) successfully closed: ${this._cfg.netHost}:${this._cfg.netPort}`);
|
|
295
|
+
resolve(true);
|
|
296
|
+
});
|
|
297
|
+
this._port.destroy();
|
|
298
|
+
}
|
|
299
|
+
else if (typeof conn._port.close === 'function') {
|
|
300
|
+
conn._port.close((err) => {
|
|
301
|
+
if (err) {
|
|
302
|
+
logger.error(`Error closing ${this._cfg.rs485Port}: ${err}`);
|
|
303
|
+
resolve(false);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
conn._port = undefined;
|
|
307
|
+
logger.info(`Successfully closed seral port ${this._cfg.rs485Port}`);
|
|
308
|
+
resolve(true);
|
|
309
|
+
this.isOpen = false;
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
resolve(true);
|
|
315
|
+
conn._port = undefined;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
if (success) {
|
|
319
|
+
if (typeof conn.buffer !== 'undefined') conn.buffer.close();
|
|
320
|
+
}
|
|
321
|
+
return success;
|
|
322
|
+
}
|
|
323
|
+
return true;
|
|
324
|
+
} catch (err) { logger.error(`Error closing comms connection: ${err.message}`); return Promise.resolve(false); }
|
|
325
|
+
finally { this._closing = false; }
|
|
326
|
+
}
|
|
252
327
|
public drain(cb: Function) {
|
|
253
328
|
if (typeof (conn._port.drain) === 'function')
|
|
254
329
|
conn._port.drain(cb);
|
|
@@ -347,7 +422,7 @@ export class SendRecieveBuffer {
|
|
|
347
422
|
public pushOut(msg) { conn.buffer._outBuffer.push(msg); setTimeout(() => { this.processPackets(); }, 0); }
|
|
348
423
|
public clear() { conn.buffer._inBuffer.length = 0; conn.buffer._outBuffer.length = 0; }
|
|
349
424
|
public close() { clearTimeout(conn.buffer.procTimer); conn.buffer.clear(); this._msg = undefined; }
|
|
350
|
-
|
|
425
|
+
public clearOutbound() { conn.buffer._outBuffer.length = 0; conn.buffer._waitingPacket = undefined; }
|
|
351
426
|
/********************************************************************
|
|
352
427
|
* RKS: 06-06-20
|
|
353
428
|
* This used to process every 175ms. While the processing was light
|
|
@@ -483,11 +558,11 @@ export class SendRecieveBuffer {
|
|
|
483
558
|
logger.verbose(`Wrote packet[${bytes}].Retries remaining: ${msg.remainingTries} `);
|
|
484
559
|
// We have all the success we are going to get so if the call succeeded then
|
|
485
560
|
// don't set the waiting packet when we aren't actually waiting for a response.
|
|
561
|
+
conn.buffer.counter.sndSuccess++;
|
|
486
562
|
if (!msg.requiresResponse) {
|
|
487
563
|
// As far as we know the message made it to OCP.
|
|
488
564
|
conn.buffer._waitingPacket = null;
|
|
489
565
|
if (typeof msg.onComplete === 'function') msg.onComplete(err, undefined);
|
|
490
|
-
conn.buffer.counter.sndSuccess++;
|
|
491
566
|
|
|
492
567
|
}
|
|
493
568
|
else if (msg.remainingTries >= 0) {
|
|
@@ -168,12 +168,13 @@ export class Inbound extends Message {
|
|
|
168
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
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)}"}`;
|
|
170
170
|
}
|
|
171
|
-
private testChlorHeader(bytes: number[], ndx: number): boolean {
|
|
171
|
+
private testChlorHeader(bytes: number[], ndx: number): boolean {
|
|
172
172
|
// if packets have 16,2 (eg status=16,2,29) in them and they come as partial packets, they would have
|
|
173
173
|
// prev been detected as chlor packets;
|
|
174
174
|
// valid chlor packets should have 16,2,0 or 16,2,[80-96];
|
|
175
175
|
// this should reduce the number of false chlor packets
|
|
176
|
-
return (ndx + 2 < bytes.length && bytes[ndx] === 16 && bytes[ndx + 1] === 2 && (bytes[ndx + 2] === 0 || (bytes[ndx + 2] >= 80 && bytes[ndx + 2] <= 96)))
|
|
176
|
+
return (ndx + 2 < bytes.length && bytes[ndx] === 16 && bytes[ndx + 1] === 2 && (bytes[ndx + 2] === 0 || (bytes[ndx + 2] >= 80 && bytes[ndx + 2] <= 96)))
|
|
177
|
+
}
|
|
177
178
|
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; }
|
|
178
179
|
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; }
|
|
179
180
|
private testChlorTerm(bytes: number[], ndx: number): boolean { return ndx < bytes.length - 2 && bytes[ndx + 1] === 16 && bytes[ndx + 2] === 3; }
|
|
@@ -404,6 +405,7 @@ export class Inbound extends Message {
|
|
|
404
405
|
case ControllerType.IntelliCenter:
|
|
405
406
|
switch (this.action) {
|
|
406
407
|
case 1: // ACK
|
|
408
|
+
this.isProcessed = true;
|
|
407
409
|
break;
|
|
408
410
|
case 2:
|
|
409
411
|
case 204:
|
|
@@ -423,12 +425,14 @@ export class Inbound extends Message {
|
|
|
423
425
|
break;
|
|
424
426
|
case 222: // A panel is asking for action 30s
|
|
425
427
|
case 228: // A panel is asking for the current version
|
|
428
|
+
this.isProcessed = true;
|
|
426
429
|
break;
|
|
427
430
|
default:
|
|
428
431
|
logger.info(`An unprocessed message was received ${this.toPacket()}`)
|
|
429
432
|
break;
|
|
430
433
|
|
|
431
434
|
}
|
|
435
|
+
if (!this.isProcessed) logger.info(`The message was not processed ${this.action} - ${this.toPacket()}`);
|
|
432
436
|
break;
|
|
433
437
|
default:
|
|
434
438
|
switch (this.action) {
|
|
@@ -500,17 +504,19 @@ export class Inbound extends Message {
|
|
|
500
504
|
case 9:
|
|
501
505
|
case 16:
|
|
502
506
|
case 34:
|
|
503
|
-
case 114:
|
|
504
507
|
case 137:
|
|
505
508
|
case 144:
|
|
506
509
|
case 162:
|
|
507
510
|
HeaterMessage.process(this);
|
|
508
511
|
break;
|
|
512
|
+
case 114:
|
|
513
|
+
case 115:
|
|
514
|
+
HeaterStateMessage.process(this);
|
|
515
|
+
break
|
|
509
516
|
case 147:
|
|
510
517
|
IntellichemMessage.process(this);
|
|
511
518
|
break;
|
|
512
519
|
default:
|
|
513
|
-
// take these out...
|
|
514
520
|
if (this.action === 109 && this.payload[1] === 3) break;
|
|
515
521
|
if (this.source === 17 && this.payload[0] === 109) break;
|
|
516
522
|
logger.debug(`Packet not processed: ${this.toPacket()}`);
|
|
@@ -27,15 +27,19 @@ export class ChlorinatorMessage {
|
|
|
27
27
|
chlorId = 1;
|
|
28
28
|
for (let i = 0; i < 4 && i + 30 < msg.payload.length; i++) {
|
|
29
29
|
let isActive = msg.extractPayloadByte(i + 22) === 1;
|
|
30
|
+
chlor = sys.chlorinators.getItemById(chlorId);
|
|
31
|
+
if (chlor.master === 1) continue; // RSG: probably never need this. See Touch chlor below.
|
|
30
32
|
if (isActive) {
|
|
31
|
-
|
|
33
|
+
chlor = sys.chlorinators.getItemById(chlorId, true);
|
|
32
34
|
let schlor = state.chlorinators.getItemById(chlor.id, true);
|
|
33
35
|
chlor.isActive = schlor.isActive = true;
|
|
34
36
|
chlor.body = msg.extractPayloadByte(i + 2);
|
|
35
37
|
chlor.type = msg.extractPayloadByte(i + 6);
|
|
36
|
-
if (!chlor.disabled) {
|
|
38
|
+
if (!chlor.disabled && !chlor.isDosing) {
|
|
37
39
|
// RKS: We don't want to change the setpoints if our chem controller disabled
|
|
38
40
|
// 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
|
+
|
|
39
43
|
chlor.poolSetpoint = msg.extractPayloadByte(i + 10);
|
|
40
44
|
chlor.spaSetpoint = msg.extractPayloadByte(i + 14);
|
|
41
45
|
}
|
|
@@ -57,6 +61,7 @@ export class ChlorinatorMessage {
|
|
|
57
61
|
}
|
|
58
62
|
chlorId++;
|
|
59
63
|
}
|
|
64
|
+
msg.isProcessed = true;
|
|
60
65
|
break;
|
|
61
66
|
default:
|
|
62
67
|
logger.debug(`Unprocessed Config Message ${msg.toPacket()}`)
|
|
@@ -65,6 +70,8 @@ export class ChlorinatorMessage {
|
|
|
65
70
|
}
|
|
66
71
|
public static processTouch(msg: Inbound) {
|
|
67
72
|
// This is for the 25 message that is broadcast from the OCP.
|
|
73
|
+
let chlor = sys.chlorinators.getItemById(1);
|
|
74
|
+
if (chlor.master === 1) return; // Some Aquarite chlors need more frequent control (via Nixie) but will be disabled via Touch. https://github.com/tagyoureit/nodejs-poolController/issues/349
|
|
68
75
|
let isActive = (msg.extractPayloadByte(0) & 0x01) === 1;
|
|
69
76
|
if (isActive) {
|
|
70
77
|
let chlor = sys.chlorinators.getItemById(1, true);
|
|
@@ -78,8 +85,9 @@ export class ChlorinatorMessage {
|
|
|
78
85
|
chlor.address = chlor.id + 79;
|
|
79
86
|
schlor.body = chlor.body = sys.equipment.maxBodies >= 1 || sys.equipment.shared === true ? 32 : 0;
|
|
80
87
|
}
|
|
81
|
-
schlor.name = chlor.name = msg.extractPayloadString(6, 16);
|
|
82
|
-
if (typeof chlor.model === 'undefined') chlor.model = schlor.name;
|
|
88
|
+
if (typeof chlor.name === 'undefined') schlor.name = chlor.name = msg.extractPayloadString(6, 16);
|
|
89
|
+
if (typeof chlor.model === 'undefined') chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(schlor.name.toLowerCase());
|
|
90
|
+
if (typeof chlor.type === 'undefined') chlor.type = schlor.type = 0;
|
|
83
91
|
schlor.saltLevel = msg.extractPayloadByte(3) * 50 || schlor.saltLevel;
|
|
84
92
|
schlor.status = msg.extractPayloadByte(4) & 0x007F; // Strip off the high bit. The chlorinator does not actually report this.;
|
|
85
93
|
// Pull the hours from the 25 message.
|
|
@@ -110,5 +118,6 @@ export class ChlorinatorMessage {
|
|
|
110
118
|
sys.chlorinators.removeItemById(1);
|
|
111
119
|
state.chlorinators.removeItemById(1);
|
|
112
120
|
}
|
|
121
|
+
msg.isProcessed = true;
|
|
113
122
|
}
|
|
114
123
|
}
|
|
@@ -80,6 +80,7 @@ export class CircuitGroupMessage {
|
|
|
80
80
|
group.circuits.removeItemByIndex(i);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
+
msg.isProcessed = true;
|
|
83
84
|
}
|
|
84
85
|
else if (msgId >= 16 && msgId <= 31) {
|
|
85
86
|
groupId = msgId - 16 + sys.board.equipmentIds.circuitGroups.start;
|
|
@@ -90,6 +91,7 @@ export class CircuitGroupMessage {
|
|
|
90
91
|
group.name = msg.extractPayloadString(2, 16);
|
|
91
92
|
sgroup.name = group.name;
|
|
92
93
|
}
|
|
94
|
+
msg.isProcessed = true;
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
}
|
|
@@ -143,6 +145,7 @@ export class CircuitGroupMessage {
|
|
|
143
145
|
state.circuitGroups.removeItemById(groupId);
|
|
144
146
|
}
|
|
145
147
|
state.emitEquipmentChanges();
|
|
148
|
+
msg.isProcessed = true;
|
|
146
149
|
}
|
|
147
150
|
private static processGroupType(msg: Inbound) {
|
|
148
151
|
var groupId = ((msg.extractPayloadByte(1) - 32) * 16) + sys.board.equipmentIds.circuitGroups.start;
|
|
@@ -188,6 +191,7 @@ export class CircuitGroupMessage {
|
|
|
188
191
|
sgroup.type = group.type;
|
|
189
192
|
}
|
|
190
193
|
state.emitEquipmentChanges();
|
|
194
|
+
msg.isProcessed = true;
|
|
191
195
|
}
|
|
192
196
|
private static processColor(msg: Inbound) {
|
|
193
197
|
var groupId = ((msg.extractPayloadByte(1) - 35)) + sys.board.equipmentIds.circuitGroups.start;
|
|
@@ -208,6 +212,7 @@ export class CircuitGroupMessage {
|
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
}
|
|
215
|
+
msg.isProcessed = true;
|
|
211
216
|
}
|
|
212
217
|
private static processEggTimer(msg: Inbound) {
|
|
213
218
|
var groupId = ((msg.extractPayloadByte(1) - 34) * 16) + sys.board.equipmentIds.circuitGroups.start;
|
|
@@ -220,5 +225,6 @@ export class CircuitGroupMessage {
|
|
|
220
225
|
// sgroup.eggTimer = group.eggTimer;
|
|
221
226
|
}
|
|
222
227
|
}
|
|
228
|
+
msg.isProcessed = true;
|
|
223
229
|
}
|
|
224
230
|
}
|
|
@@ -32,6 +32,7 @@ export class CoverMessage {
|
|
|
32
32
|
for (let i = 1; i < 10; i++) {
|
|
33
33
|
if (msg.extractPayloadByte(i + 18) !== 255) cover.circuits.push(msg.extractPayloadByte(i + 18));
|
|
34
34
|
}
|
|
35
|
+
msg.isProcessed = true;
|
|
35
36
|
break;
|
|
36
37
|
default:
|
|
37
38
|
logger.debug(`Unprocessed Config Message ${msg.toPacket()}`)
|
|
@@ -48,6 +48,7 @@ export class EquipmentMessage {
|
|
|
48
48
|
body.capacity = msg.extractPayloadByte(34) * 1000;
|
|
49
49
|
if (body.isActive && sys.equipment.maxBodies === 0) sys.bodies.removeItemById(1);
|
|
50
50
|
body.isActive = sys.equipment.maxBodies > 0;
|
|
51
|
+
msg.isProcessed = true;
|
|
51
52
|
break;
|
|
52
53
|
case 1:
|
|
53
54
|
pnl = sys.equipment.expansions.getItemById(2);
|
|
@@ -66,6 +67,7 @@ export class EquipmentMessage {
|
|
|
66
67
|
}
|
|
67
68
|
pnl = sys.equipment.expansions.getItemById(3);
|
|
68
69
|
pnl.name = msg.extractPayloadString(18, 16);
|
|
70
|
+
msg.isProcessed = true;
|
|
69
71
|
break;
|
|
70
72
|
case 2:
|
|
71
73
|
// The first name is the first body in this packet and the second is the third. Go figure.
|
|
@@ -87,6 +89,7 @@ export class EquipmentMessage {
|
|
|
87
89
|
sys.bodies.removeItemById(bodyId);
|
|
88
90
|
state.temps.bodies.removeItemById(bodyId);
|
|
89
91
|
}
|
|
92
|
+
msg.isProcessed = true;
|
|
90
93
|
break;
|
|
91
94
|
case 3:
|
|
92
95
|
// The first name is the second body and the 2nd is the 4th. This packet also contains
|
|
@@ -136,6 +139,7 @@ export class EquipmentMessage {
|
|
|
136
139
|
state.equipment.maxValves = sys.equipment.maxValves;
|
|
137
140
|
state.equipment.maxSchedules = sys.equipment.maxSchedules;
|
|
138
141
|
state.equipment.maxPumps = sys.equipment.maxPumps;
|
|
142
|
+
msg.isProcessed = true;
|
|
139
143
|
break;
|
|
140
144
|
default:
|
|
141
145
|
logger.debug(`Unprocessed Config Message ${msg.toPacket()}`)
|
|
@@ -17,7 +17,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
17
17
|
import { Inbound } from "../Messages";
|
|
18
18
|
import { sys, Body, ICircuitGroup, LightGroup, CircuitGroup } from "../../../Equipment";
|
|
19
19
|
import { state, ICircuitGroupState, LightGroupState } from "../../../State";
|
|
20
|
-
import { utils } from "../../../Constants";
|
|
20
|
+
import { Timestamp, utils } from "../../../Constants";
|
|
21
21
|
import { logger } from "../../../../logger/Logger";
|
|
22
22
|
export class ExternalMessage {
|
|
23
23
|
public static processIntelliCenter(msg: Inbound): void {
|
|
@@ -83,7 +83,8 @@ export class ExternalMessage {
|
|
|
83
83
|
if (isActive) {
|
|
84
84
|
let chem = sys.chemControllers.getItemById(id, true);
|
|
85
85
|
let schem = state.chemControllers.getItemById(id, true);
|
|
86
|
-
chem.isVirtual = false;
|
|
86
|
+
// chem.isVirtual = false;
|
|
87
|
+
chem.master = 0;
|
|
87
88
|
chem.ph.tank.capacity = chem.orp.tank.capacity = 6;
|
|
88
89
|
chem.ph.tank.units = chem.orp.tank.units = '';
|
|
89
90
|
schem.type = chem.type = 2;
|
|
@@ -106,7 +107,8 @@ export class ExternalMessage {
|
|
|
106
107
|
let valve = sys.valves.getItemById(msg.extractPayloadByte(2) + 1);
|
|
107
108
|
valve.circuit = msg.extractPayloadByte(3) + 1;
|
|
108
109
|
valve.name = msg.extractPayloadString(4, 16);
|
|
109
|
-
valve.
|
|
110
|
+
valve.master = 0;
|
|
111
|
+
// valve.isVirtual = false;
|
|
110
112
|
msg.isProcessed = true;
|
|
111
113
|
}
|
|
112
114
|
public static processPool(msg: Inbound) {
|
|
@@ -272,31 +274,30 @@ export class ExternalMessage {
|
|
|
272
274
|
private static processHeater(msg: Inbound) {
|
|
273
275
|
// So a user is changing the heater info. Lets
|
|
274
276
|
// hijack it and get it ourselves.
|
|
275
|
-
let
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
277
|
+
let isActive = msg.extractPayloadByte(3) !== 0;
|
|
278
|
+
let heaterId = msg.extractPayloadByte(2) + 1;
|
|
279
|
+
if (isActive) {
|
|
280
|
+
let heater = sys.heaters.getItemById(heaterId, true);
|
|
281
|
+
let hstate = state.heaters.getItemById(heater.id, true);
|
|
282
|
+
|
|
283
|
+
hstate.type = heater.type = msg.extractPayloadByte(3);
|
|
284
|
+
heater.body = msg.extractPayloadByte(4);
|
|
285
|
+
heater.cooldownDelay = msg.extractPayloadByte(5);
|
|
286
|
+
heater.startTempDelta = msg.extractPayloadByte(6);
|
|
287
|
+
heater.stopTempDelta = msg.extractPayloadByte(7);
|
|
288
|
+
heater.coolingEnabled = msg.extractPayloadByte(8) > 0;
|
|
289
|
+
heater.differentialTemp = msg.extractPayloadByte(9);
|
|
290
|
+
heater.address = msg.extractPayloadByte(10);
|
|
291
|
+
hstate.name = heater.name = msg.extractPayloadString(11, 16);
|
|
292
|
+
heater.efficiencyMode = msg.extractPayloadByte(27);
|
|
293
|
+
heater.maxBoostTemp = msg.extractPayloadByte(28);
|
|
294
|
+
heater.economyTime = msg.extractPayloadByte(29);
|
|
295
|
+
heater.master = 0;
|
|
291
296
|
}
|
|
292
297
|
else {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
//heater.isVirtual = hstate.isVirtual = false;
|
|
296
|
-
hstate.name = heater.name;
|
|
297
|
-
hstate.type = heater.type;
|
|
298
|
+
sys.heaters.removeItemById(heaterId);
|
|
299
|
+
state.heaters.removeItemById(heaterId);
|
|
298
300
|
}
|
|
299
|
-
|
|
300
301
|
sys.board.heaters.updateHeaterServices();
|
|
301
302
|
// Check anyway to make sure we got it all.
|
|
302
303
|
//setTimeout(() => sys.checkConfiguration(), 500);
|
|
@@ -738,6 +739,8 @@ export class ExternalMessage {
|
|
|
738
739
|
sys.general.options.clockMode = (msg.extractPayloadByte(14) & 0x0001) == 1 ? 24 : 12;
|
|
739
740
|
msg.isProcessed = true;
|
|
740
741
|
break;
|
|
742
|
+
case 12: // This is byte 15 but we don't know what it is. Numbers witnessed include 51, 52, 89, 235.
|
|
743
|
+
break;
|
|
741
744
|
case 14: // Clock source
|
|
742
745
|
if ((msg.extractPayloadByte(17) & 0x0040) === 1)
|
|
743
746
|
sys.general.options.clockSource = 'internet';
|
|
@@ -745,6 +748,8 @@ export class ExternalMessage {
|
|
|
745
748
|
sys.general.options.clockSource = 'manual';
|
|
746
749
|
msg.isProcessed = true;
|
|
747
750
|
break;
|
|
751
|
+
case 15: // This is byte 18 but we don't know what it is. Numbers witnessed include 1, 2, 3, 5, 100.
|
|
752
|
+
break;
|
|
748
753
|
case 18: // Body 1 Heat Setpoint
|
|
749
754
|
body = sys.bodies.getItemById(1, false);
|
|
750
755
|
body.heatSetpoint = msg.extractPayloadByte(21);
|
|
@@ -768,7 +773,7 @@ export class ExternalMessage {
|
|
|
768
773
|
break;
|
|
769
774
|
case 21: // Body 2 Cool Setpoint
|
|
770
775
|
body = sys.bodies.getItemById(2, false);
|
|
771
|
-
body.
|
|
776
|
+
body.coolSetpoint = msg.extractPayloadByte(24);
|
|
772
777
|
state.temps.bodies.getItemById(2).coolSetpoint = body.coolSetpoint;
|
|
773
778
|
state.emitEquipmentChanges();
|
|
774
779
|
msg.isProcessed = true;
|
|
@@ -817,6 +822,19 @@ export class ExternalMessage {
|
|
|
817
822
|
sys.general.options.manualHeat = msg.extractPayloadByte(40) !== 0;
|
|
818
823
|
msg.isProcessed = true;
|
|
819
824
|
break;
|
|
825
|
+
case 64: // Vacation mode
|
|
826
|
+
let yy = msg.extractPayloadByte(5) + 2000;
|
|
827
|
+
let mm = msg.extractPayloadByte(6);
|
|
828
|
+
let dd = msg.extractPayloadByte(7);
|
|
829
|
+
sys.general.options.vacation.startDate = new Date(yy, mm - 1, dd);
|
|
830
|
+
yy = msg.extractPayloadByte(8) + 2000;
|
|
831
|
+
mm = msg.extractPayloadByte(9);
|
|
832
|
+
dd = msg.extractPayloadByte(10);
|
|
833
|
+
sys.general.options.vacation.endDate = new Date(yy, mm - 1, dd);
|
|
834
|
+
sys.general.options.vacation.enabled = msg.extractPayloadByte(3) > 0;
|
|
835
|
+
sys.general.options.vacation.useTimeframe = msg.extractPayloadByte(4) > 0;
|
|
836
|
+
msg.isProcessed = true;
|
|
837
|
+
break;
|
|
820
838
|
}
|
|
821
839
|
}
|
|
822
840
|
public static processTouchChlorinator(msg: Inbound) {
|
|
@@ -58,6 +58,7 @@ export class FeatureMessage {
|
|
|
58
58
|
FeatureMessage.processFeatureNames(msg);
|
|
59
59
|
break;
|
|
60
60
|
case 22: // Not sure what this is.
|
|
61
|
+
msg.isProcessed = true;
|
|
61
62
|
break;
|
|
62
63
|
default:
|
|
63
64
|
logger.debug(`Unprocessed Config Message ${msg.toPacket()}`)
|
|
@@ -70,6 +71,7 @@ export class FeatureMessage {
|
|
|
70
71
|
var feature: Feature = sys.features.getItemById(featureId, false);
|
|
71
72
|
feature.dontStop = msg.extractPayloadByte(i + 1) == 1;
|
|
72
73
|
}
|
|
74
|
+
msg.isProcessed = true;
|
|
73
75
|
}
|
|
74
76
|
private static processFeatureType(msg: Inbound) {
|
|
75
77
|
for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxFeatures; i++) {
|
|
@@ -88,6 +90,7 @@ export class FeatureMessage {
|
|
|
88
90
|
state.features.removeItemById(featureId);
|
|
89
91
|
}
|
|
90
92
|
}
|
|
93
|
+
msg.isProcessed = true;
|
|
91
94
|
}
|
|
92
95
|
private static processFreezeProtect(msg: Inbound) {
|
|
93
96
|
for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxFeatures; i++) {
|
|
@@ -95,6 +98,7 @@ export class FeatureMessage {
|
|
|
95
98
|
var feature: Feature = sys.features.getItemById(featureId);
|
|
96
99
|
feature.freeze = msg.extractPayloadByte(i + 1) > 0;
|
|
97
100
|
}
|
|
101
|
+
msg.isProcessed = true;
|
|
98
102
|
}
|
|
99
103
|
private static processFeatureNames(msg: Inbound) {
|
|
100
104
|
var featureId = ((msg.extractPayloadByte(1) - 6) * 2) + sys.board.equipmentIds.features.start;
|
|
@@ -109,6 +113,7 @@ export class FeatureMessage {
|
|
|
109
113
|
if (feature.isActive) state.features.getItemById(feature.id).name = feature.name;
|
|
110
114
|
}
|
|
111
115
|
state.emitEquipmentChanges();
|
|
116
|
+
msg.isProcessed = true;
|
|
112
117
|
}
|
|
113
118
|
private static processEggTimerHours(msg: Inbound) {
|
|
114
119
|
for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxFeatures; i++) {
|
|
@@ -116,6 +121,7 @@ export class FeatureMessage {
|
|
|
116
121
|
let feature: Feature = sys.features.getItemById(featureId);
|
|
117
122
|
feature.eggTimer = (msg.extractPayloadByte(i + 1) * 60) + ((feature.eggTimer || 0) % 60);
|
|
118
123
|
}
|
|
124
|
+
msg.isProcessed = true;
|
|
119
125
|
}
|
|
120
126
|
private static processEggTimerMinutes(msg: Inbound) {
|
|
121
127
|
for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxFeatures; i++) {
|
|
@@ -123,6 +129,7 @@ export class FeatureMessage {
|
|
|
123
129
|
var feature: Feature = sys.features.getItemById(featureId);
|
|
124
130
|
feature.eggTimer = (Math.floor(feature.eggTimer / 60) * 60) + msg.extractPayloadByte(i + 1);
|
|
125
131
|
}
|
|
132
|
+
msg.isProcessed = true;
|
|
126
133
|
}
|
|
127
134
|
private static processShowInFeatures(msg: Inbound) {
|
|
128
135
|
for (let i = 1; i < msg.payload.length - 1 && i <= sys.equipment.maxFeatures; i++) {
|
|
@@ -132,6 +139,6 @@ export class FeatureMessage {
|
|
|
132
139
|
if (feature.isActive) state.features.getItemById(featureId, feature.isActive).showInFeatures = feature.showInFeatures;
|
|
133
140
|
}
|
|
134
141
|
state.emitEquipmentChanges();
|
|
142
|
+
msg.isProcessed = true;
|
|
135
143
|
}
|
|
136
|
-
|
|
137
144
|
}
|
|
@@ -24,31 +24,39 @@ export class GeneralMessage {
|
|
|
24
24
|
sys.general.alias = msg.extractPayloadString(2, 16);
|
|
25
25
|
sys.general.owner.name = msg.extractPayloadString(18, 16);
|
|
26
26
|
sys.general.location.zip = msg.extractPayloadString(34, 6);
|
|
27
|
+
msg.isProcessed = true;
|
|
27
28
|
break;
|
|
28
29
|
case 1:
|
|
29
30
|
sys.general.owner.phone = msg.extractPayloadString(2, 20);
|
|
30
31
|
sys.general.owner.phone2 = msg.extractPayloadString(21, 15);
|
|
31
32
|
sys.general.location.latitude = ((msg.extractPayloadByte(35) * 256) + msg.extractPayloadByte(34)) / 100;
|
|
33
|
+
msg.isProcessed = true;
|
|
32
34
|
break;
|
|
33
35
|
case 2:
|
|
34
36
|
sys.general.location.address = msg.extractPayloadString(2, 32);
|
|
35
37
|
sys.general.location.longitude = -(((msg.extractPayloadByte(35) * 256) + msg.extractPayloadByte(34)) / 100);
|
|
38
|
+
msg.isProcessed = true;
|
|
36
39
|
break;
|
|
37
40
|
case 3:
|
|
38
41
|
sys.general.owner.email = msg.extractPayloadString(2, 32);
|
|
39
42
|
sys.general.location.timeZone = msg.extractPayloadByte(34);
|
|
43
|
+
msg.isProcessed = true;
|
|
40
44
|
break;
|
|
41
45
|
case 4:
|
|
42
46
|
sys.general.owner.email2 = msg.extractPayloadString(2, 32);
|
|
47
|
+
msg.isProcessed = true;
|
|
43
48
|
break;
|
|
44
49
|
case 5:
|
|
45
50
|
sys.general.location.country = msg.extractPayloadString(2, 32);
|
|
51
|
+
msg.isProcessed = true;
|
|
46
52
|
break;
|
|
47
53
|
case 6:
|
|
48
54
|
sys.general.location.city = msg.extractPayloadString(2, 32);
|
|
55
|
+
msg.isProcessed = true;
|
|
49
56
|
break;
|
|
50
57
|
case 7:
|
|
51
58
|
sys.general.location.state = msg.extractPayloadString(2, 32);
|
|
59
|
+
msg.isProcessed = true;
|
|
52
60
|
break;
|
|
53
61
|
default:
|
|
54
62
|
logger.debug(`Unprocessed Config Message ${msg.toPacket()}`)
|