nodejs-poolcontroller 8.0.1 → 8.0.4
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/workflows/docker-publish-njsPC-linux.yml +50 -0
- package/Dockerfile +5 -3
- package/README.md +4 -1
- package/config/Config.ts +1 -1
- package/controller/Constants.ts +164 -67
- package/controller/Equipment.ts +78 -18
- package/controller/Lockouts.ts +15 -0
- package/controller/State.ts +281 -7
- package/controller/boards/EasyTouchBoard.ts +225 -101
- package/controller/boards/IntelliCenterBoard.ts +84 -23
- package/controller/boards/IntelliTouchBoard.ts +2 -4
- package/controller/boards/NixieBoard.ts +84 -27
- package/controller/boards/SunTouchBoard.ts +8 -2
- package/controller/boards/SystemBoard.ts +3262 -3242
- package/controller/comms/ScreenLogic.ts +47 -44
- package/controller/comms/messages/Messages.ts +4 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +10 -3
- package/controller/comms/messages/config/ExternalMessage.ts +4 -1
- package/controller/comms/messages/config/PumpMessage.ts +8 -7
- package/controller/comms/messages/config/RemoteMessage.ts +48 -43
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -2
- package/controller/comms/messages/status/EquipmentStateMessage.ts +9 -4
- package/controller/comms/messages/status/PumpStateMessage.ts +1 -1
- package/controller/nixie/NixieEquipment.ts +1 -1
- package/controller/nixie/bodies/Body.ts +1 -1
- package/controller/nixie/chemistry/ChemController.ts +37 -28
- package/controller/nixie/circuits/Circuit.ts +48 -7
- package/controller/nixie/heaters/Heater.ts +24 -5
- package/controller/nixie/pumps/Pump.ts +159 -97
- package/controller/nixie/schedules/Schedule.ts +207 -126
- package/defaultConfig.json +3 -3
- package/logger/DataLogger.ts +7 -7
- package/package.json +2 -2
- package/sendSocket.js +32 -0
- package/web/Server.ts +17 -11
- package/web/bindings/homeassistant.json +2 -2
- package/web/interfaces/mqttInterface.ts +18 -18
- package/web/services/config/Config.ts +34 -1
- package/web/services/state/State.ts +10 -3
- package/web/services/state/StateSocket.ts +7 -3
- package/web/services/utilities/Utilities.ts +3 -3
|
@@ -68,12 +68,12 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
68
68
|
// make sure status is up to date immediately
|
|
69
69
|
// especially in the case of a re-connect
|
|
70
70
|
this.bindEvent("controller", state.controllerState);
|
|
71
|
-
} catch (err) { logger.error(err); }
|
|
71
|
+
} catch (err) { logger.error(`Error connecting to MQTT Broker ${this.cfg.name} ${err.message}`); }
|
|
72
72
|
});
|
|
73
73
|
this.client.on('reconnect', () => {
|
|
74
74
|
try {
|
|
75
75
|
logger.info(`Re-connecting to MQTT broker ${this.cfg.name}`);
|
|
76
|
-
} catch (err) { logger.error(err); }
|
|
76
|
+
} catch (err) { logger.error(`Error reconnecting to MQTT Brokder ${this.cfg.name} ${err.message}`); }
|
|
77
77
|
|
|
78
78
|
});
|
|
79
79
|
this.client.on('error', (error) => {
|
|
@@ -374,7 +374,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
374
374
|
}
|
|
375
375
|
}
|
|
376
376
|
catch (err) {
|
|
377
|
-
logger.error(err);
|
|
377
|
+
logger.error(`Error binding MQTT event ${evt}: ${err.message}`);
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
// This needed to be refactored so we could extract it from an anonymous function. We want to be able to unbind
|
|
@@ -415,7 +415,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
415
415
|
try {
|
|
416
416
|
if(typeof isOn !== 'undefined') await sys.board.circuits.setCircuitStateAsync(id, isOn);
|
|
417
417
|
}
|
|
418
|
-
catch (err) { logger.error(err); }
|
|
418
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
419
419
|
break;
|
|
420
420
|
}
|
|
421
421
|
case 'features':
|
|
@@ -423,7 +423,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
423
423
|
try {
|
|
424
424
|
if (typeof isOn !== 'undefined') await sys.board.features.setFeatureStateAsync(id, isOn);
|
|
425
425
|
}
|
|
426
|
-
catch (err) { logger.error(err); }
|
|
426
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
427
427
|
break;
|
|
428
428
|
}
|
|
429
429
|
case 'lightgroups':
|
|
@@ -431,7 +431,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
431
431
|
try {
|
|
432
432
|
if (typeof isOn !== 'undefined') await sys.board.circuits.setLightGroupStateAsync(id, isOn);
|
|
433
433
|
}
|
|
434
|
-
catch (err) { logger.error(err); }
|
|
434
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
435
435
|
break;
|
|
436
436
|
}
|
|
437
437
|
case 'circuitgroups':
|
|
@@ -439,7 +439,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
439
439
|
try {
|
|
440
440
|
if (typeof isOn !== 'undefined') await sys.board.circuits.setCircuitGroupStateAsync(id, isOn);
|
|
441
441
|
}
|
|
442
|
-
catch (err) { logger.error(err); }
|
|
442
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
443
443
|
break;
|
|
444
444
|
}
|
|
445
445
|
default:
|
|
@@ -459,14 +459,14 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
459
459
|
try {
|
|
460
460
|
await sys.board.circuits.toggleCircuitStateAsync(id);
|
|
461
461
|
}
|
|
462
|
-
catch (err) { logger.error(err); }
|
|
462
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
463
463
|
break;
|
|
464
464
|
case 'features':
|
|
465
465
|
case 'feature':
|
|
466
466
|
try {
|
|
467
467
|
await sys.board.features.toggleFeatureStateAsync(id);
|
|
468
468
|
}
|
|
469
|
-
catch (err) { logger.error(err); }
|
|
469
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
470
470
|
break;
|
|
471
471
|
default:
|
|
472
472
|
logger.warn(`MQTT: Inbound topic ${topics[topics.length - 1]} not matched to event ${topics[topics.length - 2].toLowerCase()}. Message ${msg} `)
|
|
@@ -489,7 +489,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
491
|
}
|
|
492
|
-
catch (err) { logger.error(err); }
|
|
492
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
493
493
|
break;
|
|
494
494
|
case 'coolsetpoint':
|
|
495
495
|
try {
|
|
@@ -506,7 +506,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
506
506
|
}
|
|
507
507
|
}
|
|
508
508
|
}
|
|
509
|
-
} catch (err) { logger.error(err); }
|
|
509
|
+
} catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
510
510
|
break;
|
|
511
511
|
case 'setpoint':
|
|
512
512
|
try {
|
|
@@ -530,7 +530,7 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
530
530
|
}
|
|
531
531
|
}
|
|
532
532
|
}
|
|
533
|
-
catch (err) { logger.error(err); }
|
|
533
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
534
534
|
break;
|
|
535
535
|
case 'heatmode':
|
|
536
536
|
try {
|
|
@@ -553,39 +553,39 @@ export class MqttInterfaceBindings extends BaseInterfaceBindings {
|
|
|
553
553
|
}
|
|
554
554
|
let tbody = await sys.board.bodies.setHeatModeAsync(body, mode);
|
|
555
555
|
}
|
|
556
|
-
catch (err) { logger.error(err); }
|
|
556
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
557
557
|
break;
|
|
558
558
|
case 'chlorinator':
|
|
559
559
|
try {
|
|
560
560
|
let schlor = await sys.board.chlorinator.setChlorAsync(msg);
|
|
561
561
|
}
|
|
562
|
-
catch (err) { logger.error(err); }
|
|
562
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
563
563
|
break;
|
|
564
564
|
case 'chemcontroller':
|
|
565
565
|
try {
|
|
566
566
|
await sys.board.chemControllers.setChemControllerAsync(msg);
|
|
567
567
|
}
|
|
568
|
-
catch (err) { logger.error(err); }
|
|
568
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
569
569
|
break;
|
|
570
570
|
case 'settheme':
|
|
571
571
|
try {
|
|
572
572
|
let theme = await state.circuits.setLightThemeAsync(parseInt(msg.id, 10), sys.board.valueMaps.lightThemes.encode(msg.theme));
|
|
573
573
|
}
|
|
574
|
-
catch (err) { logger.error(err); }
|
|
574
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
575
575
|
break;
|
|
576
576
|
case 'temp':
|
|
577
577
|
case 'temps':
|
|
578
578
|
try {
|
|
579
579
|
await sys.board.system.setTempsAsync(msg);
|
|
580
580
|
}
|
|
581
|
-
catch (err) { logger.error(err); }
|
|
581
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
582
582
|
break;
|
|
583
583
|
case 'tempsensor':
|
|
584
584
|
case 'tempsensors':
|
|
585
585
|
try {
|
|
586
586
|
await sys.board.system.setTempSensorsAsync(msg);
|
|
587
587
|
}
|
|
588
|
-
catch (err) { logger.error(err); }
|
|
588
|
+
catch (err) { logger.error(`Error processing MQTT topic ${topics[topics.length - 2]}: ${err.message}`); }
|
|
589
589
|
break;
|
|
590
590
|
default:
|
|
591
591
|
logger.silly(`MQTT: Inbound MQTT topic not matched: ${topic}: ${message.toString()}`)
|
|
@@ -575,6 +575,39 @@ export class ConfigRoute {
|
|
|
575
575
|
}
|
|
576
576
|
catch (err) { next(err); }
|
|
577
577
|
});
|
|
578
|
+
app.put('/config/pumpCircuit', async (req, res, next) => {
|
|
579
|
+
try {
|
|
580
|
+
let pmpId = parseInt(req.body.pumpId, 10);
|
|
581
|
+
let circId = parseInt(req.body.circuitId, 10);
|
|
582
|
+
let pmp: Pump;
|
|
583
|
+
if (isNaN(pmpId)) {
|
|
584
|
+
let pmpAddress = parseInt(req.body.address, 10);
|
|
585
|
+
if (!isNaN(pmpAddress)) pmp = sys.pumps.find(x => x.address === pmpAddress);
|
|
586
|
+
}
|
|
587
|
+
else
|
|
588
|
+
pmp = sys.pumps.find(x => x.id === pmpId);
|
|
589
|
+
if (typeof pmp === 'undefined') throw new ServiceProcessError(`Pump not found`, '/config/pumpCircuit', 'Set circuit speed');
|
|
590
|
+
let data = pmp.get(true);
|
|
591
|
+
let c = typeof data.circuits !== 'undefined' && typeof data.circuits.find !== 'undefined' ? data.circuits.find(x => x.circuit === circId) : undefined;
|
|
592
|
+
if (typeof c === 'undefined') throw new ServiceProcessError(`Circuit not found`, '/config/pumpCircuit', 'Set circuit speed');
|
|
593
|
+
if (typeof req.body.speed !== 'undefined') {
|
|
594
|
+
let speed = parseInt(req.body.speed, 10);
|
|
595
|
+
if (isNaN(speed)) throw new ServiceProcessError(`Invalid circuit speed supplied`, '/config/pumpCircuit', 'Set circuit speed');
|
|
596
|
+
c.speed = speed;
|
|
597
|
+
}
|
|
598
|
+
else if (typeof req.body.flow !== 'undefined') {
|
|
599
|
+
let flow = parseInt(req.body.flow, 10);
|
|
600
|
+
if (isNaN(flow)) throw new ServiceProcessError(`Invalid circuit flow supplied`, '/config/pumpCircuit', 'Set circuit flow');
|
|
601
|
+
c.flow = flow;
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
throw new ServiceProcessError(`You must supply a target flow or speed`, '/config/pumpCircuit', 'Set circuit flow');
|
|
605
|
+
}
|
|
606
|
+
await sys.board.pumps.setPumpAsync(data);
|
|
607
|
+
return res.status(200).send((pmp).get(true));
|
|
608
|
+
} catch (err) { next(err); }
|
|
609
|
+
|
|
610
|
+
});
|
|
578
611
|
// RKS: 05-20-22 This is a remnant of the old web ui. It is not called and the setType method needed to go away.
|
|
579
612
|
//app.delete('/config/pump/:pumpId', async (req, res, next) => {
|
|
580
613
|
// try {
|
|
@@ -984,7 +1017,7 @@ export class ConfigRoute {
|
|
|
984
1017
|
} catch (e) {
|
|
985
1018
|
err = new ServiceProcessError(`Error uploading file: ${e.message}`, 'POST: app/backup/file', 'uploadFile');
|
|
986
1019
|
next(err);
|
|
987
|
-
logger.error(e);
|
|
1020
|
+
logger.error(`Error uploading file ${e.message}`);
|
|
988
1021
|
}
|
|
989
1022
|
});
|
|
990
1023
|
} catch (err) { next(err); }
|
|
@@ -415,9 +415,16 @@ export class StateRoute {
|
|
|
415
415
|
let val;
|
|
416
416
|
if (isNaN(mode)) mode = parseInt(req.body.heatMode, 10);
|
|
417
417
|
if (!isNaN(mode)) val = sys.board.valueMaps.heatModes.transform(mode);
|
|
418
|
-
else
|
|
419
|
-
|
|
420
|
-
|
|
418
|
+
else {
|
|
419
|
+
let smode = req.body.mode || req.body.heatMode;
|
|
420
|
+
if (typeof smode === 'string') smode = smode.toLowerCase();
|
|
421
|
+
else {
|
|
422
|
+
return next(new ServiceParameterError(`Invalid mode supplied ${req.body.mode || req.body.heatMode}.`, 'body', 'heatmode', smode));
|
|
423
|
+
}
|
|
424
|
+
val = sys.board.valueMaps.heatModes.transformByName(smode);
|
|
425
|
+
if (typeof val.val === 'undefined') {
|
|
426
|
+
return next(new ServiceParameterError(`Invalid value for heatMode: ${req.body.mode}`, 'body', 'heatMode', mode));
|
|
427
|
+
}
|
|
421
428
|
}
|
|
422
429
|
mode = val.val;
|
|
423
430
|
let body = sys.bodies.findByObject(req.body);
|
|
@@ -72,7 +72,7 @@ export class StateSocket {
|
|
|
72
72
|
sock.on('/temps', async (data: any) => {
|
|
73
73
|
try {
|
|
74
74
|
data = JSON.parse(data);
|
|
75
|
-
await sys.board.system.setTempsAsync(data).catch(err => logger.error(err));
|
|
75
|
+
await sys.board.system.setTempsAsync(data).catch(err => logger.error(`setTempsAsync: ${err.message}`));
|
|
76
76
|
}
|
|
77
77
|
catch (err) { logger.error(`Socket processing error /temps: ${err.message}`); }
|
|
78
78
|
});
|
|
@@ -193,16 +193,20 @@ export class StateSocket {
|
|
|
193
193
|
try {
|
|
194
194
|
data = JSON.parse(data);
|
|
195
195
|
let id = parseInt(data.id, 10);
|
|
196
|
-
if (!isNaN(id) &&
|
|
196
|
+
if (!isNaN(id) && typeof data.toggle !== 'undefined')
|
|
197
|
+
if (utils.makeBool(data.toggle)) await sys.board.circuits.toggleCircuitStateAsync(id);
|
|
198
|
+
else if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
|
|
197
199
|
await sys.board.circuits.setCircuitStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
|
-
catch (err) { logger.error(err); }
|
|
202
|
+
catch (err) { logger.error(`Socket /circuit error: ${err.message}`); }
|
|
201
203
|
});
|
|
202
204
|
sock.on('/feature', async (data: any) => {
|
|
203
205
|
try {
|
|
204
206
|
data = JSON.parse(data);
|
|
205
207
|
let id = parseInt(data.id, 10);
|
|
208
|
+
if (!isNaN(id) && typeof data.toggle !== 'undefined')
|
|
209
|
+
if (utils.makeBool(data.toggle)) await sys.board.features.toggleFeatureStateAsync(id);
|
|
206
210
|
if (!isNaN(id) && (typeof data.isOn !== 'undefined' || typeof data.state !== 'undefined')) {
|
|
207
211
|
await sys.board.features.setFeatureStateAsync(id, utils.makeBool(data.isOn || typeof data.state));
|
|
208
212
|
}
|
|
@@ -103,7 +103,7 @@ export class UtilitiesRoute {
|
|
|
103
103
|
options: {
|
|
104
104
|
protocol: 'mqtt://', host: '', port: 1883, username: '', password: '',
|
|
105
105
|
selfSignedCertificate: false,
|
|
106
|
-
rootTopic: "pool/@bind=(state.equipment.model).replace(
|
|
106
|
+
rootTopic: "pool/@bind=(state.equipment.model).replace(/ /g,'-').replace(' / ','').toLowerCase();",
|
|
107
107
|
retain: true, qos: 0, changesOnly: true
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -223,11 +223,11 @@ export class UtilitiesRoute {
|
|
|
223
223
|
} catch (e) {
|
|
224
224
|
err = new ServiceProcessError(`Error uploading file: ${e.message}`, 'POST: app/backup/file', 'uploadFile');
|
|
225
225
|
next(err);
|
|
226
|
-
logger.error(e);
|
|
226
|
+
logger.error(`File upload error: ${e.message}`);
|
|
227
227
|
}
|
|
228
228
|
});
|
|
229
229
|
} catch (err) { next(err); }
|
|
230
230
|
});
|
|
231
231
|
|
|
232
232
|
}
|
|
233
|
-
}
|
|
233
|
+
}
|