nodejs-poolcontroller 7.5.1 → 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.
@@ -7,7 +7,7 @@ published by the Free Software Foundation, either version 3 of the
7
7
  License, or (at your option) any later version.
8
8
 
9
9
  This program is distributed in the hope that it will be useful,
10
- but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of1
11
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
12
  GNU Affero General Public License for more details.
13
13
 
@@ -26,7 +26,7 @@ import { sys, Chemical, ChemController } from './Equipment';
26
26
  import { versionCheck } from '../config/VersionCheck';
27
27
  import { EquipmentStateMessage } from './comms/messages/status/EquipmentStateMessage';
28
28
  import { DataLogger, DataLoggerEntry, IDataLoggerEntry } from '../logger/DataLogger';
29
-
29
+ import { delayMgr } from './Lockouts';
30
30
 
31
31
  export class State implements IState {
32
32
  statePath: string;
@@ -140,6 +140,7 @@ export class State implements IState {
140
140
  _state.filters = this.filters.getExtended();
141
141
  _state.schedules = this.schedules.getExtended();
142
142
  _state.chemControllers = this.chemControllers.getExtended();
143
+ _state.delays = delayMgr.serialize();
143
144
  return _state;
144
145
  }
145
146
  else {
@@ -153,7 +154,6 @@ export class State implements IState {
153
154
  return extend(true, [], this.data[section] || []);
154
155
  else
155
156
  return extend(true, {}, this.data[section] || {});
156
-
157
157
  }
158
158
  }
159
159
  public async stopAsync() {
@@ -212,7 +212,8 @@ export class State implements IState {
212
212
  sunrise: self.data.sunrise || '',
213
213
  sunset: self.data.sunset || '',
214
214
  alias: sys.general.alias,
215
- freeze: utils.makeBool(self.data.freeze)
215
+ freeze: utils.makeBool(self.data.freeze),
216
+ valveMode: self.data.valveMode || {},
216
217
  };
217
218
  }
218
219
  public emitAllEquipmentChanges() {
@@ -287,6 +288,14 @@ export class State implements IState {
287
288
  this.hasChanged = true;
288
289
  }
289
290
  }
291
+ public get valveMode(): number { return typeof this.data.valveMode !== 'undefined' ? this.data.valveMode.val : 0; }
292
+ public set valveMode(val: number) {
293
+ let m = sys.board.valueMaps.valveModes.transform(val);
294
+ if (m.val !== this.valveMode) {
295
+ this.data.valveMode = m;
296
+ this.hasChanged = true;
297
+ }
298
+ }
290
299
  public get freeze(): boolean { return this.data.freeze === true; }
291
300
  public set freeze(val: boolean) {
292
301
  if (this.data.freeze !== val) {
@@ -486,12 +495,15 @@ export interface ICircuitState {
486
495
  name: string;
487
496
  nameId?: number;
488
497
  isOn: boolean;
498
+ startTime?: Timestamp;
489
499
  endTime: Timestamp;
490
500
  lightingTheme?: number;
491
501
  emitEquipmentChange();
492
502
  get(bCopy?: boolean);
493
503
  showInFeatures?: boolean;
494
504
  isActive?: boolean;
505
+ startDelay?: boolean;
506
+ stopDelay?: boolean;
495
507
  }
496
508
 
497
509
  interface IEqStateCreator<T> { ctor(data: any, name: string, parent?): T; }
@@ -883,7 +895,9 @@ export class PumpState extends EqState {
883
895
  if (typeof this.data.status === 'undefined') {
884
896
  this.data.status = { name: 'ok', desc: 'Ok', val: 0 };
885
897
  }
898
+ if (typeof this.data.pumpOnDelay === 'undefined') this.data.pumpOnDelay = false;
886
899
  }
900
+ private _pumpOnDelayTimer: NodeJS.Timeout;
887
901
  private _threshold = 0.05;
888
902
  private exceedsThreshold(origVal: number, newVal: number) {
889
903
  return Math.abs((newVal - origVal) / origVal) > this._threshold;
@@ -943,6 +957,23 @@ export class PumpState extends EqState {
943
957
  }
944
958
  public get time(): number { return this.data.time; }
945
959
  public set time(val: number) { this.setDataVal('time', val, false); }
960
+ public get pumpOnDelay() { return this.data.pumpOnDelay; }
961
+ public set pumpOnDelay(val: boolean) {
962
+ if (val === false) {
963
+ if (typeof this._pumpOnDelayTimer !== 'undefined') clearTimeout(this._pumpOnDelayTimer);
964
+ this._pumpOnDelayTimer = undefined;
965
+ }
966
+ this.setDataVal('pumpOnDelay', val);
967
+ }
968
+ public setPumpOnDelayTimeout(delay: number) {
969
+ this.pumpOnDelay = true;
970
+ logger.info(`Pump ON Delay ${this.name} for ${delay / 1000} seconds`);
971
+ this._pumpOnDelayTimer = setTimeout(() => {
972
+ logger.info(`Pump ON Delay ${this.name} expired`);
973
+ this.pumpOnDelay = false;
974
+ }, delay);
975
+ }
976
+
946
977
  public getExtended() {
947
978
  let pump = this.get(true);
948
979
  let cpump = sys.pumps.getItemById(pump.id);
@@ -1262,6 +1293,16 @@ export class BodyTempStateCollection extends EqStateCollection<BodyTempState> {
1262
1293
  }
1263
1294
  return undefined;
1264
1295
  }
1296
+ public getBodyByCircuitId(circuitId: number) {
1297
+ let b = this.data.find(x => x.circuit === circuitId);
1298
+ if (typeof b === 'undefined') {
1299
+ let circ = sys.circuits.getItemById(circuitId);
1300
+ // Find our body by circuit function.
1301
+ let cfn = sys.board.valueMaps.circuitFunctions.get(circ.type);
1302
+ if (typeof cfn.body !== 'undefined') b = this.data.find(x => x.id === cfn.body);
1303
+ }
1304
+ return typeof b !== 'undefined' ? this.createItem(b) : undefined;
1305
+ }
1265
1306
  public cleanupState() {
1266
1307
  for (let i = this.data.length - 1; i >= 0; i--) {
1267
1308
  if (isNaN(this.data[i].id)) this.data.splice(i, 1);
@@ -1289,6 +1330,9 @@ export class BodyTempState extends EqState {
1289
1330
  public initData() {
1290
1331
  if (typeof this.data.heaterOptions === 'undefined') this.data.heaterOptions = { total: 0 };
1291
1332
  if (typeof this.data.isCovered === 'undefined') this.data.isCovered = false;
1333
+ if (typeof this.heaterCooldownDelay === 'undefined') this.data.heaterCooldownDelay = false;
1334
+ if (typeof this.data.startDelay === 'undefined') this.data.startDelay = false;
1335
+ if (typeof this.data.stopDelay === 'undefined') this.data.stopDelay = false;
1292
1336
  }
1293
1337
  public get id(): number { return this.data.id; }
1294
1338
  public set id(val: number) { this.setDataVal('id', val); }
@@ -1327,8 +1371,19 @@ export class BodyTempState extends EqState {
1327
1371
  public set coolSetpoint(val: number) { this.setDataVal('coolSetpoint', val); }
1328
1372
  public get isOn(): boolean { return this.data.isOn; }
1329
1373
  public set isOn(val: boolean) { this.setDataVal('isOn', val); }
1374
+ public get startDelay(): boolean { return this.data.startDelay; }
1375
+ public set startDelay(val: boolean) { this.setDataVal('startDelay', val); }
1376
+ public get stopDelay(): boolean { return this.data.stopDelay; }
1377
+ public set stopDelay(val: boolean) { this.setDataVal('stopDelay', val); }
1378
+
1330
1379
  public get isCovered(): boolean { return this.data.isCovered; }
1331
1380
  public set isCovered(val: boolean) { this.setDataVal('isCovered', val); }
1381
+ // RKS: Heater cooldown delays force the current valve and body configuration until the
1382
+ // heater cooldown expires. This occurs at the pool level but it is triggered by the heater attached
1383
+ // to the body. Unfortunately, I think we can only detect this condition in Nixie as there really isn't an
1384
+ // indicator with Pentair OCPs. This is triggered in NixieBoard and managed by the delayMgr.
1385
+ public get heaterCooldownDelay(): boolean { return this.data.heaterCooldownDelay; }
1386
+ public set heaterCooldownDelay(val: boolean) { this.setDataVal('heaterCooldownDelay', val); }
1332
1387
  public emitData(name: string, data: any) { webApp.emitToClients('body', this.data); }
1333
1388
  // RKS: This is a very interesting object because we have a varied object. Type safety rules should not apply
1334
1389
  // here as the heater types are specific to the installed equipment. The reason is because it has no meaning without the body and the calculation of it should
@@ -1414,16 +1469,36 @@ export class HeaterStateCollection extends EqStateCollection<HeaterState> {
1414
1469
  }
1415
1470
  export class HeaterState extends EqState {
1416
1471
  public dataName: string = 'heater';
1472
+ public initData() {
1473
+ if (typeof this.data.startupDelay === 'undefined') this.data.startupDelay = false;
1474
+ if (typeof this.data.shutdownDelay === 'undefined') this.data.shutdownDelay = false;
1475
+ }
1417
1476
  public get id(): number { return this.data.id; }
1418
1477
  public set id(val: number) { this.data.id = val; }
1419
1478
  public get name(): string { return this.data.name; }
1420
1479
  public set name(val: string) { this.setDataVal('name', val); }
1421
1480
  public get isOn(): boolean { return this.data.isOn; }
1422
- public set isOn(val: boolean) { this.setDataVal('isOn', val); }
1481
+ public set isOn(val: boolean) {
1482
+ if (val !== this.data.isOn) {
1483
+ if (val) this.startTime = new Timestamp();
1484
+ else this.endTime = new Timestamp();
1485
+ }
1486
+ this.setDataVal('isOn', val);
1487
+ }
1488
+ public get startTime(): Timestamp {
1489
+ if (typeof this.data.startTime === 'undefined') return undefined;
1490
+ return new Timestamp(this.data.startTime);
1491
+ }
1492
+ public set startTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('startTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('startTime', undefined); }
1493
+
1494
+ public get endTime(): Timestamp {
1495
+ if (typeof this.data.endTime === 'undefined') return undefined;
1496
+ return new Timestamp(this.data.endTime);
1497
+ }
1498
+ public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
1499
+
1423
1500
  public get isCooling(): boolean { return this.data.isCooling; }
1424
1501
  public set isCooling(val: boolean) { this.setDataVal('isCooling', val); }
1425
- //public get isVirtual(): boolean { return this.data.isVirtual; }
1426
- //public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1427
1502
  public get type(): number | any { return typeof this.data.type !== 'undefined' ? this.data.type.val : 0; }
1428
1503
  public set type(val: number | any) {
1429
1504
  if (this.type !== val) {
@@ -1438,6 +1513,13 @@ export class HeaterState extends EqState {
1438
1513
  this.hasChanged = true;
1439
1514
  }
1440
1515
  }
1516
+ public get startupDelay(): boolean { return this.data.startupDelay; }
1517
+ public set startupDelay(val: boolean) { this.setDataVal('startupDelay', val); }
1518
+ public get shutdownDelay(): boolean { return this.data.shutdownDelay; }
1519
+ public set shutdownDelay(val: boolean) { this.setDataVal('shutdownDelay', val); }
1520
+ public get bodyId(): number { return this.data.bodyId || 0 }
1521
+ public set bodyId(val: number) { this.setDataVal('bodyId', val); }
1522
+
1441
1523
  }
1442
1524
  export class FeatureStateCollection extends EqStateCollection<FeatureState> {
1443
1525
  public createItem(data: any): FeatureState { return new FeatureState(data); }
@@ -1518,6 +1600,8 @@ export class VirtualCircuitState extends EqState implements ICircuitState {
1518
1600
  return new Timestamp(this.data.endTime);
1519
1601
  }
1520
1602
  public set endTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('endTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('endTime', undefined); }
1603
+ public get isActive(): boolean { return this.data.isActive; }
1604
+ public set isActive(val: boolean) { this.setDataVal('isActive', val); }
1521
1605
  }
1522
1606
  export class VirtualCircuitStateCollection extends EqStateCollection<VirtualCircuitState> {
1523
1607
  public createItem(data: any): VirtualCircuitState { return new VirtualCircuitState(data); }
@@ -1573,7 +1657,11 @@ export class CircuitState extends EqState implements ICircuitState {
1573
1657
  public get showInFeatures(): boolean { return this.data.showInFeatures; }
1574
1658
  public set showInFeatures(val: boolean) { this.setDataVal('showInFeatures', val); }
1575
1659
  public get isOn(): boolean { return this.data.isOn; }
1576
- public set isOn(val: boolean) { this.setDataVal('isOn', val); }
1660
+ public set isOn(val: boolean) {
1661
+ if (val && !this.data.isOn) this.startTime = new Timestamp();
1662
+ else if (!val) this.startTime = undefined;
1663
+ this.setDataVal('isOn', val);
1664
+ }
1577
1665
  public get type() { return typeof (this.data.type) !== 'undefined' ? this.data.type.val : -1; }
1578
1666
  public set type(val: number) {
1579
1667
  if (this.type !== val) {
@@ -1599,6 +1687,12 @@ export class CircuitState extends EqState implements ICircuitState {
1599
1687
  this.hasChanged = true;
1600
1688
  }
1601
1689
  }
1690
+ public get startTime(): Timestamp {
1691
+ if (typeof this.data.startTime === 'undefined') return undefined;
1692
+ return new Timestamp(this.data.startTime);
1693
+ }
1694
+ public set startTime(val: Timestamp) { typeof val !== 'undefined' ? this.setDataVal('startTime', Timestamp.toISOLocal(val.toDate())) : this.setDataVal('startTime', undefined); }
1695
+
1602
1696
  public get endTime(): Timestamp {
1603
1697
  if (typeof this.data.endTime === 'undefined') return undefined;
1604
1698
  return new Timestamp(this.data.endTime);
@@ -1610,6 +1704,16 @@ export class CircuitState extends EqState implements ICircuitState {
1610
1704
  public set freezeProtect(val: boolean) { this.setDataVal('freezeProtect', val); }
1611
1705
  public get isActive(): boolean { return this.data.isActive; }
1612
1706
  public set isActive(val: boolean) { this.setDataVal('isActive', val); }
1707
+ // The properties below are for delays and lockouts. Manual or scheduled
1708
+ // actions cannot be performed when the flags below are set.
1709
+ public get startDelay(): boolean { return this.data.startDelay; }
1710
+ public set startDelay(val: boolean) { this.setDataVal('startDelay', val); }
1711
+ public get stopDelay(): boolean { return this.data.stopDelay; }
1712
+ public set stopDelay(val: boolean) { this.setDataVal('stopDelay', val); }
1713
+ public get lockoutOn(): boolean { return this.data.lockoutOn; }
1714
+ public set lockoutOn(val: boolean) { this.setDataVal('lockoutOn', val); }
1715
+ public get lockoutOff(): boolean { return this.data.lockoutOff; }
1716
+ public set lockoutOff(val: boolean) { this.setDataVal('lockoutOff', val); }
1613
1717
  }
1614
1718
  export class ValveStateCollection extends EqStateCollection<ValveState> {
1615
1719
  public createItem(data: any): ValveState { return new ValveState(data); }
@@ -1253,18 +1253,20 @@ class TouchBodyCommands extends BodyCommands {
1253
1253
  }
1254
1254
  }
1255
1255
  export class TouchCircuitCommands extends CircuitCommands {
1256
- public getLightThemes(type?: number): any[] {
1257
- let themes = sys.board.valueMaps.lightThemes.toArray();
1258
- if (typeof type === 'undefined') return themes;
1259
- switch (type) {
1260
- case 8: // Magicstream
1261
- return themes.filter(theme => theme.type === 'magicstream');
1262
- case 16: // Intellibrite
1263
- return themes.filter(theme => theme.type === 'intellibrite');
1264
- default:
1265
- return [];
1266
- }
1267
- }
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
+ //}
1268
1270
  public async setCircuitAsync(data: any): Promise<ICircuit> {
1269
1271
  try {
1270
1272
  // example [255,0,255][165,33,16,34,139,5][17,14,209,0,0][2,120]
@@ -1336,7 +1338,7 @@ export class TouchCircuitCommands extends CircuitCommands {
1336
1338
  data.functionId = sys.board.valueMaps.circuitFunctions.getValue('notused');
1337
1339
  return this.setCircuitAsync(data);
1338
1340
  }
1339
- public async setCircuitStateAsync(id: number, val: boolean): Promise<ICircuitState> {
1341
+ public async setCircuitStateAsync(id: number, val: boolean, ignoreDelays?: boolean): Promise<ICircuitState> {
1340
1342
  if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError('Circuit or Feature id not valid', id, 'Circuit'));
1341
1343
  let c = sys.circuits.getInterfaceById(id);
1342
1344
  if (c.master !== 0) return await super.setCircuitStateAsync(id, val);
@@ -47,18 +47,18 @@ export class IntelliCenterBoard extends SystemBoard {
47
47
  this.valueMaps.circuitFunctions = new byteValueMap([
48
48
  [0, { name: 'generic', desc: 'Generic' }],
49
49
  [1, { name: 'spillway', desc: 'Spillway' }],
50
- [2, { name: 'mastercleaner', desc: 'Master Cleaner' }],
50
+ [2, { name: 'mastercleaner', desc: 'Master Cleaner', body: 1 }],
51
51
  [3, { name: 'chemrelay', desc: 'Chem Relay' }],
52
52
  [4, { name: 'light', desc: 'Light', isLight: true }],
53
- [5, { name: 'intellibrite', desc: 'Intellibrite', isLight: true }],
54
- [6, { name: 'globrite', desc: 'GloBrite', isLight: true }],
53
+ [5, { name: 'intellibrite', desc: 'Intellibrite', isLight: true, theme: 'intellibrite' }],
54
+ [6, { name: 'globrite', desc: 'GloBrite', isLight: true, themes: 'intellibrite' }],
55
55
  [7, { name: 'globritewhite', desc: 'GloBrite White', isLight: true }],
56
- [8, { name: 'magicstream', desc: 'Magicstream', isLight: true }],
56
+ [8, { name: 'magicstream', desc: 'Magicstream', isLight: true, theme: 'intellibrite' }],
57
57
  [9, { name: 'dimmer', desc: 'Dimmer', isLight: true }],
58
- [10, { name: 'colorcascade', desc: 'ColorCascade', isLight: true }],
59
- [11, { name: 'mastercleaner2', desc: 'Master Cleaner 2' }],
60
- [12, { name: 'pool', desc: 'Pool', hasHeatSource: true }],
61
- [13, { name: 'spa', desc: 'Spa', hasHeatSource: true }]
58
+ [10, { name: 'colorcascade', desc: 'ColorCascade', isLight: true, theme: 'intellibrite' }],
59
+ [11, { name: 'mastercleaner2', desc: 'Master Cleaner 2', body: 2 }],
60
+ [12, { name: 'pool', desc: 'Pool', hasHeatSource: true, body: 1 }],
61
+ [13, { name: 'spa', desc: 'Spa', hasHeatSource: true, body: 2 }]
62
62
  ]);
63
63
  this.valueMaps.pumpTypes = new byteValueMap([
64
64
  [1, { name: 'ss', desc: 'Single Speed', maxCircuits: 0, hasAddress: false, hasBody:true }],
@@ -95,8 +95,8 @@ export class IntelliCenterBoard extends SystemBoard {
95
95
  [3, { name: 'heatpump', desc: 'Heat Pump', hasAddress: true }],
96
96
  [4, { name: 'ultratemp', desc: 'UltraTemp', hasAddress: true, hasCoolSetpoint: true }],
97
97
  [5, { name: 'hybrid', desc: 'Hybrid', hasAddress: true }],
98
- [6, { name: 'maxetherm', desc: 'Max-E-Therm', hasAddress: true }],
99
- [7, { name: 'mastertemp', desc: 'MasterTemp', hasAddress: true }]
98
+ [6, { name: 'mastertemp', desc: 'MasterTemp', hasAddress: true }],
99
+ [7, { name: 'maxetherm', desc: 'Max-E-Therm', hasAddress: true }],
100
100
  ]);
101
101
 
102
102
 
@@ -173,18 +173,18 @@ export class IntelliCenterBoard extends SystemBoard {
173
173
  [2, { name: 'sunset', desc: 'Sunset' }]
174
174
  ]);
175
175
  this.valueMaps.lightThemes = new byteValueMap([
176
- [0, { name: 'white', desc: 'White', sequence: 11 }],
177
- [1, { name: 'green', desc: 'Green', sequence: 9 }],
178
- [2, { name: 'blue', desc: 'Blue', sequence: 8 }],
179
- [3, { name: 'magenta', desc: 'Magenta', sequence: 12 }],
180
- [4, { name: 'red', desc: 'Red', sequence: 10 }],
181
- [5, { name: 'sam', desc: 'SAm Mode', sequence: 1 }],
182
- [6, { name: 'party', desc: 'Party', sequence: 2 }],
183
- [7, { name: 'romance', desc: 'Romance', sequence: 3 }],
184
- [8, { name: 'caribbean', desc: 'Caribbean', sequence: 4 }],
185
- [9, { name: 'american', desc: 'American', sequence: 5 }],
186
- [10, { name: 'sunset', desc: 'Sunset', sequence: 6 }],
187
- [11, { name: 'royal', desc: 'Royal', sequence: 7 }],
176
+ [0, { name: 'white', desc: 'White', sequence: 11, types:['intellibrite', 'magicstream'] }],
177
+ [1, { name: 'green', desc: 'Green', sequence: 9, types: ['intellibrite', 'magicstream'] }],
178
+ [2, { name: 'blue', desc: 'Blue', sequence: 8, types: ['intellibrite', 'magicstream'] }],
179
+ [3, { name: 'magenta', desc: 'Magenta', sequence: 12, types: ['intellibrite', 'magicstream'] }],
180
+ [4, { name: 'red', desc: 'Red', sequence: 10, types: ['intellibrite', 'magicstream'] }],
181
+ [5, { name: 'sam', desc: 'SAm Mode', sequence: 1, types: ['intellibrite', 'magicstream'] }],
182
+ [6, { name: 'party', desc: 'Party', sequence: 2, types: ['intellibrite', 'magicstream'] }],
183
+ [7, { name: 'romance', desc: 'Romance', sequence: 3, types: ['intellibrite', 'magicstream'] }],
184
+ [8, { name: 'caribbean', desc: 'Caribbean', sequence: 4, types: ['intellibrite', 'magicstream'] }],
185
+ [9, { name: 'american', desc: 'American', sequence: 5, types: ['intellibrite', 'magicstream'] }],
186
+ [10, { name: 'sunset', desc: 'Sunset', sequence: 6, types: ['intellibrite', 'magicstream'] }],
187
+ [11, { name: 'royal', desc: 'Royal', sequence: 7, types: ['intellibrite', 'magicstream'] }],
188
188
  [255, { name: 'none', desc: 'None' }]
189
189
  ]);
190
190
  this.valueMaps.lightColors = new byteValueMap([
@@ -1292,7 +1292,7 @@ class IntelliCenterSystemCommands extends SystemCommands {
1292
1292
  let out = Outbound.create({
1293
1293
  action: 168,
1294
1294
  retries: 5,
1295
- payload: [12, 0, 10, parseInt(obj.timeZone, 10)],
1295
+ payload: [12, 0, 13, parseInt(obj.timeZone, 10)],
1296
1296
  response: IntelliCenterBoard.getAckResponse(168),
1297
1297
  onComplete: (err, msg) => {
1298
1298
  if (err) reject(err);
@@ -2002,17 +2002,18 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
2002
2002
  }
2003
2003
  return Promise.resolve(sgroup);
2004
2004
  }
2005
- public getLightThemes(type: number): any[] {
2006
- switch (type) {
2007
- case 5: // Intellibrite
2008
- case 6: // Globrite
2009
- case 8: // Magicstream
2010
- case 10: // ColorCascade
2011
- return sys.board.valueMaps.lightThemes.toArray();
2012
- default:
2013
- return [];
2014
- }
2015
- }
2005
+ // 12-01-21 RKS: This has been deprecated. This allows for multiple vendor light themes driven by the metadata on the valuemaps.
2006
+ //public getLightThemes(type: number): any[] {
2007
+ // switch (type) {
2008
+ // case 5: // Intellibrite
2009
+ // case 6: // Globrite
2010
+ // case 8: // Magicstream
2011
+ // case 10: // ColorCascade
2012
+ // return sys.board.valueMaps.lightThemes.toArray();
2013
+ // default:
2014
+ // return [];
2015
+ // }
2016
+ //}
2016
2017
  private async verifyVersionAsync(): Promise<boolean> {
2017
2018
  return new Promise<boolean>((resolve, reject) => {
2018
2019
  let out = Outbound.create({
@@ -2072,7 +2073,7 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
2072
2073
  conn.queueSendMessage(out);
2073
2074
  });
2074
2075
  }
2075
- public async setCircuitStateAsync(id: number, val: boolean): Promise<ICircuitState> {
2076
+ public async setCircuitStateAsync(id: number, val: boolean, ignoreDelays?: boolean): Promise<ICircuitState> {
2076
2077
  let c = sys.circuits.getInterfaceById(id);
2077
2078
  if (c.master !== 0) return await super.setCircuitStateAsync(id, val);
2078
2079
  // As of 1.047 there is a sequence to this.
@@ -2523,7 +2524,7 @@ class IntelliCenterChlorinatorCommands extends ChlorinatorCommands {
2523
2524
  if (typeof obj.disabled !== 'undefined') chlor.disabled = utils.makeBool(obj.disabled);
2524
2525
  if (typeof chlor.body === 'undefined') chlor.body = obj.body || 32;
2525
2526
  // Verify the data.
2526
- let body = sys.board.bodies.mapBodyAssociation(chlor.body);
2527
+ let body = sys.board.bodies.mapBodyAssociation(typeof obj.body === 'undefined' ? chlor.body || 0 : obj.body);
2527
2528
  if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Chlorinator body association is not valid: ${chlor.body}`, 'chlorinator', chlor.body));
2528
2529
  if (poolSetpoint > 100 || poolSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator poolSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.poolSetpoint));
2529
2530
  if (spaSetpoint > 100 || spaSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator spaSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.spaSetpoint));
@@ -2542,6 +2543,7 @@ class IntelliCenterChlorinatorCommands extends ChlorinatorCommands {
2542
2543
  else {
2543
2544
  let schlor = state.chlorinators.getItemById(id, true);
2544
2545
  let cchlor = sys.chlorinators.getItemById(id, true);
2546
+ schlor.body = chlor.body = body.val;
2545
2547
  chlor.disabled = disabled;
2546
2548
  chlor.model = model;
2547
2549
  schlor.type = chlor.type = chlorType;
@@ -3020,11 +3022,22 @@ class IntelliCenterBodyCommands extends BodyCommands {
3020
3022
  public async setHeatModeAsync(body: Body, mode: number): Promise<BodyTempState> {
3021
3023
  return new Promise<BodyTempState>((resolve, reject) => {
3022
3024
  const self = this;
3023
- let byte2 = 18;
3024
- let mode1 = sys.bodies.getItemById(1).setPoint || 100;
3025
- let mode2 = sys.bodies.getItemById(2).setPoint || 100;
3026
- let mode3 = sys.bodies.getItemById(3).setPoint || 100;
3027
- let mode4 = sys.bodies.getItemById(4).setPoint || 100;
3025
+ let byte2 = 22;
3026
+ let body1 = sys.bodies.getItemById(1);
3027
+ let body2 = sys.bodies.getItemById(2);
3028
+
3029
+ let heat1 = body1.heatSetpoint || 78;
3030
+ let cool1 = body1.coolSetpoint || 100;
3031
+ let heat2 = body2.heatSetpoint || 78;
3032
+ let cool2 = body2.coolSetpoint || 103;
3033
+
3034
+ let mode1 = body1.heatMode || 1;
3035
+ let mode2 = body2.heatMode || 1;
3036
+ let bitopts = 0;
3037
+ if (sys.general.options.clockSource) bitopts += 32;
3038
+ if (sys.general.options.clockMode === 24) bitopts += 64;
3039
+ if (sys.general.options.adjustDST) bitopts += 128;
3040
+
3028
3041
  switch (body.id) {
3029
3042
  case 1:
3030
3043
  byte2 = 22;
@@ -3034,19 +3047,12 @@ class IntelliCenterBodyCommands extends BodyCommands {
3034
3047
  byte2 = 23;
3035
3048
  mode2 = mode;
3036
3049
  break;
3037
- case 3:
3038
- byte2 = 24;
3039
- mode3 = mode;
3040
- break;
3041
- case 4:
3042
- byte2 = 25;
3043
- mode4 = mode;
3044
- break;
3045
3050
  }
3046
3051
  let out = Outbound.create({
3047
3052
  action: 168,
3048
- payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 100, 100, 100, 100, mode1, mode2, mode3, mode4, 15, 0
3049
- , 0, 0, 0, 100, 0, 0, 0, 0, 0, 0],
3053
+ payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, bitopts, 89, 27, 110, 3, 0, 0,
3054
+ heat1, cool1, heat2, cool2, mode1, mode2, 0, 0, 15,
3055
+ sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0],
3050
3056
  retries: 5,
3051
3057
  response: IntelliCenterBoard.getAckResponse(168),
3052
3058
  onComplete: (err, msg) => {
@@ -3067,31 +3073,25 @@ class IntelliCenterBodyCommands extends BodyCommands {
3067
3073
  let byte2 = 18;
3068
3074
  let body1 = sys.bodies.getItemById(1);
3069
3075
  let body2 = sys.bodies.getItemById(2);
3070
- let body3 = sys.bodies.getItemById(3);
3071
- let body4 = sys.bodies.getItemById(4);
3072
3076
 
3073
- let temp1 = sys.bodies.getItemById(1).setPoint || 100;
3074
- let temp2 = sys.bodies.getItemById(2).setPoint || 100;
3075
- let temp3 = sys.bodies.getItemById(3).setPoint || 100;
3076
- let temp4 = sys.bodies.getItemById(4).setPoint || 100;
3077
+ let heat1 = body1.heatSetpoint || 78;
3078
+ let cool1 = body1.coolSetpoint || 100;
3079
+ let heat2 = body2.heatSetpoint || 78;
3080
+ let cool2 = body2.coolSetpoint || 103;
3077
3081
  switch (body.id) {
3078
3082
  case 1:
3079
3083
  byte2 = 18;
3080
- temp1 = setPoint;
3084
+ heat1 = setPoint;
3081
3085
  break;
3082
3086
  case 2:
3083
3087
  byte2 = 20;
3084
- temp2 = setPoint;
3085
- break;
3086
- case 3:
3087
- byte2 = 19;
3088
- temp3 = setPoint;
3089
- break;
3090
- case 4:
3091
- byte2 = 21;
3092
- temp4 = setPoint;
3088
+ heat2 = setPoint;
3093
3089
  break;
3094
3090
  }
3091
+ let bitopts = 0;
3092
+ if (sys.general.options.clockSource) bitopts += 32;
3093
+ if (sys.general.options.clockMode === 24) bitopts += 64;
3094
+ if (sys.general.options.adjustDST) bitopts += 128;
3095
3095
  // 6 15 17 18 21 22 24 25
3096
3096
  //[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 89, 100, 98, 100, 0, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][5, 243]
3097
3097
  //[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 235, 27, 167, 1, 0, 0, 89, 81, 98, 103, 5, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][6, 48]
@@ -3099,8 +3099,8 @@ class IntelliCenterBodyCommands extends BodyCommands {
3099
3099
  action: 168,
3100
3100
  response: IntelliCenterBoard.getAckResponse(168),
3101
3101
  retries: 5,
3102
- payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0,
3103
- temp1, temp3, temp2, temp4, body1.heatMode || 0, body2.heatMode || 0, body3.heatMode || 0, body4.heatMode || 0, 15,
3102
+ payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, bitopts, 89, 27, 110, 3, 0, 0,
3103
+ heat1, cool1, heat2, cool2, body1.heatMode || 1, body2.heatMode || 1, 0, 0, 15,
3104
3104
  sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0]
3105
3105
  });
3106
3106
  return new Promise<BodyTempState>((resolve, reject) => {
@@ -3116,23 +3116,14 @@ class IntelliCenterBodyCommands extends BodyCommands {
3116
3116
  });
3117
3117
  }
3118
3118
  public async setCoolSetpointAsync(body: Body, setPoint: number): Promise<BodyTempState> {
3119
- //[165, 1, 15, 16, 168, 41][0, 0, 19, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 110, 30, 188, 3, 0, 0, 76, 99, 78, 100, 5, 5, 0, 0, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0][5, 33]
3120
- let byte2 = 19;
3119
+ let byte2 = 18;
3121
3120
  let body1 = sys.bodies.getItemById(1);
3122
3121
  let body2 = sys.bodies.getItemById(2);
3123
- let body3 = sys.bodies.getItemById(3);
3124
- let body4 = sys.bodies.getItemById(3);
3125
3122
 
3126
- let temp1 = sys.bodies.getItemById(1).setPoint || 100;
3127
- let cool1 = sys.bodies.getItemById(1).coolSetpoint || 100;
3128
- let temp2 = sys.bodies.getItemById(2).setPoint || 100;
3129
- let cool2 = sys.bodies.getItemById(2).coolSetpoint || 100;
3130
-
3131
- //Them
3132
- //[165, 63, 15, 16, 168, 41][0, 0, 19, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 59, 30, 5, 5, 0, 0, 90, 102, 98, 81, 3, 1, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][4, 129]
3133
- //Us
3134
- //[165, 63, 15, 33, 168, 40][0, 0, 19, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 5, 5, 0, 0, 90, 103, 98, 81, 3, 1, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][5, 249]
3135
- //[165, 63, 15, 33, 168, 40][0, 0, 19, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 90, 103, 98, 81, 3, 1, 0, 0, 15, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][5, 249]
3123
+ let heat1 = body1.heatSetpoint || 78;
3124
+ let cool1 = body1.coolSetpoint || 100;
3125
+ let heat2 = body2.heatSetpoint || 78;
3126
+ let cool2 = body2.coolSetpoint || 103;
3136
3127
  switch (body.id) {
3137
3128
  case 1:
3138
3129
  byte2 = 19;
@@ -3143,6 +3134,10 @@ class IntelliCenterBodyCommands extends BodyCommands {
3143
3134
  cool2 = setPoint;
3144
3135
  break;
3145
3136
  }
3137
+ let bitopts = 0;
3138
+ if (sys.general.options.clockSource) bitopts += 32;
3139
+ if (sys.general.options.clockMode === 24) bitopts += 64;
3140
+ if (sys.general.options.adjustDST) bitopts += 128;
3146
3141
  // 6 15 17 18 21 22 24 25
3147
3142
  //[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 89, 100, 98, 100, 0, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][5, 243]
3148
3143
  //[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 235, 27, 167, 1, 0, 0, 89, 81, 98, 103, 5, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][6, 48]
@@ -3150,8 +3145,8 @@ class IntelliCenterBodyCommands extends BodyCommands {
3150
3145
  action: 168,
3151
3146
  response: IntelliCenterBoard.getAckResponse(168),
3152
3147
  retries: 5,
3153
- payload: [0, 0, byte2, 1, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0,
3154
- temp1, cool1, temp2, cool2, body1.heatMode || 0, body2.heatMode || 0, body3.heatMode || 0, body4.heatMode || 0, 15,
3148
+ payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, bitopts, 89, 27, 110, 3, 0, 0,
3149
+ heat1, cool1, heat2, cool2, body1.heatMode || 1, body2.heatMode || 1, 0, 0, 15,
3155
3150
  sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0]
3156
3151
  });
3157
3152
  return new Promise<BodyTempState>((resolve, reject) => {
@@ -3159,7 +3154,7 @@ class IntelliCenterBodyCommands extends BodyCommands {
3159
3154
  if (err) reject(err);
3160
3155
  else {
3161
3156
  let bstate = state.temps.bodies.getItemById(body.id);
3162
- body.coolSetpoint = bstate.coolSetpoint = setPoint;
3157
+ body.heatSetpoint = bstate.heatSetpoint = setPoint;
3163
3158
  resolve(bstate);
3164
3159
  }
3165
3160
  };
@@ -3551,7 +3546,7 @@ class IntelliCenterHeaterCommands extends HeaterCommands {
3551
3546
 
3552
3547
  sys.board.valueMaps.heatModes = new byteValueMap([[1, { name: 'off', desc: 'Off' }]]);
3553
3548
  if (gasHeaterInstalled) sys.board.valueMaps.heatModes.merge([[2, { name: 'heater', desc: 'Heater' }]]);
3554
- if (mastertempInstalled) sys.board.valueMaps.heatModes.merge([11, { name: 'mtheater', desc: 'MasterTemp' }]);
3549
+ if (mastertempInstalled) sys.board.valueMaps.heatModes.merge([[11, { name: 'mtheater', desc: 'MasterTemp' }]]);
3555
3550
  if (solarInstalled && (gasHeaterInstalled || heatPumpInstalled || mastertempInstalled)) sys.board.valueMaps.heatModes.merge([[3, { name: 'solar', desc: 'Solar Only' }], [4, { name: 'solarpref', desc: 'Solar Preferred' }]]);
3556
3551
  else if (solarInstalled) sys.board.valueMaps.heatModes.merge([[3, { name: 'solar', desc: 'Solar' }]]);
3557
3552
  if (ultratempInstalled && (gasHeaterInstalled || heatPumpInstalled || mastertempInstalled)) sys.board.valueMaps.heatModes.merge([[5, { name: 'ultratemp', desc: 'UltraTemp Only' }], [6, { name: 'ultratemppref', desc: 'UltraTemp Pref' }]]);