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
@@ -26,6 +26,7 @@ import { state, ChlorinatorState, LightGroupState, VirtualCircuitState, ICircuit
26
26
  import { utils } from '../../controller/Constants';
27
27
  import { InvalidEquipmentIdError, InvalidEquipmentDataError, EquipmentNotFoundError, MessageError, InvalidOperationError } from '../Errors';
28
28
  import { ncp } from '../nixie/Nixie';
29
+ import { Timestamp } from "../Constants"
29
30
  export class IntelliCenterBoard extends SystemBoard {
30
31
  public needsConfigChanges: boolean = false;
31
32
  constructor(system: PoolSystem) {
@@ -389,6 +390,10 @@ export class IntelliCenterBoard extends SystemBoard {
389
390
  for (let i = 0; i < sys.circuits.length; i++) {
390
391
  let c = sys.circuits.getItemByIndex(i);
391
392
  if (c.id <= 40) c.master = 0;
393
+ if (typeof sys.board.valueMaps.circuitFunctions.get(c.type).isLight) {
394
+ let s = state.circuits.getItemById(c.id);
395
+ if (s.action !== 0) s.action = 0;
396
+ }
392
397
  }
393
398
  for (let i = 0; i < sys.valves.length; i++) {
394
399
  let v = sys.valves.getItemByIndex(i);
@@ -2258,9 +2263,9 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
2258
2263
  let dow = dt.getDay();
2259
2264
  // Convert the dow to the bit value.
2260
2265
  let sd = sys.board.valueMaps.scheduleDays.toArray().find(elem => elem.dow === dow);
2261
- let dayVal = sd.bitVal || sd.val; // The bitval allows mask overrides.
2266
+ //let dayVal = sd.bitVal || sd.val; // The bitval allows mask overrides.
2262
2267
  let ts = dt.getHours() * 60 + dt.getMinutes();
2263
- if ((sched.scheduleDays & dayVal) > 0 && ts >= sched.startTime && ts <= sched.endTime) byte = byte | (1 << bit);
2268
+ if ((sched.scheduleDays & sd.bitval) > 0 && ts >= sched.startTime && ts <= sched.endTime) byte = byte | (1 << bit);
2264
2269
  }
2265
2270
  }
2266
2271
  else if (sched.isOn) byte = byte | (1 << bit);
@@ -2862,21 +2867,28 @@ class IntelliCenterBodyCommands extends BodyCommands {
2862
2867
  retries: 2,
2863
2868
  response: IntelliCenterBoard.getAckResponse(168)
2864
2869
  });
2865
- await out.sendAsync();
2866
- let body1 = sys.bodies.getItemById(1);
2867
- let sbody1 = state.temps.bodies.getItemById(1);
2868
- body1.heatMode = sbody1.heatMode = bhs.body1.heatMode;
2869
- body1.heatSetpoint = sbody1.heatSetpoint = bhs.body1.heatSetpoint;
2870
- body1.coolSetpoint = sbody1.coolSetpoint = bhs.body1.coolSetpoint;
2871
- if (sys.equipment.dual || sys.equipment.shared) {
2872
- let body2 = sys.bodies.getItemById(2);
2873
- let sbody2 = state.temps.bodies.getItemById(2);
2874
- body2.heatMode = sbody2.heatMode = bhs.body2.heatMode;
2875
- body2.heatSetpoint = sbody2.heatSetpoint = bhs.body2.heatSetpoint;
2876
- body2.coolSetpoint = sbody2.coolSetpoint = bhs.body2.coolSetpoint;
2877
- }
2878
- bhs.processing = false;
2879
- state.emitEquipmentChanges();
2870
+ try {
2871
+ await out.sendAsync();
2872
+ let body1 = sys.bodies.getItemById(1);
2873
+ let sbody1 = state.temps.bodies.getItemById(1);
2874
+ body1.heatMode = sbody1.heatMode = bhs.body1.heatMode;
2875
+ body1.heatSetpoint = sbody1.heatSetpoint = bhs.body1.heatSetpoint;
2876
+ body1.coolSetpoint = sbody1.coolSetpoint = bhs.body1.coolSetpoint;
2877
+ if (sys.equipment.dual || sys.equipment.shared) {
2878
+ let body2 = sys.bodies.getItemById(2);
2879
+ let sbody2 = state.temps.bodies.getItemById(2);
2880
+ body2.heatMode = sbody2.heatMode = bhs.body2.heatMode;
2881
+ body2.heatSetpoint = sbody2.heatSetpoint = bhs.body2.heatSetpoint;
2882
+ body2.coolSetpoint = sbody2.coolSetpoint = bhs.body2.coolSetpoint;
2883
+ }
2884
+ state.emitEquipmentChanges();
2885
+ } catch (err) {
2886
+ bhs.processing = false;
2887
+ throw (err);
2888
+ }
2889
+ finally {
2890
+ bhs.processing = false;
2891
+ }
2880
2892
  return true;
2881
2893
  }
2882
2894
  else {
@@ -3119,6 +3131,7 @@ class IntelliCenterBodyCommands extends BodyCommands {
3119
3131
  }
3120
3132
  }
3121
3133
  class IntelliCenterScheduleCommands extends ScheduleCommands {
3134
+ _lastScheduleCheck: number = 0;
3122
3135
  public async setScheduleAsync(data: any): Promise<Schedule> {
3123
3136
  if (typeof data.id !== 'undefined') {
3124
3137
  let id = typeof data.id === 'undefined' ? -1 : parseInt(data.id, 10);
@@ -3141,6 +3154,8 @@ class IntelliCenterScheduleCommands extends ScheduleCommands {
3141
3154
  let endTime = typeof data.endTime !== 'undefined' ? data.endTime : sched.endTime;
3142
3155
  let schedDays = sys.board.schedules.transformDays(typeof data.scheduleDays !== 'undefined' ? data.scheduleDays : sched.scheduleDays);
3143
3156
  let display = typeof data.display !== 'undefined' ? data.display : sched.display || 0;
3157
+ let endTimeOffset = typeof data.endTimeOffset !== 'undefined' ? data.endTimeOffset : sched.endTimeOffset;
3158
+ let startTimeOffset = typeof data.startTimeOffset !== 'undefined' ? data.startTimeOffset : sched.startTimeOffset;
3144
3159
 
3145
3160
  // Ensure all the defaults.
3146
3161
  if (isNaN(startDate.getTime())) startDate = new Date();
@@ -3216,11 +3231,45 @@ class IntelliCenterScheduleCommands extends ScheduleCommands {
3216
3231
  ssched.isActive = sched.isActive = true;
3217
3232
  ssched.display = sched.display = display;
3218
3233
  ssched.emitEquipmentChange();
3234
+ ssched.startTimeOffset = sched.startTimeOffset = startTimeOffset;
3235
+ ssched.endTimeOffset = sched.endTimeOffset = endTimeOffset;
3219
3236
  return sched;
3220
3237
  }
3221
3238
  else
3222
3239
  return Promise.reject(new InvalidEquipmentIdError('No schedule information provided', undefined, 'Schedule'));
3223
3240
  }
3241
+ public syncScheduleStates() {
3242
+ // This is triggered from the 204 message in IntelliCenter. We will
3243
+ // be checking to ensure it does not load the server so we only do this every 10 seconds.
3244
+ if (this._lastScheduleCheck > new Date().getTime() - 10000) return;
3245
+ try {
3246
+ // The call below also calculates the schedule window either the current or next.
3247
+ ncp.schedules.triggerSchedules(); // At this point we are not adding Nixie schedules to IntelliCenter but this will trigger
3248
+ // the proper time windows if they exist.
3249
+ // Check each running circuit/feature to see when it will be going off.
3250
+ let scheds = state.schedules.getActiveSchedules();
3251
+ let circs: { state: ICircuitState, endTime: number }[] = [];
3252
+ for (let i = 0; i < scheds.length; i++) {
3253
+ let ssched = scheds[i];
3254
+ if (!ssched.isOn || ssched.disabled || !ssched.isActive) continue;
3255
+ let c = circs.find(x => x.state.id === ssched.circuit);
3256
+ if (typeof c === 'undefined') {
3257
+ let cstate = state.circuits.getInterfaceById(ssched.circuit);
3258
+ c = { state: cstate, endTime: ssched.scheduleTime.endTime.getTime() };
3259
+ circs.push;
3260
+ }
3261
+ if (c.endTime < ssched.scheduleTime.endTime.getTime()) c.endTime = ssched.scheduleTime.endTime.getTime();
3262
+ }
3263
+ for (let i = 0; i < circs.length; i++) {
3264
+ let c = circs[i];
3265
+ if (c.state.endTime.getTime() !== c.endTime) {
3266
+ c.state.endTime = new Timestamp(new Date(c.endTime));
3267
+ c.state.emitEquipmentChange();
3268
+ }
3269
+ }
3270
+ this._lastScheduleCheck = new Date().getTime();
3271
+ } catch (err) { logger.error(`Error synchronizing schedule states`); }
3272
+ }
3224
3273
  public async deleteScheduleAsync(data: any): Promise<Schedule> {
3225
3274
  if (typeof data.id !== 'undefined') {
3226
3275
  let id = typeof data.id === 'undefined' ? -1 : parseInt(data.id, 10);
@@ -3484,7 +3533,7 @@ class IntelliCenterHeaterCommands extends HeaterCommands {
3484
3533
  if (gasHeaterInstalled) sys.board.valueMaps.heatSources.merge([[2, { name: 'heater', desc: 'Heater' }]]);
3485
3534
  if (mastertempInstalled) sys.board.valueMaps.heatSources.merge([[11, { name: 'mtheater', desc: 'MasterTemp' }]]);
3486
3535
  if (solarInstalled && (gasHeaterInstalled || heatPumpInstalled)) sys.board.valueMaps.heatSources.merge([[3, { name: 'solar', desc: 'Solar Only', hasCoolSetpoint: htypes.hasCoolSetpoint }], [4, { name: 'solarpref', desc: 'Solar Preferred', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
3487
- else if (solarInstalled) sys.board.valueMaps.heatSources.merge([[3, { name: 'solar', desc: 'Solar', hasCoolsetpoint: htypes.hasCoolSetpoint }]]);
3536
+ else if (solarInstalled) sys.board.valueMaps.heatSources.merge([[3, { name: 'solar', desc: 'Solar', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
3488
3537
  if (heatPumpInstalled && (gasHeaterInstalled || solarInstalled)) sys.board.valueMaps.heatSources.merge([[9, { name: 'heatpump', desc: 'Heatpump Only' }], [25, { name: 'heatpumppref', desc: 'Heat Pump Pref' }]]);
3489
3538
  else if (heatPumpInstalled) sys.board.valueMaps.heatSources.merge([[9, { name: 'heatpump', desc: 'Heat Pump' }]]);
3490
3539
  if (ultratempInstalled && (gasHeaterInstalled || heatPumpInstalled)) sys.board.valueMaps.heatSources.merge([[5, { name: 'ultratemp', desc: 'UltraTemp Only', hasCoolSetpoint: htypes.hasCoolSetpoint }], [6, { name: 'ultratemppref', desc: 'UltraTemp Pref', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
@@ -86,7 +86,7 @@ export class IntelliTouchBoard extends EasyTouchBoard {
86
86
 
87
87
  }
88
88
  if (typeof sys.valves.find((v) => v.id === 2) === 'undefined') {
89
- let valve = sys.valves.getItemById(1, true);
89
+ let valve = sys.valves.getItemById(2, true);
90
90
  valve.isIntake = false;
91
91
  valve.isReturn = false;
92
92
  valve.type = 0;
@@ -360,9 +360,7 @@ class ITTouchConfigQueue extends TouchConfigQueue {
360
360
  this.queueItems(GetTouchConfigCategories.highSpeedCircuits, [0]);
361
361
  this.queueRange(GetTouchConfigCategories.pumpConfig, 1, sys.equipment.maxPumps);
362
362
  this.queueRange(GetTouchConfigCategories.circuitGroups, 0, sys.equipment.maxFeatures - 1);
363
- // items not required by ScreenLogic
364
- if (sys.chlorinators.getItemById(1).isActive)
365
- this.queueItems(GetTouchConfigCategories.intellichlor, [0]);
363
+ this.queueItems(GetTouchConfigCategories.intellichlor, [0]);
366
364
  if (this.remainingItems > 0) {
367
365
  var self = this;
368
366
  setTimeout(() => { self.processNext(); }, 50);
@@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
  import * as extend from 'extend';
19
19
  import { ncp } from "../nixie/Nixie";
20
20
  import { NixieHeaterBase } from "../nixie/heaters/Heater";
21
- import { utils } from '../Constants';
21
+ import { Timestamp, utils } from '../Constants';
22
22
  import {SystemBoard, byteValueMap, BodyCommands, FilterCommands, PumpCommands, SystemCommands, CircuitCommands, FeatureCommands, ValveCommands, HeaterCommands, ChlorinatorCommands, ChemControllerCommands, EquipmentIdRange} from './SystemBoard';
23
23
  import { logger } from '../../logger/Logger';
24
24
  import { state, CircuitState, ICircuitState, ICircuitGroupState, LightGroupState, ValveState, FilterState, BodyTempState, FeatureState } from '../State';
@@ -221,6 +221,21 @@ export class NixieBoard extends SystemBoard {
221
221
  [53, { name: 'greenblue', desc: 'Green-Blue', types: ['pooltone'], sequence: 14 }],
222
222
  [54, { name: 'redgreen', desc: 'Red-Green', types: ['pooltone'], sequence: 15 }],
223
223
  [55, { name: 'bluered', desc: 'Blue-red', types: ['pooltone'], sequence: 16 }],
224
+ // Jandy Pro Series WaterColors Themes
225
+ [56, { name: 'alpinewhite', desc: 'Alpine White', types: ['watercolors'], sequence: 1 }],
226
+ [57, { name: 'skyblue', desc: 'Sky Blue', types: ['watercolors'], sequence: 2 }],
227
+ [58, { name: 'cobaltblue', desc: 'Cobalt Blue', types: ['watercolors'], sequence: 3 }],
228
+ [59, { name: 'caribbeanblue', desc: 'Caribbean Blue', types: ['watercolors'], sequence: 4 }],
229
+ [60, { name: 'springgreen', desc: 'Spring Green', types: ['watercolors'], sequence: 5 }],
230
+ [61, { name: 'emeraldgreen', desc: 'Emerald Green', types: ['watercolors'], sequence: 6 }],
231
+ [62, { name: 'emeraldrose', desc: 'Emerald Rose', types: ['watercolors'], sequence: 7 }],
232
+ [63, { name: 'magenta', desc: 'Magenta', types: ['watercolors'], sequence: 8 }],
233
+ [64, { name: 'violet', desc: 'Violet', types: ['watercolors'], sequence: 9 }],
234
+ [65, { name: 'slowcolorsplash', desc: 'Slow Color Splash', types: ['watercolors'], sequence: 10 }],
235
+ [66, { name: 'fastcolorsplash', desc: 'Fast Color Splash', types: ['watercolors'], sequence: 11 }],
236
+ [67, { name: 'americathebeautiful', desc: 'America the Beautiful', types: ['watercolors'], sequence: 12 }],
237
+ [68, { name: 'fattuesday', desc: 'Fat Tuesday', types: ['watercolors'], sequence: 13 }],
238
+ [69, { name: 'discotech', desc: 'Disco Tech', types: ['watercolors'], sequence: 14 }],
224
239
  [255, { name: 'none', desc: 'None' }]
225
240
  ]);
226
241
  this.valueMaps.lightColors = new byteValueMap([
@@ -266,9 +281,35 @@ export class NixieBoard extends SystemBoard {
266
281
  [4, { name: 'spaCommand', desc: 'Spa Command', maxButtons: 10 }]
267
282
  ]);
268
283
  }
284
+ public async closeAsync() {
285
+ logger.info(`Closing Nixie Board`);
286
+ await ncp.closeAsync();
287
+ }
269
288
  public async checkConfiguration() {
270
289
  state.status = sys.board.valueMaps.controllerStatus.transform(0, 0);
271
290
  state.emitControllerChange();
291
+ // Set all the schedule data based upon the config.
292
+ for (let i = 0; i < sys.schedules.length; i++) {
293
+ let sched = sys.schedules.getItemByIndex(i);
294
+ let ssched = state.schedules.getItemById(sched.id, true);
295
+ ssched.circuit = sched.circuit;
296
+ ssched.scheduleDays = sched.scheduleDays;
297
+ ssched.scheduleType = sched.scheduleType;
298
+ ssched.changeHeatSetpoint = sched.changeHeatSetpoint;
299
+ ssched.heatSetpoint = sched.heatSetpoint;
300
+ ssched.coolSetpoint = sched.coolSetpoint;
301
+ ssched.heatSource = sched.heatSource;
302
+ ssched.startTime = sched.startTime;
303
+ ssched.endTime = sched.endTime;
304
+ ssched.startTimeType = sched.startTimeType;
305
+ ssched.endTimeType = sched.endTimeType;
306
+ ssched.startDate = sched.startDate;
307
+ ssched.isActive = sched.isActive = true;
308
+ sched.disabled = sched.disabled;
309
+ ssched.display = sched.display;
310
+
311
+ }
312
+
272
313
  state.status = sys.board.valueMaps.controllerStatus.transform(1, 100);
273
314
  state.emitControllerChange();
274
315
  }
@@ -390,7 +431,7 @@ export class NixieBoard extends SystemBoard {
390
431
  sys.circuits.removeItemById(6);
391
432
  state.circuits.removeItemById(6);
392
433
  }
393
-
434
+
394
435
  sys.equipment.setEquipmentIds();
395
436
  sys.board.bodies.initFilters();
396
437
  state.status = sys.board.valueMaps.controllerStatus.transform(2, 0);
@@ -411,10 +452,11 @@ export class NixieBoard extends SystemBoard {
411
452
  sys.board.heaters.updateHeaterServices();
412
453
  state.cleanupState();
413
454
  logger.info(`${sys.equipment.model} control board initialized`);
414
- state.status = sys.board.valueMaps.controllerStatus.transform(1, 100);
455
+ //state.status = sys.board.valueMaps.controllerStatus.transform(1, 100);
415
456
  state.mode = sys.board.valueMaps.panelModes.encode('auto');
416
457
  // At this point we should have the start of a board so lets check to see if we are ready or if we are stuck initializing.
417
458
  await setTimeout(5000);
459
+ state.status = sys.board.valueMaps.controllerStatus.transform(1, 100);
418
460
  await self.processStatusAsync();
419
461
  } catch (err) { state.status = 255; logger.error(`Error Initializing Nixie Control Panel ${err.message}`); }
420
462
  }
@@ -529,11 +571,11 @@ export class NixieSystemCommands extends SystemCommands {
529
571
  state.delay = sys.board.valueMaps.delay.getValue('nodelay');
530
572
  return Promise.resolve(state.data.delay);
531
573
  }
532
- public setManualOperationPriority(id: number): Promise<any> {
574
+ public setManualOperationPriority(id: number): Promise<any> {
533
575
  let cstate = state.circuits.getInterfaceById(id);
534
576
  delayMgr.setManualPriorityDelay(cstate);
535
- return Promise.resolve(cstate);
536
- }
577
+ return Promise.resolve(cstate);
578
+ }
537
579
  public setDateTimeAsync(obj: any): Promise<any> { return Promise.resolve(); }
538
580
  public getDOW() { return this.board.valueMaps.scheduleDays.toArray(); }
539
581
  public async setGeneralAsync(obj: any): Promise<General> {
@@ -669,7 +711,6 @@ export class NixieCircuitCommands extends CircuitCommands {
669
711
  break;
670
712
  default:
671
713
  await ncp.circuits.setCircuitStateAsync(circ, newState);
672
- await sys.board.processStatusAsync();
673
714
  break;
674
715
  }
675
716
  // Let the main nixie controller set the circuit state and affect the relays if it needs to.
@@ -680,6 +721,7 @@ export class NixieCircuitCommands extends CircuitCommands {
680
721
  state.emitEquipmentChanges();
681
722
  ncp.pumps.syncPumpStates();
682
723
  sys.board.suspendStatus(false);
724
+ await sys.board.processStatusAsync();
683
725
  }
684
726
  }
685
727
  protected async setCleanerCircuitStateAsync(id: number, val: boolean, ignoreDelays?: boolean): Promise<ICircuitState> {
@@ -742,8 +784,9 @@ export class NixieCircuitCommands extends CircuitCommands {
742
784
  }
743
785
  if (sys.general.options.cleanerStartDelay && sys.general.options.cleanerStartDelayTime) {
744
786
  let bcstate = state.circuits.getItemById(bstate.circuit);
787
+ let stime = typeof bcstate.startTime === 'undefined' ? dtNow : (dtNow - bcstate.startTime.getTime());
745
788
  // So we should be started. Lets determine whethere there should be any delay.
746
- delayTime = Math.max(Math.round(((sys.general.options.cleanerStartDelayTime * 1000) - (dtNow - bcstate.startTime.getTime())) / 1000), delayTime);
789
+ delayTime = Math.max(Math.round(((sys.general.options.cleanerStartDelayTime * 1000) - stime) / 1000), delayTime);
747
790
  logger.info(`Cleaner delay time calculated to ${delayTime}`);
748
791
  }
749
792
  }
@@ -864,6 +907,7 @@ export class NixieCircuitCommands extends CircuitCommands {
864
907
  // circuit is already under delay it should have been processed out earlier.
865
908
  delayMgr.cancelPumpValveDelays();
866
909
  delayMgr.cancelHeaterStartupDelays();
910
+ sys.board.heaters.clearPrevHeaterOffTemp();
867
911
  if (cstate.startDelay) delayMgr.clearBodyStartupDelay(bstate);
868
912
  await this.turnOffCleanerCircuits(bstate);
869
913
  if (sys.equipment.shared && bstate.id === 2) await this.turnOffDrainCircuits(ignoreDelays);
@@ -967,7 +1011,7 @@ export class NixieCircuitCommands extends CircuitCommands {
967
1011
  return cstate;
968
1012
  } catch (err) { logger.error(`Nixie: Error setDrainCircuitStateAsync ${err.message}`); return Promise.reject(new BoardProcessError(`Nixie: Error setDrainCircuitStateAsync ${err.message}`, 'setDrainCircuitStateAsync')); }
969
1013
  }
970
-
1014
+
971
1015
  public toggleCircuitStateAsync(id: number): Promise<ICircuitState> {
972
1016
  let circ = state.circuits.getInterfaceById(id);
973
1017
  return this.setCircuitStateAsync(id, !(circ.isOn || false));
@@ -1112,7 +1156,7 @@ export class NixieCircuitCommands extends CircuitCommands {
1112
1156
  if (typeof obj.eggTimer !== 'undefined') group.eggTimer = Math.min(Math.max(parseInt(obj.eggTimer, 10), 0), 1440);
1113
1157
  if (typeof obj.showInFeatures !== 'undefined') sgroup.showInFeatures = group.showInFeatures = utils.makeBool(obj.showInFeatures);
1114
1158
  sgroup.type = group.type;
1115
-
1159
+
1116
1160
  group.dontStop = group.eggTimer === 1440;
1117
1161
  group.isActive = sgroup.isActive = true;
1118
1162
 
@@ -1179,7 +1223,7 @@ export class NixieCircuitCommands extends CircuitCommands {
1179
1223
  // group.circuits.length = obj.circuits.length; // RSG - removed as this will delete circuits that were not changed
1180
1224
  group.circuits.length = obj.circuits.length;
1181
1225
  sgroup.emitEquipmentChange();
1182
-
1226
+
1183
1227
  }
1184
1228
  resolve(group);
1185
1229
  });
@@ -1266,7 +1310,7 @@ export class NixieCircuitCommands extends CircuitCommands {
1266
1310
  //grp.lightingTheme = sgrp.lightingTheme = theme;
1267
1311
  let thm = sys.board.valueMaps.lightThemes.transform(theme);
1268
1312
  sgrp.action = sys.board.valueMaps.circuitActions.getValue('lighttheme');
1269
-
1313
+
1270
1314
  try {
1271
1315
  // Go through and set the theme for all lights in the group.
1272
1316
  for (let i = 0; i < grp.circuits.length; i++) {
@@ -1380,7 +1424,7 @@ export class NixieCircuitCommands extends CircuitCommands {
1380
1424
  else if (circuit.desiredState === 5){
1381
1425
  // off/ignore
1382
1426
  if (val) cval = false;
1383
- else continue;
1427
+ else continue;
1384
1428
  }
1385
1429
  await sys.board.circuits.setCircuitStateAsync(circuit.circuit, cval);
1386
1430
  //arr.push(sys.board.circuits.setCircuitStateAsync(circuit.circuit, cval));
@@ -1402,13 +1446,23 @@ export class NixieCircuitCommands extends CircuitCommands {
1402
1446
  let arr = [];
1403
1447
  for (let i = 0; i < circuits.length; i++) {
1404
1448
  let circuit = circuits[i];
1405
- arr.push(sys.board.circuits.setCircuitStateAsync(circuit.circuit, val));
1449
+ // RSG 4/3/24 - This function was executing and returing the results to the array; not pushing the fn to the array.
1450
+ //arr.push(sys.board.circuits.setCircuitStateAsync(circuit.circuit, val));
1451
+ arr.push(async () => { await sys.board.circuits.setCircuitStateAsync(circuit.circuit, val) });
1406
1452
  }
1453
+ // return new Promise<ICircuitGroupState>(async (resolve, reject) => {
1454
+ // await Promise.all(arr).catch((err) => { reject(err) });
1455
+ // resolve(gstate);
1456
+ // });
1407
1457
  return new Promise<ICircuitGroupState>(async (resolve, reject) => {
1408
- await Promise.all(arr).catch((err) => { reject(err) });
1409
- resolve(gstate);
1458
+ try {
1459
+ Promise.all(arr.map(async func => await func()));
1460
+ resolve(gstate);
1461
+ } catch (err) {
1462
+ reject(err);
1463
+ };
1410
1464
  });
1411
- }
1465
+ };
1412
1466
  }
1413
1467
  export class NixieFeatureCommands extends FeatureCommands {
1414
1468
  public async setFeatureAsync(obj: any): Promise<Feature> {
@@ -1484,7 +1538,7 @@ export class NixieFeatureCommands extends FeatureCommands {
1484
1538
  if (!val){
1485
1539
  if (fstate.manualPriorityActive) delayMgr.cancelManualPriorityDelay(fstate.id);
1486
1540
  fstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
1487
- }
1541
+ }
1488
1542
  state.emitEquipmentChanges();
1489
1543
  return fstate;
1490
1544
  } catch (err) { return Promise.reject(new Error(`Error setting feature state ${err.message}`)); }
@@ -1572,7 +1626,7 @@ export class NixieFeatureCommands extends FeatureCommands {
1572
1626
  let circuit: CircuitGroupCircuit = grp.circuits.getItemByIndex(j);
1573
1627
  let cstate = state.circuits.getInterfaceById(circuit.circuit);
1574
1628
  // RSG: desiredState for Nixie is 1=on, 2=off, 3=ignore
1575
- if (circuit.desiredState === 1 || circuit.desiredState === 4) {
1629
+ if (circuit.desiredState === 1 || circuit.desiredState === 4) {
1576
1630
  // The circuit should be on if the value is 1.
1577
1631
  // If we are on 'ignore' we should still only treat the circuit as
1578
1632
  // desiredstate = 1.
@@ -1583,12 +1637,15 @@ export class NixieFeatureCommands extends FeatureCommands {
1583
1637
  }
1584
1638
  }
1585
1639
  let sgrp = state.circuitGroups.getItemById(grp.id);
1640
+ if (bIsOn && typeof sgrp.endTime === 'undefined') {
1641
+ sys.board.circuits.setEndTime(grp, sgrp, bIsOn, true);
1642
+ }
1586
1643
  sgrp.isOn = bIsOn;
1587
- if (sgrp.isOn && typeof sgrp.endTime === 'undefined') sys.board.circuits.setEndTime(grp, sgrp, sgrp.isOn, true);
1644
+
1588
1645
  if (!sgrp.isOn && sgrp.manualPriorityActive){
1589
1646
  delayMgr.cancelManualPriorityDelays();
1590
1647
  sgrp.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
1591
- }
1648
+ }
1592
1649
  }
1593
1650
  sys.board.valves.syncValveStates();
1594
1651
  }
@@ -1610,9 +1667,9 @@ export class NixieFeatureCommands extends FeatureCommands {
1610
1667
  if (!sgrp.isOn && sgrp.manualPriorityActive){
1611
1668
  delayMgr.cancelManualPriorityDelay(grp.id);
1612
1669
  sgrp.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
1613
- }
1670
+ }
1614
1671
  }
1615
-
1672
+
1616
1673
  sys.board.valves.syncValveStates();
1617
1674
  }
1618
1675
  state.emitEquipmentChanges();
@@ -1644,8 +1701,8 @@ export class NixiePumpCommands extends PumpCommands {
1644
1701
  // to bodies.
1645
1702
  console.log(`Body: ${pump.body} Pump: ${pump.name} Pool: ${circuitIds.includes(6)} `);
1646
1703
  if ((pump.body === 255 && (circuitIds.includes(6) || circuitIds.includes(1))) ||
1647
- (pump.body === 0 && circuitIds.includes(6)) ||
1648
- (pump.body === 101 && circuitIds.includes(1))) {
1704
+ (pump.body === 0 && circuitIds.includes(6)) ||
1705
+ (pump.body === 101 && circuitIds.includes(1))) {
1649
1706
  delayMgr.setPumpValveDelay(pstate);
1650
1707
  }
1651
1708
  break;
@@ -1817,14 +1874,14 @@ export class NixieHeaterCommands extends HeaterCommands {
1817
1874
  if (gasHeaterInstalled) sys.board.valueMaps.heatSources.merge([[2, { name: 'heater', desc: 'Heater' }]]);
1818
1875
  if (mastertempInstalled) sys.board.valueMaps.heatSources.merge([[11, { name: 'mtheater', desc: 'MasterTemp' }]]);
1819
1876
  if (solarInstalled && (gasHeaterInstalled || heatPumpInstalled)) sys.board.valueMaps.heatSources.merge([[3, { name: 'solar', desc: 'Solar Only', hasCoolSetpoint: htypes.hasCoolSetpoint }], [4, { name: 'solarpref', desc: 'Solar Preferred', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
1820
- else if (solarInstalled) sys.board.valueMaps.heatSources.merge([[3, { name: 'solar', desc: 'Solar', hasCoolsetpoint: htypes.hasCoolSetpoint }]]);
1877
+ else if (solarInstalled) sys.board.valueMaps.heatSources.merge([[3, { name: 'solar', desc: 'Solar', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
1821
1878
  if (heatPumpInstalled && (gasHeaterInstalled || solarInstalled)) sys.board.valueMaps.heatSources.merge([[9, { name: 'heatpump', desc: 'Heatpump Only' }], [25, { name: 'heatpumppref', desc: 'Heat Pump Pref' }]]);
1822
1879
  else if (heatPumpInstalled) sys.board.valueMaps.heatSources.merge([[9, { name: 'heatpump', desc: 'Heat Pump' }]]);
1823
1880
  if (ultratempInstalled && (gasHeaterInstalled || heatPumpInstalled)) sys.board.valueMaps.heatSources.merge([[5, { name: 'ultratemp', desc: 'UltraTemp Only', hasCoolSetpoint: htypes.hasCoolSetpoint }], [6, { name: 'ultratemppref', desc: 'UltraTemp Pref', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
1824
1881
  else if (ultratempInstalled) sys.board.valueMaps.heatSources.merge([[5, { name: 'ultratemp', desc: 'UltraTemp', hasCoolSetpoint: htypes.hasCoolSetpoint }]]);
1825
1882
  sys.board.valueMaps.heatSources.merge([[0, { name: 'nochange', desc: 'No Change' }]]);
1826
1883
 
1827
-
1884
+
1828
1885
  if (gasHeaterInstalled) sys.board.valueMaps.heatModes.merge([[2, { name: 'heater', desc: 'Heater' }]]);
1829
1886
  if (mastertempInstalled) sys.board.valueMaps.heatModes.merge([[11, { name: 'mtheater', desc: 'MasterTemp' }]]);
1830
1887
  if (solarInstalled && (gasHeaterInstalled || heatPumpInstalled || mastertempInstalled)) sys.board.valueMaps.heatModes.merge([[3, { name: 'solar', desc: 'Solar Only' }], [4, { name: 'solarpref', desc: 'Solar Preferred' }]]);
@@ -31,7 +31,7 @@ export class SunTouchBoard extends EasyTouchBoard {
31
31
  constructor(system: PoolSystem) {
32
32
  super(system); // graph chain to EasyTouchBoard constructor.
33
33
  this.valueMaps.expansionBoards = new byteValueMap([
34
- [41, { name: 'shared', part: '520820', desc: 'Pool and Spa controller', bodies: 2, valves: 4, circuits: 5, single: false, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }],
34
+ [41, { name: 'stshared', part: '520820', desc: 'Pool and Spa controller', bodies: 2, valves: 4, circuits: 5, single: false, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }],
35
35
  [40, { name: 'stsingle', part: '520819', desc: 'Pool or Spa controller', bodies: 2, valves: 4, circuits: 5, single: true, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }]
36
36
  ]);
37
37
  this._statusInterval = -1;
@@ -222,8 +222,10 @@ class SunTouchConfigQueue extends TouchConfigQueue {
222
222
  // 196 - [0-2]
223
223
  // 198 - [0-2]
224
224
  // 199 - [0-2]
225
+ // 200 - Heat/Temperature Status
225
226
  // 201 - [0-2]
226
227
  // 202 - [0-2] - Custom Names
228
+ // 203 - Circuit Functions
227
229
  // 204 - [0-2]
228
230
  // 205 - [0-2]
229
231
  // 206 - [0-2]
@@ -238,13 +240,16 @@ class SunTouchConfigQueue extends TouchConfigQueue {
238
240
  // 218 - [0-2]
239
241
  // 219 - [0-2]
240
242
  // 220 - [0-2]
243
+ // 221 - Valve Assignments
241
244
  // 223 - [0-2]
242
245
  // 224 - [1-2]
243
- // 226 - [0]
246
+ // 225 - Spa side remote
247
+ // 226 - [0] - Solar/HeatPump config
244
248
  // 228 - [0-2]
245
249
  // 229 - [0-2]
246
250
  // 230 - [0-2]
247
251
  // 231 - [0-2]
252
+ // 232 - Settings (Amazed that there is none of this)
248
253
  // 233 - [0-2]
249
254
  // 234 - [0-2]
250
255
  // 235 - [0-2]
@@ -264,6 +269,7 @@ class SunTouchConfigQueue extends TouchConfigQueue {
264
269
  // 249 - [0-2]
265
270
  // 250 - [0-2]
266
271
  // 251 - [0-2]
272
+ // 253 - Software Version
267
273
 
268
274
  this.queueItems(GetTouchConfigCategories.version); // 252
269
275
  this.queueItems(GetTouchConfigCategories.dateTime, [0]); //197