nodejs-poolcontroller 7.3.0 → 7.6.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.
Files changed (60) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/Changelog +23 -0
  3. package/README.md +5 -5
  4. package/app.ts +2 -0
  5. package/config/Config.ts +3 -0
  6. package/config/VersionCheck.ts +8 -4
  7. package/controller/Constants.ts +88 -0
  8. package/controller/Equipment.ts +246 -66
  9. package/controller/Errors.ts +24 -1
  10. package/controller/Lockouts.ts +423 -0
  11. package/controller/State.ts +314 -54
  12. package/controller/boards/EasyTouchBoard.ts +107 -59
  13. package/controller/boards/IntelliCenterBoard.ts +186 -125
  14. package/controller/boards/IntelliTouchBoard.ts +104 -30
  15. package/controller/boards/NixieBoard.ts +721 -159
  16. package/controller/boards/SystemBoard.ts +2370 -1108
  17. package/controller/comms/Comms.ts +85 -10
  18. package/controller/comms/messages/Messages.ts +10 -4
  19. package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
  20. package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
  21. package/controller/comms/messages/config/CoverMessage.ts +1 -0
  22. package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
  23. package/controller/comms/messages/config/ExternalMessage.ts +44 -26
  24. package/controller/comms/messages/config/FeatureMessage.ts +8 -1
  25. package/controller/comms/messages/config/GeneralMessage.ts +8 -0
  26. package/controller/comms/messages/config/HeaterMessage.ts +15 -9
  27. package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
  28. package/controller/comms/messages/config/OptionsMessage.ts +13 -1
  29. package/controller/comms/messages/config/PumpMessage.ts +4 -20
  30. package/controller/comms/messages/config/RemoteMessage.ts +4 -0
  31. package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
  32. package/controller/comms/messages/config/SecurityMessage.ts +1 -0
  33. package/controller/comms/messages/config/ValveMessage.ts +13 -3
  34. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
  35. package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
  36. package/controller/comms/messages/status/HeaterStateMessage.ts +42 -9
  37. package/controller/comms/messages/status/IntelliChemStateMessage.ts +37 -26
  38. package/controller/nixie/Nixie.ts +18 -16
  39. package/controller/nixie/bodies/Body.ts +4 -1
  40. package/controller/nixie/chemistry/ChemController.ts +80 -77
  41. package/controller/nixie/chemistry/Chlorinator.ts +9 -8
  42. package/controller/nixie/circuits/Circuit.ts +55 -6
  43. package/controller/nixie/heaters/Heater.ts +192 -32
  44. package/controller/nixie/pumps/Pump.ts +146 -84
  45. package/controller/nixie/schedules/Schedule.ts +3 -2
  46. package/controller/nixie/valves/Valve.ts +1 -1
  47. package/defaultConfig.json +32 -1
  48. package/issue_template.md +1 -1
  49. package/logger/DataLogger.ts +37 -22
  50. package/package.json +20 -18
  51. package/web/Server.ts +520 -29
  52. package/web/bindings/influxDB.json +96 -8
  53. package/web/bindings/mqtt.json +151 -40
  54. package/web/bindings/mqttAlt.json +114 -4
  55. package/web/interfaces/httpInterface.ts +2 -0
  56. package/web/interfaces/influxInterface.ts +36 -19
  57. package/web/interfaces/mqttInterface.ts +14 -3
  58. package/web/services/config/Config.ts +171 -44
  59. package/web/services/state/State.ts +49 -5
  60. package/web/services/state/StateSocket.ts +18 -1
@@ -162,10 +162,10 @@ export class EasyTouchBoard extends SystemBoard {
162
162
  // We need this because there is a no-pump thing in *Touch.
163
163
  // RKS: 05-04-21 The no-pump item was removed as this was only required for -webClient. deletePumpAsync should remove the pump from operation.
164
164
  this.valueMaps.pumpTypes = new byteValueMap([
165
- [1, { name: 'vf', desc: 'Intelliflo VF', minFlow: 15, maxFlow: 130, flowStepSize: 1, maxCircuits: 8, hasAddress: true }],
165
+ [1, { name: 'vf', desc: 'Intelliflo VF', maxPrimingTime: 6, minFlow: 15, maxFlow: 130, flowStepSize: 1, maxCircuits: 8, hasAddress: true }],
166
166
  [64, { name: 'vsf', desc: 'Intelliflo VSF', minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, minFlow: 15, maxFlow: 130, flowStepSize: 1, maxCircuits: 8, hasAddress: true }],
167
167
  [65, { name: 'ds', desc: 'Two-Speed', maxCircuits: 40, hasAddress: false, hasBody: true }],
168
- [128, { name: 'vs', desc: 'Intelliflo VS', maxPrimingTime: 6, minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, maxCircuits: 8, hasAddress: true }],
168
+ [128, { name: 'vs', desc: 'Intelliflo VS', maxPrimingTime: 10, minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, maxCircuits: 8, hasAddress: true }],
169
169
  [169, { name: 'vssvrs', desc: 'IntelliFlo VS+SVRS', maxPrimingTime: 6, minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, maxCircuits: 8, hasAddress: true }],
170
170
  [257, { name: 'ss', desc: 'Single Speed', maxCircuits: 0, hasAddress: false, hasBody: true, equipmentMaster: 1 }],
171
171
  [256, { name: 'sf', desc: 'SuperFlo VS', hasAddress: false, maxCircuits: 8, maxRelays: 4, equipmentMaster: 1 }]
@@ -306,6 +306,14 @@ export class EasyTouchBoard extends SystemBoard {
306
306
  return extend(true, {}, { val: byte, desc: customName.name, name: customName.name });
307
307
  }
308
308
  };
309
+ this.valueMaps.panelModes = new byteValueMap([
310
+ [0, { val: 0, name: 'auto', desc: 'Auto' }],
311
+ [1, { val: 1, name: 'service', desc: 'Service' }],
312
+ [8, { val: 8, name: 'freeze', desc: 'Freeze' }],
313
+ [128, { val: 128, name: 'timeout', desc: 'Timeout' }],
314
+ [129, { val: 129, name: 'service-timeout', desc: 'Service/Timeout' }],
315
+ [255, { name: 'error', desc: 'System Error' }]
316
+ ]);
309
317
  this.valueMaps.expansionBoards = new byteValueMap([
310
318
  [0, { name: 'ET28', part: 'ET2-8', desc: 'EasyTouch2 8', circuits: 8, shared: true }],
311
319
  [1, { name: 'ET28P', part: 'ET2-8P', desc: 'EasyTouch2 8P', circuits: 8, shared: false }],
@@ -337,15 +345,16 @@ export class EasyTouchBoard extends SystemBoard {
337
345
  // Add in the bodies for the configuration. These need to be set.
338
346
  let cbody = sys.bodies.getItemById(i, true);
339
347
  let tbody = state.temps.bodies.getItemById(i, true);
340
- // If the body doesn't represent a spa then we set the type.
341
- tbody.type = cbody.type = i > 1 && !sys.equipment.shared ? 1 : 0;
342
348
  cbody.isActive = true;
349
+ // If the body doesn't represent a spa then we set the type.
350
+ // RSG - 10-5-21: If a single body IT (i5+3s/i9+3s) the bodies are the same; set to pool
351
+ tbody.type = cbody.type = i > 1 && !sys.equipment.shared && sys.equipment.intakeReturnValves ? 1 : 0;
343
352
  if (typeof cbody.name === 'undefined') {
344
353
  let bt = sys.board.valueMaps.bodyTypes.transform(cbody.type);
345
354
  tbody.name = cbody.name = bt.name;
346
355
  }
347
356
  }
348
- if (!sys.equipment.shared && !sys.equipment.dual) {
357
+ if (!sys.equipment.shared && !sys.equipment.dual && state.equipment.controllerType !== 'intellitouch') {
349
358
  sys.bodies.removeItemById(2);
350
359
  state.temps.bodies.removeItemById(2);
351
360
  }
@@ -374,7 +383,7 @@ export class EasyTouchBoard extends SystemBoard {
374
383
  let md = mod.get();
375
384
  eq.maxBodies = md.bodies = typeof mt.bodies !== 'undefined' ? mt.bodies : mt.shared ? 2 : 1;
376
385
  eq.maxCircuits = md.circuits = typeof mt.circuits !== 'undefined' ? mt.circuits : 8;
377
- eq.maxFeatures = md.features = typeof mt.features !== 'undefined' ? mt.features : 10
386
+ eq.maxFeatures = md.features = typeof mt.features !== 'undefined' ? mt.features : 8;
378
387
  eq.maxValves = md.valves = typeof mt.valves !== 'undefined' ? mt.valves : mt.shared ? 4 : 2;
379
388
  eq.maxPumps = md.maxPumps = typeof mt.pumps !== 'undefined' ? mt.pumps : 2;
380
389
  eq.shared = mt.shared;
@@ -382,6 +391,7 @@ export class EasyTouchBoard extends SystemBoard {
382
391
  eq.maxChlorinators = md.chlorinators = 1;
383
392
  eq.maxChemControllers = md.chemControllers = 1;
384
393
  eq.maxCustomNames = 10;
394
+ eq.intakeReturnValves = md.intakeReturnValves = typeof mt.intakeReturnValves !== 'undefined' ? mt.intakeReturnValves : false;
385
395
  // Calculate out the invalid ids.
386
396
  sys.board.equipmentIds.invalidIds.set([]);
387
397
  if (!eq.shared) sys.board.equipmentIds.invalidIds.merge([1]);
@@ -454,8 +464,8 @@ export class TouchConfigRequest extends ConfigRequest {
454
464
  if (typeof items !== 'undefined') this.items.push(...items);
455
465
  this.oncomplete = oncomplete;
456
466
  }
457
- public category: TouchConfigCategories;
458
- public setcategory: GetTouchConfigCategories;
467
+ declare category: TouchConfigCategories;
468
+ declare setcategory: GetTouchConfigCategories;
459
469
  }
460
470
  export class TouchConfigQueue extends ConfigQueue {
461
471
  //protected _configQueueTimer: NodeJS.Timeout;
@@ -1243,18 +1253,20 @@ class TouchBodyCommands extends BodyCommands {
1243
1253
  }
1244
1254
  }
1245
1255
  export class TouchCircuitCommands extends CircuitCommands {
1246
- public getLightThemes(type?: number): any[] {
1247
- let themes = sys.board.valueMaps.lightThemes.toArray();
1248
- if (typeof type === 'undefined') return themes;
1249
- switch (type) {
1250
- case 8: // Magicstream
1251
- return themes.filter(theme => theme.type === 'magicstream');
1252
- case 16: // Intellibrite
1253
- return themes.filter(theme => theme.type === 'intellibrite');
1254
- default:
1255
- return [];
1256
- }
1257
- }
1256
+ // RKS: 12-01-2021 This has been deprecated we are now driving this through metadata on the valuemaps. This allows
1257
+ // for multiple types of standardized on/off sequences with nixie controllers.
1258
+ //public getLightThemes(type?: number): any[] {
1259
+ // let themes = sys.board.valueMaps.lightThemes.toArray();
1260
+ // if (typeof type === 'undefined') return themes;
1261
+ // switch (type) {
1262
+ // case 8: // Magicstream
1263
+ // return themes.filter(theme => theme.types.includes('magicstream'));
1264
+ // case 16: // Intellibrite
1265
+ // return themes.filter(theme => theme.types.includes('intellibrite'));
1266
+ // default:
1267
+ // return [];
1268
+ // }
1269
+ //}
1258
1270
  public async setCircuitAsync(data: any): Promise<ICircuit> {
1259
1271
  try {
1260
1272
  // example [255,0,255][165,33,16,34,139,5][17,14,209,0,0][2,120]
@@ -1262,6 +1274,7 @@ export class TouchCircuitCommands extends CircuitCommands {
1262
1274
  // response: [255,0,255][165,33,34,16,1,1][139][1,133]
1263
1275
  let id = parseInt(data.id, 10);
1264
1276
  if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError('Circuit Id is invalid', data.id, 'Feature'));
1277
+ if (id >= 255 || data.master === 1) return super.setCircuitAsync(data);
1265
1278
  let circuit = sys.circuits.getInterfaceById(id);
1266
1279
  // Alright check to see if we are adding a nixie circuit.
1267
1280
  if (id === -1 || circuit.master !== 0) {
@@ -1291,6 +1304,8 @@ export class TouchCircuitCommands extends CircuitCommands {
1291
1304
  circuit.type = cstate.type = typeByte;
1292
1305
  circuit.eggTimer = typeof data.eggTimer !== 'undefined' ? parseInt(data.eggTimer, 10) : circuit.eggTimer || 720;
1293
1306
  circuit.dontStop = (typeof data.dontStop !== 'undefined') ? utils.makeBool(data.dontStop) : circuit.eggTimer === 1620;
1307
+ cstate.isActive = circuit.isActive = true;
1308
+ circuit.master = 0;
1294
1309
  let eggTimer = sys.eggTimers.find(elem => elem.circuit === parseInt(data.id, 10));
1295
1310
  try {
1296
1311
  if (circuit.eggTimer === 720) {
@@ -1323,7 +1338,7 @@ export class TouchCircuitCommands extends CircuitCommands {
1323
1338
  data.functionId = sys.board.valueMaps.circuitFunctions.getValue('notused');
1324
1339
  return this.setCircuitAsync(data);
1325
1340
  }
1326
- public async setCircuitStateAsync(id: number, val: boolean): Promise<ICircuitState> {
1341
+ public async setCircuitStateAsync(id: number, val: boolean, ignoreDelays?: boolean): Promise<ICircuitState> {
1327
1342
  if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError('Circuit or Feature id not valid', id, 'Circuit'));
1328
1343
  let c = sys.circuits.getInterfaceById(id);
1329
1344
  if (c.master !== 0) return await super.setCircuitStateAsync(id, val);
@@ -1656,28 +1671,26 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
1656
1671
  public async setChlorAsync(obj: any): Promise<ChlorinatorState> {
1657
1672
  let id = parseInt(obj.id, 10);
1658
1673
  let isAdd = false;
1659
- let isVirtual = false;
1660
- if (id <= 0 || isNaN(id)) id = 1;
1661
1674
  let chlor = sys.chlorinators.getItemById(id);
1662
- if (id < 0 || isNaN(id)) {
1675
+ if (id <= 0 || isNaN(id)) {
1663
1676
  isAdd = true;
1664
- chlor.master = utils.makeBool(obj.isVirtual) ? 0 : 1;
1677
+ chlor.master = utils.makeBool(obj.master) ? 1 : 0;
1665
1678
  // Calculate an id for the chlorinator. The messed up part is that if a chlorinator is not attached to the OCP, its address
1666
1679
  // cannot be set by the MUX. This will have to wait.
1667
1680
  id = 1;
1668
- }
1669
- //let chlor = extend(true, {}, sys.chlorinators.getItemById(id).get(), obj);
1670
- // If this is a virtual chlorinator then go to the base class and handle it from there.
1681
+ }
1682
+ // If this is a Nixie chlorinator then go to the base class and handle it from there.
1683
+ if (chlor.master === 1) return super.setChlorAsync(obj);
1671
1684
  // RKS: I am not even sure this can be done with Touch as the master on the RS485 bus.
1672
- if (chlor.master === 1 || isVirtual) return super.setChlorAsync(obj);
1685
+ if (typeof chlor.master === 'undefined') chlor.master = 0;
1673
1686
  let name = obj.name || chlor.name || 'IntelliChlor' + id;
1674
- let poolSetpoint = parseInt(obj.poolSetpoint, 10);
1675
- let spaSetpoint = parseInt(obj.spaSetpoint, 10);
1676
1687
  let superChlorHours = parseInt(obj.superChlorHours, 10);
1677
1688
  if (typeof obj.superChlorinate !== 'undefined') obj.superChlor = utils.makeBool(obj.superChlorinate);
1678
1689
  let superChlorinate = typeof obj.superChlor === 'undefined' ? undefined : utils.makeBool(obj.superChlor);
1679
- let disabled = typeof obj.disabled !== 'undefined' ? utils.makeBool(obj.disabled) : chlor.disabled;
1680
1690
  let isDosing = typeof obj.isDosing !== 'undefined' ? utils.makeBool(obj.isDosing) : chlor.isDosing;
1691
+ let disabled = typeof obj.disabled !== 'undefined' ? utils.makeBool(obj.disabled) : chlor.disabled;
1692
+ let poolSetpoint = typeof obj.poolSetpoint !== 'undefined' ? parseInt(obj.poolSetpoint, 10) : chlor.poolSetpoint;
1693
+ let spaSetpoint = typeof obj.spaSetpoint !== 'undefined' ? parseInt(obj.spaSetpoint, 10) : chlor.spaSetpoint;
1681
1694
  let model = typeof obj.model !== 'undefined' ? obj.model : chlor.model;
1682
1695
  let chlorType = typeof obj.type !== 'undefined' ? sys.board.valueMaps.chlorinatorType.encode(obj.type) : chlor.type || 0;
1683
1696
  if (isAdd) {
@@ -1704,20 +1717,15 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
1704
1717
  if (poolSetpoint > 100 || poolSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator poolSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.poolSetpoint));
1705
1718
  if (spaSetpoint > 100 || spaSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator spaSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.spaSetpoint));
1706
1719
  if (typeof obj.ignoreSaltReading !== 'undefined') chlor.ignoreSaltReading = utils.makeBool(obj.ignoreSaltReading);
1720
+
1707
1721
  let _timeout: NodeJS.Timeout;
1708
1722
  try {
1709
1723
  let request153packet = new Promise<void>((resolve, reject) => {
1710
- _timeout = setTimeout(()=>{
1711
- if (typeof reject === 'undefined' || typeof resolve === 'undefined') return;
1712
- reject(new EquipmentTimeoutError(`no chlor response in 7 seconds`, `chlorTimeOut`));
1713
- reject = undefined;
1714
-
1715
- }, 3000);
1716
1724
  let out = Outbound.create({
1717
1725
  dest: 16,
1718
1726
  action: 153,
1719
1727
  // removed disable ? 0 : (spaSetpoint << 1) + 1 because only deleteChlorAsync should remove it from the OCP
1720
- payload: [(spaSetpoint << 1) + 1, disabled ? 0 : poolSetpoint,
1728
+ payload: [(disabled ? 0 : isDosing ? 100 << 1: spaSetpoint << 1) + 1, disabled ? 0 : isDosing ? 100 : poolSetpoint,
1721
1729
  utils.makeBool(superChlorinate) && superChlorHours > 0 ? superChlorHours + 128 : 0, // We only want to set the superChlor when the user sends superChlor = true
1722
1730
  0, 0, 0, 0, 0, 0, 0],
1723
1731
  retries: 3,
@@ -1731,13 +1739,18 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
1731
1739
  reject = undefined;
1732
1740
  }
1733
1741
  else {
1734
- logger.debug(`chlor response 153`);
1735
1742
  resolve();
1736
1743
  resolve = undefined;
1737
1744
  }
1738
1745
  }
1739
1746
  });
1740
1747
  conn.queueSendMessage(out);
1748
+ _timeout = setTimeout(()=>{
1749
+ if (typeof reject === 'undefined' || typeof resolve === 'undefined') return;
1750
+ reject(new EquipmentTimeoutError(`no chlor response in 7 seconds`, `chlorTimeOut`));
1751
+ reject = undefined;
1752
+
1753
+ }, 3000);
1741
1754
  });
1742
1755
  await request153packet;
1743
1756
  let schlor = state.chlorinators.getItemById(id, true);
@@ -1771,7 +1784,6 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
1771
1784
  reject(err);
1772
1785
  }
1773
1786
  else{
1774
- logger.debug(`chlor resolve 217`);
1775
1787
  resolve();
1776
1788
  }
1777
1789
  }
@@ -1792,7 +1804,9 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
1792
1804
  }
1793
1805
  public async deleteChlorAsync(obj: any): Promise<ChlorinatorState> {
1794
1806
  let id = parseInt(obj.id, 10);
1795
- if (isNaN(id)) obj.id = 1;
1807
+ if (isNaN(id)) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator id is not valid: ${obj.id}`, 'chlorinator', obj.id));
1808
+ let chlor = sys.chlorinators.getItemById(id);
1809
+ if (chlor.master === 1) return await super.deleteChlorAsync(obj);
1796
1810
  return new Promise<ChlorinatorState>((resolve, reject) => {
1797
1811
  let out = Outbound.create({
1798
1812
  dest: 16,
@@ -1806,8 +1820,9 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
1806
1820
  reject(err);
1807
1821
  }
1808
1822
  else {
1809
- let cstate = state.chlorinators.getItemById(id);
1810
- let chlor = sys.chlorinators.getItemById(id);
1823
+ ncp.chlorinators.deleteChlorinatorAsync(id).then(()=>{});
1824
+ let cstate = state.chlorinators.getItemById(id, true);
1825
+ chlor = sys.chlorinators.getItemById(id, true);
1811
1826
  chlor.isActive = cstate.isActive = false;
1812
1827
  sys.chlorinators.removeItemById(id);
1813
1828
  state.chlorinators.removeItemById(id);
@@ -1949,10 +1964,11 @@ class TouchPumpCommands extends PumpCommands {
1949
1964
  }
1950
1965
  }
1951
1966
  isAdd = true;
1967
+ pump = sys.pumps.getItemById(id, true);
1952
1968
  }
1953
1969
  else {
1954
1970
  pump = sys.pumps.getItemById(id, false);
1955
- if (data.master > 0 || pump.master > 0 || pump.isVirtual) return await super.setPumpAsync(data);
1971
+ if (data.master > 0 || pump.master > 0) return await super.setPumpAsync(data);
1956
1972
  ntype = typeof data.type === 'undefined' ? pump.type : parseInt(data.type, 10);
1957
1973
  if (isNaN(ntype)) return Promise.reject(new InvalidEquipmentDataError(`Pump type ${data.type} is not valid`, 'Pump', data));
1958
1974
  type = sys.board.valueMaps.pumpTypes.transform(ntype);
@@ -1983,7 +1999,6 @@ class TouchPumpCommands extends PumpCommands {
1983
1999
  if (type.name === 'ss') {
1984
2000
  // The OCP doesn't deal with single speed pumps. Simply add it to the config.
1985
2001
  data.circuits = [];
1986
- pump = sys.pumps.getItemById(id, true);
1987
2002
  pump.set(pump);
1988
2003
  let spump = state.pumps.getItemById(id, true);
1989
2004
  for (let prop in spump) {
@@ -2015,21 +2030,25 @@ class TouchPumpCommands extends PumpCommands {
2015
2030
  retries: 2,
2016
2031
  response: Response.create({ action: 1, payload: [155] })
2017
2032
  });
2018
- outc.appendPayloadByte(typeof type.maxPrimingTime !== 'undefined' ? data.primingTime : 0, pump.primingTime | 0);
2019
2033
  outc.appendPayloadBytes(0, 44);
2020
- if (typeof type.maxPrimingTime !== 'undefined' && type.maxPrimingTime > 0) {
2034
+ if (type.val === 128){
2035
+ outc.setPayloadByte(3, 2);
2036
+ }
2037
+ if (typeof type.maxPrimingTime !== 'undefined' && type.maxPrimingTime > 0 && type.val >=64) {
2038
+ outc.setPayloadByte(2, parseInt(data.primingTime, 10), pump.primingTime || 1);
2021
2039
  let primingSpeed = typeof data.primingSpeed !== 'undefined' ? parseInt(data.primingSpeed, 10) : pump.primingSpeed || type.minSpeed;
2022
2040
  outc.setPayloadByte(21, Math.floor(primingSpeed / 256));
2023
- outc.setPayloadByte(30, primingSpeed - (Math.floor(primingSpeed / 256) * 256));
2041
+ outc.setPayloadByte(30, primingSpeed % 256);
2024
2042
  }
2025
- if (type.val > 1 && type.val < 64) { // Any VF pump. It probably only goes up to Circuit 40 because that's how many circuits *Touch can support.
2043
+ if (type.val === 1) { // Any VF pump.
2026
2044
  outc.setPayloadByte(1, parseInt(data.backgroundCircuit, 10), pump.backgroundCircuit || 6);
2045
+ outc.setPayloadByte(2, parseInt(data.filterSize, 10) / 1000, pump.filterSize / 1000 || 15);
2046
+ // outc.setPayloadByte(2, body.capacity / 1000, 15); RSG - This is filter size, which may or may not equal the body size.
2027
2047
  outc.setPayloadByte(3, parseInt(data.turnovers, 10), pump.turnovers || 2);
2028
2048
  let body = sys.bodies.getItemById(1, sys.equipment.maxBodies >= 1);
2029
- outc.setPayloadByte(2, body.capacity / 1000, 15);
2030
2049
  outc.setPayloadByte(21, parseInt(data.manualFilterGPM, 10), pump.manualFilterGPM || 30);
2031
2050
  outc.setPayloadByte(22, parseInt(data.primingSpeed, 10), pump.primingSpeed || 55);
2032
- let primingTime = typeof data.primingTime !== 'undefined' ? parseInt(data.primingTime, 10) : pump.primingTime;
2051
+ let primingTime = typeof data.primingTime !== 'undefined' ? parseInt(data.primingTime, 10) : pump.primingTime || 0;
2033
2052
  let maxSystemTime = typeof data.maxSystemTime !== 'undefined' ? parseInt(data.maxSystemTime, 10) : pump.maxSystemTime;
2034
2053
  outc.setPayloadByte(23, primingTime | maxSystemTime << 4, 5);
2035
2054
  outc.setPayloadByte(24, parseInt(data.maxPressureIncrease, 10), pump.maxPressureIncrease || 10);
@@ -2037,7 +2056,7 @@ class TouchPumpCommands extends PumpCommands {
2037
2056
  outc.setPayloadByte(26, parseInt(data.backwashTime, 10), pump.backwashTime || 5);
2038
2057
  outc.setPayloadByte(27, parseInt(data.rinseTime, 10), pump.rinseTime || 1);
2039
2058
  outc.setPayloadByte(28, parseInt(data.vacuumFlow, 10), pump.vacuumFlow || 50);
2040
- outc.setPayloadByte(28, parseInt(data.vacuumTime, 10), pump.vacuumTime || 10);
2059
+ outc.setPayloadByte(30, parseInt(data.vacuumTime, 10), pump.vacuumTime || 10);
2041
2060
  }
2042
2061
  if (typeof type.maxCircuits !== 'undefined' && type.maxCircuits > 0 && typeof data.circuits !== 'undefined') { // This pump type supports circuits
2043
2062
  for (let i = 1; i <= data.circuits.length && i <= type.maxCircuits; i++) {
@@ -2050,7 +2069,7 @@ class TouchPumpCommands extends PumpCommands {
2050
2069
  c.units = parseInt(c.units, 10) || type.name === 'vf' ? sys.board.valueMaps.pumpUnits.getValue('gpm') : sys.board.valueMaps.pumpUnits.getValue('rpm');
2051
2070
  if (typeof type.minSpeed !== 'undefined' && c.units === sys.board.valueMaps.pumpUnits.getValue('rpm')) {
2052
2071
  outc.setPayloadByte(i * 2 + 4, Math.floor(speed / 256)); // Set to rpm
2053
- outc.setPayloadByte(i + 21, speed - (Math.floor(speed / 256) * 256));
2072
+ outc.setPayloadByte(i + 21, speed % 256);
2054
2073
  c.speed = speed;
2055
2074
  }
2056
2075
  else if (typeof type.minFlow !== 'undefined' && c.units === sys.board.valueMaps.pumpUnits.getValue('gpm')) {
@@ -2087,7 +2106,7 @@ class TouchPumpCommands extends PumpCommands {
2087
2106
  // [165,33,16,34,155,46],[1,128,0,2,0,16,12,6,7,1,9,4,11,11,3,128,8,0,2,18,2,3,128,8,196,184,232,152,188,238,232,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[9,75]
2088
2107
  const setPumpConfig = Outbound.create({
2089
2108
  action: 155,
2090
- payload: [pump.id, pump.type, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
2109
+ payload: [pump.id, pump.type, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
2091
2110
  retries: 2,
2092
2111
  response: true
2093
2112
  });
@@ -2163,6 +2182,34 @@ class TouchPumpCommands extends PumpCommands {
2163
2182
  spump.type = pump.type;
2164
2183
  spump.status = 0;
2165
2184
  }
2185
+ public async deletePumpAsync(pump: Pump):Promise<Pump>{
2186
+ let id = pump.id;
2187
+ if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError(`DeletePumpAsync: Pump ${id} is not valid.`, 0, `pump`))
2188
+ const outc = Outbound.create({
2189
+ action: 155,
2190
+ payload: [id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
2191
+ retries: 2,
2192
+ response: true
2193
+ });
2194
+ return new Promise<Pump>((resolve, reject) => {
2195
+ outc.onComplete = (err, msg) => {
2196
+ if (err) reject(err);
2197
+ else {
2198
+ sys.pumps.removeItemById(id);
2199
+ state.pumps.removeItemById(id);
2200
+ resolve(sys.pumps.getItemById(id,false));
2201
+ const pumpConfigRequest = Outbound.create({
2202
+ action: 216,
2203
+ payload: [id],
2204
+ retries: 2,
2205
+ response: true
2206
+ });
2207
+ conn.queueSendMessage(pumpConfigRequest);
2208
+ }
2209
+ };
2210
+ conn.queueSendMessage(outc);
2211
+ });
2212
+ }
2166
2213
  }
2167
2214
  class TouchHeaterCommands extends HeaterCommands {
2168
2215
  public getInstalledHeaterTypes(body?: number): any {
@@ -2223,13 +2270,14 @@ class TouchHeaterCommands extends HeaterCommands {
2223
2270
  }
2224
2271
  // RKS: Not sure what to do with this as the heater data for Touch isn't actually processed anywhere.
2225
2272
  public async setHeaterAsync(obj: any): Promise<Heater> {
2273
+ if (obj.master === 1 || parseInt(obj.id, 10) > 255) return super.setHeaterAsync(obj);
2226
2274
  return new Promise<Heater>((resolve, reject) => {
2227
2275
  let id = typeof obj.id === 'undefined' ? -1 : parseInt(obj.id, 10);
2228
2276
  if (isNaN(id)) return reject(new InvalidEquipmentIdError('Heater Id is not valid.', obj.id, 'Heater'));
2229
2277
  let heater: Heater;
2230
2278
  if (id <= 0) {
2231
2279
  // We are adding a heater. In this case all heaters are virtual.
2232
- let heaters = sys.heaters.filter(h => h.isVirtual === false);
2280
+ let heaters = sys.heaters.filter(h => h.master === 1);
2233
2281
  id = heaters.getMaxId() + 1;
2234
2282
  }
2235
2283
  heater = sys.heaters.getItemById(id, true);
@@ -2240,7 +2288,7 @@ class TouchHeaterCommands extends HeaterCommands {
2240
2288
  }
2241
2289
  }
2242
2290
  let hstate = state.heaters.getItemById(id, true);
2243
- //hstate.isVirtual = heater.isVirtual = true;
2291
+
2244
2292
  hstate.name = heater.name;
2245
2293
  hstate.type = heater.type;
2246
2294
  heater.master = 1;
@@ -2250,7 +2298,7 @@ class TouchHeaterCommands extends HeaterCommands {
2250
2298
  });
2251
2299
  }
2252
2300
  public async deleteHeaterAsync(obj: any): Promise<Heater> {
2253
- if (utils.makeBool(obj.isVirtual) || obj.master === 1 || parseInt(obj.id, 10) > 255) return await super.deleteHeaterAsync(obj);
2301
+ if (utils.makeBool(obj.master === 1 || parseInt(obj.id, 10) > 255)) return super.deleteHeaterAsync(obj);
2254
2302
  return new Promise<Heater>((resolve, reject) => {
2255
2303
  let id = parseInt(obj.id, 10);
2256
2304
  if (isNaN(id)) return reject(new InvalidEquipmentIdError('Cannot delete. Heater Id is not valid.', obj.id, 'Heater'));
@@ -2336,7 +2384,7 @@ class TouchChemControllerCommands extends ChemControllerCommands {
2336
2384
  // Now lets do all our validation to the incoming chem controller data.
2337
2385
  let name = typeof data.name !== 'undefined' ? data.name : chem.name || `IntelliChem - ${address - 143}`;
2338
2386
  let type = sys.board.valueMaps.chemControllerTypes.transformByName('intellichem');
2339
- // So now we are down to the nitty gritty setting the data for the REM or Homegrown Chem controller.
2387
+ // So now we are down to the nitty gritty setting the data for the REM Chem controller.
2340
2388
  let calciumHardness = typeof data.calciumHardness !== 'undefined' ? parseInt(data.calciumHardness, 10) : chem.calciumHardness;
2341
2389
  let cyanuricAcid = typeof data.cyanuricAcid !== 'undefined' ? parseInt(data.cyanuricAcid, 10) : chem.cyanuricAcid;
2342
2390
  let alkalinity = typeof data.alkalinity !== 'undefined' ? parseInt(data.alkalinity, 10) : chem.alkalinity;