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.
Files changed (47) hide show
  1. package/.docker/Dockerfile.armv6 +29 -0
  2. package/.docker/Dockerfile.armv7 +29 -0
  3. package/.docker/Dockerfile.linux +62 -0
  4. package/.docker/Dockerfile.windows +43 -0
  5. package/.docker/docker-compose.yml +47 -0
  6. package/.docker/ecosystem.config.js +35 -0
  7. package/.github/workflows/docker-publish-njsPC-linux.yml +81 -0
  8. package/.github/workflows/docker-publish-njsPC-windows.yml +41 -0
  9. package/Dockerfile +4 -3
  10. package/README.md +4 -1
  11. package/config/Config.ts +1 -1
  12. package/controller/Constants.ts +164 -67
  13. package/controller/Equipment.ts +79 -18
  14. package/controller/Lockouts.ts +15 -0
  15. package/controller/State.ts +280 -7
  16. package/controller/boards/EasyTouchBoard.ts +226 -102
  17. package/controller/boards/IntelliCenterBoard.ts +67 -18
  18. package/controller/boards/IntelliTouchBoard.ts +2 -4
  19. package/controller/boards/NixieBoard.ts +84 -27
  20. package/controller/boards/SunTouchBoard.ts +8 -2
  21. package/controller/boards/SystemBoard.ts +3259 -3242
  22. package/controller/comms/ScreenLogic.ts +60 -57
  23. package/controller/comms/messages/Messages.ts +4 -4
  24. package/controller/comms/messages/config/ChlorinatorMessage.ts +10 -3
  25. package/controller/comms/messages/config/ExternalMessage.ts +4 -1
  26. package/controller/comms/messages/config/PumpMessage.ts +8 -7
  27. package/controller/comms/messages/config/RemoteMessage.ts +48 -43
  28. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -2
  29. package/controller/comms/messages/status/EquipmentStateMessage.ts +9 -4
  30. package/controller/nixie/NixieEquipment.ts +1 -1
  31. package/controller/nixie/bodies/Body.ts +1 -1
  32. package/controller/nixie/chemistry/ChemController.ts +37 -28
  33. package/controller/nixie/circuits/Circuit.ts +36 -0
  34. package/controller/nixie/heaters/Heater.ts +24 -5
  35. package/controller/nixie/pumps/Pump.ts +155 -97
  36. package/controller/nixie/schedules/Schedule.ts +207 -126
  37. package/defaultConfig.json +4 -3
  38. package/logger/DataLogger.ts +7 -7
  39. package/package.json +3 -3
  40. package/sendSocket.js +32 -0
  41. package/web/Server.ts +17 -11
  42. package/web/bindings/homeassistant.json +2 -2
  43. package/web/interfaces/mqttInterface.ts +18 -18
  44. package/web/services/config/Config.ts +34 -1
  45. package/web/services/state/State.ts +10 -3
  46. package/web/services/state/StateSocket.ts +7 -3
  47. 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 { SLChemData, RemoteLogin, UnitConnection, FindUnits, SLEquipmentStateData, SLIntellichlorData, SLPumpStatusData, SLScheduleData, SLSystemTimeData, HeatModes, SLControllerConfigData, SLEquipmentConfigurationData, HeaterConfig, Valves } from 'node-screenlogic';
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.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: string[]) {
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
- public static decodeHighSpeed(highSpeed: number[]) {
917
- let maxCircuits = sys.controllerType === ControllerType.IntelliTouch ? 8 : 4;
918
- let arrCircuits = [];
919
- let pump = sys.pumps.getDualSpeed(true);
920
- for (let i = 0; i < maxCircuits && i < highSpeed.length; i++) {
921
- let val = highSpeed[i];
922
- if (val > 0) arrCircuits.push(val);
923
- else pump.circuits.removeItemById(i);
924
- }
925
- if (arrCircuits.length > 0) {
926
- let pump = sys.pumps.getDualSpeed(true);
927
- for (let j = 1; j <= arrCircuits.length; j++) pump.circuits.getItemById(j, true).circuit = arrCircuits[j - 1];
928
- }
929
- else sys.pumps.removeItemById(10);
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
- public static async decodePumpStatusAsync(id: number, slpump: SLPumpStatusData) {
1076
- /* {
1077
- pumpCircuits: [
1078
- { circuitId: 6,speed: 2000,isRPMs: true, },
1079
- { circuitId: 8, speed:2700,isRPMs: true, },
1080
- { circuitId: 2,speed: 2710,isRPMs: true, },
1081
- { circuitId: 2,speed:1000, isRPMs: true,},
1082
- { circuitId: 5,speed:2830, isRPMs: true,},
1083
- { circuitId: 0,speed: 30,isRPMs: false,},
1084
- { circuitId: 0,speed: 30,isRPMs: false,},
1085
- { circuitId: 0,speed: 30,isRPMs: false,},
1086
- ],
1087
- pumpType: 4,
1088
- isRunning: false,
1089
- pumpWatts: 0,
1090
- pumpRPMs: 0,
1091
- pumpUnknown1: 0,
1092
- pumpGPMs: 0,
1093
- pumpUnknown2: 255,
1094
- }
1095
- */
1096
-
1097
- let pstate = state.pumps.getItemById(id);
1098
- pstate.watts = slpump.pumpWatts;
1099
- pstate.rpm = slpump.pumpRPMs;
1100
- pstate.flow = slpump.pumpGPMs === 255 ? 0 : slpump.pumpGPMs;
1101
- pstate.command = (pstate.rpm > 0 || pstate.watts > 0) ? 10 : 0;
1102
- state.emitEquipmentChanges();
1103
- }
1104
- public static async decodeSchedules(slrecurring: SLScheduleData[], slrunonce: SLScheduleData[]) {
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
- startTime: Math.floor(slsched.startTime / 100) * 60 + slsched.startTime % 100,
1135
- endTime: Math.floor(slsched.stopTime / 100) * 60 + slsched.stopTime % 100,
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.maxBodies >= 1 || sys.equipment.shared === true ? 32 : 0;
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
- sstate.isOn = ((byte & (1 << (j))) >> j) > 0;
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
- let pump: Pump = sys.pumps.getItemById(pumpId, isActive);
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.removeItemById(pumpId);
63
- pump = sys.pumps.getItemById(pumpId, isActive);
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.getItemById(pumpId);
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.getItemById(pumpId);
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.getItemById(pumpId);
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
- let isActive = (msg.extractPayloadByte(1, 0) + msg.extractPayloadByte(4, 0) + msg.extractPayloadByte(7, 0) + msg.extractPayloadByte(10, 0)) > 0;
162
- let pump = sys.pumps.find(x => x.address === 96 && (x.master || 0) === 0);
163
- if (!isActive) {
164
- if (typeof pump !== 'undefined') {
165
- let spump = state.pumps.getItemById(pump.id, false);
166
- spump.address = 96;
167
- spump.isActive = false;
168
- sys.pumps.removeItemById(pump.id);
169
- state.pumps.removeItemById(pump.id);
170
- spump.emitEquipmentChange();
171
- }
172
- }
173
- else {
174
- if (typeof pump === 'undefined') pump = sys.pumps.getPumpByAddress(96, true,
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
- let spump = state.pumps.getItemById(pump.id, true);
189
- spump.name = pump.name;
190
- spump.address = pump.address = 96;
191
- spump.type = pump.type = 128;
192
- pump.isActive = spump.isActive = true;
193
- // Set the circuits on the pump.
194
- let cid = 0;
195
- for (let i = 1; i <= 10; i += 3) {
196
- let circuitId = msg.extractPayloadByte(i, 0);
197
- if (circuitId > 0) {
198
- cid++;
199
- let circ = pump.circuits.getItemById(cid, true);
200
- circ.circuit = circuitId;
201
- circ.speed = (msg.extractPayloadByte(i + 1, 0) * 256) + msg.extractPayloadByte(i + 2, 0);
202
- circ.units = 0;
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) chlor.model = sys.board.valueMaps.chlorinatorModel.getValue(name.toLowerCase());
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
- sys.controllerType = ControllerType.Unknown;
125
- state.status = 0;
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}`); }