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.
- package/Changelog +14 -0
- package/README.md +1 -1
- package/controller/Equipment.ts +107 -2
- package/controller/Errors.ts +10 -0
- package/controller/Lockouts.ts +423 -0
- package/controller/State.ts +112 -8
- package/controller/boards/EasyTouchBoard.ts +15 -13
- package/controller/boards/IntelliCenterBoard.ts +82 -87
- package/controller/boards/NixieBoard.ts +590 -128
- package/controller/boards/SystemBoard.ts +1264 -970
- package/controller/comms/messages/config/ExternalMessage.ts +1 -1
- package/controller/comms/messages/config/ValveMessage.ts +1 -1
- package/controller/comms/messages/status/HeaterStateMessage.ts +27 -3
- package/controller/nixie/bodies/Body.ts +3 -0
- package/controller/nixie/circuits/Circuit.ts +24 -8
- package/controller/nixie/heaters/Heater.ts +191 -31
- package/controller/nixie/pumps/Pump.ts +97 -60
- package/controller/nixie/valves/Valve.ts +1 -1
- package/package.json +1 -1
- package/web/bindings/mqtt.json +49 -38
- package/web/bindings/mqttAlt.json +12 -1
- package/web/services/config/Config.ts +6 -6
package/controller/State.ts
CHANGED
|
@@ -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
|
|
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) {
|
|
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) {
|
|
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
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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: '
|
|
99
|
-
[7, { name: '
|
|
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,
|
|
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
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
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 =
|
|
3024
|
-
let
|
|
3025
|
-
let
|
|
3026
|
-
|
|
3027
|
-
let
|
|
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,
|
|
3049
|
-
,
|
|
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
|
|
3074
|
-
let
|
|
3075
|
-
let
|
|
3076
|
-
let
|
|
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
|
-
|
|
3084
|
+
heat1 = setPoint;
|
|
3081
3085
|
break;
|
|
3082
3086
|
case 2:
|
|
3083
3087
|
byte2 = 20;
|
|
3084
|
-
|
|
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,
|
|
3103
|
-
|
|
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
|
-
|
|
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
|
|
3127
|
-
let cool1 =
|
|
3128
|
-
let
|
|
3129
|
-
let cool2 =
|
|
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,
|
|
3154
|
-
|
|
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.
|
|
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' }]]);
|