nodejs-poolcontroller 7.7.0 → 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 +26 -35
- package/Changelog +22 -0
- package/README.md +7 -3
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +13 -9
- package/config/VersionCheck.ts +6 -2
- package/controller/Constants.ts +58 -25
- package/controller/Equipment.ts +224 -41
- package/controller/Errors.ts +2 -1
- package/controller/Lockouts.ts +34 -2
- package/controller/State.ts +491 -48
- package/controller/boards/AquaLinkBoard.ts +6 -3
- package/controller/boards/BoardFactory.ts +5 -1
- package/controller/boards/EasyTouchBoard.ts +1971 -1751
- package/controller/boards/IntelliCenterBoard.ts +1311 -1688
- package/controller/boards/IntelliComBoard.ts +7 -1
- package/controller/boards/IntelliTouchBoard.ts +153 -42
- package/controller/boards/NixieBoard.ts +209 -66
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +1862 -1543
- package/controller/comms/Comms.ts +539 -138
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +242 -60
- package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +81 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +3 -1
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +12 -6
- package/controller/comms/messages/config/PumpMessage.ts +9 -12
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +43 -26
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
- package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
- package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
- package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +15 -4
- package/controller/nixie/NixieEquipment.ts +1 -0
- package/controller/nixie/chemistry/ChemController.ts +300 -129
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +133 -129
- package/controller/nixie/circuits/Circuit.ts +171 -30
- package/controller/nixie/heaters/Heater.ts +337 -173
- package/controller/nixie/pumps/Pump.ts +264 -236
- package/controller/nixie/schedules/Schedule.ts +9 -3
- package/defaultConfig.json +45 -5
- package/logger/Logger.ts +38 -9
- package/package.json +13 -9
- package/web/Server.ts +235 -122
- package/web/bindings/aqualinkD.json +114 -59
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +15 -0
- package/web/bindings/mqtt.json +28 -9
- package/web/bindings/mqttAlt.json +15 -0
- package/web/interfaces/baseInterface.ts +58 -7
- package/web/interfaces/httpInterface.ts +5 -2
- package/web/interfaces/influxInterface.ts +9 -2
- package/web/interfaces/mqttInterface.ts +234 -74
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +140 -33
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +144 -3
- package/web/services/state/StateSocket.ts +65 -14
- package/web/services/utilities/Utilities.ts +189 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
4
|
|
|
4
5
|
This program is free software: you can redistribute it and/or modify
|
|
5
6
|
it under the terms of the GNU Affero General Public License as
|
|
@@ -100,9 +101,48 @@ export class ScheduleMessage {
|
|
|
100
101
|
break;
|
|
101
102
|
|
|
102
103
|
}
|
|
103
|
-
else if (sys.controllerType !== ControllerType.Unknown)
|
|
104
|
+
else if (sys.controllerType !== ControllerType.Unknown && sys.controllerType !== ControllerType.SunTouch)
|
|
104
105
|
ScheduleMessage.processScheduleDetails(msg);
|
|
105
106
|
}
|
|
107
|
+
public static processSunTouch(msg: Inbound): void {
|
|
108
|
+
//[255, 0, 255][165, 1, 15, 16, 30, 16][0, 0, 0, 0, 2, 0, 1, 224, 1, 239, 6, 1, 1, 224, 3, 132][4, 53]
|
|
109
|
+
// Bytes 0-3 contain no data.
|
|
110
|
+
for (let i = 0; i < 2; i++) {
|
|
111
|
+
let schedId = i + 1;
|
|
112
|
+
let pos = (i * 6) + 4;
|
|
113
|
+
let cid = msg.extractPayloadByte(pos);
|
|
114
|
+
if (cid === 5) cid = 7;
|
|
115
|
+
else if (cid > 6) cid = cid + 1;
|
|
116
|
+
let start = (msg.extractPayloadByte(pos + 2) * 256) + msg.extractPayloadByte(pos + 3);
|
|
117
|
+
let end = (msg.extractPayloadByte(pos + 4) * 256) + msg.extractPayloadByte(pos + 5);
|
|
118
|
+
let circ = cid > 0 && start < end ? sys.circuits.getInterfaceById(cid, false) : undefined;
|
|
119
|
+
if (typeof circ !== 'undefined' && circ.isActive) {
|
|
120
|
+
let sched = sys.schedules.getItemById(schedId, true);
|
|
121
|
+
let ssched = state.schedules.getItemById(schedId, true);
|
|
122
|
+
sched.master = 0;
|
|
123
|
+
ssched.circuit = sched.circuit = circ.id;
|
|
124
|
+
ssched.scheduleDays = sched.scheduleDays = 127; // SunTouch does not allow you to set the days.
|
|
125
|
+
ssched.startTime = sched.startTime = start;
|
|
126
|
+
ssched.endTime = sched.endTime = end;
|
|
127
|
+
ssched.startTimeType = sched.startTimeType = 0;
|
|
128
|
+
ssched.endTimeType = sched.endTimeType = 0;
|
|
129
|
+
ssched.isActive = sched.isActive = true;
|
|
130
|
+
ssched.scheduleType = sched.scheduleType = sys.board.valueMaps.scheduleTypes.getValue('repeat');
|
|
131
|
+
if (sys.circuits.getItemById(circ.id).hasHeatSource && typeof sched.heatSource === 'undefined') ssched.heatSource = sched.heatSource = sys.board.valueMaps.heatSources.getValue('nochange');
|
|
132
|
+
if (typeof sched.heatSetpoint === 'undefined') sched.heatSetpoint = 78;
|
|
133
|
+
ssched.emitEquipmentChange();
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
let ssched = state.schedules.find(elem => elem.id === schedId);
|
|
137
|
+
if (typeof ssched !== 'undefined') {
|
|
138
|
+
ssched.isActive = false;
|
|
139
|
+
ssched.emitEquipmentChange();
|
|
140
|
+
state.schedules.removeItemById(schedId);
|
|
141
|
+
}
|
|
142
|
+
sys.schedules.removeItemById(schedId);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
106
146
|
public static processScheduleDetails(msg: Inbound) {
|
|
107
147
|
// Sample packet
|
|
108
148
|
// [165,33,15,16,17,7][6,12,25,0,6,30,0][1,76]
|
|
@@ -119,7 +159,7 @@ export class ScheduleMessage {
|
|
|
119
159
|
eggTimer.isActive = eggTimerActive;
|
|
120
160
|
const circuit = sys.circuits.getInterfaceById(circuitId);
|
|
121
161
|
circuit.eggTimer = eggTimer.runTime;
|
|
122
|
-
circuit.eggTimer === 720;
|
|
162
|
+
//circuit.eggTimer === 720;
|
|
123
163
|
circuit.dontStop = circuit.eggTimer === 1620;
|
|
124
164
|
// When eggTimers are found go back and check existing schedules to see if a runOnce schedule already exists.
|
|
125
165
|
// It is possible that the runOnce schedule will be discovered before the eggTimer so we need to adjust the endTime
|
|
@@ -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
|
|
@@ -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
|
|
@@ -56,9 +57,10 @@ export class ValveMessage {
|
|
|
56
57
|
case ControllerType.IntelliCom:
|
|
57
58
|
case ControllerType.EasyTouch:
|
|
58
59
|
case ControllerType.IntelliTouch:
|
|
60
|
+
case ControllerType.SunTouch:
|
|
59
61
|
switch (msg.action) {
|
|
60
62
|
case 29:
|
|
61
|
-
ValveMessage.process_ValveAssignment_IT(msg);
|
|
63
|
+
sys.controllerType === ControllerType.SunTouch ? ValveMessage.process_ValveAssignment_ST(msg) : ValveMessage.process_ValveAssignment_IT(msg);
|
|
62
64
|
break;
|
|
63
65
|
case 35:
|
|
64
66
|
ValveMessage.process_ValveOptions_IT(msg);
|
|
@@ -73,19 +75,38 @@ export class ValveMessage {
|
|
|
73
75
|
sys.general.options.pumpDelay = msg.extractPayloadByte(0) >> 7 === 1;
|
|
74
76
|
msg.isProcessed = true;
|
|
75
77
|
}
|
|
78
|
+
private static process_ValveAssignment_ST(msg: Inbound) {
|
|
79
|
+
// SunTouch example
|
|
80
|
+
//[165,1,15,16,29,24][2,0,0,0,20,255,255,1,2,3,4,1,72,0,0,0,3,0,0,63,4,0,0,0][3,167]
|
|
81
|
+
let vA = sys.valves.getItemById(1, true);
|
|
82
|
+
let vB = sys.valves.getItemById(2, true);
|
|
83
|
+
let vC = sys.valves.getItemById(3, true);
|
|
84
|
+
if (sys.equipment.shared && !sys.equipment.single) {
|
|
85
|
+
vA.name = 'Intake';
|
|
86
|
+
vB.circuit = vA.circuit = sys.board.valueMaps.virtualCircuits.encode('poolspa');
|
|
87
|
+
vB.name = 'Return';
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
vA.name = 'Valve A';
|
|
91
|
+
vB.name = 'Valve B';
|
|
92
|
+
vA.circuit = msg.extractPayloadByte(1);
|
|
93
|
+
vB.circuit = msg.extractPayloadByte(2);
|
|
94
|
+
}
|
|
95
|
+
vC.circuit = msg.extractPayloadByte(4);
|
|
96
|
+
vC.name = 'Valve C'
|
|
97
|
+
}
|
|
76
98
|
private static process_ValveAssignment_IT(msg: Inbound) {
|
|
77
99
|
// sample packet
|
|
78
|
-
// 165,33,16,34,157,6,0,0,1,255,255,255,4,153 [set]
|
|
100
|
+
// [165,33,16,34,157,6],[0,0,1,255,255,255],[4,153] [set]
|
|
101
|
+
// [165,33,16,34,157,6],[0,0,7,255,255,255],[4,159]] [set]
|
|
79
102
|
// [165,33,15,16,29,24],[2,0,0,0,128,1,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[4,154] [get]
|
|
80
|
-
// [[][255,0,255][165,33,16,34,157,6][0,0,7,255,255,255][4,159]] [set]
|
|
81
103
|
// what is payload[0]?
|
|
82
104
|
for (let ndx = 4, id = 1; id <= sys.equipment.maxValves; ndx++) {
|
|
83
|
-
let valve: Valve = sys.valves.getItemById(id);
|
|
84
105
|
if (id === 3) {
|
|
85
|
-
if (sys.equipment.shared) {
|
|
86
|
-
valve = sys.valves.getItemById(id
|
|
106
|
+
if (sys.equipment.shared && !sys.equipment.single) {
|
|
107
|
+
let valve: Valve = sys.valves.getItemById(id);
|
|
87
108
|
valve.circuit = 6; // pool/spa -- fix
|
|
88
|
-
valve.name =
|
|
109
|
+
valve.name = 'Intake';
|
|
89
110
|
valve.isIntake = true;
|
|
90
111
|
valve.isReturn = false;
|
|
91
112
|
valve.isActive = true;
|
|
@@ -99,13 +120,12 @@ export class ValveMessage {
|
|
|
99
120
|
sys.valves.removeItemById(id);
|
|
100
121
|
state.valves.removeItemById(id);
|
|
101
122
|
}
|
|
102
|
-
id++;
|
|
103
123
|
}
|
|
104
124
|
else if (id === 4) {
|
|
105
|
-
if (sys.equipment.shared) {
|
|
106
|
-
valve = sys.valves.getItemById(id
|
|
125
|
+
if (sys.equipment.shared && !sys.equipment.single) {
|
|
126
|
+
let valve: Valve = sys.valves.getItemById(id);
|
|
107
127
|
valve.circuit = 6; // pool/spa -- fix
|
|
108
|
-
valve.name =
|
|
128
|
+
valve.name = 'Return';
|
|
109
129
|
valve.isIntake = false;
|
|
110
130
|
valve.isReturn = true;
|
|
111
131
|
valve.isActive = true;
|
|
@@ -119,13 +139,12 @@ export class ValveMessage {
|
|
|
119
139
|
sys.valves.removeItemById(id);
|
|
120
140
|
state.valves.removeItemById(id);
|
|
121
141
|
}
|
|
122
|
-
id++;
|
|
123
142
|
}
|
|
124
143
|
else {
|
|
125
|
-
valve = sys.valves.getItemById(id
|
|
144
|
+
let valve: Valve = sys.valves.getItemById(id);
|
|
126
145
|
let circ = msg.extractPayloadByte(ndx);
|
|
127
146
|
valve.circuit = circ > 0 && circ < 255 ? circ : 0;
|
|
128
|
-
valve.circuit = msg.extractPayloadByte(ndx);
|
|
147
|
+
//valve.circuit = msg.extractPayloadByte(ndx);
|
|
129
148
|
//valve.isActive = valve.circuit > 0 && valve.circuit < 255;
|
|
130
149
|
// RKS: 04-14-21 -- Valves should always be active but shown with no assignment when
|
|
131
150
|
// there is no circuit. The circuitry for the valve always exists although I am not sure
|
|
@@ -134,25 +153,23 @@ export class ValveMessage {
|
|
|
134
153
|
valve.isReturn = false;
|
|
135
154
|
valve.isIntake = false;
|
|
136
155
|
valve.type = 0;
|
|
156
|
+
valve.master = 0;
|
|
137
157
|
// Allow users to name the valve whatever they want. *Touch apparently only allows the valve to be named the same
|
|
138
158
|
// as the circuit but this should be fine if we allow the user to edit it.
|
|
139
159
|
valve.name = (typeof valve.name === 'undefined') ? ValveMessage.getName(id, valve.circuit) : valve.name;
|
|
140
160
|
let svalve = state.valves.getItemById(id, true);
|
|
141
161
|
svalve.name = valve.name;
|
|
142
|
-
svalve.type = valve.type;
|
|
143
|
-
valve.master = 0;
|
|
144
|
-
}
|
|
145
|
-
if (!valve.isActive) {
|
|
146
|
-
sys.valves.removeItemById(id);
|
|
147
|
-
state.valves.removeItemById(id);
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
valve.master = 0;
|
|
151
|
-
// valve.isVirtual = false;
|
|
152
|
-
valve.type = 0;
|
|
153
162
|
}
|
|
154
163
|
id++;
|
|
155
164
|
}
|
|
165
|
+
// Clean up any valves that are leftovers from previous configs.
|
|
166
|
+
for (let i = sys.valves.length - 1; i >= 0; i--) {
|
|
167
|
+
let v = sys.valves.getItemByIndex(i);
|
|
168
|
+
if (v.master === 0 && v.id > sys.equipment.maxValves) {
|
|
169
|
+
sys.valves.removeItemByIndex(i);
|
|
170
|
+
state.valves.removeItemById(v.id);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
156
173
|
msg.isProcessed = true;
|
|
157
174
|
}
|
|
158
175
|
private static getName(id: number, cir: number) {
|
|
@@ -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
|
|
@@ -31,7 +32,7 @@ export class ChlorinatorStateMessage {
|
|
|
31
32
|
let chlor: Chlorinator = sys.chlorinators.findItemByPortId(msg.portId || 0);
|
|
32
33
|
if (typeof chlor === 'undefined' || chlor.isActive === false) return; // Bail out of here if we don't find an active chlorinator.
|
|
33
34
|
let cstate: ChlorinatorState = state.chlorinators.getItemById(chlor.id, true);
|
|
34
|
-
if (msg.dest >=
|
|
35
|
+
if (msg.dest >= 80 && msg.dest <= 83) {
|
|
35
36
|
// RKS: This message is from the OCP to the chlorinator. NOTE: We will not see these messages when the communication is coming from njsPC on any
|
|
36
37
|
// comms port. The processing for no comms is done in the Nixe control when the message is sent.
|
|
37
38
|
if (typeof cstate.lastComm === 'undefined') cstate.lastComm = new Date(1970, 0, 1, 0, 0, 0, 0).getTime();
|
|
@@ -40,8 +41,8 @@ export class ChlorinatorStateMessage {
|
|
|
40
41
|
// We have not talked to the chlorinator in 30 seconds so we have lost communication.
|
|
41
42
|
cstate.status = 128;
|
|
42
43
|
}
|
|
43
|
-
chlor = sys.chlorinators.getItemById(msg.dest, true);
|
|
44
|
-
chlor.address = msg.dest
|
|
44
|
+
// chlor = sys.chlorinators.getItemById(msg.dest - 79, true); chlor is retrieved above; don't get in incorrectly here.
|
|
45
|
+
chlor.address = msg.dest; // Theoretically, this will always be 80.
|
|
45
46
|
if (typeof chlor.isActive === 'undefined') cstate.isActive = chlor.isActive = true;
|
|
46
47
|
}
|
|
47
48
|
else {
|
|
@@ -68,7 +69,7 @@ export class ChlorinatorStateMessage {
|
|
|
68
69
|
// This is the model number of the chlorinator and the address is actually the second byte.
|
|
69
70
|
let name = msg.extractPayloadString(1, 16);
|
|
70
71
|
if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = cstate.name = name;
|
|
71
|
-
if (typeof chlor.model === 'undefined') chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
|
|
72
|
+
if (typeof chlor.model === 'undefined' || chlor.model === 0) chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
|
|
72
73
|
cstate.isActive = chlor.isActive;
|
|
73
74
|
state.emitEquipmentChanges();
|
|
74
75
|
break;
|
|
@@ -136,8 +137,8 @@ export class ChlorinatorStateMessage {
|
|
|
136
137
|
// Set it back to disabled. Some asshole is futzing with the chlorinator output.
|
|
137
138
|
sys.board.chlorinator.setChlorAsync({ id: chlor.id, disabled: true });
|
|
138
139
|
}
|
|
139
|
-
if (sys.controllerType === ControllerType.
|
|
140
|
-
// This lets the
|
|
140
|
+
if (sys.controllerType === ControllerType.Nixie) {
|
|
141
|
+
// This lets the current body use the iChlor temp probe
|
|
141
142
|
let tbody: BodyTempState = sys.pumps.length > 0 ? state.temps.bodies.getBodyIsOn() : state.temps.bodies.getItemById(1, true);
|
|
142
143
|
if (msg.extractPayloadByte(2) >= 40 && typeof tbody !== 'undefined') tbody.temp = msg.extractPayloadByte(2);
|
|
143
144
|
}
|
|
@@ -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)
|
|
@@ -374,13 +396,15 @@ export class EquipmentStateMessage {
|
|
|
374
396
|
// the primary is a heatpump and the secondary is gas. In the case of gas + solar or gas + heatpump
|
|
375
397
|
// the gas heater is the primary and solar or heatpump is the secondary. So we need to dance a little bit
|
|
376
398
|
// here. We do this by checking the heater options.
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
+
}
|
|
384
408
|
}
|
|
385
409
|
else {
|
|
386
410
|
//const heaterActive = (msg.extractPayloadByte(10) & 0x0C) === 12;
|
|
@@ -414,11 +438,13 @@ export class EquipmentStateMessage {
|
|
|
414
438
|
if (tbody.isOn) {
|
|
415
439
|
if (tbody.heaterOptions.hybrid > 0) {
|
|
416
440
|
// This can be the only heater solar cannot be installed with this.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
+
}
|
|
422
448
|
}
|
|
423
449
|
else {
|
|
424
450
|
//const heaterActive = (msg.extractPayloadByte(10) & 0x0C) === 12;
|
|
@@ -453,6 +479,18 @@ export class EquipmentStateMessage {
|
|
|
453
479
|
sys.board.heaters.syncHeaterStates();
|
|
454
480
|
break;
|
|
455
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;
|
|
456
494
|
case ControllerType.EasyTouch:
|
|
457
495
|
case ControllerType.IntelliCom:
|
|
458
496
|
case ControllerType.IntelliTouch:
|
|
@@ -513,7 +551,9 @@ export class EquipmentStateMessage {
|
|
|
513
551
|
state.temps.waterSensor1 = msg.extractPayloadByte(0);
|
|
514
552
|
state.temps.air = msg.extractPayloadByte(2);
|
|
515
553
|
let solar: Heater = sys.heaters.getItemById(2);
|
|
516
|
-
|
|
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);
|
|
517
557
|
// pool
|
|
518
558
|
let tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
|
|
519
559
|
let cbody: Body = sys.bodies.getItemById(1);
|
|
@@ -645,6 +685,39 @@ export class EquipmentStateMessage {
|
|
|
645
685
|
}
|
|
646
686
|
msg.isProcessed = true;
|
|
647
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
|
+
|
|
648
721
|
private static processTouchCircuits(msg: Inbound) {
|
|
649
722
|
let circuitId = 1;
|
|
650
723
|
let maxCircuitId = sys.board.equipmentIds.features.end;
|
|
@@ -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
|
|
@@ -57,10 +58,28 @@ export class HeaterStateMessage {
|
|
|
57
58
|
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
58
59
|
let sheater = state.heaters.getItemById(heater.id);
|
|
59
60
|
sheater.isOn = msg.extractPayloadByte(0) > 0;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
+
}
|
|
64
83
|
sheater.commStatus = 0;
|
|
65
84
|
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
66
85
|
msg.isProcessed = true;
|
|
@@ -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
|
|
@@ -26,7 +27,9 @@ export class IntelliChemStateMessage {
|
|
|
26
27
|
let address = (msg.dest >= 144 && msg.dest <= 158) ? msg.dest : msg.source;
|
|
27
28
|
if (address < 144 || address > 158) return;
|
|
28
29
|
let controller = sys.chemControllers.getItemByAddress(address);
|
|
29
|
-
|
|
30
|
+
// RKS: 07-13-22 Lets just assume that SunTouch doesn't report its IntelliChem at this point. The action 40 return
|
|
31
|
+
// does not contain the IntelliChem bit when it is returned for this controller.
|
|
32
|
+
if (!controller.isActive && sys.controllerType !== ControllerType.SunTouch) {
|
|
30
33
|
msg.isProcessed = true;
|
|
31
34
|
return;
|
|
32
35
|
}
|
|
@@ -272,7 +275,7 @@ export class IntelliChemStateMessage {
|
|
|
272
275
|
temps.waterSensor2 = schem.ph.probe.temperature;
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
|
-
sys.board.system.setTempsAsync(temps).catch(err => logger.error(err))
|
|
278
|
+
sys.board.system.setTempsAsync(temps).catch(err => logger.error(`Error setting temp compensation for IntelliChem State: ${err.message}`))
|
|
276
279
|
}
|
|
277
280
|
}
|
|
278
281
|
schem.ph.pump.isDosing = schem.ph.dosingStatus === 0 && chem.ph.enabled;
|
|
@@ -297,7 +300,7 @@ export class IntelliChemStateMessage {
|
|
|
297
300
|
schem.ph.appendDose(schem.ph.doseVolume - phPrev.vol, (schem.ph.timeDosed - phPrev.time) * 1000);
|
|
298
301
|
}
|
|
299
302
|
else {
|
|
300
|
-
console.log(`DOSING STATUS === ${schem.ph.dosingStatus}`);
|
|
303
|
+
//console.log(`DOSING STATUS === ${schem.ph.dosingStatus}`);
|
|
301
304
|
// Make sure we don't have a current dose going.
|
|
302
305
|
schem.ph.currentDose = undefined;
|
|
303
306
|
}
|
|
@@ -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
|