nodejs-poolcontroller 7.3.1 → 7.6.1
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 +44 -44
- package/.github/ISSUE_TEMPLATE/bug_report.md +52 -52
- package/CONTRIBUTING.md +74 -74
- package/Changelog +215 -195
- package/Dockerfile +17 -17
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +191 -186
- package/app.ts +2 -0
- package/config/Config.ts +27 -2
- package/config/VersionCheck.ts +33 -14
- package/config copy.json +299 -299
- package/controller/Constants.ts +88 -0
- package/controller/Equipment.ts +2459 -2225
- package/controller/Errors.ts +180 -157
- package/controller/Lockouts.ts +437 -0
- package/controller/State.ts +364 -79
- package/controller/boards/BoardFactory.ts +45 -45
- package/controller/boards/EasyTouchBoard.ts +2653 -2489
- package/controller/boards/IntelliCenterBoard.ts +4230 -3973
- package/controller/boards/IntelliComBoard.ts +63 -63
- package/controller/boards/IntelliTouchBoard.ts +241 -167
- package/controller/boards/NixieBoard.ts +1675 -1105
- package/controller/boards/SystemBoard.ts +4697 -3201
- package/controller/comms/Comms.ts +222 -10
- package/controller/comms/messages/Messages.ts +13 -9
- package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -4
- package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
- package/controller/comms/messages/config/CircuitMessage.ts +0 -0
- package/controller/comms/messages/config/ConfigMessage.ts +0 -0
- package/controller/comms/messages/config/CoverMessage.ts +1 -0
- package/controller/comms/messages/config/CustomNameMessage.ts +30 -30
- package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
- package/controller/comms/messages/config/ExternalMessage.ts +53 -33
- 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 +14 -28
- package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
- package/controller/comms/messages/config/OptionsMessage.ts +38 -2
- 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 +347 -331
- 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 +79 -25
- package/controller/comms/messages/status/HeaterStateMessage.ts +86 -53
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +445 -386
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +35 -35
- package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
- package/controller/comms/messages/status/VersionMessage.ts +0 -0
- package/controller/nixie/Nixie.ts +162 -160
- package/controller/nixie/NixieEquipment.ts +103 -103
- package/controller/nixie/bodies/Body.ts +120 -117
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2498 -2395
- package/controller/nixie/chemistry/Chlorinator.ts +314 -313
- package/controller/nixie/circuits/Circuit.ts +248 -210
- package/controller/nixie/heaters/Heater.ts +649 -441
- package/controller/nixie/pumps/Pump.ts +661 -599
- package/controller/nixie/schedules/Schedule.ts +257 -256
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +286 -271
- package/issue_template.md +51 -51
- package/logger/DataLogger.ts +448 -433
- package/logger/Logger.ts +0 -0
- package/package.json +56 -54
- package/tsconfig.json +25 -25
- package/web/Server.ts +522 -31
- package/web/bindings/influxDB.json +1022 -894
- package/web/bindings/mqtt.json +654 -543
- package/web/bindings/mqttAlt.json +684 -574
- package/web/bindings/rulesManager.json +54 -54
- package/web/bindings/smartThings-Hubitat.json +31 -31
- package/web/bindings/valveRelays.json +20 -20
- package/web/bindings/vera.json +25 -25
- package/web/interfaces/baseInterface.ts +136 -136
- package/web/interfaces/httpInterface.ts +124 -122
- package/web/interfaces/influxInterface.ts +245 -240
- package/web/interfaces/mqttInterface.ts +475 -464
- package/web/services/config/Config.ts +181 -152
- package/web/services/config/ConfigSocket.ts +0 -0
- package/web/services/state/State.ts +118 -7
- package/web/services/state/StateSocket.ts +18 -1
- package/web/services/utilities/Utilities.ts +42 -42
|
@@ -1,257 +1,258 @@
|
|
|
1
|
-
import { EquipmentNotFoundError, InvalidEquipmentDataError, InvalidEquipmentIdError, ParameterOutOfRangeError } from '../../Errors';
|
|
2
|
-
import { utils, Timestamp } from '../../Constants';
|
|
3
|
-
import { logger } from '../../../logger/Logger';
|
|
4
|
-
|
|
5
|
-
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
|
-
import { Schedule, ScheduleCollection, sys } from "../../../controller/Equipment";
|
|
7
|
-
import { ScheduleState, state, } from "../../State";
|
|
8
|
-
import { setTimeout, clearTimeout } from 'timers';
|
|
9
|
-
import { NixieControlPanel } from '../Nixie';
|
|
10
|
-
import { webApp, InterfaceServerResponse } from "../../../web/Server";
|
|
11
|
-
|
|
12
|
-
export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSchedule> {
|
|
13
|
-
public async setScheduleAsync(schedule: Schedule, data: any) {
|
|
14
|
-
// By the time we get here we know that we are in control and this is a schedule we should be in control of.
|
|
15
|
-
try {
|
|
16
|
-
let c: NixieSchedule = this.find(elem => elem.id === schedule.id) as NixieSchedule;
|
|
17
|
-
if (typeof c === 'undefined') {
|
|
18
|
-
schedule.master = 1;
|
|
19
|
-
c = new NixieSchedule(this.controlPanel, schedule);
|
|
20
|
-
this.push(c);
|
|
21
|
-
await c.setScheduleAsync(data);
|
|
22
|
-
logger.info(`A Schedule was not found for id #${schedule.id} creating Schedule`);
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
await c.setScheduleAsync(data);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch (err) { logger.error(`setScheduleAsync: ${err.message}`); return Promise.reject(err); }
|
|
29
|
-
}
|
|
30
|
-
public async initAsync(schedules: ScheduleCollection) {
|
|
31
|
-
try {
|
|
32
|
-
for (let i = 0; i < schedules.length; i++) {
|
|
33
|
-
let schedule = schedules.getItemByIndex(i);
|
|
34
|
-
if (schedule.master === 1) {
|
|
35
|
-
if (typeof this.find(elem => elem.id === schedule.id) === 'undefined') {
|
|
36
|
-
logger.info(`Initializing Schedule ${schedule.id}`);
|
|
37
|
-
let nSchedule = new NixieSchedule(this.controlPanel, schedule);
|
|
38
|
-
this.push(nSchedule);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch (err) { logger.error(`Nixie Schedule initAsync: ${err.message}`); return Promise.reject(err); }
|
|
44
|
-
}
|
|
45
|
-
public async triggerSchedules() {
|
|
46
|
-
try {
|
|
47
|
-
let ctx = new NixieScheduleContext();
|
|
48
|
-
for (let i = 0; i < this.length; i++) {
|
|
49
|
-
(this[i] as NixieSchedule).triggerScheduleAsync(ctx);
|
|
50
|
-
}
|
|
51
|
-
// Set the heat modes for the bodies.
|
|
52
|
-
for (let i = 0; i < ctx.heatModes.length; i++) {
|
|
53
|
-
let mode = ctx.heatModes[i];
|
|
54
|
-
let body = sys.bodies.getItemById(mode.id);
|
|
55
|
-
await sys.board.bodies.setHeatModeAsync(sys.bodies.getItemById(mode.id), mode.heatMode);
|
|
56
|
-
if (typeof mode.heatSetpoint !== 'undefined') await sys.board.bodies.setHeatSetpointAsync(body, mode.heatSetpoint);
|
|
57
|
-
if (typeof mode.coolSetpoint !== 'undefined') await sys.board.bodies.setCoolSetpointAsync(body, mode.coolSetpoint);
|
|
58
|
-
}
|
|
59
|
-
// Alright now that we are done with that we need to set all the circuit states that need changing.
|
|
60
|
-
for (let i = 0; i < ctx.circuits.length; i++) {
|
|
61
|
-
let circuit = ctx.circuits[i];
|
|
62
|
-
await sys.board.circuits.setCircuitStateAsync(circuit.id, circuit.isOn);
|
|
63
|
-
}
|
|
64
|
-
} catch (err) { logger.error(`Error triggering schedules: ${err}`); }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
export class NixieSchedule extends NixieEquipment {
|
|
68
|
-
public pollingInterval: number = 10000;
|
|
69
|
-
private _pollTimer: NodeJS.Timeout = null;
|
|
70
|
-
public schedule: Schedule;
|
|
71
|
-
private suspended: boolean = false;
|
|
72
|
-
private running: boolean = false;
|
|
73
|
-
constructor(ncp: INixieControlPanel, schedule: Schedule) {
|
|
74
|
-
super(ncp);
|
|
75
|
-
this.schedule = schedule;
|
|
76
|
-
this.pollEquipmentAsync();
|
|
77
|
-
}
|
|
78
|
-
public get id(): number { return typeof this.schedule !== 'undefined' ? this.schedule.id : -1; }
|
|
79
|
-
public async setScheduleAsync(data: any) {
|
|
80
|
-
try {
|
|
81
|
-
let schedule = this.schedule;
|
|
82
|
-
}
|
|
83
|
-
catch (err) { logger.error(`Nixie setScheduleAsync: ${err.message}`); return Promise.reject(err); }
|
|
84
|
-
}
|
|
85
|
-
public async pollEquipmentAsync() {
|
|
86
|
-
let self = this;
|
|
87
|
-
try {
|
|
88
|
-
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
89
|
-
this._pollTimer = null;
|
|
90
|
-
let success = false;
|
|
91
|
-
}
|
|
92
|
-
catch (err) { logger.error(`Nixie Error polling Schedule - ${err}`); }
|
|
93
|
-
finally { this._pollTimer = setTimeout(async () => await self.pollEquipmentAsync(), this.pollingInterval || 10000); }
|
|
94
|
-
}
|
|
95
|
-
public async validateSetupAsync(Schedule: Schedule, temp: ScheduleState) {
|
|
96
|
-
try {
|
|
97
|
-
// The validation will be different if the Schedule is on or not. So lets get that information.
|
|
98
|
-
} catch (err) { logger.error(`Nixie Error checking Schedule Hardware ${this.schedule.id}: ${err.message}`); return Promise.reject(err); }
|
|
99
|
-
}
|
|
100
|
-
public async triggerScheduleAsync(ctx: NixieScheduleContext) {
|
|
101
|
-
try {
|
|
102
|
-
if (this.schedule.isActive === false) return;
|
|
103
|
-
let ssched = state.schedules.getItemById(this.id, true);
|
|
104
|
-
// RULES FOR NIXIE SCHEDULES
|
|
105
|
-
// ------------------------------------------------------
|
|
106
|
-
// Schedules can be overridden so it is important that when the
|
|
107
|
-
// state is changed for the schedule if it is currently active that
|
|
108
|
-
// Nixie does not override the state of the scheduled circuit or feature.
|
|
109
|
-
// 1. If the feature happens to be running and the schedule is not yet turned on then
|
|
110
|
-
// it should not override what the user says.
|
|
111
|
-
// 2. If a schedule is running and the state of the circuit changes to off then the new state should suspend the schedule
|
|
112
|
-
// until which time the feature is turned back on again. Then the off time will come into play.
|
|
113
|
-
// 3. Egg timers will be managed by the individual circuit. If this is being turned on via the schedule then
|
|
114
|
-
// the egg timer is not in effect.
|
|
115
|
-
// 4. If there are overlapping schedules, then the off date is determined by
|
|
116
|
-
// the maximum off date.
|
|
117
|
-
// 5. If a schedule should be on and the user turns the schedule off then the schedule expires until such time
|
|
118
|
-
// as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the
|
|
119
|
-
// user resets the schedule by turning the circuit back on again then the schedule will resume and turn off at the specified
|
|
120
|
-
// time.
|
|
121
|
-
// 6. Heat setpoints should only be changed when the schedule is first turning on the scheduled circuit.
|
|
122
|
-
let cstate = state.circuits.getInterfaceById(this.schedule.circuit, false);
|
|
123
|
-
let circuit = sys.circuits.getInterfaceById(this.schedule.circuit, false, { isActive: false });
|
|
124
|
-
if (circuit.isActive === false) {
|
|
125
|
-
ssched.isOn = false;
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
let shouldBeOn = this.shouldBeOn(ssched); // This should also set the validity for the schedule if there are errors.
|
|
129
|
-
//console.log(`Processing schedule ${this.schedule.id} - ${circuit.name} : ShouldBeOn: ${shouldBeOn} Suspended: ${this.suspended} Running: ${this.running}`);
|
|
130
|
-
// COND 1: The schedule should be on and the schedule is not yet on.
|
|
131
|
-
if (shouldBeOn && !this.running && !this.suspended) {
|
|
132
|
-
// If the circuit is on then we need to clear the suspended flag and set the running flag.
|
|
133
|
-
if (cstate.isOn) {
|
|
134
|
-
// If the suspended flag was previously on then we need to clear it
|
|
135
|
-
// because the user turned it back on.
|
|
136
|
-
this.suspended = false;
|
|
137
|
-
}
|
|
138
|
-
ctx.setCircuit(circuit.id, true);
|
|
139
|
-
// Alright we are turning on the circuit. If these are body circuits then we need to determine
|
|
140
|
-
// whether we will be setting the setpoints/heatmode on the body.
|
|
141
|
-
let body = sys.bodies.find(elem => elem.circuit === circuit.id);
|
|
142
|
-
if (typeof body !== 'undefined') {
|
|
143
|
-
let heatSource = sys.board.valueMaps.heatSources.transform(this.schedule.heatSource);
|
|
144
|
-
if (heatSource !== 'nochange') {
|
|
145
|
-
switch (heatSource.name) {
|
|
146
|
-
case '
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
this.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
let
|
|
198
|
-
let
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
let
|
|
207
|
-
|
|
208
|
-
if
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
//
|
|
212
|
-
// [
|
|
213
|
-
// [
|
|
214
|
-
|
|
215
|
-
let
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (isNaN(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (tm
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
this._pollTimer
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
public
|
|
242
|
-
public
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
let
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (typeof
|
|
255
|
-
|
|
256
|
-
|
|
1
|
+
import { EquipmentNotFoundError, InvalidEquipmentDataError, InvalidEquipmentIdError, ParameterOutOfRangeError } from '../../Errors';
|
|
2
|
+
import { utils, Timestamp } from '../../Constants';
|
|
3
|
+
import { logger } from '../../../logger/Logger';
|
|
4
|
+
|
|
5
|
+
import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
|
|
6
|
+
import { Schedule, ScheduleCollection, sys } from "../../../controller/Equipment";
|
|
7
|
+
import { ScheduleState, state, } from "../../State";
|
|
8
|
+
import { setTimeout, clearTimeout } from 'timers';
|
|
9
|
+
import { NixieControlPanel } from '../Nixie';
|
|
10
|
+
import { webApp, InterfaceServerResponse } from "../../../web/Server";
|
|
11
|
+
|
|
12
|
+
export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSchedule> {
|
|
13
|
+
public async setScheduleAsync(schedule: Schedule, data: any) {
|
|
14
|
+
// By the time we get here we know that we are in control and this is a schedule we should be in control of.
|
|
15
|
+
try {
|
|
16
|
+
let c: NixieSchedule = this.find(elem => elem.id === schedule.id) as NixieSchedule;
|
|
17
|
+
if (typeof c === 'undefined') {
|
|
18
|
+
schedule.master = 1;
|
|
19
|
+
c = new NixieSchedule(this.controlPanel, schedule);
|
|
20
|
+
this.push(c);
|
|
21
|
+
await c.setScheduleAsync(data);
|
|
22
|
+
logger.info(`A Schedule was not found for id #${schedule.id} creating Schedule`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
await c.setScheduleAsync(data);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (err) { logger.error(`setScheduleAsync: ${err.message}`); return Promise.reject(err); }
|
|
29
|
+
}
|
|
30
|
+
public async initAsync(schedules: ScheduleCollection) {
|
|
31
|
+
try {
|
|
32
|
+
for (let i = 0; i < schedules.length; i++) {
|
|
33
|
+
let schedule = schedules.getItemByIndex(i);
|
|
34
|
+
if (schedule.master === 1) {
|
|
35
|
+
if (typeof this.find(elem => elem.id === schedule.id) === 'undefined') {
|
|
36
|
+
logger.info(`Initializing Schedule ${schedule.id}`);
|
|
37
|
+
let nSchedule = new NixieSchedule(this.controlPanel, schedule);
|
|
38
|
+
this.push(nSchedule);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) { logger.error(`Nixie Schedule initAsync: ${err.message}`); return Promise.reject(err); }
|
|
44
|
+
}
|
|
45
|
+
public async triggerSchedules() {
|
|
46
|
+
try {
|
|
47
|
+
let ctx = new NixieScheduleContext();
|
|
48
|
+
for (let i = 0; i < this.length; i++) {
|
|
49
|
+
(this[i] as NixieSchedule).triggerScheduleAsync(ctx);
|
|
50
|
+
}
|
|
51
|
+
// Set the heat modes for the bodies.
|
|
52
|
+
for (let i = 0; i < ctx.heatModes.length; i++) {
|
|
53
|
+
let mode = ctx.heatModes[i];
|
|
54
|
+
let body = sys.bodies.getItemById(mode.id);
|
|
55
|
+
await sys.board.bodies.setHeatModeAsync(sys.bodies.getItemById(mode.id), mode.heatMode);
|
|
56
|
+
if (typeof mode.heatSetpoint !== 'undefined') await sys.board.bodies.setHeatSetpointAsync(body, mode.heatSetpoint);
|
|
57
|
+
if (typeof mode.coolSetpoint !== 'undefined') await sys.board.bodies.setCoolSetpointAsync(body, mode.coolSetpoint);
|
|
58
|
+
}
|
|
59
|
+
// Alright now that we are done with that we need to set all the circuit states that need changing.
|
|
60
|
+
for (let i = 0; i < ctx.circuits.length; i++) {
|
|
61
|
+
let circuit = ctx.circuits[i];
|
|
62
|
+
await sys.board.circuits.setCircuitStateAsync(circuit.id, circuit.isOn);
|
|
63
|
+
}
|
|
64
|
+
} catch (err) { logger.error(`Error triggering schedules: ${err}`); }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export class NixieSchedule extends NixieEquipment {
|
|
68
|
+
public pollingInterval: number = 10000;
|
|
69
|
+
private _pollTimer: NodeJS.Timeout = null;
|
|
70
|
+
public schedule: Schedule;
|
|
71
|
+
private suspended: boolean = false;
|
|
72
|
+
private running: boolean = false;
|
|
73
|
+
constructor(ncp: INixieControlPanel, schedule: Schedule) {
|
|
74
|
+
super(ncp);
|
|
75
|
+
this.schedule = schedule;
|
|
76
|
+
this.pollEquipmentAsync();
|
|
77
|
+
}
|
|
78
|
+
public get id(): number { return typeof this.schedule !== 'undefined' ? this.schedule.id : -1; }
|
|
79
|
+
public async setScheduleAsync(data: any) {
|
|
80
|
+
try {
|
|
81
|
+
let schedule = this.schedule;
|
|
82
|
+
}
|
|
83
|
+
catch (err) { logger.error(`Nixie setScheduleAsync: ${err.message}`); return Promise.reject(err); }
|
|
84
|
+
}
|
|
85
|
+
public async pollEquipmentAsync() {
|
|
86
|
+
let self = this;
|
|
87
|
+
try {
|
|
88
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
89
|
+
this._pollTimer = null;
|
|
90
|
+
let success = false;
|
|
91
|
+
}
|
|
92
|
+
catch (err) { logger.error(`Nixie Error polling Schedule - ${err}`); }
|
|
93
|
+
finally { this._pollTimer = setTimeout(async () => await self.pollEquipmentAsync(), this.pollingInterval || 10000); }
|
|
94
|
+
}
|
|
95
|
+
public async validateSetupAsync(Schedule: Schedule, temp: ScheduleState) {
|
|
96
|
+
try {
|
|
97
|
+
// The validation will be different if the Schedule is on or not. So lets get that information.
|
|
98
|
+
} catch (err) { logger.error(`Nixie Error checking Schedule Hardware ${this.schedule.id}: ${err.message}`); return Promise.reject(err); }
|
|
99
|
+
}
|
|
100
|
+
public async triggerScheduleAsync(ctx: NixieScheduleContext) {
|
|
101
|
+
try {
|
|
102
|
+
if (this.schedule.isActive === false) return;
|
|
103
|
+
let ssched = state.schedules.getItemById(this.id, true);
|
|
104
|
+
// RULES FOR NIXIE SCHEDULES
|
|
105
|
+
// ------------------------------------------------------
|
|
106
|
+
// Schedules can be overridden so it is important that when the
|
|
107
|
+
// state is changed for the schedule if it is currently active that
|
|
108
|
+
// Nixie does not override the state of the scheduled circuit or feature.
|
|
109
|
+
// 1. If the feature happens to be running and the schedule is not yet turned on then
|
|
110
|
+
// it should not override what the user says.
|
|
111
|
+
// 2. If a schedule is running and the state of the circuit changes to off then the new state should suspend the schedule
|
|
112
|
+
// until which time the feature is turned back on again. Then the off time will come into play.
|
|
113
|
+
// 3. Egg timers will be managed by the individual circuit. If this is being turned on via the schedule then
|
|
114
|
+
// the egg timer is not in effect.
|
|
115
|
+
// 4. If there are overlapping schedules, then the off date is determined by
|
|
116
|
+
// the maximum off date.
|
|
117
|
+
// 5. If a schedule should be on and the user turns the schedule off then the schedule expires until such time
|
|
118
|
+
// as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the
|
|
119
|
+
// user resets the schedule by turning the circuit back on again then the schedule will resume and turn off at the specified
|
|
120
|
+
// time.
|
|
121
|
+
// 6. Heat setpoints should only be changed when the schedule is first turning on the scheduled circuit.
|
|
122
|
+
let cstate = state.circuits.getInterfaceById(this.schedule.circuit, false);
|
|
123
|
+
let circuit = sys.circuits.getInterfaceById(this.schedule.circuit, false, { isActive: false });
|
|
124
|
+
if (circuit.isActive === false) {
|
|
125
|
+
ssched.isOn = false;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
let shouldBeOn = this.shouldBeOn(ssched); // This should also set the validity for the schedule if there are errors.
|
|
129
|
+
//console.log(`Processing schedule ${this.schedule.id} - ${circuit.name} : ShouldBeOn: ${shouldBeOn} Suspended: ${this.suspended} Running: ${this.running}`);
|
|
130
|
+
// COND 1: The schedule should be on and the schedule is not yet on.
|
|
131
|
+
if (shouldBeOn && !this.running && !this.suspended) {
|
|
132
|
+
// If the circuit is on then we need to clear the suspended flag and set the running flag.
|
|
133
|
+
if (cstate.isOn) {
|
|
134
|
+
// If the suspended flag was previously on then we need to clear it
|
|
135
|
+
// because the user turned it back on.
|
|
136
|
+
this.suspended = false;
|
|
137
|
+
}
|
|
138
|
+
ctx.setCircuit(circuit.id, true);
|
|
139
|
+
// Alright we are turning on the circuit. If these are body circuits then we need to determine
|
|
140
|
+
// whether we will be setting the setpoints/heatmode on the body.
|
|
141
|
+
let body = sys.bodies.find(elem => elem.circuit === circuit.id);
|
|
142
|
+
if (typeof body !== 'undefined') {
|
|
143
|
+
let heatSource = sys.board.valueMaps.heatSources.transform(this.schedule.heatSource);
|
|
144
|
+
if (heatSource.name !== 'nochange') {
|
|
145
|
+
switch (heatSource.name) {
|
|
146
|
+
case 'nochange':
|
|
147
|
+
case 'dontchange':
|
|
148
|
+
break;
|
|
149
|
+
case 'off':
|
|
150
|
+
ctx.setHeatMode(body.id, 'off');
|
|
151
|
+
break;
|
|
152
|
+
default:
|
|
153
|
+
ctx.setHeatMode(body.id, heatSource.name, this.schedule.heatSetpoint, heatSource.hasCoolSetpoint ? this.schedule.coolSetpoint : undefined);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
ssched.isOn = true;
|
|
159
|
+
this.running = true;
|
|
160
|
+
}
|
|
161
|
+
else if (shouldBeOn && this.running) {
|
|
162
|
+
// We do nothing here.
|
|
163
|
+
this.suspended = !cstate.isOn;
|
|
164
|
+
}
|
|
165
|
+
// Our schedule has expired it is time to turn it off.
|
|
166
|
+
else if (!shouldBeOn) {
|
|
167
|
+
// Turn this sucker off. But wait if there is an overlapping schedule then we should
|
|
168
|
+
// not turn it off. We will need some logic to deal with this.
|
|
169
|
+
if (this.running) ctx.setCircuit(circuit.id, false);
|
|
170
|
+
ssched.isOn = false;
|
|
171
|
+
this.running = false;
|
|
172
|
+
this.suspended = false;
|
|
173
|
+
}
|
|
174
|
+
if (!shouldBeOn && ssched.isOn === true) {
|
|
175
|
+
// Turn off the circuit.
|
|
176
|
+
ctx.setCircuit(circuit.id, false);
|
|
177
|
+
ssched.isOn = false;
|
|
178
|
+
}
|
|
179
|
+
ssched.emitEquipmentChange();
|
|
180
|
+
} catch (err) { logger.error(`Error processing schedule: ${err.message}`); }
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
protected calcTime(dt: Timestamp, type: number, offset: number): Timestamp {
|
|
184
|
+
let tt = sys.board.valueMaps.scheduleTimeTypes.transform(type);
|
|
185
|
+
switch (tt.name) {
|
|
186
|
+
case 'sunrise':
|
|
187
|
+
return new Timestamp(state.heliotrope.sunrise);
|
|
188
|
+
case 'sunset':
|
|
189
|
+
return new Timestamp(state.heliotrope.sunset);
|
|
190
|
+
default:
|
|
191
|
+
return dt.startOfDay().addMinutes(offset);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
protected shouldBeOn(sstate: ScheduleState): boolean {
|
|
195
|
+
if (this.schedule.isActive === false) return false;
|
|
196
|
+
// Be careful with toDate since this returns a mutable date object from the state timestamp. startOfDay makes it immutable.
|
|
197
|
+
let sod = state.time.startOfDay()
|
|
198
|
+
let dow = sod.toDate().getDay();
|
|
199
|
+
let type = sys.board.valueMaps.scheduleTypes.transform(this.schedule.scheduleType);
|
|
200
|
+
if (type.name === 'runonce') {
|
|
201
|
+
// If we are not matching up with the day then we shouldn't be running.
|
|
202
|
+
if (sod.fullYear !== this.schedule.startYear || sod.month + 1 !== this.schedule.startMonth || sod.date !== this.schedule.startDay) return false;
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Convert the dow to the bit value.
|
|
206
|
+
let sd = sys.board.valueMaps.scheduleDays.toArray().find(elem => elem.dow === dow);
|
|
207
|
+
let dayVal = sd.bitVal || sd.val; // The bitval allows mask overrides.
|
|
208
|
+
// First check to see if today is one of our days.
|
|
209
|
+
if ((this.schedule.scheduleDays & dayVal) === 0) return false;
|
|
210
|
+
}
|
|
211
|
+
// Next normalize our start and end times. Fortunately, the start and end times are normalized here so that
|
|
212
|
+
// [0, {name: 'manual', desc: 'Manual }]
|
|
213
|
+
// [1, { name: 'sunrise', desc: 'Sunrise' }],
|
|
214
|
+
// [2, { name: 'sunset', desc: 'Sunset' }]
|
|
215
|
+
let tmStart = this.calcTime(sod, this.schedule.startTime, this.schedule.startTime).getTime();
|
|
216
|
+
let tmEnd = this.calcTime(sod, this.schedule.endTimeType, this.schedule.endTime).getTime();
|
|
217
|
+
|
|
218
|
+
if (isNaN(tmStart)) return false;
|
|
219
|
+
if (isNaN(tmEnd)) return false;
|
|
220
|
+
// If we are past our window we should be off.
|
|
221
|
+
let tm = state.time.getTime();
|
|
222
|
+
if (tm >= tmEnd) return false;
|
|
223
|
+
if (tm <= tmStart) return false;
|
|
224
|
+
|
|
225
|
+
// If we make it here we should be on.
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
public async closeAsync() {
|
|
229
|
+
try {
|
|
230
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
231
|
+
this._pollTimer = null;
|
|
232
|
+
}
|
|
233
|
+
catch (err) { logger.error(`Nixie Schedule closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
234
|
+
}
|
|
235
|
+
public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
|
|
236
|
+
}
|
|
237
|
+
class NixieScheduleContext {
|
|
238
|
+
constructor() {
|
|
239
|
+
|
|
240
|
+
}
|
|
241
|
+
public circuits: { id: number, isOn: boolean }[] = [];
|
|
242
|
+
public heatModes: { id: number, heatMode: number, heatSetpoint?: number, coolSetpoint?: number }[] = [];
|
|
243
|
+
public setCircuit(id: number, isOn: boolean) {
|
|
244
|
+
let c = this.circuits.find(elem => elem.id === id);
|
|
245
|
+
if (typeof c === 'undefined') this.circuits.push({ id: id, isOn: isOn });
|
|
246
|
+
else c.isOn = isOn;
|
|
247
|
+
}
|
|
248
|
+
public setHeatMode(id: number, heatMode: string, heatSetpoint?: number, coolSetpoint?: number) {
|
|
249
|
+
let mode = sys.board.valueMaps.heatModes.transformByName(heatMode);
|
|
250
|
+
let hm = this.heatModes.find(elem => elem.id == id);
|
|
251
|
+
if (typeof hm === 'undefined') this.heatModes.push({ id: id, heatMode: mode.val, heatSetpoint: heatSetpoint, coolSetpoint: coolSetpoint });
|
|
252
|
+
else {
|
|
253
|
+
hm.heatMode = mode.val;
|
|
254
|
+
if (typeof heatSetpoint !== 'undefined') hm.heatSetpoint = heatSetpoint;
|
|
255
|
+
if (typeof coolSetpoint !== 'undefined') hm.coolSetpoint = coolSetpoint;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
257
258
|
}
|