nodejs-poolcontroller 7.3.0 → 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/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
- package/Changelog +23 -0
- package/README.md +5 -5
- package/app.ts +2 -0
- package/config/Config.ts +3 -0
- package/config/VersionCheck.ts +8 -4
- package/controller/Constants.ts +88 -0
- package/controller/Equipment.ts +246 -66
- package/controller/Errors.ts +24 -1
- package/controller/Lockouts.ts +423 -0
- package/controller/State.ts +314 -54
- package/controller/boards/EasyTouchBoard.ts +107 -59
- package/controller/boards/IntelliCenterBoard.ts +186 -125
- package/controller/boards/IntelliTouchBoard.ts +104 -30
- package/controller/boards/NixieBoard.ts +721 -159
- package/controller/boards/SystemBoard.ts +2370 -1108
- package/controller/comms/Comms.ts +85 -10
- package/controller/comms/messages/Messages.ts +10 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
- package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
- package/controller/comms/messages/config/CoverMessage.ts +1 -0
- package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
- package/controller/comms/messages/config/ExternalMessage.ts +44 -26
- package/controller/comms/messages/config/FeatureMessage.ts +8 -1
- package/controller/comms/messages/config/GeneralMessage.ts +8 -0
- package/controller/comms/messages/config/HeaterMessage.ts +15 -9
- package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
- package/controller/comms/messages/config/OptionsMessage.ts +13 -1
- package/controller/comms/messages/config/PumpMessage.ts +4 -20
- package/controller/comms/messages/config/RemoteMessage.ts +4 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
- package/controller/comms/messages/config/SecurityMessage.ts +1 -0
- package/controller/comms/messages/config/ValveMessage.ts +13 -3
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +2 -3
- package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
- package/controller/comms/messages/status/HeaterStateMessage.ts +42 -9
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +37 -26
- package/controller/nixie/Nixie.ts +18 -16
- package/controller/nixie/bodies/Body.ts +4 -1
- package/controller/nixie/chemistry/ChemController.ts +80 -77
- package/controller/nixie/chemistry/Chlorinator.ts +9 -8
- package/controller/nixie/circuits/Circuit.ts +55 -6
- package/controller/nixie/heaters/Heater.ts +192 -32
- package/controller/nixie/pumps/Pump.ts +146 -84
- package/controller/nixie/schedules/Schedule.ts +3 -2
- package/controller/nixie/valves/Valve.ts +1 -1
- package/defaultConfig.json +32 -1
- package/issue_template.md +1 -1
- package/logger/DataLogger.ts +37 -22
- package/package.json +20 -18
- package/web/Server.ts +520 -29
- package/web/bindings/influxDB.json +96 -8
- package/web/bindings/mqtt.json +151 -40
- package/web/bindings/mqttAlt.json +114 -4
- package/web/interfaces/httpInterface.ts +2 -0
- package/web/interfaces/influxInterface.ts +36 -19
- package/web/interfaces/mqttInterface.ts +14 -3
- package/web/services/config/Config.ts +171 -44
- package/web/services/state/State.ts +49 -5
- package/web/services/state/StateSocket.ts +18 -1
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020. Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify
|
|
5
|
+
it under the terms of the GNU Affero General Public License as
|
|
6
|
+
published by the Free Software Foundation, either version 3 of the
|
|
7
|
+
License, or (at your option) any later version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful,
|
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
GNU Affero General Public License for more details.
|
|
13
|
+
|
|
14
|
+
You should have received a copy of the GNU Affero General Public License
|
|
15
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
import { PumpState, HeaterState, BodyTempState, ICircuitState, state } from "./State";
|
|
18
|
+
import { Equipment, sys } from "./Equipment";
|
|
19
|
+
import { utils } from "./Constants";
|
|
20
|
+
import { logger } from "../logger/Logger";
|
|
21
|
+
import { webApp } from "../web/Server";
|
|
22
|
+
// LOCKOUT PRIMER
|
|
23
|
+
// Lockouts are either time based (Delays) or based upon the current state configuration for
|
|
24
|
+
// the system. So in some cases circuits can only be engaged in pool mode or in spa mode. In
|
|
25
|
+
// others a period of time must occur before a particular action can continue. Delays can typically
|
|
26
|
+
// be cancelled manually while lockouts can only be cancelled when the condition required for the lockout
|
|
27
|
+
// is changed.
|
|
28
|
+
|
|
29
|
+
// DELAYS:
|
|
30
|
+
// Pump Off During Valve Rotation (30 sec): This turns any pump associated with the body being turned on to
|
|
31
|
+
// so that is is off. This gives the valves time to rotate so that cold water from the pool does not cycle into
|
|
32
|
+
// the spa and hot water from the spa does not escape into the pool. This has nothing to do with
|
|
33
|
+
// water hammer or anything else.
|
|
34
|
+
//
|
|
35
|
+
// Heater Cooldown Delay (based on max heater time): When the system is heating and an event is occurring
|
|
36
|
+
// that will cause the heater to be turned off, the current mode will be retained until the delay is either
|
|
37
|
+
// cancelled or expired.
|
|
38
|
+
// Delay Conditions:
|
|
39
|
+
// 1. Being in either pool or spa mode and simply turning off that mode where the heater will be turned off.
|
|
40
|
+
// 2. Switching between pool and spa when the target mode does not use the identified heater.
|
|
41
|
+
// Exceptions:
|
|
42
|
+
// 1. The last call for heat was earlier than the current time minus the cooldown delay defined for the heater.
|
|
43
|
+
// 2. The heater mode is in a cooling mode.
|
|
44
|
+
//
|
|
45
|
+
// Heater Startup: When a body is first turned on the heater will not be engaged for 10 seconds after any pump delay
|
|
46
|
+
// or the time that the body is engaged.
|
|
47
|
+
//
|
|
48
|
+
// Cleaner Circuit Start Delay: Delays turning on any circuit with a cleaner function until the delay expires. This is
|
|
49
|
+
// so booster pumps can be assured of sufficient forward pressure prior to turning on. These pumps often require sufficient
|
|
50
|
+
// pressure before engaging and will cavitate if they do not have it. The Pentair default is 5min.
|
|
51
|
+
//
|
|
52
|
+
// Cleaner Circuit Solar Delay: This only exists with Pentair panels. This shuts off any circuit
|
|
53
|
+
// designated as a pool cleaner circuit if it is on and delays turning it on for 5min after the solar starts. The assumption
|
|
54
|
+
// here is that pressure reduction that can occur when the solar kicks on can cavitate the pump.
|
|
55
|
+
//
|
|
56
|
+
// LOCKOUTS (Proposed):
|
|
57
|
+
// Spillway Lockout: This locks out any circuit or feature that is marked with a Spillway circuit function (type) whenever
|
|
58
|
+
// whenever the pool circuit is not engaged. This should mark the spillway circuit as a delayStart then release it when the
|
|
59
|
+
// pool body starts.
|
|
60
|
+
interface ILockout {
|
|
61
|
+
type: string
|
|
62
|
+
}
|
|
63
|
+
export class EquipmentLockout implements ILockout {
|
|
64
|
+
public id = utils.uuid();
|
|
65
|
+
public create() { }
|
|
66
|
+
public startTime: Date;
|
|
67
|
+
public type: string = 'lockout';
|
|
68
|
+
public message: string = '';
|
|
69
|
+
}
|
|
70
|
+
export class EquipmentDelay implements ILockout {
|
|
71
|
+
public constructor() { this.id = delayMgr.getNextId(); }
|
|
72
|
+
public id;
|
|
73
|
+
public type: string = 'delay';
|
|
74
|
+
public endTime: Date;
|
|
75
|
+
public canCancel: boolean = true;
|
|
76
|
+
public cancelDelay() { };
|
|
77
|
+
public reset() { };
|
|
78
|
+
public clearDelay() { };
|
|
79
|
+
public message;
|
|
80
|
+
protected _delayTimer: NodeJS.Timeout;
|
|
81
|
+
public serialize(): any {
|
|
82
|
+
return {
|
|
83
|
+
id: this.id,
|
|
84
|
+
type: this.type,
|
|
85
|
+
canCancel: this.canCancel,
|
|
86
|
+
message: this.message
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class PumpValveDelay extends EquipmentDelay {
|
|
91
|
+
public constructor(ps: PumpState, delay?: number) {
|
|
92
|
+
super();
|
|
93
|
+
this.type = 'pumpValveDelay';
|
|
94
|
+
this.message = `${ps.name} will start after valve Delay`;
|
|
95
|
+
this.pumpState = ps;
|
|
96
|
+
this.pumpState.pumpOnDelay = true;
|
|
97
|
+
this._delayTimer = setTimeout(() => {
|
|
98
|
+
logger.info(`Valve delay expired for ${this.pumpState.name}`);
|
|
99
|
+
this.pumpState.pumpOnDelay = false;
|
|
100
|
+
delayMgr.deleteDelay(this.id);
|
|
101
|
+
}, delay * 1000 || sys.general.options.valveDelayTime * 1000);
|
|
102
|
+
logger.info(`Valve delay started for ${this.pumpState.name} - ${delay || sys.general.options.valveDelayTime}sec`);
|
|
103
|
+
}
|
|
104
|
+
public pumpState: PumpState;
|
|
105
|
+
public cancelDelay() {
|
|
106
|
+
this.pumpState.pumpOnDelay = false;
|
|
107
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
108
|
+
logger.info(`Valve delay cancelled for ${this.pumpState.name}`);
|
|
109
|
+
this._delayTimer = undefined;
|
|
110
|
+
delayMgr.deleteDelay(this.id);
|
|
111
|
+
}
|
|
112
|
+
public clearDelay() {
|
|
113
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
114
|
+
logger.info(`Valve delay cleared for ${this.pumpState.name}`);
|
|
115
|
+
this._delayTimer = undefined;
|
|
116
|
+
delayMgr.deleteDelay(this.id);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export class HeaterStartupDelay extends EquipmentDelay {
|
|
120
|
+
public constructor(hs: HeaterState, delay?: number) {
|
|
121
|
+
super();
|
|
122
|
+
this.type = 'heaterStartupDelay';
|
|
123
|
+
this.message = `${hs.name} will start after delay`;
|
|
124
|
+
this.heaterState = hs;
|
|
125
|
+
this.heaterState.startupDelay = true;
|
|
126
|
+
this._delayTimer = setTimeout(() => {
|
|
127
|
+
logger.info(`Heater Startup delay expired for ${this.heaterState.name}`);
|
|
128
|
+
this.heaterState.startupDelay = false;
|
|
129
|
+
delayMgr.deleteDelay(this.id);
|
|
130
|
+
}, delay * 1000 || sys.general.options.valveDelayTime * 1000);
|
|
131
|
+
logger.info(`Heater delay started for ${this.heaterState.name} - ${delay || sys.general.options.heaterStartDelayTime}sec`);
|
|
132
|
+
}
|
|
133
|
+
public heaterState: HeaterState;
|
|
134
|
+
public cancelDelay() {
|
|
135
|
+
this.heaterState.startupDelay = false;
|
|
136
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
137
|
+
logger.info(`Heater Startup delay cancelled for ${this.heaterState.name}`);
|
|
138
|
+
this._delayTimer = undefined;
|
|
139
|
+
delayMgr.deleteDelay(this.id);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export class HeaterCooldownDelay extends EquipmentDelay {
|
|
143
|
+
public constructor(bsoff: BodyTempState, bson?: BodyTempState, delay?: number) {
|
|
144
|
+
super();
|
|
145
|
+
this.type = 'heaterCooldownDelay';
|
|
146
|
+
this.message = `${bsoff.name} Heater Cooldown in progress`;
|
|
147
|
+
this.bodyStateOff = bsoff;
|
|
148
|
+
this.bodyStateOff.heaterCooldownDelay = true;
|
|
149
|
+
this.bodyStateOff.heatStatus = sys.board.valueMaps.heatStatus.getValue('cooldown');
|
|
150
|
+
let cstateOff = state.circuits.getItemById(bsoff.circuit);
|
|
151
|
+
this.bodyStateOn = bson;
|
|
152
|
+
this.bodyStateOff.stopDelay = cstateOff.stopDelay = true;
|
|
153
|
+
let cstateOn = (typeof bson !== 'undefined') ? state.circuits.getItemById(bson.circuit) : undefined;
|
|
154
|
+
if (typeof cstateOn !== 'undefined') {
|
|
155
|
+
this.bodyStateOn.startDelay = cstateOn.startDelay = true;
|
|
156
|
+
}
|
|
157
|
+
logger.verbose(`Heater Cooldown Delay started for ${this.bodyStateOff.name} - ${delay/1000}sec`);
|
|
158
|
+
this._delayTimer = setTimeout(() => {
|
|
159
|
+
logger.verbose(`Heater Cooldown delay expired for ${this.bodyStateOff.name}`);
|
|
160
|
+
this.bodyStateOff.stopDelay = state.circuits.getItemById(this.bodyStateOff.circuit).stopDelay = false;
|
|
161
|
+
// Now that the startup delay expired cancel the delay and shut off the circuit.
|
|
162
|
+
(async () => {
|
|
163
|
+
try {
|
|
164
|
+
await sys.board.circuits.setCircuitStateAsync(cstateOff.id, false, true);
|
|
165
|
+
if (typeof this.bodyStateOn !== 'undefined') {
|
|
166
|
+
this.bodyStateOn.startDelay = state.circuits.getItemById(this.bodyStateOn.circuit).startDelay = false;
|
|
167
|
+
await sys.board.circuits.setCircuitStateAsync(this.bodyStateOn.circuit, true);
|
|
168
|
+
}
|
|
169
|
+
} catch (err) { logger.error(`Error executing Cooldown Delay completion: ${err}`); }
|
|
170
|
+
})();
|
|
171
|
+
this.bodyStateOff.heaterCooldownDelay = false;
|
|
172
|
+
this.bodyStateOff.heatStatus = sys.board.valueMaps.heatStatus.getValue('off');
|
|
173
|
+
delayMgr.deleteDelay(this.id);
|
|
174
|
+
}, delay);
|
|
175
|
+
state.emitEquipmentChanges();
|
|
176
|
+
}
|
|
177
|
+
public bodyStateOff: BodyTempState;
|
|
178
|
+
public bodyStateOn: BodyTempState;
|
|
179
|
+
public setBodyStateOn(bson?: BodyTempState) {
|
|
180
|
+
if (typeof this.bodyStateOn !== 'undefined' && (typeof bson === 'undefined' || this.bodyStateOn.id !== bson.id))
|
|
181
|
+
this.bodyStateOn.startDelay = state.circuits.getItemById(this.bodyStateOn.circuit).startDelay = false;
|
|
182
|
+
if (typeof bson !== 'undefined') {
|
|
183
|
+
if (typeof this.bodyStateOn === 'undefined' || this.bodyStateOn.id !== bson.id) {
|
|
184
|
+
bson.startDelay = state.circuits.getItemById(bson.circuit).startDelay = true;
|
|
185
|
+
logger.info(`${bson.name} will Start After Cooldown Delay`);
|
|
186
|
+
this.bodyStateOn = bson;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
else this.bodyStateOn = undefined;
|
|
190
|
+
}
|
|
191
|
+
public cancelDelay() {
|
|
192
|
+
let cstateOff = state.circuits.getItemById(this.bodyStateOff.circuit);
|
|
193
|
+
cstateOff.stopDelay = false;
|
|
194
|
+
(async () => {
|
|
195
|
+
await sys.board.circuits.setCircuitStateAsync(cstateOff.id, false);
|
|
196
|
+
if (typeof this.bodyStateOn !== 'undefined') {
|
|
197
|
+
this.bodyStateOn.startDelay = state.circuits.getItemById(this.bodyStateOn.circuit).startDelay = false;
|
|
198
|
+
await sys.board.circuits.setCircuitStateAsync(this.bodyStateOn.circuit, true);
|
|
199
|
+
}
|
|
200
|
+
})();
|
|
201
|
+
this.bodyStateOff.stopDelay = this.bodyStateOff.heaterCooldownDelay = false;
|
|
202
|
+
this.bodyStateOff.heatStatus = sys.board.valueMaps.heatStatus.getValue('off');
|
|
203
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
204
|
+
logger.info(`Heater Cooldown delay cancelled for ${this.bodyStateOff.name}`);
|
|
205
|
+
this._delayTimer = undefined;
|
|
206
|
+
delayMgr.deleteDelay(this.id);
|
|
207
|
+
state.emitEquipmentChanges();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
}
|
|
211
|
+
interface ICleanerDelay {
|
|
212
|
+
cleanerState: ICircuitState,
|
|
213
|
+
bodyId: number
|
|
214
|
+
}
|
|
215
|
+
export class CleanerStartDelay extends EquipmentDelay implements ICleanerDelay {
|
|
216
|
+
constructor(cs: ICircuitState, bodyId: number, delay?: number) {
|
|
217
|
+
super();
|
|
218
|
+
this.type = 'cleanerStartDelay';
|
|
219
|
+
this.message = `${cs.name} will start after delay`;
|
|
220
|
+
this.bodyId = bodyId;
|
|
221
|
+
this.cleanerState = cs;
|
|
222
|
+
cs.startDelay = true;
|
|
223
|
+
this._delayTimer = setTimeout(() => {
|
|
224
|
+
logger.info(`Cleaner delay expired for ${this.cleanerState.name}`);
|
|
225
|
+
this.cleanerState.startDelay = false;
|
|
226
|
+
(async () => {
|
|
227
|
+
try {
|
|
228
|
+
await sys.board.circuits.setCircuitStateAsync(this.cleanerState.id, true, true);
|
|
229
|
+
this.cleanerState.startDelay = false;
|
|
230
|
+
} catch (err) { logger.error(`Error executing Cleaner Start Delay completion: ${err}`); }
|
|
231
|
+
})();
|
|
232
|
+
delayMgr.deleteDelay(this.id);
|
|
233
|
+
}, delay * 1000 || sys.general.options.cleanerStartDelayTime * 1000);
|
|
234
|
+
logger.info(`Cleaner delay started for ${this.cleanerState.name} - ${delay || sys.general.options.cleanerStartDelayTime}sec`);
|
|
235
|
+
}
|
|
236
|
+
public cleanerState: ICircuitState;
|
|
237
|
+
public bodyId: number;
|
|
238
|
+
public cancelDelay() {
|
|
239
|
+
this.cleanerState.startDelay = false;
|
|
240
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
241
|
+
logger.info(`Cleaner Start delay cancelled for ${this.cleanerState.name}`);
|
|
242
|
+
this._delayTimer = undefined;
|
|
243
|
+
this.cleanerState.startDelay = false;
|
|
244
|
+
(async () => {
|
|
245
|
+
try {
|
|
246
|
+
await sys.board.circuits.setCircuitStateAsync(this.cleanerState.id, true, true);
|
|
247
|
+
} catch (err) { logger.error(`Error executing Cleaner Start Delay completion: ${err}`); }
|
|
248
|
+
})();
|
|
249
|
+
delayMgr.deleteDelay(this.id);
|
|
250
|
+
}
|
|
251
|
+
public clearDelay() {
|
|
252
|
+
this.cleanerState.startDelay = false;
|
|
253
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
254
|
+
logger.info(`Cleaner Start delay cleared for ${this.cleanerState.name}`);
|
|
255
|
+
this._delayTimer = undefined;
|
|
256
|
+
this.cleanerState.startDelay = false;
|
|
257
|
+
delayMgr.deleteDelay(this.id);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public reset(delay?: number) {
|
|
261
|
+
if (typeof this._delayTimer !== 'undefined') clearTimeout(this._delayTimer);
|
|
262
|
+
this.cleanerState.startDelay = true;
|
|
263
|
+
logger.info(`Cleaner Start delay reset for ${this.cleanerState.name}`);
|
|
264
|
+
this._delayTimer = setTimeout(() => {
|
|
265
|
+
logger.info(`Cleaner delay expired for ${this.cleanerState.name}`);
|
|
266
|
+
this.cleanerState.startDelay = false;
|
|
267
|
+
(async () => {
|
|
268
|
+
try {
|
|
269
|
+
await sys.board.circuits.setCircuitStateAsync(this.cleanerState.id, true);
|
|
270
|
+
this.cleanerState.startDelay = false;
|
|
271
|
+
} catch (err) { logger.error(`Error executing Cleaner Start Delay completion: ${err}`); }
|
|
272
|
+
})();
|
|
273
|
+
delayMgr.deleteDelay(this.id);
|
|
274
|
+
}, delay * 1000 || sys.general.options.cleanerStartDelayTime * 1000);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
export class DelayManager extends Array<EquipmentDelay> {
|
|
278
|
+
protected _id = 1;
|
|
279
|
+
private _emitTimer: NodeJS.Timeout;
|
|
280
|
+
public setDirty() {
|
|
281
|
+
if (typeof this._emitTimer) clearTimeout(this._emitTimer);
|
|
282
|
+
this._emitTimer = setTimeout(() => this.emitDelayState(), 1000);
|
|
283
|
+
}
|
|
284
|
+
public getNextId() { return this._id++; }
|
|
285
|
+
public cancelDelay(id: number) {
|
|
286
|
+
let del = this.find(x => x.id === id);
|
|
287
|
+
if (typeof del !== 'undefined') del.cancelDelay();
|
|
288
|
+
}
|
|
289
|
+
public setPumpValveDelay(ps: PumpState, delay?: number) {
|
|
290
|
+
let cds = this.filter(x => x.type === 'pumpValveDelay');
|
|
291
|
+
for (let i = 0; i < cds.length; i++) {
|
|
292
|
+
let delay = cds[i] as PumpValveDelay;
|
|
293
|
+
if (delay.pumpState.id === ps.id) delay.clearDelay();
|
|
294
|
+
}
|
|
295
|
+
this.push(new PumpValveDelay(ps, delay)); this.setDirty();
|
|
296
|
+
}
|
|
297
|
+
public cancelPumpValveDelays() { this.cancelDelaysByType('pumpValveDelay'); this.setDirty(); }
|
|
298
|
+
public setHeaterStartupDelay(hs: HeaterState, delay?: number) {
|
|
299
|
+
let cds = this.filter(x => x.type === 'heaterStartupDelay');
|
|
300
|
+
for (let i = 0; i < cds.length; i++) {
|
|
301
|
+
let delay = cds[i] as HeaterStartupDelay;
|
|
302
|
+
if (delay.heaterState.id === hs.id) delay.cancelDelay();
|
|
303
|
+
}
|
|
304
|
+
this.push(new HeaterStartupDelay(hs, delay)); this.setDirty();
|
|
305
|
+
}
|
|
306
|
+
public cancelHeaterStartupDelays() {
|
|
307
|
+
this.cancelDelaysByType('heaterStartupDelay');
|
|
308
|
+
}
|
|
309
|
+
public setHeaterCooldownDelay(bsOff: BodyTempState, bsOn?: BodyTempState, delay?: number) {
|
|
310
|
+
logger.info(`Setting Heater Cooldown Delay for ${bsOff.name}`);
|
|
311
|
+
let cds = this.filter(x => x.type === 'heaterCooldownDelay');
|
|
312
|
+
for (let i = 0; i < cds.length; i++) {
|
|
313
|
+
let delay = cds[i] as HeaterCooldownDelay;
|
|
314
|
+
if (delay.bodyStateOff.id === bsOff.id) {
|
|
315
|
+
if(typeof bsOn !== 'undefined') logger.info(`Found Cooldown Delay adding on circuit ${bsOn.name}`);
|
|
316
|
+
delay.setBodyStateOn(bsOn);
|
|
317
|
+
this.setDirty();
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
this.push(new HeaterCooldownDelay(bsOff, bsOn, delay));
|
|
322
|
+
this.setDirty();
|
|
323
|
+
}
|
|
324
|
+
public clearBodyStartupDelay(bs: BodyTempState) {
|
|
325
|
+
logger.info(`Clearing startup delays for ${bs.name}`);
|
|
326
|
+
// We are doing this non type safety thing below so that
|
|
327
|
+
// we can only emit when the body is cleared.
|
|
328
|
+
let cds = this.filter(x => {
|
|
329
|
+
return x.type === 'heaterCooldownDelay' &&
|
|
330
|
+
typeof x['bodyStateOn'] !== 'undefined' &&
|
|
331
|
+
x['bodyStateOn'].id === bs.id;
|
|
332
|
+
});
|
|
333
|
+
for (let i = 0; i < cds.length; i++) {
|
|
334
|
+
let delay = cds[i] as HeaterCooldownDelay;
|
|
335
|
+
logger.info(`Clearing ${bs.name} from Cooldown Delay`);
|
|
336
|
+
delay.setBodyStateOn();
|
|
337
|
+
}
|
|
338
|
+
if (cds.length) this.setDirty();
|
|
339
|
+
}
|
|
340
|
+
public cancelHeaterCooldownDelays() { this.cancelDelaysByType('heaterCooldownDelay'); }
|
|
341
|
+
public setCleanerStartDelay(cs: ICircuitState, bodyId: number, delay?: number) {
|
|
342
|
+
let cds = this.filter(x => x.type === ('cleanerStartDelay' || 'cleanerSolarDelay'));
|
|
343
|
+
let startDelay: CleanerStartDelay;
|
|
344
|
+
for (let i = 0; i < cds.length; i++) {
|
|
345
|
+
let delay = cds[i] as unknown as ICleanerDelay;
|
|
346
|
+
if (delay.cleanerState.id === cs.id) {
|
|
347
|
+
if (delay.bodyId !== bodyId || cds[i].type !== 'cleanerStartDelay') cds[i].cancelDelay();
|
|
348
|
+
else if (typeof startDelay !== 'undefined') {
|
|
349
|
+
startDelay.cancelDelay();
|
|
350
|
+
startDelay = cds[i] as CleanerStartDelay;
|
|
351
|
+
}
|
|
352
|
+
else startDelay = cds[i] as CleanerStartDelay;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
if (typeof startDelay !== 'undefined') {
|
|
356
|
+
startDelay.reset(delay);
|
|
357
|
+
this.setDirty();
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
this.push(new CleanerStartDelay(cs, bodyId, delay));
|
|
361
|
+
this.setDirty();
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
public cancelCleanerStartDelays(bodyId?: number) {
|
|
365
|
+
if (typeof bodyId === 'undefined') this.cancelDelaysByType('cleanerStartDelay');
|
|
366
|
+
else {
|
|
367
|
+
let delays = this.filter(x => x.type === 'cleanerStartDelay' && x['bodyId'] === bodyId);
|
|
368
|
+
for (let i = 0; i < delays.length; i++) {
|
|
369
|
+
delays[i].cancelDelay();
|
|
370
|
+
}
|
|
371
|
+
if (delays.length > 0) this.setDirty();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
public clearCleanerStartDelays(bodyId?: number) {
|
|
375
|
+
if (typeof bodyId === 'undefined') this.clearDelaysByType('cleanerStartDelay');
|
|
376
|
+
else {
|
|
377
|
+
let delays = this.filter(x => x.type === 'cleanerStartDelay' && x['bodyId'] === bodyId);
|
|
378
|
+
for (let i = 0; i < delays.length; i++) {
|
|
379
|
+
delays[i].clearDelay();
|
|
380
|
+
}
|
|
381
|
+
if (delays.length > 0) this.setDirty();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
public deleteDelay(id: number) {
|
|
385
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
386
|
+
if (this[i].id === id) {
|
|
387
|
+
this.splice(i, 1);
|
|
388
|
+
this.setDirty();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
public setSolarStartupDelay
|
|
393
|
+
protected cancelDelaysByType(type: string) {
|
|
394
|
+
let delays = this.filter(x => x.type === type);
|
|
395
|
+
for (let i = 0; i < delays.length; i++) {
|
|
396
|
+
delays[i].cancelDelay();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
protected clearDelaysByType(type: string) {
|
|
400
|
+
let delays = this.filter(x => x.type === type);
|
|
401
|
+
for (let i = 0; i < delays.length; i++) {
|
|
402
|
+
delays[i].clearDelay();
|
|
403
|
+
}
|
|
404
|
+
if (delays.length > 0) this.setDirty();
|
|
405
|
+
}
|
|
406
|
+
public serialize() {
|
|
407
|
+
try {
|
|
408
|
+
let delays = [];
|
|
409
|
+
for (let i = 0; i < this.length; i++) {
|
|
410
|
+
delays.push(this[i].serialize());
|
|
411
|
+
}
|
|
412
|
+
return delays;
|
|
413
|
+
} catch (err) { logger.error(`Error serializing delays: ${err.message}`); }
|
|
414
|
+
}
|
|
415
|
+
public emitDelayState() {
|
|
416
|
+
try {
|
|
417
|
+
// We have to use a custom serializer because the properties of
|
|
418
|
+
// our delays will create a circular reference due to the timers and state references.
|
|
419
|
+
webApp.emitToClients('delays', this.serialize());
|
|
420
|
+
} catch (err) { logger.error(`Error emitting delay states ${err.message}`); }
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
export let delayMgr = new DelayManager();
|