nodejs-poolcontroller 8.0.0 → 8.0.2
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/.docker/Dockerfile.armv6 +29 -0
- package/.docker/Dockerfile.armv7 +29 -0
- package/.docker/Dockerfile.linux +62 -0
- package/.docker/Dockerfile.windows +43 -0
- package/.docker/docker-compose.yml +47 -0
- package/.docker/ecosystem.config.js +35 -0
- package/.github/workflows/docker-publish-njsPC-linux.yml +81 -0
- package/.github/workflows/docker-publish-njsPC-windows.yml +41 -0
- package/Dockerfile +4 -3
- package/README.md +4 -1
- package/config/Config.ts +1 -1
- package/controller/Constants.ts +164 -67
- package/controller/Equipment.ts +79 -18
- package/controller/Lockouts.ts +15 -0
- package/controller/State.ts +280 -7
- package/controller/boards/EasyTouchBoard.ts +226 -102
- package/controller/boards/IntelliCenterBoard.ts +67 -18
- package/controller/boards/IntelliTouchBoard.ts +2 -4
- package/controller/boards/NixieBoard.ts +84 -27
- package/controller/boards/SunTouchBoard.ts +8 -2
- package/controller/boards/SystemBoard.ts +3259 -3242
- package/controller/comms/ScreenLogic.ts +60 -57
- package/controller/comms/messages/Messages.ts +4 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +10 -3
- package/controller/comms/messages/config/ExternalMessage.ts +4 -1
- package/controller/comms/messages/config/PumpMessage.ts +8 -7
- package/controller/comms/messages/config/RemoteMessage.ts +48 -43
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -2
- package/controller/comms/messages/status/EquipmentStateMessage.ts +9 -4
- package/controller/nixie/NixieEquipment.ts +1 -1
- package/controller/nixie/bodies/Body.ts +1 -1
- package/controller/nixie/chemistry/ChemController.ts +37 -28
- package/controller/nixie/circuits/Circuit.ts +36 -0
- package/controller/nixie/heaters/Heater.ts +24 -5
- package/controller/nixie/pumps/Pump.ts +155 -97
- package/controller/nixie/schedules/Schedule.ts +207 -126
- package/defaultConfig.json +4 -3
- package/logger/DataLogger.ts +7 -7
- package/package.json +3 -3
- package/sendSocket.js +32 -0
- package/web/Server.ts +17 -11
- package/web/bindings/homeassistant.json +2 -2
- package/web/interfaces/mqttInterface.ts +18 -18
- package/web/services/config/Config.ts +34 -1
- package/web/services/state/State.ts +10 -3
- package/web/services/state/StateSocket.ts +7 -3
- package/web/services/utilities/Utilities.ts +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ControllerType, Timestamp, Utils, utils } from '../../controller/Constants';
|
|
2
2
|
import { LightGroup, LightGroupCircuit, sys, Valve, Body, Pump, PumpCircuit, Remote} from '../../controller/Equipment';
|
|
3
3
|
import { CircuitState, state, ValveState } from '../../controller/State';
|
|
4
|
-
import {
|
|
4
|
+
import { RemoteLogin, UnitConnection, FindUnits, SLEquipmentStateData, SLIntellichlorData, SLPumpStatusData, SLScheduleData, SLSystemTimeData, HeatModes, SLControllerConfigData, SLEquipmentConfigurationData, HeaterConfig, Valves, SLChemData, SLGetCustomNamesData } from 'node-screenlogic';
|
|
5
5
|
import * as Screenlogic from 'node-screenlogic';
|
|
6
6
|
import { EasyTouchBoard } from '../../controller/boards/EasyTouchBoard';
|
|
7
7
|
import { IntelliTouchBoard } from '../../controller/boards/IntelliTouchBoard';
|
|
@@ -15,7 +15,7 @@ import { Message } from './messages/Messages';
|
|
|
15
15
|
|
|
16
16
|
export class ScreenLogicComms {
|
|
17
17
|
constructor() {
|
|
18
|
-
this._client = Screenlogic.
|
|
18
|
+
this._client = new Screenlogic.UnitConnection();
|
|
19
19
|
};
|
|
20
20
|
public a: SLChemData;
|
|
21
21
|
public counter: SLCounter = new SLCounter();
|
|
@@ -47,11 +47,10 @@ export class ScreenLogicComms {
|
|
|
47
47
|
this.controller = new SLController(this._client);
|
|
48
48
|
let cfg = config.getSection('controller.comms');
|
|
49
49
|
if (typeof cfg !== 'undefined') this._cfg = cfg;
|
|
50
|
-
this.enabled = this._cfg.enabled;
|
|
50
|
+
this.enabled = this._cfg.enabled && this._cfg.type === 'screenlogic';
|
|
51
51
|
if (!this._cfg.enabled || this._cfg.type !== 'screenlogic') {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
|
|
55
54
|
let systemName = this._cfg.screenlogic.systemName; // 'Pentair: 00-00-00';
|
|
56
55
|
let password = this._cfg.screenlogic.password.toString(); // '1111';
|
|
57
56
|
|
|
@@ -766,11 +765,11 @@ class Controller {
|
|
|
766
765
|
logger.error(`Caught error in decodeEquipmentState: ${err.message}`);
|
|
767
766
|
}
|
|
768
767
|
}
|
|
769
|
-
public static async decodeCustomNames(customNames:
|
|
768
|
+
public static async decodeCustomNames(customNames: SLGetCustomNamesData) {
|
|
770
769
|
for (let i = 0; i < sys.equipment.maxCustomNames; i++) {
|
|
771
770
|
let data = {
|
|
772
771
|
id: i,
|
|
773
|
-
name: customNames[i]
|
|
772
|
+
name: customNames.names[i]
|
|
774
773
|
}
|
|
775
774
|
try {
|
|
776
775
|
|
|
@@ -913,21 +912,21 @@ class Controller {
|
|
|
913
912
|
}
|
|
914
913
|
], */
|
|
915
914
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
915
|
+
public static decodeHighSpeed(highSpeed: number[]) {
|
|
916
|
+
let maxCircuits = sys.controllerType === ControllerType.IntelliTouch ? 8 : 4;
|
|
917
|
+
let arrCircuits = [];
|
|
918
|
+
let pump = sys.pumps.find(x => { return x.master !== 1 && x.type === 65 });
|
|
919
|
+
for (let i = 0; i < maxCircuits && i < highSpeed.length; i++) {
|
|
920
|
+
let val = highSpeed[i];
|
|
921
|
+
if (val > 0) arrCircuits.push(val);
|
|
922
|
+
else if (typeof pump !== 'undefined') pump.circuits.removeItemById(i);
|
|
923
|
+
}
|
|
924
|
+
if (arrCircuits.length > 0) {
|
|
925
|
+
let pump = sys.pumps.getDualSpeed(true);
|
|
926
|
+
for (let j = 1; j <= arrCircuits.length; j++) pump.circuits.getItemById(j, true).circuit = arrCircuits[j - 1];
|
|
927
|
+
}
|
|
928
|
+
else if (typeof pump !== 'undefined') sys.pumps.removeItemById(pump.id);
|
|
929
|
+
}
|
|
931
930
|
public static decodeRemote(remoteDataArray) {
|
|
932
931
|
if (sys.controllerType === ControllerType.EasyTouch) {
|
|
933
932
|
|
|
@@ -1072,43 +1071,46 @@ class Controller {
|
|
|
1072
1071
|
await sys.board.pumps.setPumpAsync(pData, false);
|
|
1073
1072
|
})
|
|
1074
1073
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1074
|
+
public static async decodePumpStatusAsync(id: number, slpump: SLPumpStatusData) {
|
|
1075
|
+
/* {
|
|
1076
|
+
pumpCircuits: [
|
|
1077
|
+
{ circuitId: 6,speed: 2000,isRPMs: true, },
|
|
1078
|
+
{ circuitId: 8, speed:2700,isRPMs: true, },
|
|
1079
|
+
{ circuitId: 2,speed: 2710,isRPMs: true, },
|
|
1080
|
+
{ circuitId: 2,speed:1000, isRPMs: true,},
|
|
1081
|
+
{ circuitId: 5,speed:2830, isRPMs: true,},
|
|
1082
|
+
{ circuitId: 0,speed: 30,isRPMs: false,},
|
|
1083
|
+
{ circuitId: 0,speed: 30,isRPMs: false,},
|
|
1084
|
+
{ circuitId: 0,speed: 30,isRPMs: false,},
|
|
1085
|
+
],
|
|
1086
|
+
pumpType: 4,
|
|
1087
|
+
isRunning: false,
|
|
1088
|
+
pumpWatts: 0,
|
|
1089
|
+
pumpRPMs: 0,
|
|
1090
|
+
pumpUnknown1: 0,
|
|
1091
|
+
pumpGPMs: 0,
|
|
1092
|
+
pumpUnknown2: 255,
|
|
1093
|
+
}
|
|
1094
|
+
*/
|
|
1095
|
+
// RKS: 05-07-23 - This process of getting the pump by its id is flawed. We need to pull this information by its address.
|
|
1096
|
+
//let pstate = state.pumps.getItemById(id);
|
|
1097
|
+
let pstate = state.pumps.find(x => x.address === 95 + id);
|
|
1098
|
+
if (typeof pstate !== 'undefined') {
|
|
1099
|
+
pstate.watts = slpump.pumpWatts;
|
|
1100
|
+
pstate.rpm = slpump.pumpRPMs;
|
|
1101
|
+
pstate.flow = slpump.pumpGPMs === 255 ? 0 : slpump.pumpGPMs;
|
|
1102
|
+
pstate.command = (pstate.rpm > 0 || pstate.watts > 0) ? 10 : 0;
|
|
1103
|
+
state.emitEquipmentChanges();
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
public static async decodeSchedules(slrecurring: SLScheduleData, slrunonce: SLScheduleData) {
|
|
1105
1107
|
/* reccuring schedules: [{"scheduleId":1,"circuitId":6,"startTime":"1800","stopTime":"0700","dayMask":127,"flags":0,"heatCmd":4,"heatSetPoint":70,"days":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},
|
|
1106
1108
|
|
|
1107
1109
|
{"scheduleId":4,"circuitId":2,"startTime":"1800","stopTime":"2300","dayMask":127,"flags":0,"heatCmd":0,"heatSetPoint":0,"days":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]},{"scheduleId":11,"circuitId":6,"startTime":"0800","stopTime":"1700","dayMask":127,"flags":0,"heatCmd":4,"heatSetPoint":70,"days":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]}]
|
|
1108
1110
|
|
|
1109
1111
|
Run once schedules: [{"scheduleId":12,"circuitId":6,"startTime":"0800","stopTime":"1100","dayMask":1,"flags":1,"heatCmd":4,"heatSetPoint":70,"days":["Mon"]},{"scheduleId":13,"circuitId":6,"startTime":"0800","stopTime":"1100","dayMask":1,"flags":1,"heatCmd":4,"heatSetPoint":70,"days":["Mon"]}] */
|
|
1110
1112
|
|
|
1111
|
-
for (let i = 0; i < slrecurring.length; i++) {
|
|
1113
|
+
for (let i = 0; i < slrecurring.data.length; i++) {
|
|
1112
1114
|
let slsched = slrecurring[i];
|
|
1113
1115
|
let data = {
|
|
1114
1116
|
id: slsched.scheduleId,
|
|
@@ -1126,13 +1128,14 @@ class Controller {
|
|
|
1126
1128
|
logger.error(`Error setting schedule ${slsched.scheduleId}. ${err.message}`);
|
|
1127
1129
|
}
|
|
1128
1130
|
}
|
|
1129
|
-
for (let i = 0; i < slrunonce.length; i++) {
|
|
1130
|
-
let slsched = slrunonce[i];
|
|
1131
|
+
for (let i = 0; i < slrunonce.data.length; i++) {
|
|
1132
|
+
let slsched = slrunonce.data[i];
|
|
1131
1133
|
let data = {
|
|
1132
1134
|
id: slsched.scheduleId,
|
|
1133
1135
|
circuit: slsched.circuitId,
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
+
// start and stop come in as military time string
|
|
1137
|
+
startTime: parseInt(slsched.startTime, 10),
|
|
1138
|
+
endTime: parseInt(slsched.stopTime, 10),
|
|
1136
1139
|
scheduleDays: slsched.dayMask,
|
|
1137
1140
|
changeHeatSetPoint: slsched.heatCmd > 0,
|
|
1138
1141
|
heatSetPoint: slsched.heatSetPoint,
|
|
@@ -1654,7 +1657,7 @@ export class SLController extends SLCommands {
|
|
|
1654
1657
|
public async setCustomName(idx: number, name: string){
|
|
1655
1658
|
try {
|
|
1656
1659
|
let ack = await this._unit.equipment.setCustomNameAsync(idx, name);
|
|
1657
|
-
logger.silly(`Screenlogic:set custom name result: ${ack}`);
|
|
1660
|
+
logger.silly(`Screenlogic:set custom name result: ${JSON.stringify(ack)}`);
|
|
1658
1661
|
} catch (error) {
|
|
1659
1662
|
return Promise.reject(new InvalidOperationError('Unable to set custom name.', error.message));
|
|
1660
1663
|
}
|
|
@@ -75,7 +75,7 @@ export class Message {
|
|
|
75
75
|
private static _messageId: number = 0;
|
|
76
76
|
public static get nextMessageId(): number {
|
|
77
77
|
let i = this._messageId < 80000 ? ++this._messageId : this._messageId = 0;
|
|
78
|
-
logger.debug(`Assigning message id ${i}`)
|
|
78
|
+
//logger.debug(`Assigning message id ${i}`)
|
|
79
79
|
return i; }
|
|
80
80
|
public portId = 0; // This will be the target or source port for the message. If this is from or to an Aux RS485 port the value will be > 0.
|
|
81
81
|
public timestamp: Date = new Date();
|
|
@@ -507,7 +507,7 @@ export class Inbound extends Message {
|
|
|
507
507
|
ndx = bytes.length - 5;
|
|
508
508
|
let arr = bytes.slice(0, ndx);
|
|
509
509
|
// Remove all but the last 4 bytes. This will result in nothing anyway.
|
|
510
|
-
logger.verbose(`Tossed Inbound Bytes ${arr} due to an unrecoverable collision.`);
|
|
510
|
+
logger.verbose(`[Port ${this.portId}] Tossed Inbound Bytes ${arr} due to an unrecoverable collision.`);
|
|
511
511
|
}
|
|
512
512
|
this.padding = [];
|
|
513
513
|
break;
|
|
@@ -1169,12 +1169,12 @@ export class Response extends OutboundCommon {
|
|
|
1169
1169
|
// Scenario 1. Request for pump status.
|
|
1170
1170
|
// Msg In: [165,0,16, 96, 7,15], [4,0,0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0,17,31], [1,95]
|
|
1171
1171
|
// Msg Out: [165,0,96, 16, 7, 0],[1,28]
|
|
1172
|
-
if (msgIn.source !== msgOut.dest || msgIn.dest !== msgOut.source) { return false; }
|
|
1172
|
+
if (msgIn.source !== msgOut.dest || (msgIn.dest !== msgOut.source && msgIn.dest != 16)) { return false; }
|
|
1173
1173
|
if (msgIn.action === 7 && msgOut.action === 7) { return true; }
|
|
1174
1174
|
return false;
|
|
1175
1175
|
default:
|
|
1176
1176
|
//Scenario 2, pump messages are mimics of each other but the dest/src are swapped
|
|
1177
|
-
if (msgIn.source !== msgOut.dest || msgIn.dest !== msgOut.source) { return false; }
|
|
1177
|
+
if (msgIn.source !== msgOut.dest || (msgIn.dest !== msgOut.source && msgIn.dest != 16)) { return false; }
|
|
1178
1178
|
// sub-case
|
|
1179
1179
|
// Msg In: [165,0,16, 96, 1, 2], [3,32],[1,59]
|
|
1180
1180
|
// Msg Out: [165,0,96,16, 1,4],[3,39, 3,32], [1,103]
|
|
@@ -80,6 +80,7 @@ export class ChlorinatorMessage {
|
|
|
80
80
|
if (isActive) {
|
|
81
81
|
let chlor = sys.chlorinators.getItemById(1, true);
|
|
82
82
|
let schlor = state.chlorinators.getItemById(1, true);
|
|
83
|
+
chlor.master = 0;
|
|
83
84
|
chlor.isActive = schlor.isActive = isActive;
|
|
84
85
|
if (!chlor.disabled) {
|
|
85
86
|
// RKS: We don't want these setpoints if our chem controller disabled the
|
|
@@ -87,10 +88,16 @@ export class ChlorinatorMessage {
|
|
|
87
88
|
schlor.spaSetpoint = chlor.spaSetpoint = msg.extractPayloadByte(0) >> 1;
|
|
88
89
|
schlor.poolSetpoint = chlor.poolSetpoint = msg.extractPayloadByte(1);
|
|
89
90
|
chlor.address = msg.dest;
|
|
90
|
-
schlor.body = chlor.body = sys.equipment.
|
|
91
|
+
schlor.body = chlor.body = sys.equipment.shared === true ? 32 : 0;
|
|
92
|
+
}
|
|
93
|
+
let name = msg.extractPayloadString(6, 16).trimEnd();
|
|
94
|
+
if (typeof chlor.name === 'undefined') schlor.name = chlor.name = name;
|
|
95
|
+
if (typeof chlor.model === 'undefined') {
|
|
96
|
+
chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(schlor.name.toLowerCase());
|
|
97
|
+
if (typeof chlor.model === 'undefined') {
|
|
98
|
+
if (name.startsWith('iChlor')) chlor.model = sys.board.valueMaps.chlorinatorModel.getValue('ichlor-ic30');
|
|
99
|
+
}
|
|
91
100
|
}
|
|
92
|
-
if (typeof chlor.name === 'undefined') schlor.name = chlor.name = msg.extractPayloadString(6, 16);
|
|
93
|
-
if (typeof chlor.model === 'undefined') chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(schlor.name.toLowerCase());
|
|
94
101
|
if (typeof chlor.type === 'undefined') chlor.type = schlor.type = 0;
|
|
95
102
|
schlor.saltLevel = msg.extractPayloadByte(3) * 50 || schlor.saltLevel;
|
|
96
103
|
schlor.status = msg.extractPayloadByte(4) & 0x007F; // Strip off the high bit. The chlorinator does not actually report this.;
|
|
@@ -329,6 +329,7 @@ export class ExternalMessage {
|
|
|
329
329
|
case 8: // Intellibrite
|
|
330
330
|
case 10: // Colorcascade
|
|
331
331
|
cstate.lightingTheme = circuit.lightingTheme;
|
|
332
|
+
if (!isOn) cstate.action = 0;
|
|
332
333
|
break;
|
|
333
334
|
case 9: // Dimmer
|
|
334
335
|
cstate.level = circuit.level;
|
|
@@ -354,7 +355,8 @@ export class ExternalMessage {
|
|
|
354
355
|
if (schedule.isActive) {
|
|
355
356
|
if (schedule.circuit > 0) { // Don't get the schedule state if we haven't determined the entire config for it yet.
|
|
356
357
|
let sstate = state.schedules.getItemById(scheduleId, schedule.isActive);
|
|
357
|
-
|
|
358
|
+
let isOn = ((byte & (1 << (j))) >> j) > 0;
|
|
359
|
+
sstate.isOn = isOn;
|
|
358
360
|
sstate.circuit = schedule.circuit;
|
|
359
361
|
sstate.endTime = schedule.endTime;
|
|
360
362
|
sstate.startDate = schedule.startDate;
|
|
@@ -365,6 +367,7 @@ export class ExternalMessage {
|
|
|
365
367
|
sstate.heatSource = schedule.heatSource;
|
|
366
368
|
sstate.startTimeType = schedule.startTimeType;
|
|
367
369
|
sstate.endTimeType = schedule.endTimeType;
|
|
370
|
+
sstate.startDate = schedule.startDate;
|
|
368
371
|
}
|
|
369
372
|
}
|
|
370
373
|
else
|
|
@@ -35,13 +35,14 @@ export class PumpMessage {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
public static processPumpConfig_IT(msg: Inbound) {
|
|
38
|
-
// packet 24/27/152/155 - Pump Config: IntelliTouch
|
|
38
|
+
// packet 24/27/152/155 - Pump Config: IntelliTouch. These will always be addressable pumps ds & ss are not included.
|
|
39
39
|
const pumpId = msg.extractPayloadByte(0);
|
|
40
40
|
let type = msg.extractPayloadByte(1); // Avoid setting this then setting it back if we are mapping to a different value.
|
|
41
41
|
let isActive = type !== 0 && pumpId <= sys.equipment.maxPumps;
|
|
42
42
|
// RKS: 04-14-21 - Only create the pump if it is available. If the pump was previously defined as another type
|
|
43
43
|
// then it will be removed and recreated.
|
|
44
|
-
|
|
44
|
+
// RKS: 05-06-23 - The original code did not search for the pump by its address. This is not correct.
|
|
45
|
+
let pump: Pump = sys.pumps.getPumpByAddress(95 + pumpId, isActive);
|
|
45
46
|
if(isActive) {
|
|
46
47
|
// Remap the combination pump types.
|
|
47
48
|
switch (type) {
|
|
@@ -59,8 +60,8 @@ export class PumpMessage {
|
|
|
59
60
|
break;
|
|
60
61
|
}
|
|
61
62
|
if (pump.type !== type) {
|
|
62
|
-
sys.pumps.
|
|
63
|
-
pump = sys.pumps.
|
|
63
|
+
sys.pumps.removePumpByAddress(95 + pumpId);
|
|
64
|
+
if (isActive) pump = sys.pumps.getPumpByAddress(95 + pumpId, isActive);
|
|
64
65
|
}
|
|
65
66
|
pump.address = pumpId + 95;
|
|
66
67
|
pump.master = 0;
|
|
@@ -308,7 +309,7 @@ export class PumpMessage {
|
|
|
308
309
|
// Sample Packet
|
|
309
310
|
// [255, 0, 255], [165, 33, 15, 16, 27, 46], [1, 128, 1, 2, 0, 1, 6, 2, 12, 4, 9, 11, 7, 6, 7, 128, 8, 132, 3, 15, 5, 3, 234, 128, 46, 108, 58, 2, 232, 220, 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [8, 5]
|
|
310
311
|
const pumpId = msg.extractPayloadByte(0);
|
|
311
|
-
const pump = sys.pumps.
|
|
312
|
+
const pump = sys.pumps.getPumpByAddress(95 + pumpId);
|
|
312
313
|
// [1, 128, 0, 2, 0, 6, 5, 1, 5, 158, 9, 2, 10, 0, 3, 0, 3, 0, 3, 0, 3, 3, 120, 20, 146, 240, 232, 232, 232, 232, 232]
|
|
313
314
|
// byte | val |
|
|
314
315
|
// 0 | 1 | PumpId = 1
|
|
@@ -359,7 +360,7 @@ export class PumpMessage {
|
|
|
359
360
|
// Sample Packet
|
|
360
361
|
// [255, 0, 255], [165, 33, 15, 16, 27, 46], [2, 6, 15, 2, 0, 1, 29, 11, 35, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 30, 55, 5, 10, 60, 5, 1, 50, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [3, 41]
|
|
361
362
|
const pumpId = msg.extractPayloadByte(0);
|
|
362
|
-
const pump = sys.pumps.
|
|
363
|
+
const pump = sys.pumps.getPumpByAddress(95 + pumpId);
|
|
363
364
|
if (typeof pump.model === 'undefined') pump.model = 0;
|
|
364
365
|
for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
|
|
365
366
|
let _circuit = msg.extractPayloadByte(circuitId * 2 + 3);
|
|
@@ -395,7 +396,7 @@ export class PumpMessage {
|
|
|
395
396
|
//[255, 0, 255][165, 33, 15, 16, 27, 46][2, 64, 0, 0, 2, 1, 33, 2, 4, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [2, 94]
|
|
396
397
|
//[255, 0, 255][165, 1, 15, 16, 24, 31][1, 64, 0, 0, 0, 6, 5, 2, 8, 1, 11, 7, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 220, 152, 184, 122, 0, 0, 0, 0, 0][4, 24]
|
|
397
398
|
const pumpId = msg.extractPayloadByte(0);
|
|
398
|
-
const pump = sys.pumps.
|
|
399
|
+
const pump = sys.pumps.getPumpByAddress(95 + pumpId);
|
|
399
400
|
if (typeof pump.model === 'undefined') pump.model = 0;
|
|
400
401
|
for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
|
|
401
402
|
let _circuit = msg.extractPayloadByte(circuitId * 2 + 3);
|
|
@@ -158,52 +158,57 @@ export class RemoteMessage {
|
|
|
158
158
|
// RKS: 12-1-22 This message is a message that has been mis-interpreted for quite some time
|
|
159
159
|
// it appears that early versions of EasyTouch did not include the ability to add more than one pump and only 4 potential
|
|
160
160
|
// circuits could be set. This comes as 3 bytes per pump setting. If there are no circuits assigned then the pump is not installed.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
{
|
|
176
|
-
id: sys.pumps.getNextEquipmentId(),
|
|
177
|
-
master: 0,
|
|
178
|
-
address: 96,
|
|
179
|
-
type: 128,
|
|
180
|
-
name: `Pump${sys.pumps.length + 1}`,
|
|
181
|
-
flowStepSize: 1,
|
|
182
|
-
primingTime: 0,
|
|
183
|
-
primingSpeed: 450,
|
|
184
|
-
minSpeed: 450,
|
|
185
|
-
maxSpeed: 3450
|
|
161
|
+
// RKS: 05-13-23 - As it turns out ScreenLogic always asks for this bullshit and all panels return it. However, if the firmware version is
|
|
162
|
+
// greater than 1.6 it should be ignored.
|
|
163
|
+
let fwVersion = parseFloat(sys.equipment.controllerFirmware);
|
|
164
|
+
if (!isNaN(fwVersion) && fwVersion <= 1.6) {
|
|
165
|
+
let isActive = (msg.extractPayloadByte(1, 0) + msg.extractPayloadByte(4, 0) + msg.extractPayloadByte(7, 0) + msg.extractPayloadByte(10, 0)) > 0;
|
|
166
|
+
let pump = sys.pumps.find(x => x.address === 96 && (x.master || 0) === 0);
|
|
167
|
+
if (!isActive) {
|
|
168
|
+
if (typeof pump !== 'undefined') {
|
|
169
|
+
let spump = state.pumps.getItemById(pump.id, false);
|
|
170
|
+
spump.address = 96;
|
|
171
|
+
spump.isActive = false;
|
|
172
|
+
sys.pumps.removeItemById(pump.id);
|
|
173
|
+
state.pumps.removeItemById(pump.id);
|
|
174
|
+
spump.emitEquipmentChange();
|
|
186
175
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
if (typeof pump === 'undefined') pump = sys.pumps.getPumpByAddress(96, true,
|
|
179
|
+
{
|
|
180
|
+
id: sys.pumps.getNextEquipmentId(),
|
|
181
|
+
master: 0,
|
|
182
|
+
address: 96,
|
|
183
|
+
type: 128,
|
|
184
|
+
name: `Pump${sys.pumps.length + 1}`,
|
|
185
|
+
flowStepSize: 1,
|
|
186
|
+
primingTime: 0,
|
|
187
|
+
primingSpeed: 450,
|
|
188
|
+
minSpeed: 450,
|
|
189
|
+
maxSpeed: 3450
|
|
190
|
+
}
|
|
191
|
+
);
|
|
192
|
+
let spump = state.pumps.getItemById(pump.id, true);
|
|
193
|
+
spump.name = pump.name;
|
|
194
|
+
spump.address = pump.address = 96;
|
|
195
|
+
spump.type = pump.type = 128;
|
|
196
|
+
pump.isActive = spump.isActive = true;
|
|
197
|
+
// Set the circuits on the pump.
|
|
198
|
+
let cid = 0;
|
|
199
|
+
for (let i = 1; i <= 10; i += 3) {
|
|
200
|
+
let circuitId = msg.extractPayloadByte(i, 0);
|
|
201
|
+
if (circuitId > 0) {
|
|
202
|
+
cid++;
|
|
203
|
+
let circ = pump.circuits.getItemById(cid, true);
|
|
204
|
+
circ.circuit = circuitId;
|
|
205
|
+
circ.speed = (msg.extractPayloadByte(i + 1, 0) * 256) + msg.extractPayloadByte(i + 2, 0);
|
|
206
|
+
circ.units = 0;
|
|
207
|
+
}
|
|
203
208
|
}
|
|
209
|
+
if (cid < 4) for (let i = 4; i > cid && i > 0; i--) pump.circuits.removeItemById(i);
|
|
210
|
+
spump.emitEquipmentChange();
|
|
204
211
|
}
|
|
205
|
-
if (cid < 4) for (let i = 4; i > cid && i > 0; i--) pump.circuits.removeItemById(i);
|
|
206
|
-
spump.emitEquipmentChange();
|
|
207
212
|
}
|
|
208
213
|
}
|
|
209
214
|
private static processRemoteType(msg: Inbound) {
|
|
@@ -67,9 +67,15 @@ export class ChlorinatorStateMessage {
|
|
|
67
67
|
// I n t e l l i c h l o r - - 4 0
|
|
68
68
|
//[16, 2, 0, 3][0, 73, 110, 116, 101, 108, 108, 105, 99, 104, 108, 111, 114, 45, 45, 52, 48][188, 16, 3]
|
|
69
69
|
// This is the model number of the chlorinator and the address is actually the second byte.
|
|
70
|
-
let name = msg.extractPayloadString(1, 16);
|
|
70
|
+
let name = msg.extractPayloadString(1, 16).trimEnd();
|
|
71
71
|
if (typeof chlor.name === 'undefined' || chlor.name === '') chlor.name = cstate.name = name;
|
|
72
|
-
if (typeof chlor.model === 'undefined' || chlor.model === 0)
|
|
72
|
+
if (typeof chlor.model === 'undefined' || chlor.model === 0) {
|
|
73
|
+
chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
|
|
74
|
+
// With iChlor it does not report the model.
|
|
75
|
+
if (typeof chlor.model === 'undefined') {
|
|
76
|
+
if (name.startsWith('iChlor')) chlor.model = sys.board.valueMaps.chlorinatorModel.getValue('ichlor-ic30');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
73
79
|
cstate.isActive = chlor.isActive;
|
|
74
80
|
state.emitEquipmentChanges();
|
|
75
81
|
break;
|
|
@@ -121,8 +121,12 @@ export class EquipmentStateMessage {
|
|
|
121
121
|
// Start over because we didn't have communication before but we now do. This will fall into the if
|
|
122
122
|
// below so that it goes through the intialization process. In this case we didn't see an OCP when we started
|
|
123
123
|
// but there clearly is one now.
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
(async () => {
|
|
125
|
+
await sys.board.closeAsync();
|
|
126
|
+
logger.info(`Closed ${sys.controllerType} board`);
|
|
127
|
+
sys.controllerType = ControllerType.Unknown;
|
|
128
|
+
state.status = 0;
|
|
129
|
+
})();
|
|
126
130
|
}
|
|
127
131
|
if (!state.isInitialized) {
|
|
128
132
|
msg.isProcessed = true;
|
|
@@ -592,9 +596,9 @@ export class EquipmentStateMessage {
|
|
|
592
596
|
case 204: // IntelliCenter only.
|
|
593
597
|
state.batteryVoltage = msg.extractPayloadByte(2) / 50;
|
|
594
598
|
state.comms.keepAlives = msg.extractPayloadInt(4);
|
|
595
|
-
state.time.date = msg.extractPayloadByte(6);
|
|
596
|
-
state.time.month = msg.extractPayloadByte(7);
|
|
597
599
|
state.time.year = msg.extractPayloadByte(8);
|
|
600
|
+
state.time.month = msg.extractPayloadByte(7);
|
|
601
|
+
state.time.date = msg.extractPayloadByte(6);
|
|
598
602
|
sys.equipment.controllerFirmware = (msg.extractPayloadByte(42) + (msg.extractPayloadByte(43) / 1000)).toString();
|
|
599
603
|
if (sys.chlorinators.length > 0) {
|
|
600
604
|
if (msg.extractPayloadByte(37, 255) !== 255) {
|
|
@@ -637,6 +641,7 @@ export class EquipmentStateMessage {
|
|
|
637
641
|
scover2.name = cover2.name;
|
|
638
642
|
state.temps.bodies.getItemById(cover2.body + 1).isCovered = scover2.isClosed = (msg.extractPayloadByte(30) & 0x0002) > 0;
|
|
639
643
|
}
|
|
644
|
+
sys.board.schedules.syncScheduleStates();
|
|
640
645
|
msg.isProcessed = true;
|
|
641
646
|
state.emitEquipmentChanges();
|
|
642
647
|
break;
|
|
@@ -62,7 +62,7 @@ export class NixieEquipment {
|
|
|
62
62
|
public async closeAsync() {
|
|
63
63
|
try {
|
|
64
64
|
}
|
|
65
|
-
catch (err) { logger.error(err); }
|
|
65
|
+
catch (err) { logger.error(`Error closing Nixie Equipment: ${err.message}`); }
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
export class NixieChildEquipment extends NixieEquipment {
|
|
@@ -79,7 +79,7 @@ export class NixieBody extends NixieEquipment {
|
|
|
79
79
|
try {
|
|
80
80
|
// Here we go we need to set the valve state.
|
|
81
81
|
if (bstate.isOn !== isOn) {
|
|
82
|
-
logger.info(`Nixie: Set Body ${bstate.id}-${bstate.name} to ${isOn}`);
|
|
82
|
+
logger.info(`Nixie: Set Body ${bstate.id}-${bstate.name} to ${isOn}`);
|
|
83
83
|
}
|
|
84
84
|
bstate.isOn = isOn;
|
|
85
85
|
} catch (err) { logger.error(`Nixie Error setting body state ${bstate.id}-${bstate.name}: ${err.message}`); }
|