nodejs-poolcontroller 7.5.1 → 7.7.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 (64) hide show
  1. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
  2. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
  3. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
  4. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. package/Changelog +19 -0
  6. package/Dockerfile +3 -3
  7. package/README.md +13 -8
  8. package/app.ts +1 -1
  9. package/config/Config.ts +38 -2
  10. package/config/VersionCheck.ts +27 -12
  11. package/controller/Constants.ts +2 -1
  12. package/controller/Equipment.ts +193 -9
  13. package/controller/Errors.ts +10 -0
  14. package/controller/Lockouts.ts +503 -0
  15. package/controller/State.ts +269 -64
  16. package/controller/boards/AquaLinkBoard.ts +1000 -0
  17. package/controller/boards/BoardFactory.ts +4 -0
  18. package/controller/boards/EasyTouchBoard.ts +468 -144
  19. package/controller/boards/IntelliCenterBoard.ts +466 -307
  20. package/controller/boards/IntelliTouchBoard.ts +37 -5
  21. package/controller/boards/NixieBoard.ts +671 -141
  22. package/controller/boards/SystemBoard.ts +1397 -641
  23. package/controller/comms/Comms.ts +462 -362
  24. package/controller/comms/messages/Messages.ts +174 -30
  25. package/controller/comms/messages/config/ChlorinatorMessage.ts +6 -3
  26. package/controller/comms/messages/config/CircuitMessage.ts +1 -0
  27. package/controller/comms/messages/config/ExternalMessage.ts +10 -8
  28. package/controller/comms/messages/config/HeaterMessage.ts +141 -29
  29. package/controller/comms/messages/config/OptionsMessage.ts +9 -2
  30. package/controller/comms/messages/config/PumpMessage.ts +53 -35
  31. package/controller/comms/messages/config/ScheduleMessage.ts +33 -25
  32. package/controller/comms/messages/config/ValveMessage.ts +2 -2
  33. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +38 -86
  34. package/controller/comms/messages/status/EquipmentStateMessage.ts +59 -23
  35. package/controller/comms/messages/status/HeaterStateMessage.ts +57 -3
  36. package/controller/comms/messages/status/IntelliChemStateMessage.ts +56 -8
  37. package/controller/comms/messages/status/PumpStateMessage.ts +23 -1
  38. package/controller/nixie/Nixie.ts +1 -1
  39. package/controller/nixie/bodies/Body.ts +3 -0
  40. package/controller/nixie/chemistry/ChemController.ts +164 -51
  41. package/controller/nixie/chemistry/Chlorinator.ts +137 -88
  42. package/controller/nixie/circuits/Circuit.ts +51 -19
  43. package/controller/nixie/heaters/Heater.ts +241 -31
  44. package/controller/nixie/pumps/Pump.ts +488 -206
  45. package/controller/nixie/schedules/Schedule.ts +91 -35
  46. package/controller/nixie/valves/Valve.ts +1 -1
  47. package/defaultConfig.json +20 -0
  48. package/package.json +21 -21
  49. package/web/Server.ts +94 -49
  50. package/web/bindings/aqualinkD.json +505 -0
  51. package/web/bindings/influxDB.json +71 -1
  52. package/web/bindings/mqtt.json +98 -39
  53. package/web/bindings/mqttAlt.json +59 -1
  54. package/web/interfaces/baseInterface.ts +1 -0
  55. package/web/interfaces/httpInterface.ts +23 -2
  56. package/web/interfaces/influxInterface.ts +45 -10
  57. package/web/interfaces/mqttInterface.ts +114 -54
  58. package/web/services/config/Config.ts +55 -132
  59. package/web/services/state/State.ts +81 -4
  60. package/web/services/state/StateSocket.ts +4 -4
  61. package/web/services/utilities/Utilities.ts +8 -6
  62. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
  63. package/config copy.json +0 -300
  64. package/issue_template.md +0 -52
@@ -1,5 +1,5 @@
1
1
  import { InvalidEquipmentDataError, InvalidEquipmentIdError, InvalidOperationError } from '../../Errors';
2
- import { utils, Timestamp } from '../../Constants';
2
+ import { utils, Timestamp, ControllerType } from '../../Constants';
3
3
  import { logger } from '../../../logger/Logger';
4
4
 
5
5
  import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
@@ -87,7 +87,11 @@ export class NixieChlorinator extends NixieEquipment {
87
87
  }
88
88
  public get id(): number { return typeof this.chlor !== 'undefined' ? this.chlor.id : -1; }
89
89
  public get suspendPolling(): boolean { return this._suspendPolling > 0; }
90
- public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
90
+ public set suspendPolling(val: boolean) { this._suspendPolling = Math.max(0, this._suspendPolling + (val ? 1 : -1)); }
91
+ public get superChlorRemaining(): number {
92
+ if (typeof this.superChlorStart === 'undefined' || this.superChlorStart === 0 || !this.chlor.superChlor) return 0;
93
+ return Math.max(Math.floor(((this.chlor.superChlorHours * 3600 * 1000) - (new Date().getTime() - this.superChlorStart)) / 1000), 0);
94
+ }
91
95
  public async setChlorinatorAsync(data: any) {
92
96
  try {
93
97
  let chlor = this.chlor;
@@ -97,15 +101,20 @@ export class NixieChlorinator extends NixieEquipment {
97
101
  let poolSetpoint = typeof data.poolSetpoint !== 'undefined' ? parseInt(data.poolSetpoint, 10) : chlor.poolSetpoint;
98
102
  let spaSetpoint = typeof data.spaSetpoint !== 'undefined' ? parseInt(data.spaSetpoint, 10) : chlor.spaSetpoint;
99
103
  let body = sys.board.bodies.mapBodyAssociation(typeof data.body === 'undefined' ? chlor.body : data.body);
100
- let superChlor = typeof data.superChlor !== 'undefined' ? utils.makeBool(data.superChlor) : chlor.superChlor;
104
+ let superChlor = typeof data.superChlor !== 'undefined' ? utils.makeBool(data.superChlor) : typeof data.superChlorinate !== 'undefined' ? utils.makeBool(data.superChlorinate) : chlor.superChlor;
101
105
  let chlorType = typeof data.type !== 'undefined' ? sys.board.valueMaps.chlorinatorType.encode(data.type) : chlor.type || 0;
102
106
  let superChlorHours = typeof data.superChlorHours !== 'undefined' ? parseInt(data.superChlorHours, 10) : chlor.superChlorHours;
103
107
  let disabled = typeof data.disabled !== 'undefined' ? utils.makeBool(data.disabled) : chlor.disabled;
104
108
  let isDosing = typeof data.isDosing !== 'undefined' ? utils.makeBool(data.isDosing) : chlor.isDosing;
105
- let model = typeof data.model !== 'undefined' ? data.model : chlor.model || 0;
106
- if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Invalid body assignment`, 'chlorinator', data.body || chlor.body));
109
+ let model = typeof data.model !== 'undefined' ? sys.board.valueMaps.chlorinatorModel.encode(data.model) : chlor.model || 0;
110
+ let portId = typeof data.portId !== 'undefined' ? parseInt(data.portId, 10) : chlor.portId;
111
+ if (portId === 0 && sys.controllerType !== ControllerType.Nixie) return Promise.reject(new InvalidEquipmentDataError(`You may not install a chlorinator on an ${sys.controllerType} system that is assigned to the Primary Port that is under Nixe control`, 'Chlorinator', portId));
112
+ if (portId !== chlor.portId && sys.chlorinators.count(elem => elem.id !== this.chlor.id && elem.portId === portId && elem.master !== 2) > 0) return Promise.reject(new InvalidEquipmentDataError(`Another chlorinator is installed on port #${portId}. Only one chlorinator can be installed per port.`, 'Chlorinator', portId));
113
+ if (isNaN(portId)) return Promise.reject(new InvalidEquipmentDataError(`Invalid port Id`, 'Chlorinator', data.portId));
114
+ if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Invalid body assignment`, 'Chlorinator', data.body || chlor.body));
107
115
  if (isNaN(poolSetpoint)) poolSetpoint = 0;
108
116
  if (isNaN(spaSetpoint)) spaSetpoint = 0;
117
+
109
118
  chlor.ignoreSaltReading = (typeof data.ignoreSaltReading !== 'undefined') ? utils.makeBool(data.ignoreSaltReading) : utils.makeBool(chlor.ignoreSaltReading);
110
119
  // Do a final validation pass so we dont send this off in a mess.
111
120
  let schlor = state.chlorinators.getItemById(chlor.id, true);
@@ -114,12 +123,17 @@ export class NixieChlorinator extends NixieEquipment {
114
123
  schlor.superChlor = chlor.superChlor = superChlor;
115
124
  schlor.superChlorHours = chlor.superChlorHours = superChlorHours;
116
125
  schlor.type = chlor.type = chlorType;
117
- chlor.model = model;
126
+ schlor.model = chlor.model = model;
118
127
  schlor.body = chlor.body = body.val;
128
+ chlor.portId = portId;
119
129
  chlor.disabled = disabled;
120
130
  chlor.isDosing = isDosing;
121
131
  schlor.name = chlor.name = data.name || chlor.name || `Chlorinator ${chlor.id}`;
122
132
  schlor.isActive = chlor.isActive = true;
133
+ if (!chlor.superChlor) {
134
+ this.superChlorStart = 0;
135
+ this.superChlorinating = false;
136
+ }
123
137
  }
124
138
  catch (err) { logger.error(`setChlorinatorAsync: ${err.message}`); return Promise.reject(err); }
125
139
  }
@@ -148,9 +162,23 @@ export class NixieChlorinator extends NixieEquipment {
148
162
  this.pollEquipment();
149
163
  } catch (err) { logger.error(`Error initializing ${this.chlor.name} : ${err.message}`); }
150
164
  }
151
- public isBodyOn() {
152
- let isOn = sys.board.bodies.isBodyOn(this.chlor.body);
153
- return isOn;
165
+ public isBodyOn() { return sys.board.bodies.isBodyOn(this.chlor.body); }
166
+ public setSuperChlor(cstate: ChlorinatorState) {
167
+ if (this.chlor.superChlor) {
168
+ if (!this.superChlorinating) {
169
+ // Deal with the start time.
170
+ let hours = this.chlor.superChlorHours * 3600;
171
+ let offset = cstate.superChlorRemaining > 0 ? Math.max((hours - (hours - cstate.superChlorRemaining)), 0) : 0;
172
+ this.superChlorStart = new Date().getTime() - offset;
173
+ this.superChlorinating = true;
174
+ }
175
+ cstate.superChlorRemaining = this.superChlorRemaining;
176
+ }
177
+ else {
178
+ this.superChlorStart = 0;
179
+ this.superChlorinating = false;
180
+ cstate.superChlorRemaining = 0;
181
+ }
154
182
  }
155
183
  public async pollEquipment() {
156
184
  let self = this;
@@ -190,34 +218,45 @@ export class NixieChlorinator extends NixieEquipment {
190
218
  // Disable the control panel by sending an action 0 the chlorinator should respond with an action 1.
191
219
  //[16, 2, 80, 0][0][98, 16, 3]
192
220
  let success = await new Promise<boolean>((resolve, reject) => {
193
- let out = Outbound.create({
194
- protocol: Protocol.Chlorinator,
195
- dest: this.chlor.id,
196
- action: 0,
197
- payload: [0],
198
- retries: 3, // IntelliCenter tries 4 times to get a response.
199
- response: Response.create({ protocol: Protocol.Chlorinator, action: 1 }),
200
- onAbort: () => {},
201
- onComplete: (err) => {
202
- if (err) {
203
- // This flag is cleared in ChlorinatorStateMessage
204
- cstate.status = 128;
205
- resolve(false);
206
- }
207
- else {
208
- // If this is successful the action 1 message will have been
209
- // digested by ChlorinatorStateMessage and the lastComm will have been set clearing the
210
- // communication lost flag.
211
- resolve(true);
221
+ if (conn.isPortEnabled(this.chlor.portId || 0)) {
222
+ let out = Outbound.create({
223
+ portId: this.chlor.portId || 0,
224
+ protocol: Protocol.Chlorinator,
225
+ //dest: this.chlor.id,
226
+ dest: 1,
227
+ action: 0,
228
+ payload: [0],
229
+ retries: 3, // IntelliCenter tries 4 times to get a response.
230
+ response: Response.create({ protocol: Protocol.Chlorinator, action: 1 }),
231
+ onAbort: () => { this.chlor.superChlor = cstate.superChlor = false; this.setSuperChlor(cstate); },
232
+ onComplete: (err) => {
233
+ if (err) {
234
+ // This flag is cleared in ChlorinatorStateMessage
235
+ this.chlor.superChlor = cstate.superChlor = false;
236
+ this.setSuperChlor(cstate);
237
+ cstate.status = 128;
238
+ resolve(false);
239
+ }
240
+ else {
241
+ // If this is successful the action 1 message will have been
242
+ // digested by ChlorinatorStateMessage and the lastComm will have been set clearing the
243
+ // communication lost flag.
244
+ resolve(true);
245
+ }
246
+ cstate.emitEquipmentChange();
212
247
  }
213
- }
214
- });
215
- conn.queueSendMessage(out);
248
+ });
249
+ conn.queueSendMessage(out);
250
+ }
251
+ else {
252
+ cstate.status = 0;
253
+ resolve(true);
254
+ }
216
255
  });
217
256
  return success;
218
257
  } catch (err) { logger.error(`Communication error with Chlorinator ${this.chlor.name} : ${err.message}`); }
219
258
  }
220
- public async setOutput() {
259
+ public async setOutput(): Promise<boolean> {
221
260
  try {
222
261
  // A couple of things need to be in place before setting the output.
223
262
  // 1. The chlorinator will have to have responded to the takeControl message.
@@ -226,57 +265,63 @@ export class NixieChlorinator extends NixieEquipment {
226
265
  // 3. If we are superchlorinating and the remaing superChlor time is > 0 then we need to keep it at 100%.
227
266
  // 4. If the chlorinator disabled flag is set then we need to make sure the setpoint is 0.
228
267
  let cstate = state.chlorinators.getItemById(this.chlor.id, true);
229
- let body = state.temps.bodies.getBodyIsOn();
230
268
  let setpoint = 0;
231
- if (typeof body !== 'undefined') {
232
- setpoint = (body.id === 1) ? this.chlor.poolSetpoint : this.chlor.spaSetpoint;
233
- if (this.chlor.superChlor === true || this.chlor.isDosing) setpoint = 100;
234
- if (this.chlor.disabled === true) setpoint = 0; // Our target should be 0 because we have other things going on. For instance,
235
- // we may be dosing acid which will cause the disabled flag to be true.
269
+ if (this.isBodyOn()) {
270
+ if (sys.equipment.shared) {
271
+ let body = state.temps.bodies.getBodyIsOn();
272
+ setpoint = (body.id === 1) ? this.chlor.poolSetpoint : this.chlor.spaSetpoint;
273
+ }
274
+ else setpoint = this.chlor.body === 0 ? this.chlor.poolSetpoint : this.chlor.spaSetpoint;
275
+ if (this.chlor.isDosing) setpoint = 100;
276
+ }
277
+ else {
278
+ this.chlor.superChlor = cstate.superChlor = false;
279
+ this.setSuperChlor(cstate);
236
280
  }
281
+ if (this.chlor.disabled === true) setpoint = 0; // Our target should be 0 because we have other things going on. For instance,
237
282
  // RKS: Not sure if it needs to be smart enough to force an off message when the comms die.
238
283
  //if (cstate.status === 128) setpoint = 0; // If we haven't been able to get a response from the clorinator tell is to turn itself off.
239
284
  // Perhaps we will be luckier on the next poll cycle.
240
285
  // Tell the chlorinator that we are to use the current output.
241
286
  //[16, 2, 80, 17][0][115, 16, 3]
242
- cstate.targetOutput = setpoint;
287
+ cstate.targetOutput = cstate.superChlor ? 100 : setpoint;
243
288
  let success = await new Promise<boolean>((resolve, reject) => {
244
- let out = Outbound.create({
245
- protocol: Protocol.Chlorinator,
246
- dest: this.chlor.id,
247
- action: 17,
248
- payload: [setpoint],
249
- retries: 7, // IntelliCenter tries 8 times to make this happen.
250
- response: Response.create({ protocol: Protocol.Chlorinator, action: 18 }),
251
- onAbort: () => {},
252
- onComplete: (err) => {
253
- if (err) {
254
- cstate.currentOutput = 0;
255
- cstate.status = 128;
256
- resolve(false);
257
- }
258
- else {
259
- // The action:17 message originated from us so we will not see it in the
260
- // ChlorinatorStateMessage module.
261
- cstate.currentOutput = setpoint;
262
- if (!this.superChlorinating && cstate.superChlor) {
263
- cstate.superChlorRemaining = cstate.superChlorHours * 3600;
264
- this.superChlorStart = Math.floor(new Date().getTime() / 1000) * 1000;
289
+ if (conn.isPortEnabled(this.chlor.portId || 0)) {
290
+ let out = Outbound.create({
291
+ portId: this.chlor.portId || 0,
292
+ protocol: Protocol.Chlorinator,
293
+ //dest: this.chlor.id,
294
+ dest: 1,
295
+ action: 17,
296
+ payload: [cstate.targetOutput],
297
+ retries: 7, // IntelliCenter tries 8 times to make this happen.
298
+ response: Response.create({ protocol: Protocol.Chlorinator, action: 18 }),
299
+ onAbort: () => { },
300
+ onComplete: (err) => {
301
+ if (err) {
302
+ cstate.currentOutput = 0;
303
+ cstate.status = 128;
304
+ resolve(false);
265
305
  }
266
- else if (cstate.superChlor) {
267
- cstate.superChlorRemaining = cstate.superChlorHours * 3600 - ((Math.floor(new Date().getTime() / 1000) * 1000) - this.superChlorStart);
306
+ else {
307
+ cstate.currentOutput = cstate.targetOutput;
308
+ this.setSuperChlor(cstate);
309
+ resolve(true);
268
310
  }
269
- else if (!cstate.superChlor)
270
- cstate.superChlorRemaining = 0;
271
- resolve(true);
272
311
  }
273
- }
274
- });
275
- // #338
276
- if (setpoint === 16) { out.appendPayloadByte(0); }
277
- conn.queueSendMessage(out);
312
+ });
313
+ // #338
314
+ if (setpoint === 16) { out.appendPayloadByte(0); }
315
+ conn.queueSendMessage(out);
316
+ }
317
+ else {
318
+ cstate.currentOutput = cstate.targetOutput;
319
+ this.setSuperChlor(cstate);
320
+ resolve(true);
321
+ }
278
322
  });
279
-
323
+ cstate.emitEquipmentChange();
324
+ return success;
280
325
  } catch (err) { logger.error(`Communication error with Chlorinator ${this.chlor.name} : ${err.message}`); return Promise.reject(err);}
281
326
 
282
327
  }
@@ -290,25 +335,29 @@ export class NixieChlorinator extends NixieEquipment {
290
335
  // Ask the chlorinator for its model.
291
336
  //[16, 2, 80, 20][0][118, 16, 3]
292
337
  let success = await new Promise<boolean>((resolve, reject) => {
293
- let out = Outbound.create({
294
- protocol: Protocol.Chlorinator,
295
- dest: this.chlor.id,
296
- action: 20,
297
- payload: [0],
298
- retries: 3, // IntelliCenter tries 4 times to get a response.
299
- response: Response.create({ protocol: Protocol.Chlorinator, action: 3 }),
300
- onAbort: () => { },
301
- onComplete: (err) => {
302
- if (err) resolve(false);
303
- else resolve(true);
304
- }
305
- });
306
- conn.queueSendMessage(out);
338
+ if (conn.isPortEnabled(this.chlor.portId || 0)) {
339
+ let out = Outbound.create({
340
+ portId: this.chlor.portId || 0,
341
+ protocol: Protocol.Chlorinator,
342
+ //dest: this.chlor.id,
343
+ dest: 1,
344
+ action: 20,
345
+ payload: [0],
346
+ retries: 3, // IntelliCenter tries 4 times to get a response.
347
+ response: Response.create({ protocol: Protocol.Chlorinator, action: 3 }),
348
+ onAbort: () => { },
349
+ onComplete: (err) => {
350
+ if (err) resolve(false);
351
+ else resolve(true);
352
+ }
353
+ });
354
+ conn.queueSendMessage(out);
355
+ }
356
+ else { resolve(true); }
307
357
  });
358
+ return success;
308
359
  }
309
- else return Promise.resolve(false);
360
+ else return false;
310
361
  } catch (err) { logger.error(`Communication error with Chlorinator ${this.chlor.name} : ${err.message}`); return Promise.reject(err);}
311
-
312
362
  }
313
-
314
363
  }
@@ -8,6 +8,7 @@ import { CircuitState, state, ICircuitState, } from "../../State";
8
8
  import { setTimeout, clearTimeout } from 'timers';
9
9
  import { NixieControlPanel } from '../Nixie';
10
10
  import { webApp, InterfaceServerResponse } from "../../../web/Server";
11
+ import { delayMgr } from '../../../controller/Lockouts';
11
12
 
12
13
  export class NixieCircuitCollection extends NixieEquipmentCollection<NixieCircuit> {
13
14
  public pollingInterval: number = 2000;
@@ -23,7 +24,7 @@ export class NixieCircuitCollection extends NixieEquipmentCollection<NixieCircui
23
24
  }
24
25
  } catch (err) { return Promise.reject(`Nixie Control Panel deleteCircuitAsync ${err.message}`); }
25
26
  }
26
- public async sendOnOffSequenceAsync(id: number, count: number) {
27
+ public async sendOnOffSequenceAsync(id: number, count: number | { isOn: boolean, timeout: number }[]) {
27
28
  try {
28
29
  let c: NixieCircuit = this.find(elem => elem.id === id) as NixieCircuit;
29
30
  if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${id} could not be found to send sequence ${count}.`));
@@ -60,7 +61,7 @@ export class NixieCircuitCollection extends NixieEquipmentCollection<NixieCircui
60
61
  try {
61
62
  let c: NixieCircuit = this.find(elem => elem.id === cstate.id) as NixieCircuit;
62
63
  await c.checkCircuitEggTimerExpirationAsync(cstate);
63
- } catch (err) { logger.error(`NCP: Error syncing circuit states: ${err}`); }
64
+ } catch (err) { logger.error(`NCP: Error synching circuit states: ${err}`); }
64
65
  }
65
66
  public async initAsync(circuits: CircuitCollection) {
66
67
  try {
@@ -118,6 +119,10 @@ export class NixieCircuit extends NixieEquipment {
118
119
  constructor(ncp: INixieControlPanel, circuit: Circuit) {
119
120
  super(ncp);
120
121
  this.circuit = circuit;
122
+ // Clear out the delays.
123
+ let cstate = state.circuits.getItemById(circuit.id);
124
+ cstate.startDelay = false;
125
+ cstate.stopDelay = false;
121
126
  }
122
127
  public get id(): number { return typeof this.circuit !== 'undefined' ? this.circuit.id : -1; }
123
128
  public get eggTimerOff(): Timestamp { return typeof this.timeOn !== 'undefined' && !this.circuit.dontStop ? this.timeOn.clone().addMinutes(this.circuit.eggTimer) : undefined; }
@@ -127,17 +132,20 @@ export class NixieCircuit extends NixieEquipment {
127
132
  }
128
133
  catch (err) { logger.error(`Nixie setCircuitAsync: ${err.message}`); return Promise.reject(err); }
129
134
  }
130
- public async sendOnOffSequenceAsync(count: number, timeout?:number): Promise<InterfaceServerResponse> {
135
+ public async sendOnOffSequenceAsync(count: number | { isOn: boolean, timeout: number }[], timeout?:number): Promise<InterfaceServerResponse> {
131
136
  try {
132
137
  this._sequencing = true;
133
138
  let arr = [];
134
- let t = typeof timeout === 'undefined' ? 100 : timeout;
135
- arr.push({ isOn: false, timeout: t }); // This may not be needed but we always need to start from off.
136
- //[{ isOn: true, timeout: 1000 }, { isOn: false, timeout: 1000 }]
137
- for (let i = 0; i < count; i++) {
138
- arr.push({ isOn: true, timeout: t });
139
- if(i < count - 1) arr.push({ isOn: false, timeout: t });
139
+ if (typeof count === 'number') {
140
+ let t = typeof timeout === 'undefined' ? 100 : timeout;
141
+ arr.push({ isOn: false, timeout: t }); // This may not be needed but we always need to start from off.
142
+ //[{ isOn: true, timeout: 1000 }, { isOn: false, timeout: 1000 }]
143
+ for (let i = 0; i < count; i++) {
144
+ arr.push({ isOn: true, timeout: t });
145
+ if (i < count - 1) arr.push({ isOn: false, timeout: t });
146
+ }
140
147
  }
148
+ else arr = count;
141
149
  // The documentation for IntelliBrite is incorrect. The sequence below will give us Party mode.
142
150
  // Party mode:2
143
151
  // Start: Off
@@ -157,7 +165,15 @@ export class NixieCircuit extends NixieEquipment {
157
165
  } catch (err) { logger.error(`Nixie: Error sending circuit sequence ${this.id}: ${count}`); }
158
166
  finally { this._sequencing = false; }
159
167
  }
160
- public async setCircuitStateAsync(cstate: ICircuitState, val: boolean, scheduled: boolean = false) : Promise<InterfaceServerResponse> {
168
+ public async setThemeAsync(cstate: ICircuitState, theme: number): Promise<InterfaceServerResponse> {
169
+ try {
170
+
171
+
172
+
173
+ return new InterfaceServerResponse(200, 'Sucess');
174
+ } catch (err) { logger.error(`Nixie: Error setting light theme ${cstate.id}-${cstate.name} to ${theme}`); }
175
+ }
176
+ public async setCircuitStateAsync(cstate: ICircuitState, val: boolean, scheduled: boolean = false): Promise<InterfaceServerResponse> {
161
177
  try {
162
178
  if (val !== cstate.isOn) {
163
179
  logger.info(`NCP: Setting Circuit ${cstate.name} to ${val}`);
@@ -169,18 +185,30 @@ export class NixieCircuit extends NixieEquipment {
169
185
  // Check to see if we should be on by poking the schedules.
170
186
  }
171
187
  if (utils.isNullOrEmpty(this.circuit.connectionId) || utils.isNullOrEmpty(this.circuit.deviceBinding)) {
172
- sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
173
- cstate.isOn = val;
188
+ if (val && val !== cstate.isOn){
189
+ sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
190
+ }
191
+ else if (!val){
192
+ if (cstate.manualPriorityActive) delayMgr.cancelManualPriorityDelay(cstate.id);
193
+ cstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
194
+ }
195
+ cstate.isOn = val;
174
196
  return new InterfaceServerResponse(200, 'Success');
175
197
  }
176
198
  if (this._sequencing) return new InterfaceServerResponse(200, 'Success');
177
199
  let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, { isOn: val, latch: val ? 10000 : undefined });
178
200
  if (res.status.code === 200) {
179
- sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
180
- cstate.isOn = val;
181
201
  // Set this up so we can process our egg timer.
182
- if (!cstate.isOn && val) { this.timeOn = new Timestamp(); }
183
- else if (!val) this.timeOn = undefined;
202
+ //if (!cstate.isOn && val) { cstate.startTime = this.timeOn = new Timestamp(); }
203
+ //else if (!val) cstate.startTime = this.timeOn = undefined;
204
+ if (val && val !== cstate.isOn){
205
+ sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
206
+ }
207
+ else if (!val){
208
+ delayMgr.cancelManualPriorityDelays();
209
+ cstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
210
+ }
211
+ cstate.isOn = val;
184
212
  }
185
213
  return res;
186
214
  } catch (err) { logger.error(`Nixie: Error setting circuit state ${cstate.id}-${cstate.name} to ${val}`); }
@@ -190,9 +218,11 @@ export class NixieCircuit extends NixieEquipment {
190
218
  // (this should already be turned off) or the egg timer has expired
191
219
  try {
192
220
  if (!cstate.isActive || !cstate.isOn) return;
193
- if (cstate.endTime.toDate() < new Timestamp().toDate()) {
194
- await sys.board.circuits.setCircuitStateAsync(cstate.id, false);
195
- cstate.emitEquipmentChange();
221
+ if (typeof cstate.endTime !== 'undefined') {
222
+ if (cstate.endTime.toDate() < new Timestamp().toDate()) {
223
+ await sys.board.circuits.setCircuitStateAsync(cstate.id, false);
224
+ cstate.emitEquipmentChange();
225
+ }
196
226
  }
197
227
  } catch (err) { logger.error(`Error syncing circuit: ${err}`); }
198
228
  }
@@ -220,6 +250,8 @@ export class NixieCircuit extends NixieEquipment {
220
250
  public async closeAsync() {
221
251
  try {
222
252
  let cstate = state.circuits.getItemById(this.circuit.id);
253
+ cstate.stopDelay = false;
254
+ cstate.startDelay = false;
223
255
  await this.setCircuitStateAsync(cstate, false);
224
256
  cstate.emitEquipmentChange();
225
257
  }