nodejs-poolcontroller 7.2.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 +13 -0
- package/Dockerfile +1 -0
- package/README.md +5 -5
- package/app.ts +11 -0
- package/config/Config.ts +3 -0
- package/config/VersionCheck.ts +8 -4
- package/controller/Constants.ts +165 -9
- package/controller/Equipment.ts +186 -65
- package/controller/Errors.ts +22 -1
- package/controller/State.ts +273 -57
- package/controller/boards/EasyTouchBoard.ts +194 -95
- package/controller/boards/IntelliCenterBoard.ts +115 -42
- package/controller/boards/IntelliTouchBoard.ts +104 -30
- package/controller/boards/NixieBoard.ts +155 -53
- package/controller/boards/SystemBoard.ts +1529 -514
- package/controller/comms/Comms.ts +219 -42
- package/controller/comms/messages/Messages.ts +16 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
- package/controller/comms/messages/config/CircuitMessage.ts +1 -1
- 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 +15 -9
- package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
- package/controller/comms/messages/config/OptionsMessage.ts +13 -1
- package/controller/comms/messages/config/PumpMessage.ts +4 -20
- package/controller/comms/messages/config/RemoteMessage.ts +4 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
- package/controller/comms/messages/config/SecurityMessage.ts +1 -0
- package/controller/comms/messages/config/ValveMessage.ts +12 -2
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +14 -6
- package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
- package/controller/comms/messages/status/HeaterStateMessage.ts +25 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +55 -26
- package/controller/nixie/Nixie.ts +18 -16
- package/controller/nixie/NixieEquipment.ts +6 -6
- package/controller/nixie/bodies/Body.ts +7 -4
- package/controller/nixie/bodies/Filter.ts +7 -4
- package/controller/nixie/chemistry/ChemController.ts +800 -283
- package/controller/nixie/chemistry/Chlorinator.ts +22 -14
- package/controller/nixie/circuits/Circuit.ts +42 -7
- package/controller/nixie/heaters/Heater.ts +303 -30
- package/controller/nixie/pumps/Pump.ts +57 -30
- package/controller/nixie/schedules/Schedule.ts +10 -7
- package/controller/nixie/valves/Valve.ts +7 -5
- package/defaultConfig.json +32 -1
- package/issue_template.md +1 -1
- package/logger/DataLogger.ts +37 -22
- package/package.json +20 -18
- package/web/Server.ts +529 -31
- package/web/bindings/influxDB.json +157 -5
- package/web/bindings/mqtt.json +112 -13
- package/web/bindings/mqttAlt.json +109 -11
- package/web/interfaces/baseInterface.ts +2 -1
- package/web/interfaces/httpInterface.ts +2 -0
- package/web/interfaces/influxInterface.ts +103 -54
- package/web/interfaces/mqttInterface.ts +16 -5
- package/web/services/config/Config.ts +179 -43
- package/web/services/state/State.ts +51 -5
- package/web/services/state/StateSocket.ts +19 -2
|
@@ -63,8 +63,9 @@ export class ChlorinatorStateMessage {
|
|
|
63
63
|
// I n t e l l i c h l o r - - 4 0
|
|
64
64
|
//[16, 2, 0, 3][0, 73, 110, 116, 101, 108, 108, 105, 99, 104, 108, 111, 114, 45, 45, 52, 48][188, 16, 3]
|
|
65
65
|
// This is the model number of the chlorinator and the address is actually the second byte.
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
let name = msg.extractPayloadString(1, 16);
|
|
67
|
+
if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = cstate.name = name;
|
|
68
|
+
if (typeof chlor.model === 'undefined') chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
|
|
68
69
|
cstate.isActive = chlor.isActive;
|
|
69
70
|
state.emitEquipmentChanges();
|
|
70
71
|
break;
|
|
@@ -73,9 +74,12 @@ export class ChlorinatorStateMessage {
|
|
|
73
74
|
// If the chlorinator is no longer talking to us then clear the current output.
|
|
74
75
|
if (cstate.status === 128) cstate.currentOutput = 0;
|
|
75
76
|
cstate.targetOutput = msg.extractPayloadByte(0);
|
|
76
|
-
if (chlor.disabled && cstate.targetOutput !== 0) {
|
|
77
|
+
if (chlor.disabled && (cstate.targetOutput !== 0 || cstate.superChlor || cstate.superChlorHours > 0)) {
|
|
77
78
|
// Some dumbass is trying to change our output. We need to set it back to 0.
|
|
78
|
-
sys.board.chlorinator.setChlorAsync({ id: chlor.id, disabled:
|
|
79
|
+
sys.board.chlorinator.setChlorAsync({ id: chlor.id, disabled: chlor.disabled, superChlor: false, superChlorHours: 0 });
|
|
80
|
+
}
|
|
81
|
+
else if (chlor.isDosing && cstate.targetOutput !== 100){
|
|
82
|
+
sys.board.chlorinator.setChlorAsync({ id: chlor.id, isDosing: chlor.isDosing });
|
|
79
83
|
}
|
|
80
84
|
state.emitEquipmentChanges();
|
|
81
85
|
break;
|
|
@@ -87,8 +91,12 @@ export class ChlorinatorStateMessage {
|
|
|
87
91
|
// The current output here is not correct. The reason that is is because this is a request from the OCP to the Chlorinator.
|
|
88
92
|
//cstate.currentOutput = msg.action === 17 ? msg.extractPayloadByte(0) : msg.extractPayloadByte(0) / 10;
|
|
89
93
|
cstate.targetOutput = msg.extractPayloadByte(0) / 10;
|
|
90
|
-
if (chlor.disabled && cstate.targetOutput !== 0) {
|
|
91
|
-
|
|
94
|
+
if (chlor.disabled && (cstate.targetOutput !== 0 || cstate.superChlor || cstate.superChlorHours > 0)) {
|
|
95
|
+
// Some dumbass is trying to change our output. We need to set it back to 0.
|
|
96
|
+
sys.board.chlorinator.setChlorAsync({ id: chlor.id, disabled: chlor.disabled, superChlor: false, superChlorHours: 0 });
|
|
97
|
+
}
|
|
98
|
+
else if (chlor.isDosing && cstate.targetOutput !== 100){
|
|
99
|
+
sys.board.chlorinator.setChlorAsync({ id: chlor.id, isDosing: chlor.isDosing });
|
|
92
100
|
}
|
|
93
101
|
state.emitEquipmentChanges();
|
|
94
102
|
break;
|
|
@@ -101,7 +101,9 @@ export class EquipmentStateMessage {
|
|
|
101
101
|
Message.headerSubByte = msg.header[1];
|
|
102
102
|
//console.log(process.memoryUsage());
|
|
103
103
|
if (msg.action === 2 && state.isInitialized && sys.controllerType === ControllerType.Nixie) {
|
|
104
|
-
// Start over because we didn't have communication before but we now do.
|
|
104
|
+
// Start over because we didn't have communication before but we now do. This will fall into the if
|
|
105
|
+
// below so that it goes through the intialization process. In this case we didn't see an OCP when we started
|
|
106
|
+
// but there clearly is one now.
|
|
105
107
|
sys.controllerType = ControllerType.Unknown;
|
|
106
108
|
state.status = 0;
|
|
107
109
|
}
|
|
@@ -139,19 +141,24 @@ export class EquipmentStateMessage {
|
|
|
139
141
|
|
|
140
142
|
// Shared
|
|
141
143
|
let dt = new Date();
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
144
|
+
// RKS: This was moved to the ChemControllerState message. This is flawed in that it incorrectly sets IntelliChem to no comms.
|
|
145
|
+
//if (state.chemControllers.length > 0) {
|
|
146
|
+
// // TODO: move this to chemController when we understand the packets better
|
|
147
|
+
// for (let i = 0; i < state.chemControllers.length; i++) {
|
|
148
|
+
// let ccontroller = state.chemControllers.getItemByIndex(i);
|
|
149
|
+
// if (sys.board.valueMaps.chemControllerTypes.getName(ccontroller.type) === 'intellichem') {
|
|
150
|
+
// if (dt.getTime() - ccontroller.lastComm > 60000) ccontroller.status = 1;
|
|
151
|
+
// }
|
|
152
|
+
// }
|
|
153
|
+
//}
|
|
151
154
|
state.time.hours = msg.extractPayloadByte(0);
|
|
152
155
|
state.time.minutes = msg.extractPayloadByte(1);
|
|
153
156
|
state.time.seconds = dt.getSeconds();
|
|
154
|
-
state.mode = msg.extractPayloadByte(9) & 0x81;
|
|
157
|
+
state.mode = sys.controllerType !== ControllerType.IntelliCenter ? (msg.extractPayloadByte(9) & 0x81) : (msg.extractPayloadByte(9) & 0x01);
|
|
158
|
+
|
|
159
|
+
// RKS: The units have been normalized for English and Metric for the overall panel. It is important that the val numbers match for at least the temp units since
|
|
160
|
+
// the only unit of measure native to the Touch controllers is temperature they chose to name these C or F. However, with the njsPC extensions this is non-semantic
|
|
161
|
+
// since pressure, volume, and length have been introduced.
|
|
155
162
|
sys.general.options.units = state.temps.units = msg.extractPayloadByte(9) & 0x04;
|
|
156
163
|
state.valve = msg.extractPayloadByte(10);
|
|
157
164
|
|
|
@@ -163,8 +170,8 @@ export class EquipmentStateMessage {
|
|
|
163
170
|
// 1. IntelliCenter has "manual" time set (Internet will automatically adjust) and autoAdjustDST is enabled
|
|
164
171
|
// 2. *Touch is "manual" (only option) and autoAdjustDST is enabled - (same as #1)
|
|
165
172
|
// 3. clock source is "server" isn't an OCP option but can be enabled on the clients
|
|
166
|
-
if (dt.getMinutes() % 5 === 0 && sys.general.options.clockSource === 'server') {
|
|
167
|
-
if ((Math.abs(dt.getTime() - state.time.getTime()) > 60 *
|
|
173
|
+
if (dt.getMinutes() % 5 === 0 && dt.getSeconds() <= 10 && sys.general.options.clockSource === 'server') {
|
|
174
|
+
if ((Math.abs(dt.getTime() - state.time.getTime()) > 60 * 2 * 1000) && !state.time.isUpdating) {
|
|
168
175
|
state.time.isUpdating = true;
|
|
169
176
|
sys.board.system.setDateTimeAsync({ dt, dst: sys.general.options.adjustDST || 0, })
|
|
170
177
|
.then(() => {
|
|
@@ -197,10 +204,19 @@ export class EquipmentStateMessage {
|
|
|
197
204
|
tbody.name = cbody.name;
|
|
198
205
|
tbody.circuit = cbody.circuit = 6;
|
|
199
206
|
tbody.heatStatus = msg.extractPayloadByte(11) & 0x0F;
|
|
200
|
-
|
|
207
|
+
// With the IntelliCenter i10D, bit 6 is not reliable. It is not set properly and requires the 204 message
|
|
208
|
+
// to process the data.
|
|
209
|
+
if (!sys.equipment.dual) {
|
|
210
|
+
if ((msg.extractPayloadByte(2) & 0x20) === 32) {
|
|
211
|
+
tbody.temp = state.temps.waterSensor1;
|
|
212
|
+
tbody.isOn = true;
|
|
213
|
+
} else tbody.isOn = false;
|
|
214
|
+
}
|
|
215
|
+
else if (state.circuits.getItemById(6).isOn === true) {
|
|
201
216
|
tbody.temp = state.temps.waterSensor1;
|
|
202
217
|
tbody.isOn = true;
|
|
203
|
-
}
|
|
218
|
+
}
|
|
219
|
+
else tbody.isOn = false;
|
|
204
220
|
}
|
|
205
221
|
if (sys.bodies.length > 1) {
|
|
206
222
|
const tbody: BodyTempState = state.temps.bodies.getItemById(2, true);
|
|
@@ -210,10 +226,16 @@ export class EquipmentStateMessage {
|
|
|
210
226
|
tbody.name = cbody.name;
|
|
211
227
|
tbody.circuit = cbody.circuit = 1;
|
|
212
228
|
tbody.heatStatus = (msg.extractPayloadByte(11) & 0xF0) >> 4;
|
|
213
|
-
if (
|
|
229
|
+
if (!sys.equipment.dual) {
|
|
230
|
+
if ((msg.extractPayloadByte(2) & 0x01) === 1) {
|
|
231
|
+
tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
|
|
232
|
+
tbody.isOn = true;
|
|
233
|
+
} else tbody.isOn = false;
|
|
234
|
+
} else if (state.circuits.getItemById(1).isOn === true) {
|
|
214
235
|
tbody.temp = sys.equipment.shared ? state.temps.waterSensor1 : state.temps.waterSensor2;
|
|
215
236
|
tbody.isOn = true;
|
|
216
|
-
}
|
|
237
|
+
}
|
|
238
|
+
else tbody.isOn = false;
|
|
217
239
|
}
|
|
218
240
|
if (sys.bodies.length > 2) {
|
|
219
241
|
state.temps.waterSensor3 = fnTempFromByte(msg.extractPayloadByte(20));
|
|
@@ -390,13 +412,14 @@ export class EquipmentStateMessage {
|
|
|
390
412
|
sys.board.circuits.syncCircuitRelayStates();
|
|
391
413
|
sys.board.circuits.syncVirtualCircuitStates();
|
|
392
414
|
sys.board.valves.syncValveStates();
|
|
415
|
+
sys.board.filters.syncFilterStates();
|
|
393
416
|
state.emitControllerChange();
|
|
394
417
|
state.emitEquipmentChanges();
|
|
395
418
|
sys.board.heaters.syncHeaterStates();
|
|
396
419
|
break;
|
|
397
420
|
}
|
|
398
|
-
case ControllerType.IntelliCom:
|
|
399
421
|
case ControllerType.EasyTouch:
|
|
422
|
+
case ControllerType.IntelliCom:
|
|
400
423
|
case ControllerType.IntelliTouch:
|
|
401
424
|
{
|
|
402
425
|
EquipmentStateMessage.processTouchCircuits(msg);
|
|
@@ -405,6 +428,7 @@ export class EquipmentStateMessage {
|
|
|
405
428
|
sys.board.features.syncGroupStates();
|
|
406
429
|
sys.board.circuits.syncVirtualCircuitStates();
|
|
407
430
|
sys.board.valves.syncValveStates();
|
|
431
|
+
sys.board.filters.syncFilterStates();
|
|
408
432
|
state.emitControllerChange();
|
|
409
433
|
state.emitEquipmentChanges();
|
|
410
434
|
sys.board.heaters.syncHeaterStates();
|
|
@@ -495,8 +519,7 @@ export class EquipmentStateMessage {
|
|
|
495
519
|
state.time.date = msg.extractPayloadByte(6);
|
|
496
520
|
state.time.month = msg.extractPayloadByte(7);
|
|
497
521
|
state.time.year = msg.extractPayloadByte(8);
|
|
498
|
-
sys.equipment.controllerFirmware = (msg.extractPayloadByte(42)
|
|
499
|
-
+ (msg.extractPayloadByte(43) / 1000)).toString();
|
|
522
|
+
sys.equipment.controllerFirmware = (msg.extractPayloadByte(42) + (msg.extractPayloadByte(43) / 1000)).toString();
|
|
500
523
|
if (sys.chlorinators.length > 0) {
|
|
501
524
|
if (msg.extractPayloadByte(37, 255) !== 255) {
|
|
502
525
|
const chlor = state.chlorinators.getItemById(1);
|
|
@@ -508,9 +531,38 @@ export class EquipmentStateMessage {
|
|
|
508
531
|
}
|
|
509
532
|
}
|
|
510
533
|
ExternalMessage.processFeatureState(9, msg);
|
|
534
|
+
//if (sys.equipment.dual === true) {
|
|
535
|
+
// // For IntelliCenter i10D the body state is on byte 26 of the 204. This impacts circuit 6.
|
|
536
|
+
// let byte = msg.extractPayloadByte(26);
|
|
537
|
+
// let pstate = state.circuits.getItemById(6, true);
|
|
538
|
+
// let oldstate = pstate.isOn;
|
|
539
|
+
// pstate.isOn = ((byte & 0x0010) === 0x0010);
|
|
540
|
+
// logger.info(`Checking i10D pool state ${byte} old:${oldstate} new: ${pstate.isOn}`);
|
|
541
|
+
// //if (oldstate !== pstate.isOn) {
|
|
542
|
+
// state.temps.bodies.getItemById(1, true).isOn = pstate.isOn;
|
|
543
|
+
// sys.board.circuits.syncCircuitRelayStates();
|
|
544
|
+
// sys.board.circuits.syncVirtualCircuitStates();
|
|
545
|
+
// sys.board.valves.syncValveStates();
|
|
546
|
+
// sys.board.filters.syncFilterStates();
|
|
547
|
+
// sys.board.heaters.syncHeaterStates();
|
|
548
|
+
// //}
|
|
549
|
+
// if (oldstate !== pstate.isOn) pstate.emitEquipmentChange();
|
|
550
|
+
//}
|
|
551
|
+
// At this point normally on is ignored. Not sure what this does.
|
|
552
|
+
let cover1 = sys.covers.getItemById(1);
|
|
553
|
+
let cover2 = sys.covers.getItemById(2);
|
|
554
|
+
if (cover1.isActive) {
|
|
555
|
+
let scover1 = state.covers.getItemById(1, true);
|
|
556
|
+
scover1.name = cover1.name;
|
|
557
|
+
state.temps.bodies.getItemById(cover1.body + 1).isCovered = scover1.isClosed = (msg.extractPayloadByte(30) & 0x0001) > 0;
|
|
558
|
+
}
|
|
559
|
+
if (cover2.isActive) {
|
|
560
|
+
let scover2 = state.covers.getItemById(2, true);
|
|
561
|
+
scover2.name = cover2.name;
|
|
562
|
+
state.temps.bodies.getItemById(cover2.body + 1).isCovered = scover2.isClosed = (msg.extractPayloadByte(30) & 0x0002) > 0;
|
|
563
|
+
}
|
|
511
564
|
msg.isProcessed = true;
|
|
512
|
-
|
|
513
|
-
// state.emitEquipmentChanges();
|
|
565
|
+
state.emitEquipmentChanges();
|
|
514
566
|
break;
|
|
515
567
|
}
|
|
516
568
|
}
|
|
@@ -528,13 +580,15 @@ export class EquipmentStateMessage {
|
|
|
528
580
|
let circuit = sys.circuits.getItemById(circuitId, false, { isActive: false });
|
|
529
581
|
if (circuit.isActive !== false) {
|
|
530
582
|
let cstate = state.circuits.getItemById(circuitId, circuit.isActive);
|
|
531
|
-
|
|
532
|
-
sys.
|
|
583
|
+
// For IntelliCenter i10D body circuits are not reported here.
|
|
584
|
+
let isOn = ((circuitId === 6 || circuitId === 1) && sys.equipment.dual === true) ? cstate.isOn : (byte & (1 << j)) > 0;
|
|
585
|
+
//let isOn = (byte & (1 << j)) > 0;
|
|
533
586
|
cstate.isOn = isOn;
|
|
534
587
|
cstate.name = circuit.name;
|
|
535
588
|
cstate.nameId = circuit.nameId;
|
|
536
589
|
cstate.showInFeatures = circuit.showInFeatures;
|
|
537
590
|
cstate.type = circuit.type;
|
|
591
|
+
sys.board.circuits.setEndTime(circuit, cstate, isOn);
|
|
538
592
|
if (sys.controllerType === ControllerType.IntelliCenter) {
|
|
539
593
|
// intellitouch sends a separate msg with themes
|
|
540
594
|
switch (circuit.type) {
|
|
@@ -31,13 +31,33 @@ export class HeaterStateMessage {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
public static processHeaterStatus(msg: Inbound) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
//
|
|
34
|
+
// RKS: 07-03-21 - We only know byte 2 at this point for Ultratemp for the 115 message we are processing here. The
|
|
35
|
+
// byte description
|
|
36
|
+
// ------------------------------------------------
|
|
37
|
+
// 0 Unknown (always seems to be 160 for response)
|
|
38
|
+
// 1 Unknown (always 1)
|
|
39
|
+
// 2 Current heater status 0=off, 1=heat, 2=cool
|
|
40
|
+
// 3-9 Unknown
|
|
41
|
+
|
|
42
|
+
// 114 message - outbound response
|
|
39
43
|
//[165, 0, 112, 16, 114, 10][144, 0, 0, 0, 0, 0, 0, 0, 0, 0][2, 49] // OCP to Heater
|
|
44
|
+
// byte description
|
|
45
|
+
// ------------------------------------------------
|
|
46
|
+
// 0 Unknown (always seems to be 144 for request)
|
|
47
|
+
// 1 Current heater status 0=off, 1=heat, 2=cool
|
|
48
|
+
// 3 Believed to be ofset temp
|
|
49
|
+
// 4-9 Unknown
|
|
50
|
+
|
|
51
|
+
// byto 0: always seems to be 144 for outbound
|
|
52
|
+
// byte 1: Sets heater mode to 0 = Off 1 = Heat 2 = Cool
|
|
40
53
|
//[165, 0, 16, 112, 115, 10][160, 1, 0, 3, 0, 0, 0, 0, 0, 0][2, 70] // Heater Reply
|
|
54
|
+
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
55
|
+
let sheater = state.heaters.getItemById(heater.id);
|
|
56
|
+
let byte = msg.extractPayloadByte(2);
|
|
57
|
+
sheater.isOn = byte >= 1;
|
|
58
|
+
sheater.isCooling = byte === 2;
|
|
59
|
+
sheater.commStatus = 0;
|
|
60
|
+
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
41
61
|
msg.isProcessed = true;
|
|
42
62
|
}
|
|
43
63
|
}
|
|
@@ -23,15 +23,25 @@ import { Timestamp, utils } from "../../../Constants"
|
|
|
23
23
|
export class IntelliChemStateMessage {
|
|
24
24
|
public static process(msg: Inbound) {
|
|
25
25
|
if (sys.controllerType === ControllerType.Unknown) return;
|
|
26
|
-
|
|
26
|
+
let address = (msg.dest >= 144 && msg.dest <= 158) ? msg.dest : msg.source;
|
|
27
|
+
if (address < 144 || address > 158) return;
|
|
28
|
+
let controller = sys.chemControllers.getItemByAddress(address);
|
|
29
|
+
if (!controller.isActive) {
|
|
30
|
+
msg.isProcessed = true;
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
27
33
|
switch (msg.action) {
|
|
28
|
-
|
|
29
34
|
// ---------- IntelliChem Control panel is spitting out its status ----------- //
|
|
30
35
|
case 18: // IntelliChem is sending us it's status.
|
|
31
36
|
IntelliChemStateMessage.processState(msg);
|
|
32
37
|
break;
|
|
33
38
|
case 210: // OCP is asking IntelliChem controller for it's current status info.
|
|
34
39
|
// [165,0,144,16,210,1],[210],[2,234]
|
|
40
|
+
let schem = state.chemControllers.getItemById(controller.id);
|
|
41
|
+
if (schem.lastComm + (30 * 1000) < new Date().getTime()) {
|
|
42
|
+
// We have not talked to the chem controller in 30 seconds so we have lost communication.
|
|
43
|
+
schem.status = schem.alarms.comms = 1;
|
|
44
|
+
}
|
|
35
45
|
msg.isProcessed = true;
|
|
36
46
|
break;
|
|
37
47
|
// ---------- End IntelliChem set get ----------- //
|
|
@@ -43,7 +53,7 @@ export class IntelliChemStateMessage {
|
|
|
43
53
|
|
|
44
54
|
/* RKS: This is processed in the IntellichemMessage.processTouch() and is the results of asking for the IntelliChem configuration.
|
|
45
55
|
case 147: // OCP is broadcasting it's known ic values... Need to change our settings if virtual.
|
|
46
|
-
// 147 is a proto:broadcast message;
|
|
56
|
+
// 147 is a proto:broadcast message;
|
|
47
57
|
// it has exactly the same format as 18 but there is payload[0] which is inserted at the beginning. Likely the chem controller id.
|
|
48
58
|
if (msg.dest < 144 || msg.dest > 158) return;
|
|
49
59
|
IntelliChemStateMessage.processControllerChange(msg);
|
|
@@ -57,14 +67,11 @@ export class IntelliChemStateMessage {
|
|
|
57
67
|
msg.isProcessed = true;
|
|
58
68
|
break;
|
|
59
69
|
case 146: // OCP is telling IntelliChem that it needs to change its settings to...
|
|
60
|
-
let
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
// We have not talked to the chem controller in 30 seconds so we have lost communication.
|
|
66
|
-
scontroller.status = scontroller.alarms.comms = 1;
|
|
67
|
-
}
|
|
70
|
+
//let scontroller = state.chemControllers.getItemById(controller.id, true);
|
|
71
|
+
//if (scontroller.lastComm + (30 * 1000) < new Date().getTime()) {
|
|
72
|
+
// // We have not talked to the chem controller in 30 seconds so we have lost communication.
|
|
73
|
+
// scontroller.status = scontroller.alarms.comms = 1;
|
|
74
|
+
//}
|
|
68
75
|
controller.ph.tank.capacity = controller.orp.tank.capacity = 6;
|
|
69
76
|
controller.ph.tank.units = controller.orp.tank.units = '';
|
|
70
77
|
msg.isProcessed = true;
|
|
@@ -89,8 +96,8 @@ export class IntelliChemStateMessage {
|
|
|
89
96
|
// 6-7 : ORP Setpoint : byte(6) x 256 + byte(7)
|
|
90
97
|
// 8 : Unknown = 0
|
|
91
98
|
// 9 : Unknown = 0
|
|
92
|
-
// 10 :
|
|
93
|
-
//
|
|
99
|
+
// 10-11 : pH Dose time seconds. The number of seconds since the dose started. byte(10) x 256 + byte(11)
|
|
100
|
+
// 12: Unknown
|
|
94
101
|
// 13 : Unknown
|
|
95
102
|
// 14-15 : ORP Dose time seconds. The number of seconds since the dose started. byte(14) x 256 + byte(15)
|
|
96
103
|
// 16-17 : pH Dose volume (unknown units) - These appear to be mL.
|
|
@@ -122,10 +129,13 @@ export class IntelliChemStateMessage {
|
|
|
122
129
|
schem.type = chem.type = sys.board.valueMaps.chemControllerTypes.getValue('intellichem');
|
|
123
130
|
chem.name = chem.name || `IntelliChem ${chem.address - 143}`; // default to true id if no name is set
|
|
124
131
|
schem.lastComm = new Date().getTime();
|
|
125
|
-
schem.status = schem.alarms.comms = 0;
|
|
132
|
+
schem.status = schem.alarms.comms = 0;
|
|
126
133
|
chem.ph.tank.capacity = chem.orp.tank.capacity = 6;
|
|
127
134
|
chem.ph.tank.units = chem.orp.tank.units = '';
|
|
128
|
-
|
|
135
|
+
chem.ph.tank.alarmEmptyEnabled = false;
|
|
136
|
+
chem.ph.tank.alarmEmptyLevel = 1;
|
|
137
|
+
chem.orp.tank.alarmEmptyEnabled = false;
|
|
138
|
+
chem.orp.tank.alarmEmptyLevel = 1;
|
|
129
139
|
schem.address = chem.address;
|
|
130
140
|
schem.ph.level = schem.ph.probe.level = msg.extractPayloadIntBE(0) / 100;
|
|
131
141
|
schem.orp.level = schem.orp.probe.level = msg.extractPayloadIntBE(2);
|
|
@@ -133,11 +143,11 @@ export class IntelliChemStateMessage {
|
|
|
133
143
|
chem.orp.setpoint = msg.extractPayloadIntBE(6);
|
|
134
144
|
// Missing information on the related bytes.
|
|
135
145
|
// Bytes 8-14 (Probably Total Dissolved Solids in here if no IntelliChlor)
|
|
136
|
-
let phPrev = { status: schem.
|
|
146
|
+
let phPrev = { status: schem.ph.dosingStatus, time: schem.ph.timeDosed || 0, vol: schem.ph.volumeDosed };
|
|
137
147
|
let orpPrev = { status: schem.orp.dosingStatus, time: schem.orp.timeDosed || 0, vol: schem.orp.volumeDosed };
|
|
138
148
|
// IntelliChem never tells us what the dose time or volume is so we will let that dog lie.
|
|
139
|
-
// 11
|
|
140
|
-
schem.ph.timeDosed = (msg.extractPayloadByte(
|
|
149
|
+
// 10-11 : pH Dose time
|
|
150
|
+
schem.ph.timeDosed = (msg.extractPayloadByte(10) * 256) + msg.extractPayloadByte(11);
|
|
141
151
|
// 14-15 : ORP Dose time seconds. The number of seconds since the dose started.
|
|
142
152
|
schem.orp.timeDosed = (msg.extractPayloadByte(14) * 256) + msg.extractPayloadByte(15);
|
|
143
153
|
// 16-17 : pH Dose volume (unknown units) = 35
|
|
@@ -157,7 +167,7 @@ export class IntelliChemStateMessage {
|
|
|
157
167
|
chem.cyanuricAcid = msg.extractPayloadByte(26);
|
|
158
168
|
// 27-28 : Alkalinity
|
|
159
169
|
chem.alkalinity = (msg.extractPayloadByte(27) * 256) + msg.extractPayloadByte(28);
|
|
160
|
-
// 29 : Salt level = 20
|
|
170
|
+
// 29 : Salt level = 20
|
|
161
171
|
if (sys.chlorinators.length > 0) {
|
|
162
172
|
let chlor = state.chlorinators.find(elem => elem.id === 1);
|
|
163
173
|
schem.orp.probe.saltLevel = (typeof chlor !== 'undefined') ? chlor.saltLevel : msg.extractPayloadByte(29) * 50;
|
|
@@ -169,6 +179,7 @@ export class IntelliChemStateMessage {
|
|
|
169
179
|
// 32 : Alarms = 8 = (no alarm)
|
|
170
180
|
const alarms = schem.alarms;
|
|
171
181
|
alarms.flow = msg.extractPayloadByte(32) & 0x01;
|
|
182
|
+
if (alarms.flow === 0) schem.flowDetected = true;
|
|
172
183
|
alarms.pH = msg.extractPayloadByte(32) & 0x06;
|
|
173
184
|
alarms.orp = msg.extractPayloadByte(32) & 0x18;
|
|
174
185
|
alarms.pHTank = msg.extractPayloadByte(32) & 0x20;
|
|
@@ -187,16 +198,34 @@ export class IntelliChemStateMessage {
|
|
|
187
198
|
schem.ph.dosingStatus = (msg.extractPayloadByte(34) & 0x30) >> 4; // mask 00xx0000 and shift bit 5 & 6
|
|
188
199
|
schem.orp.dosingStatus = (msg.extractPayloadByte(34) & 0xC0) >> 6; // mask xx000000 and shift bit 7 & 8
|
|
189
200
|
// 35 : Delays = 0
|
|
190
|
-
schem.status = msg.extractPayloadByte(35) & 0x80 >> 7; // to be verified as comms lost
|
|
201
|
+
schem.status = (msg.extractPayloadByte(35) & 0x80) >> 7; // to be verified as comms lost
|
|
191
202
|
schem.ph.manualDosing = (msg.extractPayloadByte(35) & 0x08) === 1 ? true : false;
|
|
192
203
|
chem.orp.useChlorinator = (msg.extractPayloadByte(35) & 0x10) === 1 ? true : false;
|
|
193
204
|
chem.HMIAdvancedDisplay = (msg.extractPayloadByte(35) & 0x20) === 1 ? true : false;
|
|
194
|
-
chem.ph.phSupply = (msg.extractPayloadByte(35) & 0x40) === 1 ?
|
|
205
|
+
chem.ph.phSupply = (msg.extractPayloadByte(35) & 0x40) === 1 ? 'acid' : 'base'; // acid pH dosing = 1; base pH dosing = 0;
|
|
195
206
|
// 36-37 : Firmware = 80,1 = 1.080
|
|
196
207
|
chem.firmware = `${msg.extractPayloadByte(37)}.${msg.extractPayloadByte(36).toString().padStart(3, '0')}`
|
|
197
208
|
// 38 : Water Chemistry Warning
|
|
198
209
|
schem.warnings.waterChemistry = msg.extractPayloadByte(38);
|
|
199
210
|
if (typeof chem.body === 'undefined') chem.body = schem.body = 0;
|
|
211
|
+
if (state.equipment.controllerType === 'nixie') {
|
|
212
|
+
if (chem.ph.probe.feedBodyTemp) {
|
|
213
|
+
let temps: any = {};
|
|
214
|
+
let body = state.temps.bodies.getBodyIsOn();
|
|
215
|
+
if (typeof body !== 'undefined') {
|
|
216
|
+
if (body.id === 1 && (schem.body === 0 || schem.body === 32)) {
|
|
217
|
+
temps.waterSensor1 = schem.ph.probe.temperature;
|
|
218
|
+
}
|
|
219
|
+
else if (body.id === 2 && (schem.body === 2 || schem.body === 32)) {
|
|
220
|
+
temps.waterSensor1 = schem.ph.probe.temperature;
|
|
221
|
+
}
|
|
222
|
+
else if (body.id === 2 && chem.body === 1) {
|
|
223
|
+
temps.waterSensor2 = schem.ph.probe.temperature;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
sys.board.system.setTempsAsync(temps).catch(err => logger.error(err))
|
|
227
|
+
}
|
|
228
|
+
}
|
|
200
229
|
schem.ph.pump.isDosing = schem.ph.dosingStatus === 0 && chem.ph.enabled;
|
|
201
230
|
schem.orp.pump.isDosing = schem.orp.dosingStatus === 0 && chem.orp.enabled;
|
|
202
231
|
schem.calculateSaturationIndex();
|
|
@@ -212,12 +241,12 @@ export class IntelliChemStateMessage {
|
|
|
212
241
|
if (typeof schem.ph.currentDose !== 'undefined') {
|
|
213
242
|
// We just ended a dose so write it out to the chem logs.
|
|
214
243
|
schem.ph.endDose(Timestamp.now.addSeconds(-(schem.ph.doseTime - phPrev.time)).toDate(), 'completed',
|
|
215
|
-
schem.ph.volumeDosed - phPrev.vol, (schem.ph.
|
|
244
|
+
schem.ph.volumeDosed - phPrev.vol, (schem.ph.timeDosed - phPrev.time) * 1000);
|
|
216
245
|
}
|
|
217
246
|
}
|
|
218
247
|
else if (schem.ph.dosingStatus === 0) {
|
|
219
248
|
// We are still dosing so add the time and volume to the dose.
|
|
220
|
-
schem.ph.appendDose(schem.ph.doseVolume - phPrev.vol, (schem.ph.
|
|
249
|
+
schem.ph.appendDose(schem.ph.doseVolume - phPrev.vol, (schem.ph.timeDosed - phPrev.time) * 1000);
|
|
221
250
|
}
|
|
222
251
|
else {
|
|
223
252
|
console.log(`DOSING STATUS === ${schem.ph.dosingStatus}`);
|
|
@@ -228,19 +257,19 @@ export class IntelliChemStateMessage {
|
|
|
228
257
|
if (schem.orp.dosingStatus === 0 && orpPrev.status !== 0) {
|
|
229
258
|
if (schem.orp.dosingStatus === 0) {
|
|
230
259
|
// We are starting a dose so we need to set the current dose.
|
|
231
|
-
schem.orp.startDose(Timestamp.now.addSeconds(-schem.orp.doseTime).toDate(), schem.orp.manualDosing ? 'manual' : 'auto', 0, schem.orp.volumeDosed, schem.orp.timeDosed * 1000);
|
|
260
|
+
schem.orp.startDose(Timestamp.now.addSeconds(-schem.orp.doseTime).toDate(), schem.orp.manualDosing ? 'manual' : 'auto', 0, schem.orp.volumeDosed, 0, schem.orp.timeDosed * 1000);
|
|
232
261
|
}
|
|
233
262
|
}
|
|
234
263
|
else if (schem.orp.dosingStatus !== 0 && orpPrev.status === 0) {
|
|
235
264
|
if (typeof schem.orp.currentDose !== 'undefined') {
|
|
236
265
|
// We just ended a dose so write it out to the chem logs.
|
|
237
266
|
schem.orp.endDose(Timestamp.now.addSeconds(-(schem.orp.doseTime - orpPrev.time)).toDate(), 'completed',
|
|
238
|
-
schem.orp.volumeDosed - orpPrev.vol, schem.orp.
|
|
267
|
+
schem.orp.volumeDosed - orpPrev.vol, (schem.orp.timeDosed - orpPrev.time) * 1000);
|
|
239
268
|
}
|
|
240
269
|
}
|
|
241
270
|
else if (schem.orp.dosingStatus === 0) {
|
|
242
271
|
// We are still dosing so add the time and volume to the dose.
|
|
243
|
-
schem.orp.appendDose(schem.orp.
|
|
272
|
+
schem.orp.appendDose(schem.orp.doseVolume - orpPrev.vol, (schem.orp.timeDosed - orpPrev.time) * 1000);
|
|
244
273
|
}
|
|
245
274
|
else {
|
|
246
275
|
// Make sure we don't have a current dose going.
|
|
@@ -136,23 +136,25 @@ export class NixieControlPanel implements INixieControlPanel {
|
|
|
136
136
|
try {
|
|
137
137
|
let srv = [];
|
|
138
138
|
let servers = webApp.findServersByType('rem');
|
|
139
|
-
|
|
140
|
-
let
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
139
|
+
if (typeof servers !== 'undefined') {
|
|
140
|
+
for (let i = 0; i < servers.length; i++) {
|
|
141
|
+
let server = servers[i];
|
|
142
|
+
// Sometimes I hate type safety.
|
|
143
|
+
let devices = typeof server['getDevices'] === 'function' ? await server['getDevices']() : [];
|
|
144
|
+
let int = config.getInterfaceByUuid(servers[i].uuid);
|
|
145
|
+
srv.push({
|
|
146
|
+
uuid: servers[i].uuid,
|
|
147
|
+
name: servers[i].name,
|
|
148
|
+
type: servers[i].type,
|
|
149
|
+
isRunning: servers[i].isRunning,
|
|
150
|
+
isConnected: servers[i].isConnected,
|
|
151
|
+
devices: devices,
|
|
152
|
+
remoteConnectionId: servers[i].remoteConnectionId,
|
|
153
|
+
interface: int
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
await ncp.chemControllers.syncRemoteREMFeeds(srv);
|
|
154
157
|
}
|
|
155
|
-
await ncp.chemControllers.syncRemoteREMFeeds(srv);
|
|
156
158
|
return srv;
|
|
157
159
|
} catch (err) { logger.error(err); }
|
|
158
160
|
}
|
|
@@ -93,12 +93,12 @@ export class NixieEquipmentCollection<T> extends Array<NixieEquipment> {
|
|
|
93
93
|
catch (err) { return Promise.reject(err); }
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
export class NixieRelay extends NixieEquipment {
|
|
96
|
+
//export class NixieRelay extends NixieEquipment {
|
|
97
97
|
|
|
98
|
-
}
|
|
99
|
-
export class NixieCircuit extends NixieRelay {
|
|
98
|
+
//}
|
|
99
|
+
//export class NixieCircuit extends NixieRelay {
|
|
100
100
|
|
|
101
|
-
}
|
|
102
|
-
export class NixieValve extends NixieRelay {
|
|
101
|
+
//}
|
|
102
|
+
//export class NixieValve extends NixieRelay {
|
|
103
103
|
|
|
104
|
-
}
|
|
104
|
+
//}
|
|
@@ -33,9 +33,11 @@ export class NixieBodyCollection extends NixieEquipmentCollection<NixieBody> {
|
|
|
33
33
|
for (let i = 0; i < bodies.length; i++) {
|
|
34
34
|
let body = bodies.getItemByIndex(i);
|
|
35
35
|
if (body.master === 1) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if (typeof this.find(elem => elem.id === body.id) === 'undefined') {
|
|
37
|
+
logger.info(`Initializing Nixie body ${body.name}`);
|
|
38
|
+
let nbody = new NixieBody(this.controlPanel, body);
|
|
39
|
+
this.push(nbody);
|
|
40
|
+
}
|
|
39
41
|
}
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -81,13 +83,14 @@ export class NixieBody extends NixieEquipment {
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
public async pollEquipmentAsync() {
|
|
86
|
+
let self = this;
|
|
84
87
|
try {
|
|
85
88
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
86
89
|
this._pollTimer = null;
|
|
87
90
|
let success = false;
|
|
88
91
|
}
|
|
89
92
|
catch (err) { logger.error(`Nixie Error polling body - ${err}`); }
|
|
90
|
-
finally { this._pollTimer = setTimeout(async () => await
|
|
93
|
+
finally { this._pollTimer = setTimeout(async () => await self.pollEquipmentAsync(), this.pollingInterval || 10000); }
|
|
91
94
|
}
|
|
92
95
|
private async checkHardwareStatusAsync(connectionId: string, deviceBinding: string) {
|
|
93
96
|
try {
|
|
@@ -42,9 +42,11 @@ export class NixieFilterCollection extends NixieEquipmentCollection<NixieFilter>
|
|
|
42
42
|
for (let i = 0; i < filters.length; i++) {
|
|
43
43
|
let filter = filters.getItemByIndex(i);
|
|
44
44
|
if (filter.master === 1) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
if (typeof this.find(elem => elem.id === filter.id) === 'undefined') {
|
|
46
|
+
logger.info(`Initializing Filter ${Filter.name}`);
|
|
47
|
+
let nFilter = new NixieFilter(this.controlPanel, filter);
|
|
48
|
+
this.push(nFilter);
|
|
49
|
+
}
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
}
|
|
@@ -98,13 +100,14 @@ export class NixieFilter extends NixieEquipment {
|
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
public async pollEquipmentAsync() {
|
|
103
|
+
let self = this;
|
|
101
104
|
try {
|
|
102
105
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
103
106
|
this._pollTimer = null;
|
|
104
107
|
let success = false;
|
|
105
108
|
}
|
|
106
109
|
catch (err) { logger.error(`Nixie Error polling Filter - ${err}`); }
|
|
107
|
-
finally { this._pollTimer = setTimeout(async () => await
|
|
110
|
+
finally { this._pollTimer = setTimeout(async () => await self.pollEquipmentAsync(), this.pollingInterval || 10000); }
|
|
108
111
|
}
|
|
109
112
|
private async checkHardwareStatusAsync(connectionId: string, deviceBinding: string) {
|
|
110
113
|
try {
|