nodejs-poolcontroller 8.1.2 → 8.4.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.
Files changed (106) hide show
  1. package/.eslintrc.json +36 -36
  2. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -84
  3. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -12
  4. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -28
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -8
  6. package/.github/copilot-instructions.md +63 -0
  7. package/.github/workflows/ghcr-publish.yml +67 -0
  8. package/AGENTS.md +597 -0
  9. package/CONTRIBUTING.md +74 -74
  10. package/Changelog +292 -257
  11. package/Dockerfile +62 -19
  12. package/Gruntfile.js +40 -40
  13. package/LICENSE +661 -661
  14. package/README.md +318 -191
  15. package/anslq25/MessagesMock.ts +221 -221
  16. package/anslq25/boards/MockBoardFactory.ts +49 -49
  17. package/anslq25/boards/MockEasyTouchBoard.ts +696 -696
  18. package/anslq25/boards/MockSystemBoard.ts +216 -216
  19. package/anslq25/chemistry/MockChlorinator.ts +98 -98
  20. package/anslq25/pumps/MockPump.ts +83 -83
  21. package/app.ts +115 -115
  22. package/config/Config.ts +57 -7
  23. package/config/VersionCheck.ts +63 -35
  24. package/controller/Constants.ts +809 -805
  25. package/controller/Equipment.ts +2688 -2664
  26. package/controller/Errors.ts +181 -181
  27. package/controller/Lockouts.ts +549 -549
  28. package/controller/State.ts +3738 -3690
  29. package/controller/boards/AquaLinkBoard.ts +1003 -1003
  30. package/controller/boards/BoardFactory.ts +53 -53
  31. package/controller/boards/EasyTouchBoard.ts +3202 -3202
  32. package/controller/boards/IntelliCenterBoard.ts +4393 -3899
  33. package/controller/boards/IntelliComBoard.ts +69 -69
  34. package/controller/boards/IntelliTouchBoard.ts +382 -382
  35. package/controller/boards/NixieBoard.ts +1944 -1929
  36. package/controller/boards/SunTouchBoard.ts +400 -400
  37. package/controller/boards/SystemBoard.ts +5268 -5268
  38. package/controller/comms/Comms.ts +1272 -1214
  39. package/controller/comms/ScreenLogic.ts +1665 -1665
  40. package/controller/comms/messages/Messages.ts +1433 -1243
  41. package/controller/comms/messages/config/ChlorinatorMessage.ts +5 -0
  42. package/controller/comms/messages/config/CircuitGroupMessage.ts +0 -0
  43. package/controller/comms/messages/config/CircuitMessage.ts +0 -0
  44. package/controller/comms/messages/config/ConfigMessage.ts +6 -0
  45. package/controller/comms/messages/config/CoverMessage.ts +0 -0
  46. package/controller/comms/messages/config/CustomNameMessage.ts +31 -31
  47. package/controller/comms/messages/config/EquipmentMessage.ts +216 -210
  48. package/controller/comms/messages/config/ExternalMessage.ts +96 -10
  49. package/controller/comms/messages/config/FeatureMessage.ts +0 -0
  50. package/controller/comms/messages/config/GeneralMessage.ts +0 -0
  51. package/controller/comms/messages/config/HeaterMessage.ts +0 -0
  52. package/controller/comms/messages/config/IntellichemMessage.ts +0 -0
  53. package/controller/comms/messages/config/OptionsMessage.ts +194 -174
  54. package/controller/comms/messages/config/PumpMessage.ts +0 -0
  55. package/controller/comms/messages/config/RemoteMessage.ts +0 -0
  56. package/controller/comms/messages/config/ScheduleMessage.ts +401 -390
  57. package/controller/comms/messages/config/SecurityMessage.ts +0 -0
  58. package/controller/comms/messages/config/ValveMessage.ts +0 -0
  59. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +0 -0
  60. package/controller/comms/messages/status/EquipmentStateMessage.ts +1158 -822
  61. package/controller/comms/messages/status/HeaterStateMessage.ts +135 -135
  62. package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -448
  63. package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -36
  64. package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
  65. package/controller/comms/messages/status/RegalModbusStateMessage.ts +411 -0
  66. package/controller/comms/messages/status/VersionMessage.ts +103 -41
  67. package/controller/nixie/Nixie.ts +173 -173
  68. package/controller/nixie/NixieEquipment.ts +104 -104
  69. package/controller/nixie/bodies/Body.ts +120 -120
  70. package/controller/nixie/bodies/Filter.ts +135 -135
  71. package/controller/nixie/chemistry/ChemController.ts +2724 -2724
  72. package/controller/nixie/chemistry/ChemDoser.ts +806 -806
  73. package/controller/nixie/chemistry/Chlorinator.ts +367 -367
  74. package/controller/nixie/circuits/Circuit.ts +478 -478
  75. package/controller/nixie/heaters/Heater.ts +834 -834
  76. package/controller/nixie/pumps/Pump.ts +1194 -996
  77. package/controller/nixie/schedules/Schedule.ts +401 -401
  78. package/controller/nixie/valves/Valve.ts +170 -170
  79. package/defaultConfig.json +352 -347
  80. package/docker-compose.yml +32 -0
  81. package/logger/DataLogger.ts +448 -448
  82. package/logger/Logger.ts +448 -436
  83. package/package.json +58 -60
  84. package/sendSocket.js +32 -32
  85. package/tsconfig.json +25 -25
  86. package/types/express-multer.d.ts +32 -0
  87. package/web/Server.ts +1937 -1927
  88. package/web/bindings/aqualinkD.json +559 -559
  89. package/web/bindings/influxDB.json +1066 -1066
  90. package/web/bindings/mqtt.json +721 -721
  91. package/web/bindings/mqttAlt.json +746 -746
  92. package/web/bindings/rulesManager.json +54 -54
  93. package/web/bindings/smartThings-Hubitat.json +31 -31
  94. package/web/bindings/valveRelays.json +20 -20
  95. package/web/bindings/vera.json +25 -25
  96. package/web/interfaces/baseInterface.ts +188 -188
  97. package/web/interfaces/httpInterface.ts +148 -148
  98. package/web/interfaces/influxInterface.ts +283 -283
  99. package/web/interfaces/mqttInterface.ts +695 -695
  100. package/web/interfaces/ruleInterface.ts +101 -87
  101. package/web/services/config/Config.ts +1063 -1053
  102. package/web/services/config/ConfigSocket.ts +0 -0
  103. package/web/services/state/State.ts +0 -0
  104. package/web/services/state/StateSocket.ts +0 -0
  105. package/web/services/utilities/Utilities.ts +233 -233
  106. package/.github/workflows/docker-publish-njsPC-linux.yml +0 -50
@@ -1,401 +1,401 @@
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 { CircuitGroup, CircuitGroupCircuit, ICircuitGroup, ICircuitGroupCircuit, LightGroup, LightGroupCircuit, Schedule, ScheduleCollection, sys } from "../../../controller/Equipment";
7
- import { ICircuitState, CircuitGroupState, ICircuitGroupState, ScheduleState, ScheduleTime, state, } from "../../State";
8
- import { setTimeout, clearTimeout } from 'timers';
9
- import { NixieControlPanel } from '../Nixie';
10
- import { webApp, InterfaceServerResponse } from "../../../web/Server";
11
- import { delayMgr } from '../../../controller/Lockouts';
12
- import { time } from 'console';
13
-
14
-
15
- export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSchedule> {
16
- public async setScheduleAsync(schedule: Schedule, data: any) {
17
- // By the time we get here we know that we are in control and this is a schedule we should be in control of.
18
- try {
19
- let c: NixieSchedule = this.find(elem => elem.id === schedule.id) as NixieSchedule;
20
- if (typeof c === 'undefined') {
21
- schedule.master = 1;
22
- c = new NixieSchedule(this.controlPanel, schedule);
23
- this.push(c);
24
- await c.setScheduleAsync(data);
25
- logger.info(`A Schedule was not found for id #${schedule.id} creating Schedule`);
26
- }
27
- else
28
- await c.setScheduleAsync(data);
29
- }
30
- catch (err) { logger.error(`setScheduleAsync: ${err.message}`); return Promise.reject(err); }
31
- }
32
- public async initAsync(schedules: ScheduleCollection) {
33
- try {
34
- for (let i = 0; i < schedules.length; i++) {
35
- let schedule = schedules.getItemByIndex(i);
36
- if (schedule.master === 1) {
37
- if (typeof this.find(elem => elem.id === schedule.id) === 'undefined') {
38
- logger.info(`Initializing Schedule ${schedule.id}`);
39
- let nSchedule = new NixieSchedule(this.controlPanel, schedule);
40
- this.push(nSchedule);
41
- }
42
- }
43
- }
44
- }
45
- catch (err) { logger.error(`Nixie Schedule initAsync: ${err.message}`); return Promise.reject(err); }
46
- }
47
- public async triggerSchedules() {
48
- try {
49
- // This is a listing of all the active schedules that are either currently on or should be on.
50
- let sscheds: ScheduleState[] = state.schedules.getActiveSchedules();
51
- // Go through all the schedules and hash them by circuit id.
52
- let circuits: { circuitId: number, cstate: ICircuitState, hasNixie: boolean, sscheds: ScheduleState[] }[] = []
53
- for (let i = 0; i < sscheds.length; i++) {
54
- // We only care about schedules that are currently running or should be running.
55
- if (!sscheds[i].isOn && !sscheds[i].scheduleTime.shouldBeOn) continue;
56
- let circ = circuits.find(elem => elem.circuitId === sscheds[i].circuit);
57
- let sched = sys.schedules.getItemById(sscheds[i].id)
58
- if (typeof circ === 'undefined') circuits.push({
59
- circuitId: sscheds[i].circuit,
60
- cstate: state.circuits.getInterfaceById(sscheds[i].circuit), hasNixie: sched.master !== 0, sscheds: [sscheds[i]]
61
- });
62
- else {
63
- if (sched.master !== 0) circ.hasNixie = true;
64
- circ.sscheds.push(sscheds[i]);
65
- }
66
- }
67
- // Sort this so that body circuits are evaluated first. This is required when there are schedules for things like cleaner
68
- // or delay circuits. If we do not do this then a schedule that requires the pool to be on for instance will never
69
- // get triggered.
70
- circuits.sort((x, y) => y.circuitId === 6 || y.circuitId === 1 ? 1 : y.circuitId - x.circuitId);
71
-
72
-
73
- /*
74
- RSG 5-8-22
75
- Manual OP needs to play a role here.From the IC manual:
76
- # Manual OP General
77
-
78
- From the manual:
79
- Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program,
80
- the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled
81
- program will resume. This feature will turn off any scheduled program to allow manual pump override.The Default setting is OFF.
82
-
83
- ## When on
84
- 1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has
85
- expired.When that occurs the schedule should be reset to run at the designated time.If the user resets the schedule by turning the
86
- circuit back on again ~~then the schedule will resume and turn off at the specified time~~then the schedule will be ignored and
87
- the circuit will run until the egg timer expires or the circuit / feature is manually turned off.This setting WILL affect
88
- other schedules that may impact this circuit.
89
-
90
- ## When off
91
- 1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time
92
- off has expired.When that occurs the schedule should be reset to run at the designated time.If the user resets the schedule by
93
- turning the circuit back on again then the schedule will resume and turn off at the specified time.
94
- */
95
-
96
- let mOP = sys.general.options.manualPriority;
97
-
98
- // Now lets evaluate the schedules by virtue of their state related to the circuits.
99
- for (let i = 0; i < circuits.length; i++) {
100
- let c = circuits[i];
101
- if (!c.hasNixie) continue; // If this has nothing to do with Nixie move on.
102
- let shouldBeOn = typeof c.sscheds.find(elem => elem.scheduleTime.shouldBeOn === true) !== 'undefined';
103
- // 1. If the feature is currently running and the schedule is not on then it will set the priority for the circuit to [scheduled].
104
- // 2. If the feature is currently running but there are overlapping schedules then this will catch any schedules that need to be turned off.
105
- if (c.cstate.isOn && shouldBeOn) {
106
- c.cstate.priority = shouldBeOn ? 'scheduled' : 'manual';
107
- for (let j = 0; j < c.sscheds.length; j++) {
108
- let ssched = c.sscheds[j];
109
- ssched.triggered = ssched.scheduleTime.shouldBeOn;
110
- if (mOP && ssched.manualPriorityActive) {
111
- ssched.isOn = false;
112
- // Not sure what setting a delay for this does but ok.
113
- if (!c.cstate.manualPriorityActive) delayMgr.setManualPriorityDelay(c.cstate);
114
- }
115
- else ssched.isOn = ssched.scheduleTime.shouldBeOn && !ssched.manualPriorityActive;
116
- }
117
- }
118
- // 3. If the schedule should be on and it isn't and the schedule has not been triggered then we need to
119
- // turn the schedule and circuit on.
120
- else if (!c.cstate.isOn && shouldBeOn) {
121
- // The circuit is not on but it should be. Check to ensure all schedules have been triggered.
122
- let untriggered = false;
123
- // If this schedule has been triggered then mOP comes into play if manualPriority has been set in the config.
124
- for (let j = 0; j < c.sscheds.length; j++) {
125
- let ssched = c.sscheds[j];
126
- // If this schedule is turned back on then the egg timer will come into play. This is all that is required
127
- // for the mOP function. The setEndDate for the circuit makes the determination as to when off will occur.
128
- if (mOP && ssched.scheduleTime.shouldBeOn && ssched.triggered) {
129
- ssched.manualPriorityActive = true;
130
- }
131
- // The reason we check to see if anything has not been triggered is so we do not have to perform the circuit changes
132
- // if the schedule has already been triggered.
133
- else if (!ssched.triggered) untriggered = true;
134
- }
135
- let heatSource = { heatMode: 'nochange', heatSetpoint: undefined, coolSetpoint: undefined };
136
- // Check to see if any of the schedules have not been triggered. If they haven't then trigger them and turn the circuit on.
137
- if (untriggered) {
138
- // Get the heat modes and temps for all the schedules that have not been triggered.
139
- let body = sys.bodies.find(elem => elem.circuit === c.circuitId);
140
- if (typeof body !== 'undefined') {
141
- // If this is a body circuit then we need to set the heat mode and the temperature but only do this once. If
142
- // the user changes it later then that is on them.
143
- for (let j = 0; j < c.sscheds.length; j++) {
144
- if (sscheds[j].triggered) continue;
145
- let ssched = sscheds[j];
146
- let hs = sys.board.valueMaps.heatSources.transform(c.sscheds[i].heatSource);
147
- switch (hs.name) {
148
- case 'nochange':
149
- case 'dontchange':
150
- break;
151
- case 'off':
152
- // If the heatsource setting is off only change it if it is currently don't change.
153
- if (heatSource.heatMode === 'nochange') heatSource.heatMode = hs.name;
154
- break;
155
- default:
156
- switch (heatSource.heatMode) {
157
- case 'off':
158
- case 'nochange':
159
- case 'dontchange':
160
- heatSource.heatMode = hs.name;
161
- heatSource.heatSetpoint = ssched.heatSetpoint;
162
- heatSource.coolSetpoint = hs.hasCoolSetpoint ? ssched.coolSetpoint : undefined;
163
- break;
164
- }
165
- break;
166
- }
167
- // Ok if we need to change the setpoint or the heatmode then lets do it.
168
- if (heatSource.heatMode !== 'nochange') {
169
- await sys.board.bodies.setHeatModeAsync(body, sys.board.valueMaps.heatSources.getValue(heatSource.heatMode));
170
- if (typeof heatSource.heatSetpoint !== 'undefined') await sys.board.bodies.setHeatSetpointAsync(body, heatSource.heatSetpoint);
171
- if (typeof heatSource.coolSetpoint !== 'undefined') await sys.board.bodies.setCoolSetpointAsync(body, heatSource.coolSetpoint);
172
- }
173
- }
174
- }
175
- // By now we have everything we need to turn on the circuit.
176
- for (let j = 0; j < c.sscheds.length; j++) {
177
- let ssched = c.sscheds[j];
178
- if (!ssched.triggered && ssched.scheduleTime.shouldBeOn) {
179
- if (!c.cstate.isOn) {
180
- await sys.board.circuits.setCircuitStateAsync(c.circuitId, true);
181
- }
182
- let ssched = c.sscheds[j];
183
- c.cstate.priority = 'scheduled';
184
- ssched.triggered = ssched.isOn = ssched.scheduleTime.shouldBeOn;
185
- ssched.manualPriorityActive = false;
186
- }
187
- }
188
- }
189
- }
190
- else if (c.cstate.isOn && !shouldBeOn) {
191
- // Time to turn off the schedule.
192
- for (let j = 0; j < c.sscheds.length; j++) {
193
- let ssched = c.sscheds[j];
194
- // Only turn off the schedule if it is not actively mOP.
195
- if (c.cstate.isOn && !ssched.manualPriorityActive) await sys.board.circuits.setCircuitStateAsync(c.circuitId, false);
196
- c.cstate.priority = 'manual';
197
- // The schedule has expired we need to clear all the info for it.
198
- ssched.manualPriorityActive = ssched.triggered = ssched.isOn = c.sscheds[j].scheduleTime.shouldBeOn;
199
- }
200
- }
201
- else if (!c.cstate.isOn && !shouldBeOn) {
202
- // Everything is off so lets clear it all.
203
- for (let j = 0; j < c.sscheds.length; j++) {
204
- let ssched = c.sscheds[j];
205
- ssched.isOn = ssched.manualPriorityActive = ssched.triggered = false;
206
- }
207
- }
208
- state.emitEquipmentChanges();
209
- }
210
- } catch (err) { logger.error(`Error triggering nixie schedules: ${err.message}`); }
211
- }
212
- }
213
- export class NixieSchedule extends NixieEquipment {
214
- public pollingInterval: number = 10000;
215
- private _pollTimer: NodeJS.Timeout = null;
216
- public schedule: Schedule;
217
- private suspended: boolean = false;
218
- private resumed: boolean = false;
219
- private running: boolean = false;
220
- constructor(ncp: INixieControlPanel, schedule: Schedule) {
221
- super(ncp);
222
- this.schedule = schedule;
223
- this.pollEquipmentAsync();
224
- let ssched = state.schedules.getItemById(schedule.id, true);
225
- ssched.circuit = schedule.circuit;
226
- ssched.scheduleDays = schedule.scheduleDays;
227
- ssched.scheduleType = schedule.scheduleType;
228
- ssched.changeHeatSetpoint = schedule.changeHeatSetpoint;
229
- ssched.heatSetpoint = schedule.heatSetpoint;
230
- ssched.coolSetpoint = schedule.coolSetpoint;
231
- ssched.heatSource = schedule.heatSource;
232
- ssched.startTime = schedule.startTime;
233
- ssched.endTime = schedule.endTime;
234
- ssched.startTimeType = schedule.startTimeType;
235
- ssched.endTimeType = schedule.endTimeType;
236
- ssched.startDate = schedule.startDate;
237
- }
238
- public get id(): number { return typeof this.schedule !== 'undefined' ? this.schedule.id : -1; }
239
- public async setScheduleAsync(data: any) {
240
- try {
241
- let schedule = this.schedule;
242
- let sschedule = state.schedules.getItemById(schedule.id);
243
- sschedule.scheduleTime.calculated = false;
244
- }
245
- catch (err) { logger.error(`Nixie setScheduleAsync: ${err.message}`); return Promise.reject(err); }
246
- }
247
- public async pollEquipmentAsync() {}
248
- public async validateSetupAsync(Schedule: Schedule, temp: ScheduleState) {
249
- try {
250
- // The validation will be different if the Schedule is on or not. So lets get that information.
251
- } catch (err) { logger.error(`Nixie Error checking Schedule Hardware ${this.schedule.id}: ${err.message}`); return Promise.reject(err); }
252
- }
253
- public async closeAsync() {
254
- try {
255
- if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
256
- this._pollTimer = null;
257
- }
258
- catch (err) { logger.error(`Nixie Schedule closeAsync: ${err.message}`); return Promise.reject(err); }
259
- }
260
- public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
261
- /*
262
- public async triggerScheduleAsync(ctx: NixieScheduleContext) {
263
- try {
264
- if (this.schedule.isActive === false) return;
265
- let ssched = state.schedules.getItemById(this.id, true);
266
- // RULES FOR NIXIE SCHEDULES
267
- // ------------------------------------------------------
268
- // Schedules can be overridden so it is important that when the
269
- // state is changed for the schedule if it is currently active that
270
- // Nixie does not override the state of the scheduled circuit or feature.
271
- //RSG 5-8-22
272
- //Manual OP needs to play a role here. From the IC manual:
273
- //# Manual OP General
274
-
275
- //From the manual:
276
- //Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program, the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled program will resume. This feature will turn off any scheduled program to allow manual pump override. The Default setting is OFF.
277
-
278
- //## When on
279
- //1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again ~~then the schedule will resume and turn off at the specified time~~ then the schedule will be ignored and the circuit will run until the egg timer expires or the circuit/feature is manually turned off. This setting WILL affect other schedules that may impact this circuit.
280
-
281
- //## When off
282
- //1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again then the schedule will resume and turn off at the specified time.
283
-
284
- //Interestingly, there also seems to be a schedule level setting for this. We will ignore that for now as the logic could get much more complicated.
285
- // 1. If the feature happens to be running and the schedule is not yet turned on then
286
- // it should not override what the user says.
287
- // 2. If a schedule is running and the state of the circuit changes to off then the new state should suspend the schedule
288
- // until which time the feature is turned back on again.
289
- // Manual OP Off: Then the off time will come into play.
290
- // Manual OP On: The egg timer will take precedence. No other schedules will turn off this feature
291
- // 3. Egg timers will be managed by the individual circuit. If this is being turned on via the schedule then
292
- // the egg timer is not in effect.
293
- // 4. If there are overlapping schedules, then the off date is determined by
294
- // the maximum off date.
295
- // 5. If a schedule should be on and the user turns the schedule off then the schedule expires until such time
296
- // as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the
297
- // user resets the schedule by turning the circuit back on again then the schedule will...
298
- // Manual OP Off: ..resume and turn off at the specified time.
299
- // Manual OP On: ...continue to run until the egg timer time is reached or the circuit is manually turned off.
300
- // 6. Heat setpoints should only be changed when the schedule is first turning on the scheduled circuit.
301
- // 7. If schedule is disabled, skip it
302
- // 8. Manual OP On: If another schedule has been resumed and this schedule would affect that feature, do not start this schedule.
303
-
304
- // Definitions:
305
- // this.resumed = Flag to show if this schedule has been suspended and resumed.
306
- // this.manualPriorityActive = Flag to show if this schedule has been suspended and resumed, and Manual Priority (global is active)
307
- // this.delayed = Flag to show if this schedule is running, but another schedule is mOP and overriding this one.
308
- let cstate = state.circuits.getInterfaceById(this.schedule.circuit, false);
309
- let circuit = sys.circuits.getInterfaceById(this.schedule.circuit, false, { isActive: false });
310
- if (circuit.isActive === false) {
311
- ssched.isOn = false;
312
- return;
313
- }
314
- let shouldBeOn = ssched.shouldBeOn(); // This should also set the validity for the schedule if there are errors.
315
- let manualPriorityActive: boolean = shouldBeOn ? sys.board.schedules.manualPriorityActive(ssched) : false;
316
- //console.log(`Processing schedule ${this.schedule.id} - ${circuit.name} : ShouldBeOn: ${shouldBeOn} ManualPriorityActive: ${manualPriorityActive} Running: ${this.running} Suspended: ${this.suspended} Resumed: ${this.resumed}`);
317
-
318
-
319
- // COND 1: The schedule should be on and the schedule is not yet on.
320
- if (shouldBeOn && !this.running && !this.suspended || manualPriorityActive) {
321
- // If the circuit is on then we need to clear the suspended flag and set the running flag.
322
- if (cstate.isOn) {
323
- // If the suspended flag was previously on then we need to clear it
324
- // because the user turned it back on.
325
- this.suspended = false;
326
- }
327
- if (manualPriorityActive) {
328
- ssched.manualPriorityActive = true;
329
- ssched.isOn = false;
330
- }
331
- else {
332
- ctx.setCircuit(circuit.id, true);
333
- // Alright we are turning on the circuit. If these are body circuits then we need to determine
334
- // whether we will be setting the setpoints/heatmode on the body.
335
- let body = sys.bodies.find(elem => elem.circuit === circuit.id);
336
- if (typeof body !== 'undefined') {
337
- let heatSource = sys.board.valueMaps.heatSources.transform(this.schedule.heatSource);
338
- if (heatSource.name !== 'nochange') {
339
- switch (heatSource.name) {
340
- case 'nochange':
341
- case 'dontchange':
342
- break;
343
- case 'off':
344
- ctx.setHeatMode(body.id, 'off');
345
- break;
346
- default:
347
- ctx.setHeatMode(body.id, heatSource.name, this.schedule.heatSetpoint, heatSource.hasCoolSetpoint ? this.schedule.coolSetpoint : undefined);
348
- break;
349
- }
350
- }
351
- }
352
- ssched.manualPriorityActive = false;
353
- ssched.isOn = true;
354
- }
355
- this.running = true;
356
- }
357
- else if (shouldBeOn && this.running) {
358
- // Check to see if circuit is on, if not turn it on.
359
- // RKS: 07-09-23 - This was in PR#819 buut this needs further review since the circuit states are not to be set here. This would
360
- // trash delays and manualPriority.
361
- // if(!cstate.isOn) ctx.setCircuit(circuit.id, true);
362
-
363
- // With mOP, we need to see if the schedule will come back into play and also set the circut
364
- if (this.suspended && cstate.isOn) {
365
- if (sys.general.options.manualPriority) {
366
- delayMgr.setManualPriorityDelay(cstate);
367
- ssched.manualPriorityActive = true;
368
- }
369
- this.resumed = true;
370
- }
371
- this.suspended = !cstate.isOn;
372
- if (manualPriorityActive) {
373
- ssched.isOn = false;
374
- ssched.manualPriorityActive = true;
375
- }
376
- else {
377
- ssched.isOn = cstate.isOn;
378
- ssched.manualPriorityActive = false;
379
- }
380
- }
381
- // Our schedule has expired it is time to turn it off, but only if !manualPriorityActive.
382
- else if (!shouldBeOn) {
383
- // Turn this sucker off. But wait if there is an overlapping schedule then we should
384
- // not turn it off. We will need some logic to deal with this.
385
- if (this.running && !cstate.manualPriorityActive) ctx.setCircuit(circuit.id, false);
386
- ssched.isOn = false;
387
- this.running = false;
388
- this.suspended = false;
389
- this.resumed = false;
390
- ssched.manualPriorityActive = false;
391
- }
392
- if (!shouldBeOn && ssched.isOn === true) {
393
- // Turn off the circuit.
394
- if (!manualPriorityActive) ctx.setCircuit(circuit.id, false);
395
- ssched.isOn = false;
396
- }
397
- ssched.emitEquipmentChange();
398
- } catch (err) { logger.error(`Error processing schedule: ${err.message}`); }
399
- }
400
- */
401
- }
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 { CircuitGroup, CircuitGroupCircuit, ICircuitGroup, ICircuitGroupCircuit, LightGroup, LightGroupCircuit, Schedule, ScheduleCollection, sys } from "../../../controller/Equipment";
7
+ import { ICircuitState, CircuitGroupState, ICircuitGroupState, ScheduleState, ScheduleTime, state, } from "../../State";
8
+ import { setTimeout, clearTimeout } from 'timers';
9
+ import { NixieControlPanel } from '../Nixie';
10
+ import { webApp, InterfaceServerResponse } from "../../../web/Server";
11
+ import { delayMgr } from '../../../controller/Lockouts';
12
+ import { time } from 'console';
13
+
14
+
15
+ export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSchedule> {
16
+ public async setScheduleAsync(schedule: Schedule, data: any) {
17
+ // By the time we get here we know that we are in control and this is a schedule we should be in control of.
18
+ try {
19
+ let c: NixieSchedule = this.find(elem => elem.id === schedule.id) as NixieSchedule;
20
+ if (typeof c === 'undefined') {
21
+ schedule.master = 1;
22
+ c = new NixieSchedule(this.controlPanel, schedule);
23
+ this.push(c);
24
+ await c.setScheduleAsync(data);
25
+ logger.info(`A Schedule was not found for id #${schedule.id} creating Schedule`);
26
+ }
27
+ else
28
+ await c.setScheduleAsync(data);
29
+ }
30
+ catch (err) { logger.error(`setScheduleAsync: ${err.message}`); return Promise.reject(err); }
31
+ }
32
+ public async initAsync(schedules: ScheduleCollection) {
33
+ try {
34
+ for (let i = 0; i < schedules.length; i++) {
35
+ let schedule = schedules.getItemByIndex(i);
36
+ if (schedule.master === 1) {
37
+ if (typeof this.find(elem => elem.id === schedule.id) === 'undefined') {
38
+ logger.info(`Initializing Schedule ${schedule.id}`);
39
+ let nSchedule = new NixieSchedule(this.controlPanel, schedule);
40
+ this.push(nSchedule);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ catch (err) { logger.error(`Nixie Schedule initAsync: ${err.message}`); return Promise.reject(err); }
46
+ }
47
+ public async triggerSchedules() {
48
+ try {
49
+ // This is a listing of all the active schedules that are either currently on or should be on.
50
+ let sscheds: ScheduleState[] = state.schedules.getActiveSchedules();
51
+ // Go through all the schedules and hash them by circuit id.
52
+ let circuits: { circuitId: number, cstate: ICircuitState, hasNixie: boolean, sscheds: ScheduleState[] }[] = []
53
+ for (let i = 0; i < sscheds.length; i++) {
54
+ // We only care about schedules that are currently running or should be running.
55
+ if (!sscheds[i].isOn && !sscheds[i].scheduleTime.shouldBeOn) continue;
56
+ let circ = circuits.find(elem => elem.circuitId === sscheds[i].circuit);
57
+ let sched = sys.schedules.getItemById(sscheds[i].id)
58
+ if (typeof circ === 'undefined') circuits.push({
59
+ circuitId: sscheds[i].circuit,
60
+ cstate: state.circuits.getInterfaceById(sscheds[i].circuit), hasNixie: sched.master !== 0, sscheds: [sscheds[i]]
61
+ });
62
+ else {
63
+ if (sched.master !== 0) circ.hasNixie = true;
64
+ circ.sscheds.push(sscheds[i]);
65
+ }
66
+ }
67
+ // Sort this so that body circuits are evaluated first. This is required when there are schedules for things like cleaner
68
+ // or delay circuits. If we do not do this then a schedule that requires the pool to be on for instance will never
69
+ // get triggered.
70
+ circuits.sort((x, y) => y.circuitId === 6 || y.circuitId === 1 ? 1 : y.circuitId - x.circuitId);
71
+
72
+
73
+ /*
74
+ RSG 5-8-22
75
+ Manual OP needs to play a role here.From the IC manual:
76
+ # Manual OP General
77
+
78
+ From the manual:
79
+ Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program,
80
+ the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled
81
+ program will resume. This feature will turn off any scheduled program to allow manual pump override.The Default setting is OFF.
82
+
83
+ ## When on
84
+ 1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has
85
+ expired.When that occurs the schedule should be reset to run at the designated time.If the user resets the schedule by turning the
86
+ circuit back on again ~~then the schedule will resume and turn off at the specified time~~then the schedule will be ignored and
87
+ the circuit will run until the egg timer expires or the circuit / feature is manually turned off.This setting WILL affect
88
+ other schedules that may impact this circuit.
89
+
90
+ ## When off
91
+ 1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time
92
+ off has expired.When that occurs the schedule should be reset to run at the designated time.If the user resets the schedule by
93
+ turning the circuit back on again then the schedule will resume and turn off at the specified time.
94
+ */
95
+
96
+ let mOP = sys.general.options.manualPriority;
97
+
98
+ // Now lets evaluate the schedules by virtue of their state related to the circuits.
99
+ for (let i = 0; i < circuits.length; i++) {
100
+ let c = circuits[i];
101
+ if (!c.hasNixie) continue; // If this has nothing to do with Nixie move on.
102
+ let shouldBeOn = typeof c.sscheds.find(elem => elem.scheduleTime.shouldBeOn === true) !== 'undefined';
103
+ // 1. If the feature is currently running and the schedule is not on then it will set the priority for the circuit to [scheduled].
104
+ // 2. If the feature is currently running but there are overlapping schedules then this will catch any schedules that need to be turned off.
105
+ if (c.cstate.isOn && shouldBeOn) {
106
+ c.cstate.priority = shouldBeOn ? 'scheduled' : 'manual';
107
+ for (let j = 0; j < c.sscheds.length; j++) {
108
+ let ssched = c.sscheds[j];
109
+ ssched.triggered = ssched.scheduleTime.shouldBeOn;
110
+ if (mOP && ssched.manualPriorityActive) {
111
+ ssched.isOn = false;
112
+ // Not sure what setting a delay for this does but ok.
113
+ if (!c.cstate.manualPriorityActive) delayMgr.setManualPriorityDelay(c.cstate);
114
+ }
115
+ else ssched.isOn = ssched.scheduleTime.shouldBeOn && !ssched.manualPriorityActive;
116
+ }
117
+ }
118
+ // 3. If the schedule should be on and it isn't and the schedule has not been triggered then we need to
119
+ // turn the schedule and circuit on.
120
+ else if (!c.cstate.isOn && shouldBeOn) {
121
+ // The circuit is not on but it should be. Check to ensure all schedules have been triggered.
122
+ let untriggered = false;
123
+ // If this schedule has been triggered then mOP comes into play if manualPriority has been set in the config.
124
+ for (let j = 0; j < c.sscheds.length; j++) {
125
+ let ssched = c.sscheds[j];
126
+ // If this schedule is turned back on then the egg timer will come into play. This is all that is required
127
+ // for the mOP function. The setEndDate for the circuit makes the determination as to when off will occur.
128
+ if (mOP && ssched.scheduleTime.shouldBeOn && ssched.triggered) {
129
+ ssched.manualPriorityActive = true;
130
+ }
131
+ // The reason we check to see if anything has not been triggered is so we do not have to perform the circuit changes
132
+ // if the schedule has already been triggered.
133
+ else if (!ssched.triggered) untriggered = true;
134
+ }
135
+ let heatSource = { heatMode: 'nochange', heatSetpoint: undefined, coolSetpoint: undefined };
136
+ // Check to see if any of the schedules have not been triggered. If they haven't then trigger them and turn the circuit on.
137
+ if (untriggered) {
138
+ // Get the heat modes and temps for all the schedules that have not been triggered.
139
+ let body = sys.bodies.find(elem => elem.circuit === c.circuitId);
140
+ if (typeof body !== 'undefined') {
141
+ // If this is a body circuit then we need to set the heat mode and the temperature but only do this once. If
142
+ // the user changes it later then that is on them.
143
+ for (let j = 0; j < c.sscheds.length; j++) {
144
+ if (sscheds[j].triggered) continue;
145
+ let ssched = sscheds[j];
146
+ let hs = sys.board.valueMaps.heatSources.transform(c.sscheds[i].heatSource);
147
+ switch (hs.name) {
148
+ case 'nochange':
149
+ case 'dontchange':
150
+ break;
151
+ case 'off':
152
+ // If the heatsource setting is off only change it if it is currently don't change.
153
+ if (heatSource.heatMode === 'nochange') heatSource.heatMode = hs.name;
154
+ break;
155
+ default:
156
+ switch (heatSource.heatMode) {
157
+ case 'off':
158
+ case 'nochange':
159
+ case 'dontchange':
160
+ heatSource.heatMode = hs.name;
161
+ heatSource.heatSetpoint = ssched.heatSetpoint;
162
+ heatSource.coolSetpoint = hs.hasCoolSetpoint ? ssched.coolSetpoint : undefined;
163
+ break;
164
+ }
165
+ break;
166
+ }
167
+ // Ok if we need to change the setpoint or the heatmode then lets do it.
168
+ if (heatSource.heatMode !== 'nochange') {
169
+ await sys.board.bodies.setHeatModeAsync(body, sys.board.valueMaps.heatSources.getValue(heatSource.heatMode));
170
+ if (typeof heatSource.heatSetpoint !== 'undefined') await sys.board.bodies.setHeatSetpointAsync(body, heatSource.heatSetpoint);
171
+ if (typeof heatSource.coolSetpoint !== 'undefined') await sys.board.bodies.setCoolSetpointAsync(body, heatSource.coolSetpoint);
172
+ }
173
+ }
174
+ }
175
+ // By now we have everything we need to turn on the circuit.
176
+ for (let j = 0; j < c.sscheds.length; j++) {
177
+ let ssched = c.sscheds[j];
178
+ if (!ssched.triggered && ssched.scheduleTime.shouldBeOn) {
179
+ if (!c.cstate.isOn) {
180
+ await sys.board.circuits.setCircuitStateAsync(c.circuitId, true);
181
+ }
182
+ let ssched = c.sscheds[j];
183
+ c.cstate.priority = 'scheduled';
184
+ ssched.triggered = ssched.isOn = ssched.scheduleTime.shouldBeOn;
185
+ ssched.manualPriorityActive = false;
186
+ }
187
+ }
188
+ }
189
+ }
190
+ else if (c.cstate.isOn && !shouldBeOn) {
191
+ // Time to turn off the schedule.
192
+ for (let j = 0; j < c.sscheds.length; j++) {
193
+ let ssched = c.sscheds[j];
194
+ // Only turn off the schedule if it is not actively mOP.
195
+ if (c.cstate.isOn && !ssched.manualPriorityActive) await sys.board.circuits.setCircuitStateAsync(c.circuitId, false);
196
+ c.cstate.priority = 'manual';
197
+ // The schedule has expired we need to clear all the info for it.
198
+ ssched.manualPriorityActive = ssched.triggered = ssched.isOn = c.sscheds[j].scheduleTime.shouldBeOn;
199
+ }
200
+ }
201
+ else if (!c.cstate.isOn && !shouldBeOn) {
202
+ // Everything is off so lets clear it all.
203
+ for (let j = 0; j < c.sscheds.length; j++) {
204
+ let ssched = c.sscheds[j];
205
+ ssched.isOn = ssched.manualPriorityActive = ssched.triggered = false;
206
+ }
207
+ }
208
+ state.emitEquipmentChanges();
209
+ }
210
+ } catch (err) { logger.error(`Error triggering nixie schedules: ${err.message}`); }
211
+ }
212
+ }
213
+ export class NixieSchedule extends NixieEquipment {
214
+ public pollingInterval: number = 10000;
215
+ private _pollTimer: NodeJS.Timeout = null;
216
+ public schedule: Schedule;
217
+ private suspended: boolean = false;
218
+ private resumed: boolean = false;
219
+ private running: boolean = false;
220
+ constructor(ncp: INixieControlPanel, schedule: Schedule) {
221
+ super(ncp);
222
+ this.schedule = schedule;
223
+ this.pollEquipmentAsync();
224
+ let ssched = state.schedules.getItemById(schedule.id, true);
225
+ ssched.circuit = schedule.circuit;
226
+ ssched.scheduleDays = schedule.scheduleDays;
227
+ ssched.scheduleType = schedule.scheduleType;
228
+ ssched.changeHeatSetpoint = schedule.changeHeatSetpoint;
229
+ ssched.heatSetpoint = schedule.heatSetpoint;
230
+ ssched.coolSetpoint = schedule.coolSetpoint;
231
+ ssched.heatSource = schedule.heatSource;
232
+ ssched.startTime = schedule.startTime;
233
+ ssched.endTime = schedule.endTime;
234
+ ssched.startTimeType = schedule.startTimeType;
235
+ ssched.endTimeType = schedule.endTimeType;
236
+ ssched.startDate = schedule.startDate;
237
+ }
238
+ public get id(): number { return typeof this.schedule !== 'undefined' ? this.schedule.id : -1; }
239
+ public async setScheduleAsync(data: any) {
240
+ try {
241
+ let schedule = this.schedule;
242
+ let sschedule = state.schedules.getItemById(schedule.id);
243
+ sschedule.scheduleTime.calculated = false;
244
+ }
245
+ catch (err) { logger.error(`Nixie setScheduleAsync: ${err.message}`); return Promise.reject(err); }
246
+ }
247
+ public async pollEquipmentAsync() {}
248
+ public async validateSetupAsync(Schedule: Schedule, temp: ScheduleState) {
249
+ try {
250
+ // The validation will be different if the Schedule is on or not. So lets get that information.
251
+ } catch (err) { logger.error(`Nixie Error checking Schedule Hardware ${this.schedule.id}: ${err.message}`); return Promise.reject(err); }
252
+ }
253
+ public async closeAsync() {
254
+ try {
255
+ if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
256
+ this._pollTimer = null;
257
+ }
258
+ catch (err) { logger.error(`Nixie Schedule closeAsync: ${err.message}`); return Promise.reject(err); }
259
+ }
260
+ public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
261
+ /*
262
+ public async triggerScheduleAsync(ctx: NixieScheduleContext) {
263
+ try {
264
+ if (this.schedule.isActive === false) return;
265
+ let ssched = state.schedules.getItemById(this.id, true);
266
+ // RULES FOR NIXIE SCHEDULES
267
+ // ------------------------------------------------------
268
+ // Schedules can be overridden so it is important that when the
269
+ // state is changed for the schedule if it is currently active that
270
+ // Nixie does not override the state of the scheduled circuit or feature.
271
+ //RSG 5-8-22
272
+ //Manual OP needs to play a role here. From the IC manual:
273
+ //# Manual OP General
274
+
275
+ //From the manual:
276
+ //Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program, the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled program will resume. This feature will turn off any scheduled program to allow manual pump override. The Default setting is OFF.
277
+
278
+ //## When on
279
+ //1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again ~~then the schedule will resume and turn off at the specified time~~ then the schedule will be ignored and the circuit will run until the egg timer expires or the circuit/feature is manually turned off. This setting WILL affect other schedules that may impact this circuit.
280
+
281
+ //## When off
282
+ //1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again then the schedule will resume and turn off at the specified time.
283
+
284
+ //Interestingly, there also seems to be a schedule level setting for this. We will ignore that for now as the logic could get much more complicated.
285
+ // 1. If the feature happens to be running and the schedule is not yet turned on then
286
+ // it should not override what the user says.
287
+ // 2. If a schedule is running and the state of the circuit changes to off then the new state should suspend the schedule
288
+ // until which time the feature is turned back on again.
289
+ // Manual OP Off: Then the off time will come into play.
290
+ // Manual OP On: The egg timer will take precedence. No other schedules will turn off this feature
291
+ // 3. Egg timers will be managed by the individual circuit. If this is being turned on via the schedule then
292
+ // the egg timer is not in effect.
293
+ // 4. If there are overlapping schedules, then the off date is determined by
294
+ // the maximum off date.
295
+ // 5. If a schedule should be on and the user turns the schedule off then the schedule expires until such time
296
+ // as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the
297
+ // user resets the schedule by turning the circuit back on again then the schedule will...
298
+ // Manual OP Off: ..resume and turn off at the specified time.
299
+ // Manual OP On: ...continue to run until the egg timer time is reached or the circuit is manually turned off.
300
+ // 6. Heat setpoints should only be changed when the schedule is first turning on the scheduled circuit.
301
+ // 7. If schedule is disabled, skip it
302
+ // 8. Manual OP On: If another schedule has been resumed and this schedule would affect that feature, do not start this schedule.
303
+
304
+ // Definitions:
305
+ // this.resumed = Flag to show if this schedule has been suspended and resumed.
306
+ // this.manualPriorityActive = Flag to show if this schedule has been suspended and resumed, and Manual Priority (global is active)
307
+ // this.delayed = Flag to show if this schedule is running, but another schedule is mOP and overriding this one.
308
+ let cstate = state.circuits.getInterfaceById(this.schedule.circuit, false);
309
+ let circuit = sys.circuits.getInterfaceById(this.schedule.circuit, false, { isActive: false });
310
+ if (circuit.isActive === false) {
311
+ ssched.isOn = false;
312
+ return;
313
+ }
314
+ let shouldBeOn = ssched.shouldBeOn(); // This should also set the validity for the schedule if there are errors.
315
+ let manualPriorityActive: boolean = shouldBeOn ? sys.board.schedules.manualPriorityActive(ssched) : false;
316
+ //console.log(`Processing schedule ${this.schedule.id} - ${circuit.name} : ShouldBeOn: ${shouldBeOn} ManualPriorityActive: ${manualPriorityActive} Running: ${this.running} Suspended: ${this.suspended} Resumed: ${this.resumed}`);
317
+
318
+
319
+ // COND 1: The schedule should be on and the schedule is not yet on.
320
+ if (shouldBeOn && !this.running && !this.suspended || manualPriorityActive) {
321
+ // If the circuit is on then we need to clear the suspended flag and set the running flag.
322
+ if (cstate.isOn) {
323
+ // If the suspended flag was previously on then we need to clear it
324
+ // because the user turned it back on.
325
+ this.suspended = false;
326
+ }
327
+ if (manualPriorityActive) {
328
+ ssched.manualPriorityActive = true;
329
+ ssched.isOn = false;
330
+ }
331
+ else {
332
+ ctx.setCircuit(circuit.id, true);
333
+ // Alright we are turning on the circuit. If these are body circuits then we need to determine
334
+ // whether we will be setting the setpoints/heatmode on the body.
335
+ let body = sys.bodies.find(elem => elem.circuit === circuit.id);
336
+ if (typeof body !== 'undefined') {
337
+ let heatSource = sys.board.valueMaps.heatSources.transform(this.schedule.heatSource);
338
+ if (heatSource.name !== 'nochange') {
339
+ switch (heatSource.name) {
340
+ case 'nochange':
341
+ case 'dontchange':
342
+ break;
343
+ case 'off':
344
+ ctx.setHeatMode(body.id, 'off');
345
+ break;
346
+ default:
347
+ ctx.setHeatMode(body.id, heatSource.name, this.schedule.heatSetpoint, heatSource.hasCoolSetpoint ? this.schedule.coolSetpoint : undefined);
348
+ break;
349
+ }
350
+ }
351
+ }
352
+ ssched.manualPriorityActive = false;
353
+ ssched.isOn = true;
354
+ }
355
+ this.running = true;
356
+ }
357
+ else if (shouldBeOn && this.running) {
358
+ // Check to see if circuit is on, if not turn it on.
359
+ // RKS: 07-09-23 - This was in PR#819 buut this needs further review since the circuit states are not to be set here. This would
360
+ // trash delays and manualPriority.
361
+ // if(!cstate.isOn) ctx.setCircuit(circuit.id, true);
362
+
363
+ // With mOP, we need to see if the schedule will come back into play and also set the circut
364
+ if (this.suspended && cstate.isOn) {
365
+ if (sys.general.options.manualPriority) {
366
+ delayMgr.setManualPriorityDelay(cstate);
367
+ ssched.manualPriorityActive = true;
368
+ }
369
+ this.resumed = true;
370
+ }
371
+ this.suspended = !cstate.isOn;
372
+ if (manualPriorityActive) {
373
+ ssched.isOn = false;
374
+ ssched.manualPriorityActive = true;
375
+ }
376
+ else {
377
+ ssched.isOn = cstate.isOn;
378
+ ssched.manualPriorityActive = false;
379
+ }
380
+ }
381
+ // Our schedule has expired it is time to turn it off, but only if !manualPriorityActive.
382
+ else if (!shouldBeOn) {
383
+ // Turn this sucker off. But wait if there is an overlapping schedule then we should
384
+ // not turn it off. We will need some logic to deal with this.
385
+ if (this.running && !cstate.manualPriorityActive) ctx.setCircuit(circuit.id, false);
386
+ ssched.isOn = false;
387
+ this.running = false;
388
+ this.suspended = false;
389
+ this.resumed = false;
390
+ ssched.manualPriorityActive = false;
391
+ }
392
+ if (!shouldBeOn && ssched.isOn === true) {
393
+ // Turn off the circuit.
394
+ if (!manualPriorityActive) ctx.setCircuit(circuit.id, false);
395
+ ssched.isOn = false;
396
+ }
397
+ ssched.emitEquipmentChange();
398
+ } catch (err) { logger.error(`Error processing schedule: ${err.message}`); }
399
+ }
400
+ */
401
+ }