nodejs-poolcontroller 7.4.0 → 7.5.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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/Changelog +3 -0
- package/README.md +2 -2
- package/app.ts +2 -0
- package/config/Config.ts +3 -0
- package/config/VersionCheck.ts +8 -4
- package/controller/Equipment.ts +89 -29
- package/controller/Errors.ts +14 -1
- package/controller/State.ts +75 -31
- package/controller/boards/EasyTouchBoard.ts +81 -36
- package/controller/boards/IntelliCenterBoard.ts +96 -32
- package/controller/boards/IntelliTouchBoard.ts +103 -29
- package/controller/boards/NixieBoard.ts +79 -27
- package/controller/boards/SystemBoard.ts +1552 -822
- package/controller/comms/Comms.ts +84 -9
- 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 +43 -25
- 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 +10 -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 +12 -2
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
- package/controller/comms/messages/status/EquipmentStateMessage.ts +74 -22
- package/controller/comms/messages/status/HeaterStateMessage.ts +15 -6
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +37 -26
- package/controller/nixie/Nixie.ts +18 -16
- package/controller/nixie/chemistry/ChemController.ts +57 -37
- package/controller/nixie/chemistry/Chlorinator.ts +7 -8
- package/controller/nixie/circuits/Circuit.ts +17 -0
- package/controller/nixie/pumps/Pump.ts +49 -24
- package/controller/nixie/schedules/Schedule.ts +1 -1
- package/defaultConfig.json +15 -0
- package/issue_template.md +1 -1
- package/logger/DataLogger.ts +37 -22
- package/package.json +3 -1
- package/web/Server.ts +515 -27
- package/web/bindings/influxDB.json +35 -0
- package/web/bindings/mqtt.json +62 -3
- package/web/bindings/mqttAlt.json +57 -4
- package/web/interfaces/httpInterface.ts +2 -0
- package/web/interfaces/influxInterface.ts +3 -2
- package/web/interfaces/mqttInterface.ts +12 -1
- package/web/services/config/Config.ts +162 -37
- package/web/services/state/State.ts +47 -3
- package/web/services/state/StateSocket.ts +1 -1
|
@@ -335,15 +335,16 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
335
335
|
let pt = sys.board.valueMaps.pumpTypes.get(this.pump.type);
|
|
336
336
|
// Since these process are async the closing flag can be set
|
|
337
337
|
// between calls. We need to check it in between each call.
|
|
338
|
-
if (!this.closing) await this.setDriveStateAsync(
|
|
339
|
-
if (!this.closing) {
|
|
340
|
-
if (this._targetSpeed >= pt.minFlow && this._targetSpeed <= pt.maxFlow) await this.setPumpGPMAsync(
|
|
341
|
-
else if (this._targetSpeed >= pt.minSpeed && this._targetSpeed <= pt.maxSpeed) await this.setPumpRPMAsync(
|
|
342
|
-
}
|
|
338
|
+
try { if (!this.closing) await this.setDriveStateAsync(); } catch (err) {}
|
|
339
|
+
try { if (!this.closing) {
|
|
340
|
+
if (this._targetSpeed >= pt.minFlow && this._targetSpeed <= pt.maxFlow) await this.setPumpGPMAsync();
|
|
341
|
+
else if (this._targetSpeed >= pt.minSpeed && this._targetSpeed <= pt.maxSpeed) await this.setPumpRPMAsync();
|
|
342
|
+
} } catch (err) {}
|
|
343
343
|
|
|
344
|
-
if(!this.closing) await
|
|
345
|
-
if(!this.closing) await
|
|
346
|
-
if(!this.closing) await this.
|
|
344
|
+
try { if(!this.closing) await this.setPumpFeature(6); } catch (err) {};
|
|
345
|
+
try { if(!this.closing) await utils.sleep(1000); } catch (err) {};
|
|
346
|
+
try { if(!this.closing) await this.requestPumpStatus(); } catch (err) {};
|
|
347
|
+
try { if(!this.closing) await this.setPumpToRemoteControl(); } catch (err) {};
|
|
347
348
|
return new InterfaceServerResponse(200, 'Success');
|
|
348
349
|
}
|
|
349
350
|
catch (err) {
|
|
@@ -353,7 +354,7 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
353
354
|
finally { this.suspendPolling = false; }
|
|
354
355
|
|
|
355
356
|
};
|
|
356
|
-
protected async setDriveStateAsync(
|
|
357
|
+
protected async setDriveStateAsync(running: boolean = true) {
|
|
357
358
|
return new Promise<void>((resolve, reject) => {
|
|
358
359
|
let out = Outbound.create({
|
|
359
360
|
protocol: Protocol.Pump,
|
|
@@ -373,7 +374,7 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
373
374
|
conn.queueSendMessage(out);
|
|
374
375
|
});
|
|
375
376
|
};
|
|
376
|
-
protected async requestPumpStatus(
|
|
377
|
+
protected async requestPumpStatus() {
|
|
377
378
|
return new Promise<void>((resolve, reject) => {
|
|
378
379
|
let out = Outbound.create({
|
|
379
380
|
protocol: Protocol.Pump,
|
|
@@ -393,7 +394,7 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
393
394
|
conn.queueSendMessage(out);
|
|
394
395
|
})
|
|
395
396
|
};
|
|
396
|
-
protected setPumpToRemoteControl(
|
|
397
|
+
protected setPumpToRemoteControl(running: boolean = true) {
|
|
397
398
|
return new Promise<void>((resolve, reject) => {
|
|
398
399
|
let out = Outbound.create({
|
|
399
400
|
protocol: Protocol.Pump,
|
|
@@ -414,13 +415,15 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
414
415
|
conn.queueSendMessage(out);
|
|
415
416
|
});
|
|
416
417
|
}
|
|
417
|
-
protected
|
|
418
|
+
protected setPumpFeature(feature?: number) {
|
|
419
|
+
// empty payload (possibly 0?, too) is no feature
|
|
420
|
+
// 6: Feature 1
|
|
418
421
|
return new Promise<void>((resolve, reject) => {
|
|
419
422
|
let out = Outbound.create({
|
|
420
423
|
protocol: Protocol.Pump,
|
|
421
424
|
dest: this.pump.address,
|
|
422
425
|
action: 5,
|
|
423
|
-
payload: [],
|
|
426
|
+
payload: typeof feature === 'undefined' ? [] : [ feature ],
|
|
424
427
|
retries: 2,
|
|
425
428
|
repsonse: true,
|
|
426
429
|
onComplete: (err, msg: Outbound) => {
|
|
@@ -434,7 +437,7 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
434
437
|
conn.queueSendMessage(out);
|
|
435
438
|
});
|
|
436
439
|
};
|
|
437
|
-
protected async setPumpRPMAsync(
|
|
440
|
+
protected async setPumpRPMAsync() {
|
|
438
441
|
return new Promise<void>((resolve, reject) => {
|
|
439
442
|
let out = Outbound.create({
|
|
440
443
|
protocol: Protocol.Pump,
|
|
@@ -455,14 +458,14 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
455
458
|
conn.queueSendMessage(out);
|
|
456
459
|
});
|
|
457
460
|
};
|
|
458
|
-
protected async setPumpGPMAsync(
|
|
461
|
+
protected async setPumpGPMAsync() {
|
|
459
462
|
// packet for vf; vsf will override
|
|
460
463
|
return new Promise<void>((resolve, reject) => {
|
|
461
464
|
let out = Outbound.create({
|
|
462
465
|
protocol: Protocol.Pump,
|
|
463
466
|
dest: this.pump.address,
|
|
464
|
-
action:
|
|
465
|
-
payload: [
|
|
467
|
+
action: 1,
|
|
468
|
+
payload: [2, 228, 0, this._targetSpeed],
|
|
466
469
|
retries: 1,
|
|
467
470
|
response: true,
|
|
468
471
|
onComplete: (err, msg) => {
|
|
@@ -484,10 +487,10 @@ export class NixiePumpRS485 extends NixiePump {
|
|
|
484
487
|
this._pollTimer = null;
|
|
485
488
|
let pstate = state.pumps.getItemById(this.pump.id);
|
|
486
489
|
this._targetSpeed = 0;
|
|
487
|
-
try { await this.setDriveStateAsync(
|
|
488
|
-
try { await this.
|
|
489
|
-
try { await this.setDriveStateAsync(
|
|
490
|
-
try { await this.setPumpToRemoteControl(
|
|
490
|
+
try { await this.setDriveStateAsync(false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
|
|
491
|
+
try { await this.setPumpFeature(); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
|
|
492
|
+
try { await this.setDriveStateAsync(false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
|
|
493
|
+
try { await this.setPumpToRemoteControl(false); } catch (err) { logger.error(`Error closing pump ${this.pump.name}: ${err.message}`) }
|
|
491
494
|
this.closing = true;
|
|
492
495
|
// Make sure the polling timer is dead after we have closted this all off. That way we do not
|
|
493
496
|
// have another process that revives it from the dead.
|
|
@@ -574,14 +577,36 @@ export class NixiePumpVSF extends NixiePumpRS485 {
|
|
|
574
577
|
if (this._targetSpeed !== _newSpeed) logger.info(`NCP: Setting Pump ${this.pump.name} to ${_newSpeed} ${flows > 0 ? 'GPM' : 'RPM'}.`);
|
|
575
578
|
this._targetSpeed = _newSpeed;
|
|
576
579
|
}
|
|
577
|
-
protected async
|
|
578
|
-
// vsf
|
|
580
|
+
protected async setPumpRPMAsync() {
|
|
581
|
+
// vsf action is 10 for rpm
|
|
579
582
|
return new Promise<void>((resolve, reject) => {
|
|
580
583
|
let out = Outbound.create({
|
|
581
584
|
protocol: Protocol.Pump,
|
|
582
585
|
dest: this.pump.address,
|
|
583
586
|
action: 10,
|
|
584
|
-
payload: [
|
|
587
|
+
payload: [2, 196, Math.floor(this._targetSpeed / 256), this._targetSpeed % 256],
|
|
588
|
+
retries: 1,
|
|
589
|
+
// timeout: 250,
|
|
590
|
+
response: true,
|
|
591
|
+
onComplete: (err, msg) => {
|
|
592
|
+
if (err) {
|
|
593
|
+
logger.error(`Error sending setPumpRPMAsync for ${this.pump.name}: ${err.message}`);
|
|
594
|
+
reject(err);
|
|
595
|
+
}
|
|
596
|
+
else resolve();
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
conn.queueSendMessage(out);
|
|
600
|
+
});
|
|
601
|
+
};
|
|
602
|
+
protected async setPumpGPMAsync() {
|
|
603
|
+
// vsf payload; different from vf payload
|
|
604
|
+
return new Promise<void>((resolve, reject) => {
|
|
605
|
+
let out = Outbound.create({
|
|
606
|
+
protocol: Protocol.Pump,
|
|
607
|
+
dest: this.pump.address,
|
|
608
|
+
action: 9,
|
|
609
|
+
payload: [2, 196, 0, this._targetSpeed],
|
|
585
610
|
retries: 1,
|
|
586
611
|
response: true,
|
|
587
612
|
onComplete: (err, msg) => {
|
|
@@ -234,7 +234,7 @@ export class NixieSchedule extends NixieEquipment {
|
|
|
234
234
|
}
|
|
235
235
|
public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
|
|
236
236
|
}
|
|
237
|
-
class
|
|
237
|
+
class NixieScheduleContext {
|
|
238
238
|
constructor() {
|
|
239
239
|
|
|
240
240
|
}
|
package/defaultConfig.json
CHANGED
|
@@ -17,7 +17,18 @@
|
|
|
17
17
|
"autoOpen": false,
|
|
18
18
|
"lock": false
|
|
19
19
|
}
|
|
20
|
+
},
|
|
21
|
+
"backups": {
|
|
22
|
+
"automatic": false,
|
|
23
|
+
"interval": {
|
|
24
|
+
"days": 30,
|
|
25
|
+
"hours": 0
|
|
26
|
+
},
|
|
27
|
+
"keepCount": 5,
|
|
28
|
+
"njsPC": true,
|
|
29
|
+
"servers": []
|
|
20
30
|
}
|
|
31
|
+
|
|
21
32
|
},
|
|
22
33
|
"web": {
|
|
23
34
|
"servers": {
|
|
@@ -52,6 +63,7 @@
|
|
|
52
63
|
"interfaces": {
|
|
53
64
|
"smartThings": {
|
|
54
65
|
"name": "SmartThings",
|
|
66
|
+
"type": "rest",
|
|
55
67
|
"enabled": false,
|
|
56
68
|
"fileName": "smartThings-Hubitat.json",
|
|
57
69
|
"globals": {},
|
|
@@ -62,6 +74,7 @@
|
|
|
62
74
|
},
|
|
63
75
|
"hubitat": {
|
|
64
76
|
"name": "Hubitat",
|
|
77
|
+
"type": "rest",
|
|
65
78
|
"enabled": false,
|
|
66
79
|
"fileName": "smartThings-Hubitat.json",
|
|
67
80
|
"globals": {},
|
|
@@ -72,6 +85,7 @@
|
|
|
72
85
|
},
|
|
73
86
|
"vera": {
|
|
74
87
|
"name": "Vera",
|
|
88
|
+
"type": "rest",
|
|
75
89
|
"enabled": false,
|
|
76
90
|
"fileName": "vera.json",
|
|
77
91
|
"vars": {
|
|
@@ -83,6 +97,7 @@
|
|
|
83
97
|
}
|
|
84
98
|
},
|
|
85
99
|
"valveRelay": {
|
|
100
|
+
"type": "rest",
|
|
86
101
|
"name": "Valve Relays",
|
|
87
102
|
"enabled": false,
|
|
88
103
|
"fileName": "valveRelays.json",
|
package/issue_template.md
CHANGED
|
@@ -33,7 +33,7 @@ Follow the instructions to complete a [packet capture](https://github.com/tagyou
|
|
|
33
33
|
- Pump(s) manufacturer and model: [e.g. IntelliFlow 2 VST 011056]
|
|
34
34
|
- Chlorinator: [e.g. iChlor, IntelliChlor-40]
|
|
35
35
|
- Heater(s): [e.g. gas, solar, heatpump, ultratemp]
|
|
36
|
-
- Chemical controller: [e.g. IntelliChem,
|
|
36
|
+
- Chemical controller: [e.g. IntelliChem, Relay Equipment Manager (REM)]
|
|
37
37
|
- Valves: [e.g. Intellivalve]
|
|
38
38
|
- Any other relevant equipment:
|
|
39
39
|
|
package/logger/DataLogger.ts
CHANGED
|
@@ -29,8 +29,10 @@ export class DataLogger {
|
|
|
29
29
|
}
|
|
30
30
|
let arr: T[] = [];
|
|
31
31
|
for (let i = 0; i < lines.length; i++) {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
try {
|
|
33
|
+
let entry = DataLogger.createEntry<T>(type, lines[i]);
|
|
34
|
+
arr.push(entry);
|
|
35
|
+
} catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
|
|
34
36
|
}
|
|
35
37
|
return arr;
|
|
36
38
|
} catch (err) { logger.error(err); }
|
|
@@ -47,6 +49,7 @@ export class DataLogger {
|
|
|
47
49
|
let newLines = ['\r', '\n'];
|
|
48
50
|
let arr: T[] = [];
|
|
49
51
|
if (fs.existsSync(logPath)) {
|
|
52
|
+
console.log(`Reading logfile ${logPath}`);
|
|
50
53
|
// Alright what we have created here is a method to read the data from the end of
|
|
51
54
|
// a log file in reverse order (tail) that works for all os implementations. It is
|
|
52
55
|
// really dumb that this isn't part of the actual file processing.
|
|
@@ -81,15 +84,16 @@ export class DataLogger {
|
|
|
81
84
|
// record then we shoud save off the line and read the next record.
|
|
82
85
|
if (newLines.includes(char) || pos === 0) {
|
|
83
86
|
if (chars.length > 0) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
try {
|
|
88
|
+
let entry = DataLogger.createEntry<T>(type, chars.join(''));
|
|
89
|
+
if (typeof fn === 'function') {
|
|
90
|
+
let rc = fn(arr.length + 1, entry, arr);
|
|
91
|
+
if (rc === true) arr.push(entry);
|
|
92
|
+
else if (rc === false) break;
|
|
93
|
+
}
|
|
94
|
+
else
|
|
95
|
+
arr.push(entry);
|
|
96
|
+
} catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
|
|
93
97
|
}
|
|
94
98
|
chars = [];
|
|
95
99
|
}
|
|
@@ -142,14 +146,16 @@ export class DataLogger {
|
|
|
142
146
|
// record then we shoud save off the line and read the next record.
|
|
143
147
|
if (newLines.includes(char) || pos === 0) {
|
|
144
148
|
if (chars.length > 0) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
149
|
+
try {
|
|
150
|
+
let entry = DataLogger.createEntry<T>(type, chars.join(''));
|
|
151
|
+
if (typeof fn === 'function') {
|
|
152
|
+
let rc = fn(arr.length + 1, entry, arr);
|
|
153
|
+
if (rc === true) arr.push(entry);
|
|
154
|
+
else if (rc === false) break;
|
|
155
|
+
}
|
|
156
|
+
else
|
|
157
|
+
arr.push(entry);
|
|
158
|
+
} catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
|
|
153
159
|
}
|
|
154
160
|
chars = [];
|
|
155
161
|
}
|
|
@@ -276,7 +282,6 @@ export class DataLogger {
|
|
|
276
282
|
let entry = DataLogger.createEntry<T>(type, chars.join(''));
|
|
277
283
|
if (typeof fn === 'function') {
|
|
278
284
|
let rc = fn(arr.length + 1, entry, arr);
|
|
279
|
-
console.log(rc);
|
|
280
285
|
if (rc === true) arr.push(entry);
|
|
281
286
|
else if (rc === false) break;
|
|
282
287
|
}
|
|
@@ -399,11 +404,21 @@ export class DataLoggerEntry {
|
|
|
399
404
|
// Parse the data from the log entry if it exists.
|
|
400
405
|
if (typeof entry === 'object') entry = JSON.stringify(entry);
|
|
401
406
|
if (typeof entry === 'string') this.parse(entry);
|
|
407
|
+
else {
|
|
408
|
+
//console.log(`A DATALOGGER ENTRY DOES NOT HAVE A PROPER TYPE ${typeof entry} *************************************`);
|
|
409
|
+
//console.log(entry);
|
|
410
|
+
}
|
|
402
411
|
}
|
|
403
|
-
public createInstance(entry?: string) { return new DataLoggerEntry(entry); }
|
|
412
|
+
public static createInstance(entry?: string) { return new DataLoggerEntry(entry); }
|
|
404
413
|
public parse(entry: string) {
|
|
405
414
|
let obj = typeof entry !== 'undefined' ? JSON.parse(entry, this.dateParser) : {};
|
|
406
|
-
|
|
415
|
+
if (typeof entry === 'undefined') {
|
|
416
|
+
console.log(`A DATALOGGER ENTRY WAS NOT DEFINED *************************`);
|
|
417
|
+
}
|
|
418
|
+
else if (entry === '') {
|
|
419
|
+
console.log(`THE INCOMING DATALOGGER ENTRY WAS EMPTY ***************************`)
|
|
420
|
+
}
|
|
421
|
+
let o = extend(true, this, obj);
|
|
407
422
|
}
|
|
408
423
|
protected dateParser(key, value) {
|
|
409
424
|
if (typeof value === 'string') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodejs-poolcontroller",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.5.1",
|
|
4
4
|
"description": "nodejs-poolController",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"author": {
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"extend": "^3.0.2",
|
|
26
26
|
"jszip": "^3.7.1",
|
|
27
27
|
"mqtt": "^4.2.8",
|
|
28
|
+
"multer": "^1.4.3",
|
|
28
29
|
"multicast-dns": "^7.2.3",
|
|
29
30
|
"node-ssdp": "^4.0.1",
|
|
30
31
|
"serialport": "^9.2.1",
|
|
@@ -36,6 +37,7 @@
|
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"@types/express": "^4.17.13",
|
|
38
39
|
"@types/extend": "^3.0.1",
|
|
40
|
+
"@types/multer": "^1.4.7",
|
|
39
41
|
"@types/node": "^12.20.23",
|
|
40
42
|
"@typescript-eslint/eslint-plugin": "^4.30.0",
|
|
41
43
|
"@typescript-eslint/parser": "^4.30.0",
|