nodejs-poolcontroller 7.6.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +36 -45
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/CONTRIBUTING.md +74 -74
- package/Changelog +242 -215
- package/Dockerfile +17 -17
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +195 -191
- 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 +26 -8
- package/config/VersionCheck.ts +8 -4
- package/controller/Constants.ts +59 -25
- package/controller/Equipment.ts +2667 -2459
- package/controller/Errors.ts +181 -180
- package/controller/Lockouts.ts +534 -436
- package/controller/State.ts +596 -77
- package/controller/boards/AquaLinkBoard.ts +1003 -0
- package/controller/boards/BoardFactory.ts +53 -45
- package/controller/boards/EasyTouchBoard.ts +3079 -2653
- package/controller/boards/IntelliCenterBoard.ts +3821 -4230
- package/controller/boards/IntelliComBoard.ts +69 -63
- package/controller/boards/IntelliTouchBoard.ts +384 -241
- package/controller/boards/NixieBoard.ts +1871 -1675
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +5244 -4697
- package/controller/comms/Comms.ts +905 -541
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +382 -54
- package/controller/comms/messages/config/ChlorinatorMessage.ts +8 -4
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +82 -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 +31 -30
- 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 +145 -11
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +16 -27
- package/controller/comms/messages/config/PumpMessage.ts +62 -47
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +390 -347
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +44 -27
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +44 -91
- package/controller/comms/messages/status/EquipmentStateMessage.ts +139 -30
- package/controller/comms/messages/status/HeaterStateMessage.ts +135 -86
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -445
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -35
- package/controller/comms/messages/status/PumpStateMessage.ts +92 -2
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +173 -162
- package/controller/nixie/NixieEquipment.ts +104 -103
- package/controller/nixie/bodies/Body.ts +120 -120
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2682 -2498
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +367 -314
- package/controller/nixie/circuits/Circuit.ts +402 -248
- package/controller/nixie/heaters/Heater.ts +815 -649
- package/controller/nixie/pumps/Pump.ts +934 -661
- package/controller/nixie/schedules/Schedule.ts +319 -257
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +346 -286
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +38 -9
- package/package.json +60 -56
- package/tsconfig.json +25 -25
- package/web/Server.ts +275 -117
- package/web/bindings/aqualinkD.json +560 -0
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +1066 -1021
- package/web/bindings/mqtt.json +721 -654
- package/web/bindings/mqttAlt.json +746 -684
- package/web/bindings/rulesManager.json +54 -54
- package/web/bindings/smartThings-Hubitat.json +31 -31
- package/web/bindings/valveRelays.json +20 -20
- package/web/bindings/vera.json +25 -25
- package/web/interfaces/baseInterface.ts +188 -136
- package/web/interfaces/httpInterface.ts +148 -124
- package/web/interfaces/influxInterface.ts +283 -245
- package/web/interfaces/mqttInterface.ts +695 -475
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +177 -49
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +154 -3
- package/web/services/state/StateSocket.ts +69 -18
- package/web/services/utilities/Utilities.ts +232 -42
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
- package/config copy.json +0 -300
- package/issue_template.md +0 -52
|
@@ -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
|
|
@@ -16,6 +17,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16
17
|
*/
|
|
17
18
|
import { IntelliCenterBoard } from 'controller/boards/IntelliCenterBoard';
|
|
18
19
|
import { EasyTouchBoard } from 'controller/boards/EasyTouchBoard';
|
|
20
|
+
import { IntelliTouchBoard } from 'controller/boards/IntelliTouchBoard';
|
|
21
|
+
import { SunTouchBoard } from "controller/boards/SunTouchBoard";
|
|
19
22
|
|
|
20
23
|
import { logger } from '../../../../logger/Logger';
|
|
21
24
|
import { ControllerType } from '../../../Constants';
|
|
@@ -59,23 +62,29 @@ export class EquipmentStateMessage {
|
|
|
59
62
|
case 3:
|
|
60
63
|
case 4:
|
|
61
64
|
case 5:
|
|
65
|
+
logger.info(`Found IntelliTouch Controller`);
|
|
62
66
|
sys.controllerType = ControllerType.IntelliTouch;
|
|
63
67
|
model1 = msg.extractPayloadByte(28);
|
|
64
68
|
model2 = msg.extractPayloadByte(9);
|
|
69
|
+
(sys.board as IntelliTouchBoard).initExpansionModules(model1, model2);
|
|
65
70
|
break;
|
|
66
71
|
case 11:
|
|
67
|
-
|
|
72
|
+
logger.info(`Found SunTouch Controller`);
|
|
73
|
+
sys.controllerType = ControllerType.SunTouch;
|
|
74
|
+
(sys.board as SunTouchBoard).initExpansionModules(model1, model2);
|
|
68
75
|
break;
|
|
69
76
|
case 13:
|
|
70
77
|
case 14:
|
|
78
|
+
logger.info(`Found EasyTouch Controller`);
|
|
71
79
|
sys.controllerType = ControllerType.EasyTouch;
|
|
80
|
+
(sys.board as EasyTouchBoard).initExpansionModules(model1, model2);
|
|
72
81
|
break;
|
|
73
82
|
default:
|
|
74
83
|
logger.error(`Unknown Touch Controller ${msg.extractPayloadByte(28)}:${msg.extractPayloadByte(27)}`);
|
|
75
84
|
break;
|
|
76
85
|
}
|
|
77
|
-
let board = sys.board as EasyTouchBoard;
|
|
78
|
-
board.initExpansionModules(model1, model2);
|
|
86
|
+
//let board = sys.board as EasyTouchBoard;
|
|
87
|
+
//board.initExpansionModules(model1, model2);
|
|
79
88
|
}
|
|
80
89
|
private static initController(msg: Inbound) {
|
|
81
90
|
state.status = 1;
|
|
@@ -83,7 +92,16 @@ export class EquipmentStateMessage {
|
|
|
83
92
|
const model2 = msg.extractPayloadByte(28);
|
|
84
93
|
// RKS: 06-15-20 -- While this works for now the way we are detecting seems a bit dubious. First, the 2 status message
|
|
85
94
|
// contains two model bytes. Right now the ones witness in the wild include 23 = fw1.023, 40 = fw1.040, 47 = fw1.047.
|
|
86
|
-
|
|
95
|
+
// RKS: 07-21-22 -- Pentair is about to release fw1.232. Unfortunately, the byte mapping for this has changed such that
|
|
96
|
+
// the bytes [27,28] are [0,2] respectively. This looks like it might be in conflict with IntelliTouch but it is not. Below
|
|
97
|
+
// are the combinations of 27,28 we have seen for IntelliTouch
|
|
98
|
+
// [1,0] = i5+3
|
|
99
|
+
// [0,1] = i7+3
|
|
100
|
+
// [1,3] = i5+3s
|
|
101
|
+
// [1,4] = i9+3s
|
|
102
|
+
// [1,5] = i10+3d
|
|
103
|
+
if ((model2 === 0 && (model1 === 23 || model1 >= 40)) ||
|
|
104
|
+
(model2 === 2 && model1 == 0)) {
|
|
87
105
|
state.equipment.controllerType = 'intellicenter';
|
|
88
106
|
sys.board.modulesAcquired = false;
|
|
89
107
|
sys.controllerType = ControllerType.IntelliCenter;
|
|
@@ -92,7 +110,6 @@ export class EquipmentStateMessage {
|
|
|
92
110
|
}
|
|
93
111
|
else {
|
|
94
112
|
EquipmentStateMessage.initTouch(msg);
|
|
95
|
-
logger.info(`Found Controller Board ${state.equipment.model}`);
|
|
96
113
|
sys.board.needsConfigChanges = true;
|
|
97
114
|
setTimeout(function () { sys.checkConfiguration(); }, 300);
|
|
98
115
|
}
|
|
@@ -165,7 +182,12 @@ export class EquipmentStateMessage {
|
|
|
165
182
|
|
|
166
183
|
// RSG - added 7/8/2020
|
|
167
184
|
// Every 30 mins, check the timezone and adjust DST settings
|
|
168
|
-
if (dt.getMinutes() % 30 === 0)
|
|
185
|
+
if (dt.getMinutes() % 30 === 0) {
|
|
186
|
+
sys.board.system.setTZ();
|
|
187
|
+
sys.board.schedules.updateSunriseSunsetAsync().then((updated: boolean)=>{
|
|
188
|
+
if (updated) {logger.debug(`Sunrise/sunset times updated on schedules.`);}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
169
191
|
// Check and update clock when it is off by >5 mins (just for a small buffer) and:
|
|
170
192
|
// 1. IntelliCenter has "manual" time set (Internet will automatically adjust) and autoAdjustDST is enabled
|
|
171
193
|
// 2. *Touch is "manual" (only option) and autoAdjustDST is enabled - (same as #1)
|
|
@@ -357,17 +379,43 @@ export class EquipmentStateMessage {
|
|
|
357
379
|
tbody.setPoint = cbody.setPoint;
|
|
358
380
|
tbody.name = cbody.name;
|
|
359
381
|
tbody.circuit = cbody.circuit = 6;
|
|
360
|
-
|
|
382
|
+
|
|
383
|
+
//RKS: This heat mode did not include all the bits necessary for hybrid heaters
|
|
384
|
+
//tbody.heatMode = cbody.heatMode = msg.extractPayloadByte(22) & 0x03;
|
|
385
|
+
tbody.heatMode = cbody.heatMode = msg.extractPayloadByte(22) & 0x33;
|
|
361
386
|
let heatStatus = sys.board.valueMaps.heatStatus.getValue('off');
|
|
362
387
|
if (tbody.isOn) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
388
|
+
if (tbody.heaterOptions.hybrid > 0) {
|
|
389
|
+
// ETi When heating with
|
|
390
|
+
// Heatpump (1) = 12 H:true S:false C:false
|
|
391
|
+
// Gas (2) = 48 H:false S:true C:false
|
|
392
|
+
// Hybrid (3) = 48 H:true S:false C:false
|
|
393
|
+
// Dual (16) = 60 H:true S:true C:false
|
|
394
|
+
// What this means is that Touch actually treats the heat status as either heating with
|
|
395
|
+
// the primary heater for the body or the secondary. In the case of a hybrid heater
|
|
396
|
+
// the primary is a heatpump and the secondary is gas. In the case of gas + solar or gas + heatpump
|
|
397
|
+
// the gas heater is the primary and solar or heatpump is the secondary. So we need to dance a little bit
|
|
398
|
+
// here. We do this by checking the heater options.
|
|
399
|
+
if (tbody.heatMode > 0) { // Turns out that ET sometimes reports the last heat status when off.
|
|
400
|
+
// This can be the only heater solar cannot be installed with this.
|
|
401
|
+
let byte = msg.extractPayloadByte(10);
|
|
402
|
+
// Either the primary, secondary, or both is engaged.
|
|
403
|
+
if ((byte & 0x14) === 0x14) heatStatus = sys.board.valueMaps.heatStatus.getValue('dual');
|
|
404
|
+
// else if ((byte & 0x0c) === 0x0c) heatStatus = sys.board.valueMaps.heatStatus.getValue('off'); // don't need since we test for heatMode>0
|
|
405
|
+
else if (byte & 0x10) heatStatus = sys.board.valueMaps.heatStatus.getValue('heater');
|
|
406
|
+
else if (byte & 0x04) heatStatus = sys.board.valueMaps.heatStatus.getValue('hpheat');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
//const heaterActive = (msg.extractPayloadByte(10) & 0x0C) === 12;
|
|
411
|
+
//const solarActive = (msg.extractPayloadByte(10) & 0x30) === 48;
|
|
412
|
+
const heaterActive = (msg.extractPayloadByte(10) & 0x04) === 0x04;
|
|
413
|
+
const solarActive = (msg.extractPayloadByte(10) & 0x10) === 0x10;
|
|
414
|
+
const cooling = solarActive && tbody.temp > tbody.setPoint;
|
|
415
|
+
if (heaterActive) heatStatus = sys.board.valueMaps.heatStatus.getValue('heater');
|
|
416
|
+
if (cooling) heatStatus = sys.board.valueMaps.heatStatus.getValue('cooling');
|
|
417
|
+
else if (solarActive) heatStatus = sys.board.valueMaps.heatStatus.getValue('solar');
|
|
418
|
+
}
|
|
371
419
|
}
|
|
372
420
|
tbody.heatStatus = heatStatus;
|
|
373
421
|
sys.board.schedules.syncScheduleHeatSourceAndSetpoint(cbody, tbody);
|
|
@@ -380,21 +428,34 @@ export class EquipmentStateMessage {
|
|
|
380
428
|
tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
|
|
381
429
|
tbody.isOn = true;
|
|
382
430
|
} else tbody.isOn = false;
|
|
383
|
-
|
|
431
|
+
//RKS: This heat mode did not include all the bits necessary for hybrid heaters
|
|
432
|
+
//tbody.heatMode = cbody.heatMode = (msg.extractPayloadByte(22) & 0x0C) >> 2;
|
|
433
|
+
tbody.heatMode = cbody.heatMode = (msg.extractPayloadByte(22) & 0xCC) >> 2;
|
|
384
434
|
tbody.setPoint = cbody.setPoint;
|
|
385
435
|
tbody.name = cbody.name;
|
|
386
436
|
tbody.circuit = cbody.circuit = 1;
|
|
387
437
|
let heatStatus = sys.board.valueMaps.heatStatus.getValue('off');
|
|
388
438
|
if (tbody.isOn) {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
439
|
+
if (tbody.heaterOptions.hybrid > 0) {
|
|
440
|
+
// This can be the only heater solar cannot be installed with this.
|
|
441
|
+
if (tbody.heatMode > 0) {
|
|
442
|
+
let byte = msg.extractPayloadByte(10);
|
|
443
|
+
// Either the primary, secondary, or both is engaged.
|
|
444
|
+
if ((byte & 0x28) === 0x28) heatStatus = sys.board.valueMaps.heatStatus.getValue('dual');
|
|
445
|
+
else if (byte & 0x20) heatStatus = sys.board.valueMaps.heatStatus.getValue('heater');
|
|
446
|
+
else if (byte & 0x08) heatStatus = sys.board.valueMaps.heatStatus.getValue('hpheat');
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
//const heaterActive = (msg.extractPayloadByte(10) & 0x0C) === 12;
|
|
451
|
+
//const solarActive = (msg.extractPayloadByte(10) & 0x30) === 48;
|
|
452
|
+
const heaterActive = (msg.extractPayloadByte(10) & 0x08) === 0x08;
|
|
453
|
+
const solarActive = (msg.extractPayloadByte(10) & 0x20) === 0x20;
|
|
454
|
+
const cooling = solarActive && tbody.temp > tbody.setPoint;
|
|
455
|
+
if (heaterActive) heatStatus = sys.board.valueMaps.heatStatus.getValue('heater');
|
|
456
|
+
if (cooling) heatStatus = sys.board.valueMaps.heatStatus.getValue('cooling');
|
|
457
|
+
else if (solarActive) heatStatus = sys.board.valueMaps.heatStatus.getValue('solar');
|
|
458
|
+
}
|
|
398
459
|
}
|
|
399
460
|
tbody.heatStatus = heatStatus;
|
|
400
461
|
sys.board.schedules.syncScheduleHeatSourceAndSetpoint(cbody, tbody);
|
|
@@ -418,6 +479,18 @@ export class EquipmentStateMessage {
|
|
|
418
479
|
sys.board.heaters.syncHeaterStates();
|
|
419
480
|
break;
|
|
420
481
|
}
|
|
482
|
+
case ControllerType.SunTouch:
|
|
483
|
+
EquipmentStateMessage.processSunTouchCircuits(msg);
|
|
484
|
+
sys.board.circuits.syncCircuitRelayStates();
|
|
485
|
+
sys.board.features.syncGroupStates();
|
|
486
|
+
sys.board.circuits.syncVirtualCircuitStates();
|
|
487
|
+
sys.board.valves.syncValveStates();
|
|
488
|
+
sys.board.filters.syncFilterStates();
|
|
489
|
+
state.emitControllerChange();
|
|
490
|
+
state.emitEquipmentChanges();
|
|
491
|
+
sys.board.heaters.syncHeaterStates();
|
|
492
|
+
sys.board.schedules.syncScheduleStates();
|
|
493
|
+
break;
|
|
421
494
|
case ControllerType.EasyTouch:
|
|
422
495
|
case ControllerType.IntelliCom:
|
|
423
496
|
case ControllerType.IntelliTouch:
|
|
@@ -478,11 +551,14 @@ export class EquipmentStateMessage {
|
|
|
478
551
|
state.temps.waterSensor1 = msg.extractPayloadByte(0);
|
|
479
552
|
state.temps.air = msg.extractPayloadByte(2);
|
|
480
553
|
let solar: Heater = sys.heaters.getItemById(2);
|
|
481
|
-
|
|
554
|
+
// RKS: 05-18-22 - This is not correct the solar temp is not stored on this message. It is always 0
|
|
555
|
+
// on an intelliTouch system with solar.
|
|
556
|
+
//if (solar.isActive) state.temps.solar = msg.extractPayloadByte(8);
|
|
482
557
|
// pool
|
|
483
558
|
let tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
|
|
484
559
|
let cbody: Body = sys.bodies.getItemById(1);
|
|
485
|
-
|
|
560
|
+
// RKS: 02-26-22 - See communications doc for explanation of bits. This needs to support UltraTemp ETi heatpumps.
|
|
561
|
+
tbody.heatMode = cbody.heatMode = msg.extractPayloadByte(5) & 0x33;
|
|
486
562
|
tbody.setPoint = cbody.setPoint = msg.extractPayloadByte(3);
|
|
487
563
|
tbody.coolSetpoint = cbody.coolSetpoint = msg.extractPayloadByte(9);
|
|
488
564
|
if (tbody.isOn) tbody.temp = state.temps.waterSensor1;
|
|
@@ -490,8 +566,8 @@ export class EquipmentStateMessage {
|
|
|
490
566
|
if (cbody.isActive) {
|
|
491
567
|
// spa
|
|
492
568
|
tbody = state.temps.bodies.getItemById(2, true);
|
|
493
|
-
tbody.heatMode = cbody.heatMode =
|
|
494
|
-
|
|
569
|
+
tbody.heatMode = cbody.heatMode = (msg.extractPayloadByte(5) & 0xCC) >> 2;
|
|
570
|
+
//tbody.heatMode = cbody.heatMode = (msg.extractPayloadByte(5) & 12) >> 2;
|
|
495
571
|
tbody.setPoint = cbody.setPoint = msg.extractPayloadByte(4);
|
|
496
572
|
if (tbody.isOn) tbody.temp = state.temps.waterSensor2 = msg.extractPayloadByte(1);
|
|
497
573
|
}
|
|
@@ -609,6 +685,39 @@ export class EquipmentStateMessage {
|
|
|
609
685
|
}
|
|
610
686
|
msg.isProcessed = true;
|
|
611
687
|
}
|
|
688
|
+
private static processSunTouchCircuits(msg: Inbound) {
|
|
689
|
+
// SunTouch has really twisted bit mapping for its
|
|
690
|
+
// circuit states. Features are intertwined within the
|
|
691
|
+
// features.
|
|
692
|
+
let byte = msg.extractPayloadByte(2);
|
|
693
|
+
for (let i = 0; i < 8; i++) {
|
|
694
|
+
let id = i === 4 ? 7 : i > 5 ? i + 2 : i + 1;
|
|
695
|
+
let circ = sys.circuits.getInterfaceById(id, false, { isActive: false });
|
|
696
|
+
if (circ.isActive) {
|
|
697
|
+
let isOn = ((1 << i) & byte) > 0;
|
|
698
|
+
let cstate = state.circuits.getInterfaceById(id, circ.isActive);
|
|
699
|
+
if (isOn !== cstate.isOn) {
|
|
700
|
+
sys.board.circuits.setEndTime(circ, cstate, isOn);
|
|
701
|
+
cstate.isOn = isOn;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
byte = msg.extractPayloadByte(3);
|
|
706
|
+
{
|
|
707
|
+
let circ = sys.circuits.getInterfaceById(10, false, { isActive: false });
|
|
708
|
+
if (circ.isActive) {
|
|
709
|
+
let isOn = (byte & 1) > 0;
|
|
710
|
+
let cstate = state.circuits.getInterfaceById(circ.id, circ.isActive);
|
|
711
|
+
if (isOn !== cstate.isOn) {
|
|
712
|
+
sys.board.circuits.setEndTime(circ, cstate, isOn);
|
|
713
|
+
cstate.isOn = isOn;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
state.emitEquipmentChanges();
|
|
718
|
+
msg.isProcessed = true;
|
|
719
|
+
}
|
|
720
|
+
|
|
612
721
|
private static processTouchCircuits(msg: Inbound) {
|
|
613
722
|
let circuitId = 1;
|
|
614
723
|
let maxCircuitId = sys.board.equipmentIds.features.end;
|
|
@@ -1,87 +1,136 @@
|
|
|
1
|
-
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
case
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
let
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
1
|
+
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as
|
|
7
|
+
published by the Free Software Foundation, either version 3 of the
|
|
8
|
+
License, or (at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import { Inbound, Protocol } from "../Messages";
|
|
19
|
+
import { state, BodyTempState, HeaterState } from "../../../State";
|
|
20
|
+
import { sys, ControllerType, Heater } from "../../../Equipment";
|
|
21
|
+
|
|
22
|
+
export class HeaterStateMessage {
|
|
23
|
+
public static process(msg: Inbound) {
|
|
24
|
+
if (msg.protocol === Protocol.Heater) {
|
|
25
|
+
switch (msg.action) {
|
|
26
|
+
case 112: // This is a message from a master controlling MasterTemp or UltraTemp ETi
|
|
27
|
+
break;
|
|
28
|
+
case 114: // This is a message from a master controlling UltraTemp
|
|
29
|
+
msg.isProcessed = true;
|
|
30
|
+
break;
|
|
31
|
+
case 113:
|
|
32
|
+
HeaterStateMessage.processHybridStatus(msg);
|
|
33
|
+
break;
|
|
34
|
+
case 116:
|
|
35
|
+
HeaterStateMessage.processMasterTempStatus(msg);
|
|
36
|
+
break;
|
|
37
|
+
case 115:
|
|
38
|
+
HeaterStateMessage.processUltraTempStatus(msg);
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
public static processHeaterCommand(msg: Inbound) {
|
|
44
|
+
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
45
|
+
// At this point there is no other configuration data for ET
|
|
46
|
+
if (sys.controllerType === ControllerType.EasyTouch) {
|
|
47
|
+
let htype = sys.board.valueMaps.heaterTypes.transform(heater.type);
|
|
48
|
+
switch (htype.name) {
|
|
49
|
+
case 'hybrid':
|
|
50
|
+
heater.economyTime = msg.extractPayloadByte(3);
|
|
51
|
+
heater.maxBoostTemp = msg.extractPayloadByte(4);
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
public static processHybridStatus(msg: Inbound) {
|
|
57
|
+
//[165, 0, 16, 112, 113, 10][1, 1, 0, 0, 0, 0, 0, 0, 0, 0][1, 162]
|
|
58
|
+
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
59
|
+
let sheater = state.heaters.getItemById(heater.id);
|
|
60
|
+
sheater.isOn = msg.extractPayloadByte(0) > 0;
|
|
61
|
+
if (heater.master > 0) {
|
|
62
|
+
let sbody = sheater.bodyId > 0 ? state.temps.bodies.getItemById(sheater.bodyId) : undefined;
|
|
63
|
+
if (typeof sbody !== 'undefined') {
|
|
64
|
+
switch (msg.extractPayloadByte(1)) {
|
|
65
|
+
case 1:
|
|
66
|
+
sbody.heatStatus = sys.board.valueMaps.heatStatus.getValue('hpheat');
|
|
67
|
+
break;
|
|
68
|
+
case 2:
|
|
69
|
+
sbody.heatStatus = sys.board.valueMaps.heatStatus.getValue('heater');
|
|
70
|
+
break;
|
|
71
|
+
case 3:
|
|
72
|
+
sbody.heatStatus = sys.board.valueMaps.heatStatus.getValue('dual');
|
|
73
|
+
break;
|
|
74
|
+
case 4:
|
|
75
|
+
sbody.heatStatus = sys.board.valueMaps.heatStatus.getValue('dual');
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
sbody.heatStatus = sys.board.valueMaps.heatStatus.getValue('off');
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
sheater.commStatus = 0;
|
|
84
|
+
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
85
|
+
msg.isProcessed = true;
|
|
86
|
+
}
|
|
87
|
+
public static processUltraTempStatus(msg: Inbound) {
|
|
88
|
+
// RKS: 07-03-21 - We only know byte 2 at this point for Ultratemp for the 115 message we are processing here. The
|
|
89
|
+
// byte description
|
|
90
|
+
// ------------------------------------------------
|
|
91
|
+
// 0 Unknown (always seems to be 160 for response)
|
|
92
|
+
// 1 Unknown (always 1)
|
|
93
|
+
// 2 Current heater status 0=off, 1=heat, 2=cool
|
|
94
|
+
// 3-9 Unknown
|
|
95
|
+
|
|
96
|
+
// 114 message - outbound response
|
|
97
|
+
//[165, 0, 112, 16, 114, 10][144, 0, 0, 0, 0, 0, 0, 0, 0, 0][2, 49] // OCP to Heater
|
|
98
|
+
// byte description
|
|
99
|
+
// ------------------------------------------------
|
|
100
|
+
// 0 Unknown (always seems to be 144 for request)
|
|
101
|
+
// 1 Current heater status 0=off, 1=heat, 2=cool
|
|
102
|
+
// 3 Believed to be ofset temp
|
|
103
|
+
// 4-9 Unknown
|
|
104
|
+
|
|
105
|
+
// byto 0: always seems to be 144 for outbound
|
|
106
|
+
// byte 1: Sets heater mode to 0 = Off 1 = Heat 2 = Cool
|
|
107
|
+
//[165, 0, 16, 112, 115, 10][160, 1, 0, 3, 0, 0, 0, 0, 0, 0][2, 70] // Heater Reply
|
|
108
|
+
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
109
|
+
let sheater = state.heaters.getItemById(heater.id);
|
|
110
|
+
let byte = msg.extractPayloadByte(2);
|
|
111
|
+
sheater.isOn = byte >= 1;
|
|
112
|
+
sheater.isCooling = byte === 2;
|
|
113
|
+
sheater.commStatus = 0;
|
|
114
|
+
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
115
|
+
msg.isProcessed = true;
|
|
116
|
+
}
|
|
117
|
+
public static processMasterTempStatus(msg: Inbound) {
|
|
118
|
+
//[255, 0, 255][165, 0, 16, 112, 116, 23][67, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0][2, 66]
|
|
119
|
+
// Byte 1 is the indicator to which setpoint it is heating to.
|
|
120
|
+
// Byte 8 increments over time when the heater is on.
|
|
121
|
+
// Byte 13 looks like the mode the heater is in for instance it is in cooldown mode.
|
|
122
|
+
// 0 = Normal
|
|
123
|
+
// 2 = ??????
|
|
124
|
+
// 6 = Cooldown
|
|
125
|
+
// Byte 14 looks like the cooldown delay in minutes.
|
|
126
|
+
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
127
|
+
let sheater = state.heaters.getItemById(heater.id);
|
|
128
|
+
let byte = msg.extractPayloadByte(1);
|
|
129
|
+
sheater.isOn = byte >= 1;
|
|
130
|
+
sheater.isCooling = false;
|
|
131
|
+
sheater.commStatus = 0;
|
|
132
|
+
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
133
|
+
msg.isProcessed = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
87
136
|
}
|