nodejs-poolcontroller 7.7.0 → 8.0.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/.eslintrc.json +26 -35
- package/Changelog +22 -0
- package/README.md +7 -3
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +13 -9
- package/config/VersionCheck.ts +6 -2
- package/controller/Constants.ts +58 -25
- package/controller/Equipment.ts +224 -41
- package/controller/Errors.ts +2 -1
- package/controller/Lockouts.ts +34 -2
- package/controller/State.ts +491 -48
- package/controller/boards/AquaLinkBoard.ts +6 -3
- package/controller/boards/BoardFactory.ts +5 -1
- package/controller/boards/EasyTouchBoard.ts +1971 -1751
- package/controller/boards/IntelliCenterBoard.ts +1311 -1688
- package/controller/boards/IntelliComBoard.ts +7 -1
- package/controller/boards/IntelliTouchBoard.ts +153 -42
- package/controller/boards/NixieBoard.ts +209 -66
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +1862 -1543
- package/controller/comms/Comms.ts +539 -138
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +242 -60
- package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +81 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +3 -1
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +12 -6
- package/controller/comms/messages/config/PumpMessage.ts +9 -12
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +43 -26
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
- package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
- package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
- package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +15 -4
- package/controller/nixie/NixieEquipment.ts +1 -0
- package/controller/nixie/chemistry/ChemController.ts +300 -129
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +133 -129
- package/controller/nixie/circuits/Circuit.ts +171 -30
- package/controller/nixie/heaters/Heater.ts +337 -173
- package/controller/nixie/pumps/Pump.ts +264 -236
- package/controller/nixie/schedules/Schedule.ts +9 -3
- package/defaultConfig.json +45 -5
- package/logger/Logger.ts +38 -9
- package/package.json +13 -9
- package/web/Server.ts +235 -122
- package/web/bindings/aqualinkD.json +114 -59
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +15 -0
- package/web/bindings/mqtt.json +28 -9
- package/web/bindings/mqttAlt.json +15 -0
- package/web/interfaces/baseInterface.ts +58 -7
- package/web/interfaces/httpInterface.ts +5 -2
- package/web/interfaces/influxInterface.ts +9 -2
- package/web/interfaces/mqttInterface.ts +234 -74
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +140 -33
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +144 -3
- package/web/services/state/StateSocket.ts +65 -14
- package/web/services/utilities/Utilities.ts +189 -1
|
@@ -3,13 +3,13 @@ import { utils, Timestamp } from '../../Constants';
|
|
|
3
3
|
import { logger } from '../../../logger/Logger';
|
|
4
4
|
|
|
5
5
|
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
|
-
import { Heater, HeaterCollection, sys } from "../../../controller/Equipment";
|
|
6
|
+
import { Body, Heater, HeaterCollection, sys } from "../../../controller/Equipment";
|
|
7
7
|
import { BodyTempState, HeaterState, state, } from "../../State";
|
|
8
8
|
import { setTimeout, clearTimeout } from 'timers';
|
|
9
9
|
import { NixieControlPanel } from '../Nixie';
|
|
10
10
|
import { webApp, InterfaceServerResponse } from "../../../web/Server";
|
|
11
11
|
import { conn } from '../../../controller/comms/Comms';
|
|
12
|
-
import { Outbound, Protocol, Response } from '../../../controller/comms/messages/Messages';
|
|
12
|
+
import { Inbound, Outbound, Protocol, Response } from '../../../controller/comms/messages/Messages';
|
|
13
13
|
import { delayMgr } from '../../Lockouts';
|
|
14
14
|
|
|
15
15
|
export class NixieHeaterCollection extends NixieEquipmentCollection<NixieHeaterBase> {
|
|
@@ -86,6 +86,14 @@ export class NixieHeaterCollection extends NixieEquipmentCollection<NixieHeaterB
|
|
|
86
86
|
return c;
|
|
87
87
|
} catch (err) { logger.error(`initHeaterAsync: ${err.message}`); return Promise.reject(err); }
|
|
88
88
|
}
|
|
89
|
+
public async setServiceModeAsync() {
|
|
90
|
+
try {
|
|
91
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
92
|
+
let heater = this[i] as NixieHeaterBase;
|
|
93
|
+
await heater.setServiceModeAsync();
|
|
94
|
+
}
|
|
95
|
+
} catch (err) { return Promise.reject(`Nixie Control Panel setServiceMode ${err.message}`); }
|
|
96
|
+
}
|
|
89
97
|
}
|
|
90
98
|
export class NixieHeaterBase extends NixieEquipment {
|
|
91
99
|
protected _suspendPolling: number = 0;
|
|
@@ -120,6 +128,8 @@ export class NixieHeaterBase extends NixieEquipment {
|
|
|
120
128
|
return new NixieMastertemp(ncp, heater);
|
|
121
129
|
case 'solar':
|
|
122
130
|
return new NixieSolarHeater(ncp, heater);
|
|
131
|
+
case 'hybrid':
|
|
132
|
+
return new NixieUltraTempETi(ncp, heater);
|
|
123
133
|
default:
|
|
124
134
|
return new NixieHeaterBase(ncp, heater);
|
|
125
135
|
}
|
|
@@ -140,11 +150,15 @@ export class NixieHeaterBase extends NixieEquipment {
|
|
|
140
150
|
public async setHeaterAsync(data: any) {
|
|
141
151
|
try {
|
|
142
152
|
let heater = this.heater;
|
|
143
|
-
|
|
153
|
+
|
|
144
154
|
}
|
|
145
155
|
catch (err) { logger.error(`Nixie setHeaterAsync: ${err.message}`); return Promise.reject(err); }
|
|
146
156
|
}
|
|
147
|
-
public async closeAsync() {}
|
|
157
|
+
public async closeAsync() { }
|
|
158
|
+
public async setServiceModeAsync() {
|
|
159
|
+
let hstate = state.heaters.getItemById(this.heater.id);
|
|
160
|
+
await this.setHeaterStateAsync(hstate, false, false);
|
|
161
|
+
}
|
|
148
162
|
}
|
|
149
163
|
export class NixieGasHeater extends NixieHeaterBase {
|
|
150
164
|
public pollingInterval: number = 10000;
|
|
@@ -161,7 +175,7 @@ export class NixieGasHeater extends NixieHeaterBase {
|
|
|
161
175
|
// Delays are always in terms of seconds so convert the minute to seconds.
|
|
162
176
|
if (this.heater.cooldownDelay === 0 || typeof this.lastHeatCycle === 'undefined') return 0;
|
|
163
177
|
let now = new Date().getTime();
|
|
164
|
-
let cooldown = this.isOn ? this.heater.cooldownDelay * 60000 : Math.round(
|
|
178
|
+
let cooldown = this.isOn ? this.heater.cooldownDelay * 60000 : Math.round(((this.lastHeatCycle.getDate() + this.heater.cooldownDelay * 60000) - now) / 1000);
|
|
165
179
|
return Math.min(Math.max(0, cooldown), this.heater.cooldownDelay * 60);
|
|
166
180
|
}
|
|
167
181
|
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean) {
|
|
@@ -349,93 +363,104 @@ export class NixieSolarHeater extends NixieHeaterBase {
|
|
|
349
363
|
public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
|
|
350
364
|
}
|
|
351
365
|
export class NixieHeatpump extends NixieHeaterBase {
|
|
352
|
-
public
|
|
366
|
+
public pollingInterval: number = 10000;
|
|
367
|
+
//declare heater: Heater;
|
|
353
368
|
constructor(ncp: INixieControlPanel, heater: Heater) {
|
|
354
369
|
super(ncp, heater);
|
|
355
|
-
|
|
370
|
+
this.heater = heater;
|
|
371
|
+
if (typeof this.heater.stopTempDelta === 'undefined') this.heater.stopTempDelta = 1;
|
|
372
|
+
if (typeof this.heater.minCycleTime === 'undefined') this.heater.minCycleTime = 2;
|
|
356
373
|
this.pollEquipmentAsync();
|
|
357
374
|
}
|
|
358
|
-
public
|
|
375
|
+
public get id(): number { return typeof this.heater !== 'undefined' ? this.heater.id : -1; }
|
|
376
|
+
public getCooldownTime(): number { return 0; } // There is no cooldown delay at this time for a heatpump
|
|
377
|
+
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean) {
|
|
359
378
|
try {
|
|
360
|
-
|
|
379
|
+
// Initialize the desired state.
|
|
361
380
|
this.isOn = isOn;
|
|
362
|
-
this.isCooling =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
381
|
+
this.isCooling = false;
|
|
382
|
+
let target = hstate.startupDelay === false && isOn;
|
|
383
|
+
if (target && typeof hstate.endTime !== 'undefined') {
|
|
384
|
+
// Calculate a short cycle time so that the gas heater does not cycle
|
|
385
|
+
// too often. For gas heaters this is 60 seconds. This gives enough time
|
|
386
|
+
// for the heater control circuit to make a full cycle.
|
|
387
|
+
if (new Date().getTime() - hstate.endTime.getTime() < this.heater.minCycleTime * 60000) {
|
|
388
|
+
logger.verbose(`${hstate.name} short cycle detected deferring turn on state`);
|
|
389
|
+
target = false;
|
|
390
|
+
}
|
|
366
391
|
}
|
|
367
|
-
//
|
|
368
|
-
|
|
369
|
-
|
|
392
|
+
// Here we go we need to set the firemans switch state.
|
|
393
|
+
if (hstate.isOn !== target) {
|
|
394
|
+
logger.info(`Nixie: Set Heatpump ${hstate.id}-${hstate.name} to ${isOn}`);
|
|
395
|
+
}
|
|
396
|
+
if (typeof this._lastState === 'undefined' || target || this._lastState !== target) {
|
|
397
|
+
if (utils.isNullOrEmpty(this.heater.connectionId) || utils.isNullOrEmpty(this.heater.deviceBinding)) {
|
|
398
|
+
this._lastState = hstate.isOn = target;
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
let res = await NixieEquipment.putDeviceService(this.heater.connectionId, `/state/device/${this.heater.deviceBinding}`,
|
|
402
|
+
{ isOn: target, latch: target ? 10000 : undefined });
|
|
403
|
+
if (res.status.code === 200) this._lastState = hstate.isOn = target;
|
|
404
|
+
else logger.error(`Nixie Error setting heatpump state: ${res.status.code} -${res.status.message} ${res.error.message}`);
|
|
405
|
+
}
|
|
406
|
+
if (target) this.lastHeatCycle = new Date();
|
|
407
|
+
}
|
|
408
|
+
} catch (err) { return logger.error(`Nixie Error setting heatpump state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
370
409
|
}
|
|
371
410
|
public async pollEquipmentAsync() {
|
|
372
411
|
let self = this;
|
|
373
412
|
try {
|
|
374
|
-
this.suspendPolling = true;
|
|
375
413
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
376
414
|
this._pollTimer = null;
|
|
377
|
-
|
|
378
|
-
let sheater = state.heaters.getItemById(this.heater.id, !this.closing);
|
|
379
|
-
// If the body isn't on then we won't communicate with the chem controller. There is no need
|
|
380
|
-
// since most of the time these are attached to the filter relay.
|
|
381
|
-
if (this.isBodyOn() && !this.closing) {
|
|
382
|
-
await this.sendState(sheater);
|
|
383
|
-
//if (!this.closing) await this.requestStatus(sheater);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
catch (err) { logger.error(`Error polling Heat Pump - ${err}`); }
|
|
387
|
-
finally {
|
|
388
|
-
this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(async () => {
|
|
389
|
-
try { await self.pollEquipmentAsync() } catch (err) { }
|
|
390
|
-
}, this.pollingInterval || 10000);
|
|
415
|
+
let success = false;
|
|
391
416
|
}
|
|
417
|
+
catch (err) { logger.error(`Nixie Error polling Heatpump - ${err}`); }
|
|
418
|
+
finally { this._pollTimer = setTimeout(async () => await self.pollEquipmentAsync(), this.pollingInterval || 10000); }
|
|
392
419
|
}
|
|
393
|
-
|
|
394
|
-
try {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
}
|
|
413
|
-
else { resolve(true); }
|
|
414
|
-
}
|
|
415
|
-
});
|
|
416
|
-
conn.queueSendMessage(out);
|
|
417
|
-
});
|
|
418
|
-
return success;
|
|
419
|
-
} catch (err) { logger.error(`Communication error with Ultratemp : ${err.message}`); }
|
|
420
|
+
private async checkHardwareStatusAsync(connectionId: string, deviceBinding: string) {
|
|
421
|
+
try {
|
|
422
|
+
let dev = await NixieEquipment.getDeviceService(connectionId, `/status/device/${deviceBinding}`);
|
|
423
|
+
return dev;
|
|
424
|
+
} catch (err) { logger.error(`Nixie Heatpump Error checkHardwareStatusAsync: ${err.message}`); return { hasFault: true } }
|
|
425
|
+
}
|
|
426
|
+
public async validateSetupAsync(heater: Heater, hstate: HeaterState) {
|
|
427
|
+
try {
|
|
428
|
+
if (typeof heater.connectionId !== 'undefined' && heater.connectionId !== ''
|
|
429
|
+
&& typeof heater.deviceBinding !== 'undefined' && heater.deviceBinding !== '') {
|
|
430
|
+
try {
|
|
431
|
+
let stat = await this.checkHardwareStatusAsync(heater.connectionId, heater.deviceBinding);
|
|
432
|
+
// If we have a status check the return.
|
|
433
|
+
hstate.commStatus = stat.hasFault ? 1 : 0;
|
|
434
|
+
} catch (err) { hstate.commStatus = 1; }
|
|
435
|
+
}
|
|
436
|
+
else
|
|
437
|
+
hstate.commStatus = 0;
|
|
438
|
+
} catch (err) { logger.error(`Nixie Error checking heatpump Hardware ${this.heater.name}: ${err.message}`); hstate.commStatus = 1; return Promise.reject(err); }
|
|
420
439
|
}
|
|
421
440
|
public async closeAsync() {
|
|
422
441
|
try {
|
|
423
|
-
this.suspendPolling = true;
|
|
424
|
-
this.closing = true;
|
|
425
442
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
426
443
|
this._pollTimer = null;
|
|
427
|
-
|
|
428
|
-
|
|
444
|
+
let hstate = state.heaters.getItemById(this.heater.id);
|
|
445
|
+
await this.setHeaterStateAsync(hstate, false);
|
|
446
|
+
hstate.emitEquipmentChange();
|
|
429
447
|
}
|
|
430
|
-
catch (err) { logger.error(`
|
|
448
|
+
catch (err) { logger.error(`Nixie Heatpump closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
431
449
|
}
|
|
450
|
+
public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
|
|
432
451
|
}
|
|
433
|
-
export class NixieUltratemp extends
|
|
452
|
+
export class NixieUltratemp extends NixieHeaterBase {
|
|
434
453
|
constructor(ncp: INixieControlPanel, heater: Heater) {
|
|
435
454
|
super(ncp, heater);
|
|
436
455
|
// Set the polling interval to 3 seconds.
|
|
437
456
|
this.pollEquipmentAsync();
|
|
438
457
|
}
|
|
458
|
+
public async setServiceModeAsync() {
|
|
459
|
+
let hstate = state.heaters.getItemById(this.heater.id);
|
|
460
|
+
await this.setHeaterStateAsync(hstate, false, false);
|
|
461
|
+
await this.releaseHeater(hstate);
|
|
462
|
+
}
|
|
463
|
+
|
|
439
464
|
public async pollEquipmentAsync() {
|
|
440
465
|
let self = this;
|
|
441
466
|
try {
|
|
@@ -453,7 +478,7 @@ export class NixieUltratemp extends NixieHeatpump {
|
|
|
453
478
|
catch (err) { logger.error(`Error polling UltraTemp heater - ${err}`); }
|
|
454
479
|
finally {
|
|
455
480
|
this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(async () => {
|
|
456
|
-
try { await self.pollEquipmentAsync() } catch (err) {}
|
|
481
|
+
try { await self.pollEquipmentAsync() } catch (err) { }
|
|
457
482
|
}, this.pollingInterval || 10000);
|
|
458
483
|
}
|
|
459
484
|
}
|
|
@@ -463,84 +488,76 @@ export class NixieUltratemp extends NixieHeatpump {
|
|
|
463
488
|
this.isCooling = isCooling;
|
|
464
489
|
if (hstate.isOn !== isOn) {
|
|
465
490
|
logger.info(`Nixie: Set Heater ${hstate.id}-${hstate.name} to ${isCooling ? 'cooling' : isOn ? 'heating' : 'off'}`);
|
|
466
|
-
|
|
491
|
+
|
|
467
492
|
}
|
|
468
493
|
if (isOn && !hstate.startupDelay) this.lastHeatCycle = new Date();
|
|
469
|
-
hstate.isOn = isOn;
|
|
494
|
+
this.isOn = hstate.isOn = isOn;
|
|
470
495
|
} catch (err) { return logger.error(`Nixie Error setting heater state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
471
496
|
}
|
|
472
497
|
public async releaseHeater(sheater: HeaterState): Promise<boolean> {
|
|
473
498
|
try {
|
|
474
|
-
let
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
onAbort: () => { },
|
|
485
|
-
onComplete: (err) => {
|
|
486
|
-
if (err) {
|
|
487
|
-
// If the Ultratemp is not responding we need to store that off but at this point we know none of the codes. If a 115 does
|
|
488
|
-
// come across this will be cleared by the processing of that message.
|
|
489
|
-
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
490
|
-
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
491
|
-
resolve(false);
|
|
492
|
-
}
|
|
493
|
-
else { resolve(true); }
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
out.appendPayloadBytes(0, 10);
|
|
497
|
-
out.setPayloadByte(0, 144);
|
|
498
|
-
out.setPayloadByte(1, 0, 0);
|
|
499
|
-
conn.queueSendMessage(out);
|
|
499
|
+
let out = Outbound.create({
|
|
500
|
+
portId: this.heater.portId || 0,
|
|
501
|
+
protocol: Protocol.Heater,
|
|
502
|
+
source: 16,
|
|
503
|
+
dest: this.heater.address,
|
|
504
|
+
action: 114,
|
|
505
|
+
payload: [],
|
|
506
|
+
retries: 3, // We are going to try 4 times.
|
|
507
|
+
response: Response.create({ protocol: Protocol.Heater, action: 115 }),
|
|
508
|
+
onAbort: () => { }
|
|
500
509
|
});
|
|
501
|
-
|
|
502
|
-
|
|
510
|
+
out.appendPayloadBytes(0, 10);
|
|
511
|
+
out.setPayloadByte(0, 144);
|
|
512
|
+
out.setPayloadByte(1, 0, 0);
|
|
513
|
+
await out.sendAsync();
|
|
514
|
+
return true;
|
|
515
|
+
|
|
516
|
+
} catch (err) {
|
|
517
|
+
// If the Ultratemp is not responding we need to store that off but at this point we know none of the codes. If a 115 does
|
|
518
|
+
// come across this will be cleared by the processing of that message.
|
|
519
|
+
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
520
|
+
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
521
|
+
logger.error(`Communication error with Ultratemp : ${err.message}`);
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
503
524
|
}
|
|
504
525
|
public async setStatus(sheater: HeaterState): Promise<boolean> {
|
|
505
526
|
try {
|
|
506
|
-
let
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
onAbort: () => { },
|
|
517
|
-
onComplete: (err) => {
|
|
518
|
-
if (err) {
|
|
519
|
-
// If the Ultratemp is not responding we need to store that off but at this point we know none of the codes. If a 115 does
|
|
520
|
-
// come across this will be cleared by the processing of that message.
|
|
521
|
-
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
522
|
-
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
523
|
-
resolve(false);
|
|
524
|
-
}
|
|
525
|
-
else { resolve(true); }
|
|
526
|
-
}
|
|
527
|
-
});
|
|
528
|
-
out.appendPayloadBytes(0, 10);
|
|
529
|
-
out.setPayloadByte(0, 144);
|
|
530
|
-
// If we are in startup delay simply tell the heater that it is off.
|
|
531
|
-
if (sheater.startupDelay || this.closing)
|
|
532
|
-
out.setPayloadByte(1, 0, 0);
|
|
533
|
-
else {
|
|
534
|
-
if (this.isOn) {
|
|
535
|
-
if (!this.isCooling) this.lastHeatCycle = new Date();
|
|
536
|
-
else this.lastCoolCycle = new Date();
|
|
537
|
-
}
|
|
538
|
-
out.setPayloadByte(1, this.isOn ? (this.isCooling ? 2 : 1) : 0, 0);
|
|
539
|
-
}
|
|
540
|
-
conn.queueSendMessage(out);
|
|
527
|
+
let out = Outbound.create({
|
|
528
|
+
portId: this.heater.portId || 0,
|
|
529
|
+
protocol: Protocol.Heater,
|
|
530
|
+
source: 16,
|
|
531
|
+
dest: this.heater.address,
|
|
532
|
+
action: 114,
|
|
533
|
+
payload: [],
|
|
534
|
+
retries: 3, // We are going to try 4 times.
|
|
535
|
+
response: Response.create({ protocol: Protocol.Heater, action: 115 }),
|
|
536
|
+
onAbort: () => { }
|
|
541
537
|
});
|
|
538
|
+
out.appendPayloadBytes(0, 10);
|
|
539
|
+
out.setPayloadByte(0, 144);
|
|
540
|
+
// If we are in startup delay simply tell the heater that it is off.
|
|
541
|
+
if (sheater.startupDelay || this.closing)
|
|
542
|
+
out.setPayloadByte(1, 0, 0);
|
|
543
|
+
else {
|
|
544
|
+
if (this.isOn) {
|
|
545
|
+
if (!this.isCooling) this.lastHeatCycle = new Date();
|
|
546
|
+
else this.lastCoolCycle = new Date();
|
|
547
|
+
}
|
|
548
|
+
//console.log(`Setting the heater byte ${this.isOn} ${sheater.isOn} to ${this.isOn ? (this.isCooling ? 2 : 1) : 0}`);
|
|
549
|
+
out.setPayloadByte(1, this.isOn ? (this.isCooling ? 2 : 1) : 0, 0);
|
|
550
|
+
}
|
|
551
|
+
let success = await out.sendAsync();
|
|
542
552
|
return success;
|
|
543
|
-
} catch (err) {
|
|
553
|
+
} catch (err) {
|
|
554
|
+
// If the Ultratemp is not responding we need to store that off but at this point we know none of the codes. If a 115 does
|
|
555
|
+
// come across this will be cleared by the processing of that message.
|
|
556
|
+
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
557
|
+
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
558
|
+
logger.error(`Communication error with Ultratemp : ${err.message}`);
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
544
561
|
}
|
|
545
562
|
public async closeAsync() {
|
|
546
563
|
try {
|
|
@@ -562,13 +579,13 @@ export class NixieMastertemp extends NixieGasHeater {
|
|
|
562
579
|
this.pollEquipmentAsync();
|
|
563
580
|
this.pollingInterval = 3000;
|
|
564
581
|
}
|
|
565
|
-
/* public getCooldownTime(): number {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
582
|
+
/* public getCooldownTime(): number {
|
|
583
|
+
// Delays are always in terms of seconds so convert the minute to seconds.
|
|
584
|
+
if (this.heater.cooldownDelay === 0 || typeof this.lastHeatCycle === 'undefined') return 0;
|
|
585
|
+
let now = new Date().getTime();
|
|
586
|
+
let cooldown = this.isOn ? this.heater.cooldownDelay * 60000 : Math.round(((this.lastHeatCycle.getDate() + this.heater.cooldownDelay * 60000) - now) / 1000);
|
|
587
|
+
return Math.min(Math.max(0, cooldown), this.heater.cooldownDelay * 60);
|
|
588
|
+
} */
|
|
572
589
|
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean) {
|
|
573
590
|
try {
|
|
574
591
|
// Initialize the desired state.
|
|
@@ -600,42 +617,41 @@ export class NixieMastertemp extends NixieGasHeater {
|
|
|
600
617
|
}
|
|
601
618
|
public async setStatus(sheater: HeaterState): Promise<boolean> {
|
|
602
619
|
try {
|
|
603
|
-
let
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
onAbort: () => { },
|
|
614
|
-
onComplete: (err) => {
|
|
615
|
-
if (err) {
|
|
616
|
-
// If the MasterTemp is not responding we need to store that off but at this point we know none of the codes. If a 115 does
|
|
617
|
-
// come across this will be cleared by the processing of that message.
|
|
618
|
-
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
619
|
-
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
620
|
-
resolve(false);
|
|
621
|
-
}
|
|
622
|
-
else { resolve(true); }
|
|
623
|
-
}
|
|
624
|
-
});
|
|
625
|
-
out.appendPayloadBytes(0, 11);
|
|
626
|
-
// If we have a startup delay we need to simply send 0 to the heater to make sure that it is off.
|
|
627
|
-
if (sheater.startupDelay)
|
|
628
|
-
out.setPayloadByte(0, 0);
|
|
629
|
-
else {
|
|
630
|
-
// The cooldown delay is a bit hard to figure out here since I think the heater does it on its own.
|
|
631
|
-
out.setPayloadByte(0, sheater.bodyId <= 2 ? sheater.bodyId : 0);
|
|
632
|
-
}
|
|
633
|
-
out.setPayloadByte(1, sys.bodies.getItemById(1).heatSetpoint || 0);
|
|
634
|
-
out.setPayloadByte(2, sys.bodies.getItemById(2).heatSetpoint || 0);
|
|
635
|
-
conn.queueSendMessage(out);
|
|
620
|
+
let out = Outbound.create({
|
|
621
|
+
portId: this.heater.portId || 0,
|
|
622
|
+
protocol: Protocol.Heater,
|
|
623
|
+
source: 16,
|
|
624
|
+
dest: this.heater.address,
|
|
625
|
+
action: 112,
|
|
626
|
+
payload: [],
|
|
627
|
+
retries: 3, // We are going to try 4 times.
|
|
628
|
+
response: Response.create({ protocol: Protocol.Heater, action: 116 }),
|
|
629
|
+
onAbort: () => { }
|
|
636
630
|
});
|
|
631
|
+
out.appendPayloadBytes(0, 11);
|
|
632
|
+
// If we have a startup delay we need to simply send 0 to the heater to make sure that it is off.
|
|
633
|
+
if (sheater.startupDelay)
|
|
634
|
+
out.setPayloadByte(0, 0);
|
|
635
|
+
else {
|
|
636
|
+
// The cooldown delay is a bit hard to figure out here since I think the heater does it on its own.
|
|
637
|
+
out.setPayloadByte(0, sheater.bodyId <= 2 ? sheater.bodyId : 0);
|
|
638
|
+
}
|
|
639
|
+
out.setPayloadByte(1, sys.bodies.getItemById(1).heatSetpoint || 0);
|
|
640
|
+
out.setPayloadByte(2, sys.bodies.getItemById(2).heatSetpoint || 0);
|
|
641
|
+
let success = await out.sendAsync();
|
|
637
642
|
return success;
|
|
638
|
-
} catch (err) {
|
|
643
|
+
} catch (err) {
|
|
644
|
+
// If the MasterTemp is not responding we need to store that off but at this point we know none of the codes. If a 115 does
|
|
645
|
+
// come across this will be cleared by the processing of that message.
|
|
646
|
+
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
647
|
+
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
648
|
+
logger.error(`Communication error with MasterTemp : ${err.message}`);
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
public async setServiceModeAsync() {
|
|
653
|
+
let hstate = state.heaters.getItemById(this.heater.id);
|
|
654
|
+
await this.setHeaterStateAsync(hstate, false);
|
|
639
655
|
}
|
|
640
656
|
public async closeAsync() {
|
|
641
657
|
try {
|
|
@@ -648,4 +664,152 @@ export class NixieMastertemp extends NixieGasHeater {
|
|
|
648
664
|
}
|
|
649
665
|
catch (err) { logger.error(`MasterTemp closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
650
666
|
}
|
|
651
|
-
}
|
|
667
|
+
}
|
|
668
|
+
export class NixieUltraTempETi extends NixieHeaterBase {
|
|
669
|
+
constructor(ncp: INixieControlPanel, heater: Heater) {
|
|
670
|
+
super(ncp, heater);
|
|
671
|
+
// Set the polling interval to 3 seconds.
|
|
672
|
+
this.pollEquipmentAsync();
|
|
673
|
+
}
|
|
674
|
+
public async pollEquipmentAsync() {
|
|
675
|
+
let self = this;
|
|
676
|
+
try {
|
|
677
|
+
this.suspendPolling = true;
|
|
678
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
679
|
+
this._pollTimer = null;
|
|
680
|
+
if (this._suspendPolling > 1) return;
|
|
681
|
+
let sheater = state.heaters.getItemById(this.heater.id, !this.closing);
|
|
682
|
+
// If the body isn't on then we won't communicate with the chem controller. There is no need
|
|
683
|
+
// since most of the time these are attached to the filter relay.
|
|
684
|
+
if (!this.closing) {
|
|
685
|
+
await this.setStatus(sheater);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
catch (err) { logger.error(`Error polling UltraTemp ETi heater - ${err}`); }
|
|
689
|
+
finally {
|
|
690
|
+
this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(async () => {
|
|
691
|
+
try { await self.pollEquipmentAsync() } catch (err) { }
|
|
692
|
+
}, this.pollingInterval || 10000);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean, isCooling: boolean) {
|
|
696
|
+
try {
|
|
697
|
+
// Initialize the desired state.
|
|
698
|
+
this.isCooling = isCooling;
|
|
699
|
+
if (hstate.isOn !== isOn) {
|
|
700
|
+
logger.info(`Nixie: Set Heater ${hstate.id}-${hstate.name} to ${isCooling ? 'cooling' : isOn ? 'heating' : 'off'}`);
|
|
701
|
+
|
|
702
|
+
}
|
|
703
|
+
if (isOn && !hstate.startupDelay) this.lastHeatCycle = new Date();
|
|
704
|
+
this.isOn = hstate.isOn = isOn;
|
|
705
|
+
} catch (err) { return logger.error(`Nixie Error setting heater state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
706
|
+
}
|
|
707
|
+
public async releaseHeater(sheater: HeaterState): Promise<boolean> {
|
|
708
|
+
try {
|
|
709
|
+
let out = Outbound.create({
|
|
710
|
+
portId: this.heater.portId || 0,
|
|
711
|
+
protocol: Protocol.Heater,
|
|
712
|
+
source: 16,
|
|
713
|
+
dest: this.heater.address,
|
|
714
|
+
action: 112,
|
|
715
|
+
payload: [],
|
|
716
|
+
retries: 3, // We are going to try 4 times.
|
|
717
|
+
response: Response.create({ protocol: Protocol.Heater, action: 113 }),
|
|
718
|
+
onAbort: () => { }
|
|
719
|
+
});
|
|
720
|
+
out.appendPayloadBytes(0, 10);
|
|
721
|
+
out.setPayloadByte(0, 0);
|
|
722
|
+
out.setPayloadByte(1, 0);
|
|
723
|
+
out.setPayloadByte(2, 78);
|
|
724
|
+
out.setPayloadByte(3, 1);
|
|
725
|
+
out.setPayloadByte(4, 5);
|
|
726
|
+
let success = await out.sendAsync();
|
|
727
|
+
return success;
|
|
728
|
+
} catch (err) {
|
|
729
|
+
// If the Ultratemp is not responding we need to store that off but at this point we know none of the codes. If a 113 does
|
|
730
|
+
// come across this will be cleared by the processing of that message.
|
|
731
|
+
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
732
|
+
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
733
|
+
logger.error(`Communication error with Ultratemp : ${err.message}`);
|
|
734
|
+
return false;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
protected calcHeatModeByte(body: Body): number {
|
|
738
|
+
let byte = 0;
|
|
739
|
+
if (this.closing) return 0; // We are closing so just set the heat mode to off.
|
|
740
|
+
let mode = sys.board.valueMaps.heatModes.transform(body.heatMode || 0);
|
|
741
|
+
switch (mode.name) {
|
|
742
|
+
case 'hpump':
|
|
743
|
+
case 'heatpump':
|
|
744
|
+
byte = 1;
|
|
745
|
+
break;
|
|
746
|
+
case 'heater':
|
|
747
|
+
byte = 2;
|
|
748
|
+
break;
|
|
749
|
+
case 'heatpumpref':
|
|
750
|
+
case 'heatpumppref':
|
|
751
|
+
case 'hybrid':
|
|
752
|
+
byte = 3;
|
|
753
|
+
break;
|
|
754
|
+
case 'dual':
|
|
755
|
+
byte = 4;
|
|
756
|
+
break;
|
|
757
|
+
}
|
|
758
|
+
return byte;
|
|
759
|
+
}
|
|
760
|
+
public async setServiceModeAsync() {
|
|
761
|
+
let hstate = state.heaters.getItemById(this.heater.id);
|
|
762
|
+
await this.setHeaterStateAsync(hstate, false, false);
|
|
763
|
+
await this.releaseHeater(hstate);
|
|
764
|
+
}
|
|
765
|
+
public async setStatus(sheater: HeaterState): Promise<boolean> {
|
|
766
|
+
try {
|
|
767
|
+
let out = Outbound.create({
|
|
768
|
+
portId: this.heater.portId || 0,
|
|
769
|
+
protocol: Protocol.Heater,
|
|
770
|
+
source: 16,
|
|
771
|
+
dest: this.heater.address,
|
|
772
|
+
action: 112,
|
|
773
|
+
payload: [],
|
|
774
|
+
retries: 3, // We are going to try 4 times.
|
|
775
|
+
response: Response.create({ protocol: Protocol.Heater, action: 113 }),
|
|
776
|
+
onAbort: () => { }
|
|
777
|
+
});
|
|
778
|
+
out.appendPayloadBytes(0, 10);
|
|
779
|
+
out.setPayloadByte(0, this.isOn && !sheater.startupDelay && !this.closing ? 1 : 0);
|
|
780
|
+
if (sheater.bodyId > 0) {
|
|
781
|
+
let body = sys.bodies.getItemById(sheater.bodyId);
|
|
782
|
+
out.setPayloadByte(1, this.calcHeatModeByte(body));
|
|
783
|
+
out.setPayloadByte(2, body.setPoint);
|
|
784
|
+
}
|
|
785
|
+
else out.setPayloadByte(2, utils.convert.temperature.convertUnits(78, 'F', sys.board.valueMaps.tempUnits.getName(state.temps.units) || 'F')); // Just set it to a valid setpoint and call it a day.
|
|
786
|
+
out.setPayloadByte(3, this.heater.economyTime, 1);
|
|
787
|
+
out.setPayloadByte(4, this.heater.maxBoostTemp, 5);
|
|
788
|
+
if (this.isOn) {
|
|
789
|
+
if (!this.isCooling) this.lastHeatCycle = new Date();
|
|
790
|
+
else this.lastCoolCycle = new Date();
|
|
791
|
+
}
|
|
792
|
+
let success = await out.sendAsync();
|
|
793
|
+
return success;
|
|
794
|
+
} catch (err) {
|
|
795
|
+
// If the Ultratemp ETi is not responding we need to store that off but at this point we know none of the codes. If a 113 does
|
|
796
|
+
// come across this will be cleared by the processing of that message.
|
|
797
|
+
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
798
|
+
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
799
|
+
logger.error(`Communication error with Ultratemp ETi : ${err.message}`);
|
|
800
|
+
return false;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
public async closeAsync() {
|
|
804
|
+
try {
|
|
805
|
+
this.suspendPolling = true;
|
|
806
|
+
this.closing = true;
|
|
807
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
808
|
+
this._pollTimer = null;
|
|
809
|
+
let sheater = state.heaters.getItemById(this.id);
|
|
810
|
+
await this.releaseHeater(sheater);
|
|
811
|
+
logger.info(`Closing Heater ${this.heater.name}`);
|
|
812
|
+
}
|
|
813
|
+
catch (err) { logger.error(`Ultratemp closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
814
|
+
}
|
|
815
|
+
}
|