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
|
@@ -773,7 +773,7 @@ export class ExternalMessage {
|
|
|
773
773
|
break;
|
|
774
774
|
case 21: // Body 2 Cool Setpoint
|
|
775
775
|
body = sys.bodies.getItemById(2, false);
|
|
776
|
-
body.
|
|
776
|
+
body.coolSetpoint = msg.extractPayloadByte(24);
|
|
777
777
|
state.temps.bodies.getItemById(2).coolSetpoint = body.coolSetpoint;
|
|
778
778
|
state.emitEquipmentChanges();
|
|
779
779
|
msg.isProcessed = true;
|
|
@@ -174,7 +174,7 @@ export class ValveMessage {
|
|
|
174
174
|
// for i10d.
|
|
175
175
|
let ndx: number = 2;
|
|
176
176
|
let id = 1;
|
|
177
|
-
for (let i = 0; i < sys.equipment.maxValves
|
|
177
|
+
for (let i = 0; i < sys.equipment.maxValves; i++) {
|
|
178
178
|
if (id === 3 && !sys.equipment.shared) {
|
|
179
179
|
// The intake/return valves are skipped for non-shared systems.
|
|
180
180
|
sys.valves.removeItemById(3);
|
|
@@ -22,15 +22,20 @@ export class HeaterStateMessage {
|
|
|
22
22
|
public static process(msg: Inbound) {
|
|
23
23
|
if (msg.protocol === Protocol.Heater) {
|
|
24
24
|
switch (msg.action) {
|
|
25
|
-
case
|
|
25
|
+
case 112: // This is a message from a master controlling MasterTemp
|
|
26
|
+
case 114: // This is a message from a master controlling UltraTemp
|
|
27
|
+
msg.isProcessed = true;
|
|
28
|
+
break;
|
|
29
|
+
case 116:
|
|
30
|
+
HeaterStateMessage.processMasterTempStatus(msg);
|
|
26
31
|
break;
|
|
27
32
|
case 115:
|
|
28
|
-
HeaterStateMessage.
|
|
33
|
+
HeaterStateMessage.processUltraTempStatus(msg);
|
|
29
34
|
break;
|
|
30
35
|
}
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
|
-
public static
|
|
38
|
+
public static processUltraTempStatus(msg: Inbound) {
|
|
34
39
|
// RKS: 07-03-21 - We only know byte 2 at this point for Ultratemp for the 115 message we are processing here. The
|
|
35
40
|
// byte description
|
|
36
41
|
// ------------------------------------------------
|
|
@@ -60,4 +65,23 @@ export class HeaterStateMessage {
|
|
|
60
65
|
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
61
66
|
msg.isProcessed = true;
|
|
62
67
|
}
|
|
68
|
+
public static processMasterTempStatus(msg: Inbound) {
|
|
69
|
+
//[255, 0, 255][165, 0, 16, 112, 116, 23][67, 0, 0, 0, 0, 0, 0, 0, 68, 0, 0, 0, 10, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0][2, 66]
|
|
70
|
+
// Byte 1 is the indicator to which setpoint it is heating to.
|
|
71
|
+
// Byte 8 increments over time when the heater is on.
|
|
72
|
+
// Byte 13 looks like the mode the heater is in for instance it is in cooldown mode.
|
|
73
|
+
// 0 = Normal
|
|
74
|
+
// 2 = ??????
|
|
75
|
+
// 6 = Cooldown
|
|
76
|
+
// Byte 14 looks like the cooldown delay in minutes.
|
|
77
|
+
let heater: Heater = sys.heaters.getItemByAddress(msg.source);
|
|
78
|
+
let sheater = state.heaters.getItemById(heater.id);
|
|
79
|
+
let byte = msg.extractPayloadByte(1);
|
|
80
|
+
sheater.isOn = byte >= 1;
|
|
81
|
+
sheater.isCooling = false;
|
|
82
|
+
sheater.commStatus = 0;
|
|
83
|
+
state.equipment.messages.removeItemByCode(`heater:${heater.id}:comms`);
|
|
84
|
+
msg.isProcessed = true;
|
|
85
|
+
}
|
|
86
|
+
|
|
63
87
|
}
|
|
@@ -64,6 +64,9 @@ export class NixieBody extends NixieEquipment {
|
|
|
64
64
|
super(ncp);
|
|
65
65
|
this.body = body;
|
|
66
66
|
this.pollEquipmentAsync();
|
|
67
|
+
let bs = state.temps.bodies.getItemById(body.id);
|
|
68
|
+
bs.heaterCooldownDelay = false;
|
|
69
|
+
bs.heatStatus = 0;
|
|
67
70
|
}
|
|
68
71
|
public get id(): number { return typeof this.body !== 'undefined' ? this.body.id : -1; }
|
|
69
72
|
public async setBodyAsync(data: any) {
|
|
@@ -60,7 +60,7 @@ export class NixieCircuitCollection extends NixieEquipmentCollection<NixieCircui
|
|
|
60
60
|
try {
|
|
61
61
|
let c: NixieCircuit = this.find(elem => elem.id === cstate.id) as NixieCircuit;
|
|
62
62
|
await c.checkCircuitEggTimerExpirationAsync(cstate);
|
|
63
|
-
} catch (err) { logger.error(`NCP: Error
|
|
63
|
+
} catch (err) { logger.error(`NCP: Error synching circuit states: ${err}`); }
|
|
64
64
|
}
|
|
65
65
|
public async initAsync(circuits: CircuitCollection) {
|
|
66
66
|
try {
|
|
@@ -118,6 +118,10 @@ export class NixieCircuit extends NixieEquipment {
|
|
|
118
118
|
constructor(ncp: INixieControlPanel, circuit: Circuit) {
|
|
119
119
|
super(ncp);
|
|
120
120
|
this.circuit = circuit;
|
|
121
|
+
// Clear out the delays.
|
|
122
|
+
let cstate = state.circuits.getItemById(circuit.id);
|
|
123
|
+
cstate.startDelay = false;
|
|
124
|
+
cstate.stopDelay = false;
|
|
121
125
|
}
|
|
122
126
|
public get id(): number { return typeof this.circuit !== 'undefined' ? this.circuit.id : -1; }
|
|
123
127
|
public get eggTimerOff(): Timestamp { return typeof this.timeOn !== 'undefined' && !this.circuit.dontStop ? this.timeOn.clone().addMinutes(this.circuit.eggTimer) : undefined; }
|
|
@@ -157,7 +161,15 @@ export class NixieCircuit extends NixieEquipment {
|
|
|
157
161
|
} catch (err) { logger.error(`Nixie: Error sending circuit sequence ${this.id}: ${count}`); }
|
|
158
162
|
finally { this._sequencing = false; }
|
|
159
163
|
}
|
|
160
|
-
public async
|
|
164
|
+
public async setThemeAsync(cstate: ICircuitState, theme: number): Promise<InterfaceServerResponse> {
|
|
165
|
+
try {
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
return new InterfaceServerResponse(200, 'Sucess');
|
|
170
|
+
} catch (err) { logger.error(`Nixie: Error setting light theme ${cstate.id}-${cstate.name} to ${theme}`); }
|
|
171
|
+
}
|
|
172
|
+
public async setCircuitStateAsync(cstate: ICircuitState, val: boolean, scheduled: boolean = false): Promise<InterfaceServerResponse> {
|
|
161
173
|
try {
|
|
162
174
|
if (val !== cstate.isOn) {
|
|
163
175
|
logger.info(`NCP: Setting Circuit ${cstate.name} to ${val}`);
|
|
@@ -177,10 +189,10 @@ export class NixieCircuit extends NixieEquipment {
|
|
|
177
189
|
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, { isOn: val, latch: val ? 10000 : undefined });
|
|
178
190
|
if (res.status.code === 200) {
|
|
179
191
|
sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
|
|
180
|
-
cstate.isOn = val;
|
|
181
192
|
// Set this up so we can process our egg timer.
|
|
182
|
-
if (!cstate.isOn && val) { this.timeOn = new Timestamp(); }
|
|
183
|
-
else if (!val) this.timeOn = undefined;
|
|
193
|
+
//if (!cstate.isOn && val) { cstate.startTime = this.timeOn = new Timestamp(); }
|
|
194
|
+
//else if (!val) cstate.startTime = this.timeOn = undefined;
|
|
195
|
+
cstate.isOn = val;
|
|
184
196
|
}
|
|
185
197
|
return res;
|
|
186
198
|
} catch (err) { logger.error(`Nixie: Error setting circuit state ${cstate.id}-${cstate.name} to ${val}`); }
|
|
@@ -190,9 +202,11 @@ export class NixieCircuit extends NixieEquipment {
|
|
|
190
202
|
// (this should already be turned off) or the egg timer has expired
|
|
191
203
|
try {
|
|
192
204
|
if (!cstate.isActive || !cstate.isOn) return;
|
|
193
|
-
if (cstate.endTime
|
|
194
|
-
|
|
195
|
-
|
|
205
|
+
if (typeof cstate.endTime !== 'undefined') {
|
|
206
|
+
if (cstate.endTime.toDate() < new Timestamp().toDate()) {
|
|
207
|
+
await sys.board.circuits.setCircuitStateAsync(cstate.id, false);
|
|
208
|
+
cstate.emitEquipmentChange();
|
|
209
|
+
}
|
|
196
210
|
}
|
|
197
211
|
} catch (err) { logger.error(`Error syncing circuit: ${err}`); }
|
|
198
212
|
}
|
|
@@ -220,6 +234,8 @@ export class NixieCircuit extends NixieEquipment {
|
|
|
220
234
|
public async closeAsync() {
|
|
221
235
|
try {
|
|
222
236
|
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
237
|
+
cstate.stopDelay = false;
|
|
238
|
+
cstate.startDelay = false;
|
|
223
239
|
await this.setCircuitStateAsync(cstate, false);
|
|
224
240
|
cstate.emitEquipmentChange();
|
|
225
241
|
}
|
|
@@ -4,12 +4,13 @@ import { logger } from '../../../logger/Logger';
|
|
|
4
4
|
|
|
5
5
|
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
6
|
import { Heater, HeaterCollection, sys } from "../../../controller/Equipment";
|
|
7
|
-
import { HeaterState, state, } from "../../State";
|
|
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
12
|
import { Outbound, Protocol, Response } from '../../../controller/comms/messages/Messages';
|
|
13
|
+
import { delayMgr } from '../../Lockouts';
|
|
13
14
|
|
|
14
15
|
export class NixieHeaterCollection extends NixieEquipmentCollection<NixieHeaterBase> {
|
|
15
16
|
public async deleteHeaterAsync(id: number) {
|
|
@@ -96,6 +97,8 @@ export class NixieHeaterBase extends NixieEquipment {
|
|
|
96
97
|
protected bodyOnTime: number;
|
|
97
98
|
protected isOn: boolean = false;
|
|
98
99
|
protected isCooling: boolean = false;
|
|
100
|
+
protected lastHeatCycle: Date;
|
|
101
|
+
protected lastCoolCycle: Date;
|
|
99
102
|
constructor(ncp: INixieControlPanel, heater: Heater) {
|
|
100
103
|
super(ncp);
|
|
101
104
|
this.heater = heater;
|
|
@@ -103,6 +106,7 @@ export class NixieHeaterBase extends NixieEquipment {
|
|
|
103
106
|
public get suspendPolling(): boolean { return this._suspendPolling > 0; }
|
|
104
107
|
public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
|
|
105
108
|
public get id(): number { return typeof this.heater !== 'undefined' ? this.heater.id : -1; }
|
|
109
|
+
public getCooldownTime() { return 0; }
|
|
106
110
|
public static create(ncp: INixieControlPanel, heater: Heater): NixieHeaterBase {
|
|
107
111
|
let type = sys.board.valueMaps.heaterTypes.transform(heater.type);
|
|
108
112
|
switch (type.name) {
|
|
@@ -144,34 +148,50 @@ export class NixieHeaterBase extends NixieEquipment {
|
|
|
144
148
|
}
|
|
145
149
|
export class NixieGasHeater extends NixieHeaterBase {
|
|
146
150
|
public pollingInterval: number = 10000;
|
|
147
|
-
declare heater: Heater;
|
|
151
|
+
//declare heater: Heater;
|
|
148
152
|
constructor(ncp: INixieControlPanel, heater: Heater) {
|
|
149
153
|
super(ncp, heater);
|
|
150
154
|
this.heater = heater;
|
|
151
155
|
this.pollEquipmentAsync();
|
|
152
156
|
}
|
|
153
157
|
public get id(): number { return typeof this.heater !== 'undefined' ? this.heater.id : -1; }
|
|
158
|
+
public getCooldownTime(): number {
|
|
159
|
+
// Delays are always in terms of seconds so convert the minute to seconds.
|
|
160
|
+
if (this.heater.cooldownDelay === 0 || typeof this.lastHeatCycle === 'undefined') return 0;
|
|
161
|
+
let now = new Date().getTime();
|
|
162
|
+
let cooldown = this.isOn ? this.heater.cooldownDelay * 60000 : Math.round( ((this.lastHeatCycle.getDate() + this.heater.cooldownDelay * 60000) - now) / 1000);
|
|
163
|
+
return Math.min(Math.max(0, cooldown), this.heater.cooldownDelay * 60);
|
|
164
|
+
}
|
|
154
165
|
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean) {
|
|
155
166
|
try {
|
|
156
167
|
// Initialize the desired state.
|
|
157
168
|
this.isOn = isOn;
|
|
158
169
|
this.isCooling = false;
|
|
170
|
+
let target = hstate.startupDelay === false && isOn;
|
|
171
|
+
if (target && typeof hstate.endTime !== 'undefined') {
|
|
172
|
+
// Calculate a short cycle time so that the solar heater does not cycle
|
|
173
|
+
// too often. For gas heaters this is 60 seconds. This gives enough time
|
|
174
|
+
// for the heater control circuit to make a full cycle.
|
|
175
|
+
if (new Date().getTime() - hstate.endTime.getTime() < 60000) {
|
|
176
|
+
logger.verbose(`${hstate.name} short cycle detected deferring turn on state`);
|
|
177
|
+
target = false;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
159
180
|
// Here we go we need to set the firemans switch state.
|
|
160
|
-
if (hstate.isOn !==
|
|
181
|
+
if (hstate.isOn !== target) {
|
|
161
182
|
logger.info(`Nixie: Set Heater ${hstate.id}-${hstate.name} to ${isOn}`);
|
|
162
183
|
}
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return;
|
|
184
|
+
if (typeof this._lastState === 'undefined' || target || this._lastState !== target) {
|
|
185
|
+
if (utils.isNullOrEmpty(this.heater.connectionId) || utils.isNullOrEmpty(this.heater.deviceBinding)) {
|
|
186
|
+
this._lastState = hstate.isOn = target;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
let res = await NixieEquipment.putDeviceService(this.heater.connectionId, `/state/device/${this.heater.deviceBinding}`,
|
|
190
|
+
{ isOn: target, latch: target ? 10000 : undefined });
|
|
191
|
+
if (res.status.code === 200) this._lastState = hstate.isOn = target;
|
|
192
|
+
else logger.error(`Nixie Error setting heater state: ${res.status.code} -${res.status.message} ${res.error.message}`);
|
|
193
|
+
}
|
|
194
|
+
if (target) this.lastHeatCycle = new Date();
|
|
175
195
|
}
|
|
176
196
|
} catch (err) { return logger.error(`Nixie Error setting heater state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
177
197
|
}
|
|
@@ -228,25 +248,59 @@ export class NixieSolarHeater extends NixieHeaterBase {
|
|
|
228
248
|
public get id(): number { return typeof this.heater !== 'undefined' ? this.heater.id : -1; }
|
|
229
249
|
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean, isCooling: boolean) {
|
|
230
250
|
try {
|
|
251
|
+
let origState = hstate.isOn;
|
|
231
252
|
// Initialize the desired state.
|
|
232
253
|
this.isOn = isOn;
|
|
233
254
|
this.isCooling = isCooling;
|
|
234
|
-
|
|
235
|
-
if (hstate.
|
|
236
|
-
|
|
255
|
+
let target = hstate.startupDelay === false && isOn;
|
|
256
|
+
if (target && typeof hstate.endTime !== 'undefined') {
|
|
257
|
+
// Calculate a short cycle time so that the solar heater does not cycle
|
|
258
|
+
// too often. For solar heaters this is 60 seconds. This gives enough time
|
|
259
|
+
// for the valve to rotate and start heating. If the solar and water sensors are
|
|
260
|
+
// not having issues this should be plenty of time.
|
|
261
|
+
if (new Date().getTime() - hstate.endTime.getTime() < 60000) {
|
|
262
|
+
logger.verbose(`${hstate.name} short cycle detected deferring turn on state`);
|
|
263
|
+
target = false;
|
|
264
|
+
}
|
|
237
265
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
266
|
+
|
|
267
|
+
// Here we go we need to set the valve status that is attached to solar.
|
|
268
|
+
if (hstate.isOn !== target) {
|
|
269
|
+
logger.info(`Nixie: Set Heater ${hstate.id}-${hstate.name} to ${isOn}`);
|
|
241
270
|
}
|
|
242
|
-
if (typeof this._lastState === 'undefined' ||
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
271
|
+
if (typeof this._lastState === 'undefined' || target || this._lastState !== target) {
|
|
272
|
+
if (utils.isNullOrEmpty(this.heater.connectionId) || utils.isNullOrEmpty(this.heater.deviceBinding)) {
|
|
273
|
+
this._lastState = hstate.isOn = target;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
let res = await NixieEquipment.putDeviceService(this.heater.connectionId, `/state/device/${this.heater.deviceBinding}`,
|
|
277
|
+
{ isOn: target, latch: target ? 10000 : undefined });
|
|
278
|
+
if (res.status.code === 200) this._lastState = hstate.isOn = target;
|
|
279
|
+
else logger.error(`Nixie Error setting heater state: ${res.status.code} -${res.status.message} ${res.error.message}`);
|
|
280
|
+
}
|
|
281
|
+
if (target) {
|
|
282
|
+
if (isCooling) this.lastCoolCycle = new Date();
|
|
283
|
+
else if (isOn) this.lastHeatCycle = new Date();
|
|
284
|
+
}
|
|
246
285
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
286
|
+
// In this instance we need to see if there are cleaner circuits that we need to turn off
|
|
287
|
+
// then delay for the current body because the solar just came on.
|
|
288
|
+
if (hstate.isOn && sys.general.options.cleanerSolarDelay && !origState) {
|
|
289
|
+
let arrTypes = sys.board.valueMaps.circuitFunctions.toArray().filter(x => { return x.name.indexOf('cleaner') !== -1 && x.body === hstate.bodyId });
|
|
290
|
+
let cleaners = sys.circuits.filter(x => { return arrTypes.findIndex(t => { return t.val === x.type }) !== -1 });
|
|
291
|
+
// Turn off all the cleaner circuits and set an on delay if they are on.
|
|
292
|
+
for (let i = 0; i < cleaners.length; i++) {
|
|
293
|
+
let cleaner = cleaners.getItemByIndex(i);
|
|
294
|
+
if (cleaner.isActive) {
|
|
295
|
+
let cstate = state.circuits.getItemById(cleaner.id);
|
|
296
|
+
if (cstate.isOn && sys.general.options.cleanerSolarDelayTime > 0) {
|
|
297
|
+
// Turn off the circuit then set a delay.
|
|
298
|
+
logger.info(`Setting cleaner solar delay for ${cleaner.name} to ${sys.general.options.cleanerSolarDelayTime}`);
|
|
299
|
+
await sys.board.circuits.setCircuitStateAsync(cstate.id, false);
|
|
300
|
+
delayMgr.setCleanerStartDelay(cstate, hstate.bodyId, sys.general.options.cleanerSolarDelayTime);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
250
304
|
}
|
|
251
305
|
} catch (err) { return logger.error(`Nixie Error setting heater state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
252
306
|
}
|
|
@@ -304,6 +358,11 @@ export class NixieHeatpump extends NixieHeaterBase {
|
|
|
304
358
|
this.suspendPolling = true;
|
|
305
359
|
this.isOn = isOn;
|
|
306
360
|
this.isCooling = isCooling;
|
|
361
|
+
if (!hstate.startupDelay) {
|
|
362
|
+
if (isOn && !isCooling) this.lastHeatCycle = new Date();
|
|
363
|
+
else if (isCooling) this.lastCoolCycle = new Date();
|
|
364
|
+
}
|
|
365
|
+
// When this is implemented lets not forget to deal with the startup delay. See UltraTemp for implementation.
|
|
307
366
|
} catch (err) { return logger.error(`Nixie Error setting heater state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
308
367
|
finally { this.suspendPolling = false; }
|
|
309
368
|
}
|
|
@@ -420,7 +479,16 @@ export class NixieUltratemp extends NixieHeatpump {
|
|
|
420
479
|
});
|
|
421
480
|
out.appendPayloadBytes(0, 10);
|
|
422
481
|
out.setPayloadByte(0, 144);
|
|
423
|
-
|
|
482
|
+
// If we are in startup delay simply tell the heater that it is off.
|
|
483
|
+
if (sheater.startupDelay)
|
|
484
|
+
out.setPayloadByte(1, 0, 0);
|
|
485
|
+
else {
|
|
486
|
+
if (this.isOn) {
|
|
487
|
+
if (!this.isCooling) this.lastHeatCycle = new Date();
|
|
488
|
+
else this.lastCoolCycle = new Date();
|
|
489
|
+
}
|
|
490
|
+
out.setPayloadByte(1, this.isOn ? (this.isCooling ? 2 : 1) : 0, 0);
|
|
491
|
+
}
|
|
424
492
|
conn.queueSendMessage(out);
|
|
425
493
|
});
|
|
426
494
|
return success;
|
|
@@ -433,9 +501,101 @@ export class NixieUltratemp extends NixieHeatpump {
|
|
|
433
501
|
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
434
502
|
this._pollTimer = null;
|
|
435
503
|
logger.info(`Closing Heater ${this.heater.name}`);
|
|
436
|
-
|
|
437
504
|
}
|
|
438
505
|
catch (err) { logger.error(`Ultratemp closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
439
506
|
}
|
|
440
507
|
}
|
|
441
|
-
export class NixieMastertemp extends NixieGasHeater {
|
|
508
|
+
export class NixieMastertemp extends NixieGasHeater {
|
|
509
|
+
constructor(ncp: INixieControlPanel, heater: Heater) {
|
|
510
|
+
super(ncp, heater);
|
|
511
|
+
// Set the polling interval to 3 seconds.
|
|
512
|
+
this.pollEquipmentAsync();
|
|
513
|
+
this.pollingInterval = 3000;
|
|
514
|
+
}
|
|
515
|
+
public getCooldownTime(): number {
|
|
516
|
+
// Delays are always in terms of seconds so convert the minute to seconds.
|
|
517
|
+
if (this.heater.cooldownDelay === 0 || typeof this.lastHeatCycle === 'undefined') return 0;
|
|
518
|
+
let now = new Date().getTime();
|
|
519
|
+
let cooldown = this.isOn ? this.heater.cooldownDelay * 60000 : Math.round(((this.lastHeatCycle.getDate() + this.heater.cooldownDelay * 60000) - now) / 1000);
|
|
520
|
+
return Math.min(Math.max(0, cooldown), this.heater.cooldownDelay * 60);
|
|
521
|
+
}
|
|
522
|
+
public async setHeaterStateAsync(hstate: HeaterState, isOn: boolean) {
|
|
523
|
+
try {
|
|
524
|
+
// Initialize the desired state.
|
|
525
|
+
this.isOn = isOn;
|
|
526
|
+
this.isCooling = false;
|
|
527
|
+
// Here we go we need to set the firemans switch state.
|
|
528
|
+
if (hstate.isOn !== isOn) {
|
|
529
|
+
logger.info(`Nixie: Set Heater ${hstate.id}-${hstate.name} to ${isOn}`);
|
|
530
|
+
}
|
|
531
|
+
if (isOn && !hstate.startupDelay) this.lastHeatCycle = new Date();
|
|
532
|
+
hstate.isOn = isOn;
|
|
533
|
+
} catch (err) { return logger.error(`Nixie Error setting heater state ${hstate.id}-${hstate.name}: ${err.message}`); }
|
|
534
|
+
}
|
|
535
|
+
public async pollEquipmentAsync() {
|
|
536
|
+
let self = this;
|
|
537
|
+
try {
|
|
538
|
+
this.suspendPolling = true;
|
|
539
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
540
|
+
this._pollTimer = null;
|
|
541
|
+
if (this._suspendPolling > 1) return;
|
|
542
|
+
let sheater = state.heaters.getItemById(this.heater.id, !this.closing);
|
|
543
|
+
if (!this.closing) await this.setStatus(sheater);
|
|
544
|
+
}
|
|
545
|
+
catch (err) { logger.error(`Error polling MasterTemp heater - ${err}`); }
|
|
546
|
+
finally {
|
|
547
|
+
this.suspendPolling = false; if (!this.closing) this._pollTimer = setTimeout(async () => {
|
|
548
|
+
try { await self.pollEquipmentAsync() } catch (err) { }
|
|
549
|
+
}, this.pollingInterval || 3000);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
public async setStatus(sheater: HeaterState): Promise<boolean> {
|
|
553
|
+
try {
|
|
554
|
+
let success = await new Promise<boolean>((resolve, reject) => {
|
|
555
|
+
let out = Outbound.create({
|
|
556
|
+
protocol: Protocol.Heater,
|
|
557
|
+
source: 16,
|
|
558
|
+
dest: this.heater.address,
|
|
559
|
+
action: 112,
|
|
560
|
+
payload: [],
|
|
561
|
+
retries: 3, // We are going to try 4 times.
|
|
562
|
+
response: Response.create({ protocol: Protocol.Heater, action: 116 }),
|
|
563
|
+
onAbort: () => { },
|
|
564
|
+
onComplete: (err) => {
|
|
565
|
+
if (err) {
|
|
566
|
+
// 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
|
|
567
|
+
// come across this will be cleared by the processing of that message.
|
|
568
|
+
sheater.commStatus = sys.board.valueMaps.equipmentCommStatus.getValue('commerr');
|
|
569
|
+
state.equipment.messages.setMessageByCode(`heater:${sheater.id}:comms`, 'error', `Communication error with ${sheater.name}`);
|
|
570
|
+
resolve(false);
|
|
571
|
+
}
|
|
572
|
+
else { resolve(true); }
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
out.appendPayloadBytes(0, 11);
|
|
576
|
+
// If we have a startup delay we need to simply send 0 to the heater to make sure that it is off.
|
|
577
|
+
if (sheater.startupDelay)
|
|
578
|
+
out.setPayloadByte(0, 0);
|
|
579
|
+
else {
|
|
580
|
+
// The cooldown delay is a bit hard to figure out here since I think the heater does it on its own.
|
|
581
|
+
out.setPayloadByte(0, sheater.bodyId <= 2 ? sheater.bodyId : 0);
|
|
582
|
+
}
|
|
583
|
+
out.setPayloadByte(1, sys.bodies.getItemById(1).heatSetpoint || 0);
|
|
584
|
+
out.setPayloadByte(2, sys.bodies.getItemById(2).heatSetpoint || 0);
|
|
585
|
+
conn.queueSendMessage(out);
|
|
586
|
+
});
|
|
587
|
+
return success;
|
|
588
|
+
} catch (err) { logger.error(`Communication error with MasterTemp : ${err.message}`); }
|
|
589
|
+
}
|
|
590
|
+
public async closeAsync() {
|
|
591
|
+
try {
|
|
592
|
+
this.suspendPolling = true;
|
|
593
|
+
this.closing = true;
|
|
594
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
595
|
+
this._pollTimer = null;
|
|
596
|
+
logger.info(`Closing Heater ${this.heater.name}`);
|
|
597
|
+
|
|
598
|
+
}
|
|
599
|
+
catch (err) { logger.error(`MasterTemp closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
600
|
+
}
|
|
601
|
+
}
|