nodejs-poolcontroller 8.3.0 → 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.
- package/.eslintrc.json +36 -36
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -84
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -12
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -28
- package/.github/ISSUE_TEMPLATE/config.yml +8 -8
- package/.github/copilot-instructions.md +63 -63
- package/.github/workflows/ghcr-publish.yml +67 -67
- package/AGENTS.md +597 -0
- package/CONTRIBUTING.md +74 -74
- package/Changelog +292 -284
- package/Dockerfile +62 -62
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +318 -309
- package/anslq25/MessagesMock.ts +221 -221
- package/anslq25/boards/MockBoardFactory.ts +49 -49
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -696
- package/anslq25/boards/MockSystemBoard.ts +216 -216
- package/anslq25/chemistry/MockChlorinator.ts +98 -98
- package/anslq25/pumps/MockPump.ts +83 -83
- package/app.ts +115 -115
- package/config/Config.ts +0 -0
- package/config/VersionCheck.ts +0 -0
- package/controller/Constants.ts +809 -805
- package/controller/Equipment.ts +2688 -2664
- package/controller/Errors.ts +181 -181
- package/controller/Lockouts.ts +549 -549
- package/controller/State.ts +3738 -3701
- package/controller/boards/AquaLinkBoard.ts +1003 -1003
- package/controller/boards/BoardFactory.ts +53 -53
- package/controller/boards/EasyTouchBoard.ts +3202 -3202
- package/controller/boards/IntelliCenterBoard.ts +4393 -3899
- package/controller/boards/IntelliComBoard.ts +69 -69
- package/controller/boards/IntelliTouchBoard.ts +382 -382
- package/controller/boards/NixieBoard.ts +1944 -1944
- package/controller/boards/SunTouchBoard.ts +400 -400
- package/controller/boards/SystemBoard.ts +5268 -5268
- package/controller/comms/Comms.ts +1272 -1255
- package/controller/comms/ScreenLogic.ts +1665 -1665
- package/controller/comms/messages/Messages.ts +1433 -1406
- package/controller/comms/messages/config/ChlorinatorMessage.ts +5 -0
- package/controller/comms/messages/config/CircuitGroupMessage.ts +0 -0
- package/controller/comms/messages/config/CircuitMessage.ts +0 -0
- package/controller/comms/messages/config/ConfigMessage.ts +6 -0
- package/controller/comms/messages/config/CoverMessage.ts +0 -0
- package/controller/comms/messages/config/CustomNameMessage.ts +31 -31
- package/controller/comms/messages/config/EquipmentMessage.ts +216 -210
- package/controller/comms/messages/config/ExternalMessage.ts +96 -10
- package/controller/comms/messages/config/FeatureMessage.ts +0 -0
- package/controller/comms/messages/config/GeneralMessage.ts +0 -0
- package/controller/comms/messages/config/HeaterMessage.ts +0 -0
- package/controller/comms/messages/config/IntellichemMessage.ts +0 -0
- package/controller/comms/messages/config/OptionsMessage.ts +194 -174
- package/controller/comms/messages/config/PumpMessage.ts +0 -0
- package/controller/comms/messages/config/RemoteMessage.ts +0 -0
- package/controller/comms/messages/config/ScheduleMessage.ts +401 -390
- package/controller/comms/messages/config/SecurityMessage.ts +0 -0
- package/controller/comms/messages/config/ValveMessage.ts +0 -0
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +0 -0
- package/controller/comms/messages/status/EquipmentStateMessage.ts +1158 -822
- package/controller/comms/messages/status/HeaterStateMessage.ts +135 -135
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -448
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -36
- package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
- package/controller/comms/messages/status/RegalModbusStateMessage.ts +410 -410
- package/controller/comms/messages/status/VersionMessage.ts +103 -41
- package/controller/nixie/Nixie.ts +173 -173
- package/controller/nixie/NixieEquipment.ts +104 -104
- package/controller/nixie/bodies/Body.ts +120 -120
- package/controller/nixie/bodies/Filter.ts +135 -135
- package/controller/nixie/chemistry/ChemController.ts +2724 -2724
- package/controller/nixie/chemistry/ChemDoser.ts +806 -806
- package/controller/nixie/chemistry/Chlorinator.ts +367 -367
- package/controller/nixie/circuits/Circuit.ts +478 -478
- package/controller/nixie/heaters/Heater.ts +834 -834
- package/controller/nixie/pumps/Pump.ts +1193 -1193
- package/controller/nixie/schedules/Schedule.ts +401 -401
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +352 -352
- package/docker-compose.yml +31 -31
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +448 -436
- package/package.json +58 -58
- package/sendSocket.js +32 -32
- package/tsconfig.json +25 -25
- package/types/express-multer.d.ts +32 -32
- package/web/Server.ts +1937 -1927
- package/web/bindings/aqualinkD.json +559 -559
- package/web/bindings/influxDB.json +1066 -1066
- package/web/bindings/mqtt.json +721 -721
- package/web/bindings/mqttAlt.json +746 -746
- 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 +188 -188
- package/web/interfaces/httpInterface.ts +148 -148
- package/web/interfaces/influxInterface.ts +283 -283
- package/web/interfaces/mqttInterface.ts +695 -695
- package/web/interfaces/ruleInterface.ts +101 -87
- package/web/services/config/Config.ts +1063 -1053
- package/web/services/config/ConfigSocket.ts +0 -0
- package/web/services/state/State.ts +0 -0
- package/web/services/state/StateSocket.ts +0 -0
- package/web/services/utilities/Utilities.ts +233 -233
|
@@ -1,478 +1,478 @@
|
|
|
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 { Circuit, CircuitCollection, sys } from "../../../controller/Equipment";
|
|
7
|
-
import { CircuitState, state, ICircuitState, } 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
|
-
export class NixieCircuitCollection extends NixieEquipmentCollection<NixieCircuit> {
|
|
15
|
-
public pollingInterval: number = 2000;
|
|
16
|
-
private _pollTimer: NodeJS.Timeout = null;
|
|
17
|
-
public async deleteCircuitAsync(id: number) {
|
|
18
|
-
try {
|
|
19
|
-
for (let i = this.length - 1; i >= 0; i--) {
|
|
20
|
-
let circ = this[i];
|
|
21
|
-
if (circ.id === id) {
|
|
22
|
-
await circ.closeAsync();
|
|
23
|
-
this.splice(i, 1);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
} catch (err) { return Promise.reject(`Nixie Control Panel deleteCircuitAsync ${err.message}`); }
|
|
27
|
-
}
|
|
28
|
-
public async sendOnOffSequenceAsync(id: number, count: number | { isOn: boolean, timeout: number }[]) {
|
|
29
|
-
try {
|
|
30
|
-
let c: NixieCircuit = this.find(elem => elem.id === id) as NixieCircuit;
|
|
31
|
-
if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${id} could not be found to send sequence ${count}.`));
|
|
32
|
-
await c.sendOnOffSequenceAsync(count);
|
|
33
|
-
|
|
34
|
-
} catch (err) { return logger.error(`NCP: sendOnOffSequence: ${err.message}`); }
|
|
35
|
-
}
|
|
36
|
-
public async setServiceModeAsync() {
|
|
37
|
-
try {
|
|
38
|
-
for (let i = this.length - 1; i >= 0; i--) {
|
|
39
|
-
try {
|
|
40
|
-
let c = this[i] as NixieCircuit;
|
|
41
|
-
await c.setServiceModeAsync();
|
|
42
|
-
} catch (err) { logger.error(`Error stopping Nixie Circuit ${err}`); }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
} catch (err) { return logger.error(`NCP: setServiceModeAsync: ${err.message}`); }
|
|
46
|
-
}
|
|
47
|
-
public async setLightThemeAsync(id: number, theme: any) {
|
|
48
|
-
let c: NixieCircuit = this.find(elem => elem.id === id) as NixieCircuit;
|
|
49
|
-
if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${id} could not be found to set light theme ${theme.name}.`));
|
|
50
|
-
await c.setLightThemeAsync(theme);
|
|
51
|
-
} catch(err) { return logger.error(`NCP: sendOnOffSequence: ${err.message}`); }
|
|
52
|
-
public async setCircuitStateAsync(cstate: ICircuitState, val: boolean) {
|
|
53
|
-
try {
|
|
54
|
-
let c: NixieCircuit = this.find(elem => elem.id === cstate.id) as NixieCircuit;
|
|
55
|
-
if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${cstate.id}-${cstate.name} could not be found to set the state to ${val}.`));
|
|
56
|
-
await c.setCircuitStateAsync(cstate, val);
|
|
57
|
-
}
|
|
58
|
-
catch (err) { return logger.error(`NCP: setCircuitStateAsync ${cstate.id}-${cstate.name}: ${err.message}`); }
|
|
59
|
-
}
|
|
60
|
-
public async setCircuitAsync(circuit: Circuit, data: any) {
|
|
61
|
-
// By the time we get here we know that we are in control and this is a REMChem.
|
|
62
|
-
try {
|
|
63
|
-
let c: NixieCircuit = this.find(elem => elem.id === circuit.id) as NixieCircuit;
|
|
64
|
-
if (typeof c === 'undefined') {
|
|
65
|
-
circuit.master = 1;
|
|
66
|
-
c = new NixieCircuit(this.controlPanel, circuit);
|
|
67
|
-
this.push(c);
|
|
68
|
-
await c.setCircuitAsync(data);
|
|
69
|
-
logger.debug(`NixieController: A circuit was not found for id #${circuit.id} creating circuit`);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
await c.setCircuitAsync(data);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
catch (err) { logger.error(`setCircuitAsync: ${err.message}`); return Promise.reject(err); }
|
|
76
|
-
}
|
|
77
|
-
public async checkCircuitEggTimerExpirationAsync(cstate: ICircuitState) {
|
|
78
|
-
try {
|
|
79
|
-
let c: NixieCircuit = this.find(elem => elem.id === cstate.id) as NixieCircuit;
|
|
80
|
-
await c.checkCircuitEggTimerExpirationAsync(cstate);
|
|
81
|
-
} catch (err) { logger.error(`NCP: Error synching circuit states: ${err}`); }
|
|
82
|
-
}
|
|
83
|
-
public async initAsync(circuits: CircuitCollection) {
|
|
84
|
-
try {
|
|
85
|
-
for (let i = 0; i < circuits.length; i++) {
|
|
86
|
-
let circuit = circuits.getItemByIndex(i);
|
|
87
|
-
if (circuit.master === 1) {
|
|
88
|
-
if (typeof this.find(elem => elem.id === circuit.id) === 'undefined') {
|
|
89
|
-
logger.info(`Initializing Nixie circuit ${circuit.name}`);
|
|
90
|
-
let ncircuit = new NixieCircuit(this.controlPanel, circuit);
|
|
91
|
-
this.push(ncircuit);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (err) { return Promise.reject(logger.error(`NixieController: Circuit initAsync: ${err.message}`)); }
|
|
97
|
-
}
|
|
98
|
-
public async closeAsync() {
|
|
99
|
-
try {
|
|
100
|
-
for (let i = this.length - 1; i >= 0; i--) {
|
|
101
|
-
try {
|
|
102
|
-
await this[i].closeAsync();
|
|
103
|
-
this.splice(i, 1);
|
|
104
|
-
} catch (err) { logger.error(`Error stopping Nixie Circuit ${err}`); }
|
|
105
|
-
}
|
|
106
|
-
} catch (err) { } // Don't bail if we have an errror.
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
public async initCircuitAsync(circuit: Circuit): Promise<NixieCircuit> {
|
|
110
|
-
try {
|
|
111
|
-
let c: NixieCircuit = this.find(elem => elem.id === circuit.id) as NixieCircuit;
|
|
112
|
-
if (typeof c === 'undefined') {
|
|
113
|
-
c = new NixieCircuit(this.controlPanel, circuit);
|
|
114
|
-
this.push(c);
|
|
115
|
-
}
|
|
116
|
-
return c;
|
|
117
|
-
} catch (err) { logger.error(`initCircuitAsync: ${err.message}`); return Promise.reject(err); }
|
|
118
|
-
}
|
|
119
|
-
public async pollCircuitsAsync() {
|
|
120
|
-
let self = this;
|
|
121
|
-
try {
|
|
122
|
-
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
123
|
-
this._pollTimer = null;
|
|
124
|
-
let success = false;
|
|
125
|
-
|
|
126
|
-
} catch (err) { logger.error(`Error polling circuits: ${err.message}`); return Promise.reject(err); }
|
|
127
|
-
finally { this._pollTimer = setTimeout(async () => await self.pollCircuitsAsync(), this.pollingInterval || 10000); }
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
export class NixieCircuit extends NixieEquipment {
|
|
131
|
-
public circuit: Circuit;
|
|
132
|
-
private _sequencing = false;
|
|
133
|
-
private scheduled = false;
|
|
134
|
-
private timeOn: Timestamp;
|
|
135
|
-
private timeOff: Timestamp;
|
|
136
|
-
constructor(ncp: INixieControlPanel, circuit: Circuit) {
|
|
137
|
-
super(ncp);
|
|
138
|
-
this.circuit = circuit;
|
|
139
|
-
// Clear out the delays.
|
|
140
|
-
let cstate = state.circuits.getItemById(circuit.id);
|
|
141
|
-
cstate.startDelay = false;
|
|
142
|
-
cstate.stopDelay = false;
|
|
143
|
-
cstate.name = circuit.name;
|
|
144
|
-
cstate.type = circuit.type;
|
|
145
|
-
cstate.showInFeatures = circuit.showInFeatures;
|
|
146
|
-
}
|
|
147
|
-
public async setServiceModeAsync() {
|
|
148
|
-
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
149
|
-
await this.setCircuitStateAsync(cstate, false, false);
|
|
150
|
-
}
|
|
151
|
-
public get id(): number { return typeof this.circuit !== 'undefined' ? this.circuit.id : -1; }
|
|
152
|
-
public get eggTimerOff(): Timestamp { return typeof this.timeOn !== 'undefined' && !this.circuit.dontStop ? this.timeOn.clone().addMinutes(this.circuit.eggTimer) : undefined; }
|
|
153
|
-
public async setCircuitAsync(data: any) {
|
|
154
|
-
try {
|
|
155
|
-
let circuit = this.circuit;
|
|
156
|
-
}
|
|
157
|
-
catch (err) { logger.error(`Nixie setCircuitAsync: ${err.message}`); return Promise.reject(err); }
|
|
158
|
-
}
|
|
159
|
-
protected async setIntelliBriteThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
160
|
-
let arr = [];
|
|
161
|
-
let count = typeof theme !== 'undefined' && theme.sequence ? theme.sequence : 0;
|
|
162
|
-
|
|
163
|
-
// Removing this. No need to turn the light off first. We actually need it on to start the sequence for theme setting to work correctly when the light is starting from the off state.
|
|
164
|
-
// if (cstate.isOn) arr.push({ isOn: false, timeout: 1000 });
|
|
165
|
-
|
|
166
|
-
// Start the sequence of off/on after the light is on.
|
|
167
|
-
arr.push({ isOn: true, timeout: 100 });
|
|
168
|
-
for (let i = 0; i < count; i++) {
|
|
169
|
-
arr.push({ isOn: false, timeout: 100 });
|
|
170
|
-
arr.push({ isOn: true, timeout: 100 });
|
|
171
|
-
}
|
|
172
|
-
// Ensure light stays on long enough for the theme to stick (required for light group theme setting to function correctly).
|
|
173
|
-
// 2s was too short.
|
|
174
|
-
arr.push({ isOn: true, timeout: 3000 });
|
|
175
|
-
|
|
176
|
-
logger.debug(arr);
|
|
177
|
-
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
178
|
-
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
179
|
-
if (!res.error) {
|
|
180
|
-
this._sequencing = false;
|
|
181
|
-
await this.setCircuitStateAsync(cstate, true, false);
|
|
182
|
-
}
|
|
183
|
-
return res;
|
|
184
|
-
}
|
|
185
|
-
protected async setPoolToneThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
186
|
-
let ptheme = sys.board.valueMaps.lightThemes.findItem(cstate.lightingTheme) || { val: 0, sequence: 0 };
|
|
187
|
-
// First check to see if we are on. If we are not then we need to emit our status as if we are initializing and busy.
|
|
188
|
-
let arr = [];
|
|
189
|
-
if (ptheme.val === 0) {
|
|
190
|
-
// We don't know our previous theme so we are going to sync the lights to get a starting point.
|
|
191
|
-
arr.push({ isOn: true, timeout: 1000 }); // Turn on for 1 second
|
|
192
|
-
arr.push({ isOn: false, timeout: 5000 }); // Turn off for 5 seconds
|
|
193
|
-
arr.push({ isOn: true, timeout: 1000 });
|
|
194
|
-
ptheme = sys.board.valueMaps.lightThemes.findItem('eveningsea');
|
|
195
|
-
}
|
|
196
|
-
let count = theme.sequence - ptheme.sequence;
|
|
197
|
-
if (count < 0) count = count + 16;
|
|
198
|
-
for (let i = 0; i < count; i++) {
|
|
199
|
-
arr.push({ isOn: true, timeout: 200 });
|
|
200
|
-
arr.push({ isOn: false, timeout: 200 });
|
|
201
|
-
}
|
|
202
|
-
console.log(arr);
|
|
203
|
-
if (arr.length === 0) return new InterfaceServerResponse(200, 'Success');
|
|
204
|
-
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
205
|
-
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
206
|
-
if (!res.error) {
|
|
207
|
-
cstate.lightingTheme = ptheme.val;
|
|
208
|
-
cstate.isOn = true; // At this point the relay will be off but we want the process
|
|
209
|
-
// to assume that the relay state is not actually changing.
|
|
210
|
-
this._sequencing = false;
|
|
211
|
-
await this.setCircuitStateAsync(cstate, true, false);
|
|
212
|
-
}
|
|
213
|
-
return res;
|
|
214
|
-
}
|
|
215
|
-
protected async setWaterColorsThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
216
|
-
// RSG 2024.12.24 - This logic was aligned with the Pool Tone themes. I haven't checked if that
|
|
217
|
-
// logic is correct, but made a copy and adjusted for the watercolors themes.
|
|
218
|
-
|
|
219
|
-
let ptheme = sys.board.valueMaps.lightThemes.findItem(cstate.lightingTheme) || { val: 0, sequence: 0 };
|
|
220
|
-
// First check to see if we are on. If we are not then we need to emit our status as if we are initializing and busy.
|
|
221
|
-
let arr = [];
|
|
222
|
-
if (ptheme.val === 0) {
|
|
223
|
-
// We don't know our previous theme so we are going to sync the lights to get a starting point.
|
|
224
|
-
arr.push({ isOn: true, timeout: 1000 }); // Turn on for 1 second
|
|
225
|
-
arr.push({ isOn: false, timeout: 5000 }); // Turn off for 5 seconds
|
|
226
|
-
arr.push({ isOn: true, timeout: 1000 });
|
|
227
|
-
ptheme = sys.board.valueMaps.lightThemes.findItem('alpinewhite');
|
|
228
|
-
}
|
|
229
|
-
let count = theme.sequence - ptheme.sequence;
|
|
230
|
-
if (count < 0) count = count + 14;
|
|
231
|
-
for (let i = 0; i < count; i++) {
|
|
232
|
-
arr.push({ isOn: true, timeout: 200 });
|
|
233
|
-
arr.push({ isOn: false, timeout: 200 });
|
|
234
|
-
}
|
|
235
|
-
console.log(arr);
|
|
236
|
-
if (arr.length === 0) return new InterfaceServerResponse(200, 'Success');
|
|
237
|
-
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
238
|
-
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
239
|
-
if (!res.error) {
|
|
240
|
-
cstate.lightingTheme = ptheme.val;
|
|
241
|
-
cstate.isOn = true; // At this point the relay will be off but we want the process
|
|
242
|
-
// to assume that the relay state is not actually changing.
|
|
243
|
-
this._sequencing = false;
|
|
244
|
-
await this.setCircuitStateAsync(cstate, true, false);
|
|
245
|
-
}
|
|
246
|
-
return res;
|
|
247
|
-
}
|
|
248
|
-
protected async setColorLogicThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
249
|
-
let ptheme = sys.board.valueMaps.lightThemes.findItem(cstate.lightingTheme) || { val: 0, sequence: 0 };
|
|
250
|
-
// First check to see if we are on. If we are not then we need to emit our status as if we are initializing and busy.
|
|
251
|
-
let arr = [];
|
|
252
|
-
if (ptheme.val === 0) {
|
|
253
|
-
// We don't know our previous theme so we are going to sync the lights to get a starting point.
|
|
254
|
-
arr.push({ isOn: true, timeout: 1000 }); // Turn on for 1 second
|
|
255
|
-
arr.push({ isOn: false, timeout: 12000 }); // Turn off for 12 seconds
|
|
256
|
-
arr.push({ isOn: true, timeout: 1000 });
|
|
257
|
-
ptheme = sys.board.valueMaps.lightThemes.findItem('voodoolounge');
|
|
258
|
-
}
|
|
259
|
-
else if (!cstate.isOn) {
|
|
260
|
-
if (typeof this.timeOff === 'undefined' || new Date().getTime() - this.timeOff.getTime() > 15000) {
|
|
261
|
-
// We have been off for more than 15 seconds so we need to turn it on then wait for 17 seconds while the safety light processes.
|
|
262
|
-
arr.push({ isOn: true, timeout: 17000 }); // Crazy pants
|
|
263
|
-
}
|
|
264
|
-
else arr.push({ isOn: true, timeout: 1000 }); // Start with on
|
|
265
|
-
}
|
|
266
|
-
let count = theme.sequence - ptheme.sequence;
|
|
267
|
-
if (count < 0) count = count + 17;
|
|
268
|
-
for (let i = 0; i < count; i++) {
|
|
269
|
-
arr.push({ isOn: true, timeout: 200 }); // Use 200ms since @Crewski verified 200ms is reliable
|
|
270
|
-
arr.push({ isOn: false, timeout: 200 });
|
|
271
|
-
}
|
|
272
|
-
console.log(arr);
|
|
273
|
-
if (arr.length === 0) return new InterfaceServerResponse(200, 'Success');
|
|
274
|
-
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
275
|
-
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
276
|
-
if (!res.error) {
|
|
277
|
-
cstate.lightingTheme = ptheme.val;
|
|
278
|
-
cstate.isOn = true; // At this point the relay will be off but we want the process
|
|
279
|
-
// to assume that the relay state is not actually changing.
|
|
280
|
-
this._sequencing = false;
|
|
281
|
-
await this.setCircuitStateAsync(cstate, true, false);
|
|
282
|
-
}
|
|
283
|
-
return res;
|
|
284
|
-
}
|
|
285
|
-
// This method only dispatches to the proper light setting algorithm. Previously we assumed that simply switching on/off sequences the proper
|
|
286
|
-
// number of times was all there was but the nutcases who make these things must torture small animals.
|
|
287
|
-
public async setLightThemeAsync(theme: any) {
|
|
288
|
-
try {
|
|
289
|
-
this._sequencing = true;
|
|
290
|
-
let res = new InterfaceServerResponse(200, 'Success');
|
|
291
|
-
let arr = [];
|
|
292
|
-
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
293
|
-
let type = sys.board.valueMaps.circuitFunctions.transform(this.circuit.type);
|
|
294
|
-
// Now set the command state so that users do not get all button mashy.
|
|
295
|
-
cstate.action = sys.board.valueMaps.circuitActions.getValue('settheme');
|
|
296
|
-
cstate.emitEquipmentChange();
|
|
297
|
-
switch (type.name) {
|
|
298
|
-
case 'colorcascade':
|
|
299
|
-
case 'globrite':
|
|
300
|
-
case 'magicstream':
|
|
301
|
-
case 'intellibrite':
|
|
302
|
-
res = await this.setIntelliBriteThemeAsync(cstate, theme);
|
|
303
|
-
break;
|
|
304
|
-
case 'colorlogic':
|
|
305
|
-
res = await this.setColorLogicThemeAsync(cstate, theme);
|
|
306
|
-
break;
|
|
307
|
-
case 'watercolors':
|
|
308
|
-
res = await this.setWaterColorsThemeAsync(cstate, theme);
|
|
309
|
-
break;
|
|
310
|
-
case 'pooltone':
|
|
311
|
-
res = await this.setPoolToneThemeAsync(cstate, theme);
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
cstate.action = 0;
|
|
315
|
-
// Make sure clients know that we are done.
|
|
316
|
-
cstate.emitEquipmentChange();
|
|
317
|
-
return res;
|
|
318
|
-
} catch (err) { logger.error(`Nixie: Error setting lighting theme ${this.id} - ${theme.desc}: ${err.message}`); }
|
|
319
|
-
finally { this._sequencing = false; }
|
|
320
|
-
}
|
|
321
|
-
public async sendOnOffSequenceAsync(count: number | { isOn: boolean, timeout: number }[], timeout?: number): Promise<InterfaceServerResponse> {
|
|
322
|
-
try {
|
|
323
|
-
|
|
324
|
-
this._sequencing = true;
|
|
325
|
-
let arr = [];
|
|
326
|
-
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
327
|
-
|
|
328
|
-
if (typeof count === 'number') {
|
|
329
|
-
if (cstate.isOn) arr.push({ isOn: false, timeout: 1000 });
|
|
330
|
-
let t = typeof timeout === 'undefined' ? 100 : timeout;
|
|
331
|
-
//arr.push({ isOn: false, timeout: t }); // This may not be needed but we always need to start from off.
|
|
332
|
-
//[{ isOn: true, timeout: 1000 }, { isOn: false, timeout: 1000 }]
|
|
333
|
-
for (let i = 0; i < count; i++) {
|
|
334
|
-
if (i < count - 1) {
|
|
335
|
-
arr.push({ isOn: true, timeout: t });
|
|
336
|
-
arr.push({ isOn: false, timeout: t });
|
|
337
|
-
}
|
|
338
|
-
else arr.push({ isOn: true, timeout: 1000 });
|
|
339
|
-
}
|
|
340
|
-
console.log(arr);
|
|
341
|
-
}
|
|
342
|
-
else arr = count;
|
|
343
|
-
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
344
|
-
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
345
|
-
if (!res.error) {
|
|
346
|
-
this._sequencing = false;
|
|
347
|
-
await this.setCircuitStateAsync(cstate, true, false);
|
|
348
|
-
}
|
|
349
|
-
return res;
|
|
350
|
-
} catch (err) { logger.error(`Nixie: Error sending circuit sequence ${this.id}: ${count}`); }
|
|
351
|
-
finally { this._sequencing = false; }
|
|
352
|
-
}
|
|
353
|
-
public async setCircuitStateAsync(cstate: ICircuitState, val: boolean, scheduled: boolean = false): Promise<InterfaceServerResponse> {
|
|
354
|
-
try {
|
|
355
|
-
// Lets do some special processing here for service mode
|
|
356
|
-
if (state.mode !== 0 && val) {
|
|
357
|
-
// Always set the state to off if we are in service mode for bodies. Other circuits
|
|
358
|
-
// may actually be turned on but only if they are not one of the body circuits.
|
|
359
|
-
switch (sys.board.valueMaps.circuitFunctions.getName(this.circuit.type)) {
|
|
360
|
-
case 'pool':
|
|
361
|
-
case 'spa':
|
|
362
|
-
case 'chemrelay':
|
|
363
|
-
val = false;
|
|
364
|
-
break;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
if (val !== cstate.isOn) {
|
|
368
|
-
logger.info(`NCP: Setting Circuit ${cstate.name} to ${val}`);
|
|
369
|
-
if (cstate.isOn && val) {
|
|
370
|
-
// We are already on so lets check the egg timer and shut it off if it has expired.
|
|
371
|
-
let eggOff = this.eggTimerOff;
|
|
372
|
-
if (typeof eggOff !== 'undefined' && eggOff.getTime() <= new Date().getTime()) val = false;
|
|
373
|
-
}
|
|
374
|
-
// Check to see if we should be on by poking the schedules.
|
|
375
|
-
}
|
|
376
|
-
if (utils.isNullOrEmpty(this.circuit.connectionId) || utils.isNullOrEmpty(this.circuit.deviceBinding)) {
|
|
377
|
-
if (val && val !== cstate.isOn) {
|
|
378
|
-
sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
|
|
379
|
-
}
|
|
380
|
-
else if (!val) {
|
|
381
|
-
if (cstate.manualPriorityActive) delayMgr.cancelManualPriorityDelay(cstate.id);
|
|
382
|
-
cstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
|
|
383
|
-
}
|
|
384
|
-
cstate.isOn = val;
|
|
385
|
-
return new InterfaceServerResponse(200, 'Success');
|
|
386
|
-
}
|
|
387
|
-
if (this._sequencing) return new InterfaceServerResponse(200, 'Success');
|
|
388
|
-
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, { isOn: val, latch: val ? 10000 : undefined });
|
|
389
|
-
if (res.status.code === 200) {
|
|
390
|
-
// Set this up so we can process our egg timer.
|
|
391
|
-
if (val && val !== cstate.isOn) {
|
|
392
|
-
sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
|
|
393
|
-
switch (sys.board.valueMaps.circuitFunctions.getName(this.circuit.type)) {
|
|
394
|
-
case 'colorlogic':
|
|
395
|
-
if (!this._sequencing) {
|
|
396
|
-
// We need a little bit of special time for ColorLogic circuits.
|
|
397
|
-
let timeDiff = typeof this.timeOff === 'undefined' ? 30000 : new Date().getTime() - this.timeOff.getTime();
|
|
398
|
-
//logger.info(`Resetting ColorLogic themes ${cstate.isOn}:${val} ${cstate.lightingTheme}... ${timeDiff}`);
|
|
399
|
-
if (timeDiff > 15000) {
|
|
400
|
-
// There is this wacko thing that the lights will come on white for 15 seconds
|
|
401
|
-
// so we need to make sure they don't try to advance the theme setting during this period. We will simply set this to a holding pattern for
|
|
402
|
-
// that timeframe.
|
|
403
|
-
cstate.action = sys.board.valueMaps.circuitActions.getValue('settheme');
|
|
404
|
-
let theme = cstate.lightingTheme;
|
|
405
|
-
cstate.lightingTheme = sys.board.valueMaps.lightThemes.getValue('cloudwhite');
|
|
406
|
-
cstate.startDelay = true;
|
|
407
|
-
setTimeout(() => { cstate.startDelay = false; cstate.action = 0; cstate.lightingTheme = theme; cstate.emitEquipmentChange(); }, 17000);
|
|
408
|
-
}
|
|
409
|
-
else if (timeDiff <= 10000) {
|
|
410
|
-
// If the user turns the light back on within 10 seconds. Surprise! You are forced into the next theme.
|
|
411
|
-
let thm = sys.board.valueMaps.lightThemes.get(cstate.lightingTheme);
|
|
412
|
-
let themes = this.circuit.getLightThemes();
|
|
413
|
-
cstate.lightingTheme = thm.sequence === 17 ? themes.find(elem => elem.sequence === 1).val : themes.find(elem => elem.sequence === thm.sequence + 1).val;
|
|
414
|
-
}
|
|
415
|
-
else if (timeDiff <= 15000) {
|
|
416
|
-
// If the user turns the light back on before 15 seconds expire then we are going to do voodoo. Switch the theme to voodoolounge.
|
|
417
|
-
cstate.lightingTheme = sys.board.valueMaps.lightThemes.getValue('voodoolounge');
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
else if (!val) {
|
|
424
|
-
delayMgr.cancelManualPriorityDelays();
|
|
425
|
-
cstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
|
|
426
|
-
}
|
|
427
|
-
if (!val && cstate.isOn) this.timeOff = new Timestamp();
|
|
428
|
-
cstate.isOn = val;
|
|
429
|
-
}
|
|
430
|
-
return res;
|
|
431
|
-
} catch (err) { logger.error(`Nixie: Error setting circuit state ${cstate.id}-${cstate.name} to ${val}`); }
|
|
432
|
-
}
|
|
433
|
-
public async checkCircuitEggTimerExpirationAsync(cstate: ICircuitState) {
|
|
434
|
-
// if circuit end time is past current time, either the schedule is finished
|
|
435
|
-
// (this should already be turned off) or the egg timer has expired
|
|
436
|
-
try {
|
|
437
|
-
if (!cstate.isActive || !cstate.isOn) return;
|
|
438
|
-
if (typeof cstate.endTime !== 'undefined') {
|
|
439
|
-
if (cstate.endTime.toDate() < new Timestamp().toDate()) {
|
|
440
|
-
await sys.board.circuits.setCircuitStateAsync(cstate.id, false);
|
|
441
|
-
cstate.emitEquipmentChange();
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
} catch (err) { logger.error(`Error syncing circuit: ${err}`); }
|
|
445
|
-
}
|
|
446
|
-
private async checkHardwareStatusAsync(connectionId: string, deviceBinding: string) {
|
|
447
|
-
try {
|
|
448
|
-
let dev = await NixieEquipment.getDeviceService(connectionId, `/status/device/${deviceBinding}`);
|
|
449
|
-
return dev;
|
|
450
|
-
} catch (err) { logger.error(`Nixie Circuit checkHardwareStatusAsync: ${err.message}`); return { hasFault: true } }
|
|
451
|
-
}
|
|
452
|
-
public async validateSetupAsync(circuit: Circuit, cstate: CircuitState) {
|
|
453
|
-
try {
|
|
454
|
-
if (typeof circuit.connectionId !== 'undefined' && circuit.connectionId !== ''
|
|
455
|
-
&& typeof circuit.deviceBinding !== 'undefined' && circuit.deviceBinding !== '') {
|
|
456
|
-
try {
|
|
457
|
-
let stat = await this.checkHardwareStatusAsync(circuit.connectionId, circuit.deviceBinding);
|
|
458
|
-
// If we have a status check the return.
|
|
459
|
-
cstate.commStatus = stat.hasFault ? 1 : 0;
|
|
460
|
-
} catch (err) { cstate.commStatus = 1; }
|
|
461
|
-
}
|
|
462
|
-
else
|
|
463
|
-
cstate.commStatus = 0;
|
|
464
|
-
// The validation will be different if the circuit is on or not. So lets get that information.
|
|
465
|
-
} catch (err) { logger.error(`Nixie Error checking Circuit Hardware ${this.circuit.name}: ${err.message}`); cstate.commStatus = 1; return Promise.reject(err); }
|
|
466
|
-
}
|
|
467
|
-
public async closeAsync() {
|
|
468
|
-
try {
|
|
469
|
-
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
470
|
-
cstate.stopDelay = false;
|
|
471
|
-
cstate.startDelay = false;
|
|
472
|
-
await this.setCircuitStateAsync(cstate, false);
|
|
473
|
-
cstate.emitEquipmentChange();
|
|
474
|
-
}
|
|
475
|
-
catch (err) { logger.error(`Nixie Circuit closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
476
|
-
}
|
|
477
|
-
public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
|
|
478
|
-
}
|
|
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 { Circuit, CircuitCollection, sys } from "../../../controller/Equipment";
|
|
7
|
+
import { CircuitState, state, ICircuitState, } 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
|
+
export class NixieCircuitCollection extends NixieEquipmentCollection<NixieCircuit> {
|
|
15
|
+
public pollingInterval: number = 2000;
|
|
16
|
+
private _pollTimer: NodeJS.Timeout = null;
|
|
17
|
+
public async deleteCircuitAsync(id: number) {
|
|
18
|
+
try {
|
|
19
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
20
|
+
let circ = this[i];
|
|
21
|
+
if (circ.id === id) {
|
|
22
|
+
await circ.closeAsync();
|
|
23
|
+
this.splice(i, 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} catch (err) { return Promise.reject(`Nixie Control Panel deleteCircuitAsync ${err.message}`); }
|
|
27
|
+
}
|
|
28
|
+
public async sendOnOffSequenceAsync(id: number, count: number | { isOn: boolean, timeout: number }[]) {
|
|
29
|
+
try {
|
|
30
|
+
let c: NixieCircuit = this.find(elem => elem.id === id) as NixieCircuit;
|
|
31
|
+
if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${id} could not be found to send sequence ${count}.`));
|
|
32
|
+
await c.sendOnOffSequenceAsync(count);
|
|
33
|
+
|
|
34
|
+
} catch (err) { return logger.error(`NCP: sendOnOffSequence: ${err.message}`); }
|
|
35
|
+
}
|
|
36
|
+
public async setServiceModeAsync() {
|
|
37
|
+
try {
|
|
38
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
39
|
+
try {
|
|
40
|
+
let c = this[i] as NixieCircuit;
|
|
41
|
+
await c.setServiceModeAsync();
|
|
42
|
+
} catch (err) { logger.error(`Error stopping Nixie Circuit ${err}`); }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
} catch (err) { return logger.error(`NCP: setServiceModeAsync: ${err.message}`); }
|
|
46
|
+
}
|
|
47
|
+
public async setLightThemeAsync(id: number, theme: any) {
|
|
48
|
+
let c: NixieCircuit = this.find(elem => elem.id === id) as NixieCircuit;
|
|
49
|
+
if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${id} could not be found to set light theme ${theme.name}.`));
|
|
50
|
+
await c.setLightThemeAsync(theme);
|
|
51
|
+
} catch(err) { return logger.error(`NCP: sendOnOffSequence: ${err.message}`); }
|
|
52
|
+
public async setCircuitStateAsync(cstate: ICircuitState, val: boolean) {
|
|
53
|
+
try {
|
|
54
|
+
let c: NixieCircuit = this.find(elem => elem.id === cstate.id) as NixieCircuit;
|
|
55
|
+
if (typeof c === 'undefined') return Promise.reject(new Error(`NCP: Circuit ${cstate.id}-${cstate.name} could not be found to set the state to ${val}.`));
|
|
56
|
+
await c.setCircuitStateAsync(cstate, val);
|
|
57
|
+
}
|
|
58
|
+
catch (err) { return logger.error(`NCP: setCircuitStateAsync ${cstate.id}-${cstate.name}: ${err.message}`); }
|
|
59
|
+
}
|
|
60
|
+
public async setCircuitAsync(circuit: Circuit, data: any) {
|
|
61
|
+
// By the time we get here we know that we are in control and this is a REMChem.
|
|
62
|
+
try {
|
|
63
|
+
let c: NixieCircuit = this.find(elem => elem.id === circuit.id) as NixieCircuit;
|
|
64
|
+
if (typeof c === 'undefined') {
|
|
65
|
+
circuit.master = 1;
|
|
66
|
+
c = new NixieCircuit(this.controlPanel, circuit);
|
|
67
|
+
this.push(c);
|
|
68
|
+
await c.setCircuitAsync(data);
|
|
69
|
+
logger.debug(`NixieController: A circuit was not found for id #${circuit.id} creating circuit`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
await c.setCircuitAsync(data);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) { logger.error(`setCircuitAsync: ${err.message}`); return Promise.reject(err); }
|
|
76
|
+
}
|
|
77
|
+
public async checkCircuitEggTimerExpirationAsync(cstate: ICircuitState) {
|
|
78
|
+
try {
|
|
79
|
+
let c: NixieCircuit = this.find(elem => elem.id === cstate.id) as NixieCircuit;
|
|
80
|
+
await c.checkCircuitEggTimerExpirationAsync(cstate);
|
|
81
|
+
} catch (err) { logger.error(`NCP: Error synching circuit states: ${err}`); }
|
|
82
|
+
}
|
|
83
|
+
public async initAsync(circuits: CircuitCollection) {
|
|
84
|
+
try {
|
|
85
|
+
for (let i = 0; i < circuits.length; i++) {
|
|
86
|
+
let circuit = circuits.getItemByIndex(i);
|
|
87
|
+
if (circuit.master === 1) {
|
|
88
|
+
if (typeof this.find(elem => elem.id === circuit.id) === 'undefined') {
|
|
89
|
+
logger.info(`Initializing Nixie circuit ${circuit.name}`);
|
|
90
|
+
let ncircuit = new NixieCircuit(this.controlPanel, circuit);
|
|
91
|
+
this.push(ncircuit);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) { return Promise.reject(logger.error(`NixieController: Circuit initAsync: ${err.message}`)); }
|
|
97
|
+
}
|
|
98
|
+
public async closeAsync() {
|
|
99
|
+
try {
|
|
100
|
+
for (let i = this.length - 1; i >= 0; i--) {
|
|
101
|
+
try {
|
|
102
|
+
await this[i].closeAsync();
|
|
103
|
+
this.splice(i, 1);
|
|
104
|
+
} catch (err) { logger.error(`Error stopping Nixie Circuit ${err}`); }
|
|
105
|
+
}
|
|
106
|
+
} catch (err) { } // Don't bail if we have an errror.
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public async initCircuitAsync(circuit: Circuit): Promise<NixieCircuit> {
|
|
110
|
+
try {
|
|
111
|
+
let c: NixieCircuit = this.find(elem => elem.id === circuit.id) as NixieCircuit;
|
|
112
|
+
if (typeof c === 'undefined') {
|
|
113
|
+
c = new NixieCircuit(this.controlPanel, circuit);
|
|
114
|
+
this.push(c);
|
|
115
|
+
}
|
|
116
|
+
return c;
|
|
117
|
+
} catch (err) { logger.error(`initCircuitAsync: ${err.message}`); return Promise.reject(err); }
|
|
118
|
+
}
|
|
119
|
+
public async pollCircuitsAsync() {
|
|
120
|
+
let self = this;
|
|
121
|
+
try {
|
|
122
|
+
if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
|
|
123
|
+
this._pollTimer = null;
|
|
124
|
+
let success = false;
|
|
125
|
+
|
|
126
|
+
} catch (err) { logger.error(`Error polling circuits: ${err.message}`); return Promise.reject(err); }
|
|
127
|
+
finally { this._pollTimer = setTimeout(async () => await self.pollCircuitsAsync(), this.pollingInterval || 10000); }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export class NixieCircuit extends NixieEquipment {
|
|
131
|
+
public circuit: Circuit;
|
|
132
|
+
private _sequencing = false;
|
|
133
|
+
private scheduled = false;
|
|
134
|
+
private timeOn: Timestamp;
|
|
135
|
+
private timeOff: Timestamp;
|
|
136
|
+
constructor(ncp: INixieControlPanel, circuit: Circuit) {
|
|
137
|
+
super(ncp);
|
|
138
|
+
this.circuit = circuit;
|
|
139
|
+
// Clear out the delays.
|
|
140
|
+
let cstate = state.circuits.getItemById(circuit.id);
|
|
141
|
+
cstate.startDelay = false;
|
|
142
|
+
cstate.stopDelay = false;
|
|
143
|
+
cstate.name = circuit.name;
|
|
144
|
+
cstate.type = circuit.type;
|
|
145
|
+
cstate.showInFeatures = circuit.showInFeatures;
|
|
146
|
+
}
|
|
147
|
+
public async setServiceModeAsync() {
|
|
148
|
+
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
149
|
+
await this.setCircuitStateAsync(cstate, false, false);
|
|
150
|
+
}
|
|
151
|
+
public get id(): number { return typeof this.circuit !== 'undefined' ? this.circuit.id : -1; }
|
|
152
|
+
public get eggTimerOff(): Timestamp { return typeof this.timeOn !== 'undefined' && !this.circuit.dontStop ? this.timeOn.clone().addMinutes(this.circuit.eggTimer) : undefined; }
|
|
153
|
+
public async setCircuitAsync(data: any) {
|
|
154
|
+
try {
|
|
155
|
+
let circuit = this.circuit;
|
|
156
|
+
}
|
|
157
|
+
catch (err) { logger.error(`Nixie setCircuitAsync: ${err.message}`); return Promise.reject(err); }
|
|
158
|
+
}
|
|
159
|
+
protected async setIntelliBriteThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
160
|
+
let arr = [];
|
|
161
|
+
let count = typeof theme !== 'undefined' && theme.sequence ? theme.sequence : 0;
|
|
162
|
+
|
|
163
|
+
// Removing this. No need to turn the light off first. We actually need it on to start the sequence for theme setting to work correctly when the light is starting from the off state.
|
|
164
|
+
// if (cstate.isOn) arr.push({ isOn: false, timeout: 1000 });
|
|
165
|
+
|
|
166
|
+
// Start the sequence of off/on after the light is on.
|
|
167
|
+
arr.push({ isOn: true, timeout: 100 });
|
|
168
|
+
for (let i = 0; i < count; i++) {
|
|
169
|
+
arr.push({ isOn: false, timeout: 100 });
|
|
170
|
+
arr.push({ isOn: true, timeout: 100 });
|
|
171
|
+
}
|
|
172
|
+
// Ensure light stays on long enough for the theme to stick (required for light group theme setting to function correctly).
|
|
173
|
+
// 2s was too short.
|
|
174
|
+
arr.push({ isOn: true, timeout: 3000 });
|
|
175
|
+
|
|
176
|
+
logger.debug(arr);
|
|
177
|
+
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
178
|
+
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
179
|
+
if (!res.error) {
|
|
180
|
+
this._sequencing = false;
|
|
181
|
+
await this.setCircuitStateAsync(cstate, true, false);
|
|
182
|
+
}
|
|
183
|
+
return res;
|
|
184
|
+
}
|
|
185
|
+
protected async setPoolToneThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
186
|
+
let ptheme = sys.board.valueMaps.lightThemes.findItem(cstate.lightingTheme) || { val: 0, sequence: 0 };
|
|
187
|
+
// First check to see if we are on. If we are not then we need to emit our status as if we are initializing and busy.
|
|
188
|
+
let arr = [];
|
|
189
|
+
if (ptheme.val === 0) {
|
|
190
|
+
// We don't know our previous theme so we are going to sync the lights to get a starting point.
|
|
191
|
+
arr.push({ isOn: true, timeout: 1000 }); // Turn on for 1 second
|
|
192
|
+
arr.push({ isOn: false, timeout: 5000 }); // Turn off for 5 seconds
|
|
193
|
+
arr.push({ isOn: true, timeout: 1000 });
|
|
194
|
+
ptheme = sys.board.valueMaps.lightThemes.findItem('eveningsea');
|
|
195
|
+
}
|
|
196
|
+
let count = theme.sequence - ptheme.sequence;
|
|
197
|
+
if (count < 0) count = count + 16;
|
|
198
|
+
for (let i = 0; i < count; i++) {
|
|
199
|
+
arr.push({ isOn: true, timeout: 200 });
|
|
200
|
+
arr.push({ isOn: false, timeout: 200 });
|
|
201
|
+
}
|
|
202
|
+
console.log(arr);
|
|
203
|
+
if (arr.length === 0) return new InterfaceServerResponse(200, 'Success');
|
|
204
|
+
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
205
|
+
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
206
|
+
if (!res.error) {
|
|
207
|
+
cstate.lightingTheme = ptheme.val;
|
|
208
|
+
cstate.isOn = true; // At this point the relay will be off but we want the process
|
|
209
|
+
// to assume that the relay state is not actually changing.
|
|
210
|
+
this._sequencing = false;
|
|
211
|
+
await this.setCircuitStateAsync(cstate, true, false);
|
|
212
|
+
}
|
|
213
|
+
return res;
|
|
214
|
+
}
|
|
215
|
+
protected async setWaterColorsThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
216
|
+
// RSG 2024.12.24 - This logic was aligned with the Pool Tone themes. I haven't checked if that
|
|
217
|
+
// logic is correct, but made a copy and adjusted for the watercolors themes.
|
|
218
|
+
|
|
219
|
+
let ptheme = sys.board.valueMaps.lightThemes.findItem(cstate.lightingTheme) || { val: 0, sequence: 0 };
|
|
220
|
+
// First check to see if we are on. If we are not then we need to emit our status as if we are initializing and busy.
|
|
221
|
+
let arr = [];
|
|
222
|
+
if (ptheme.val === 0) {
|
|
223
|
+
// We don't know our previous theme so we are going to sync the lights to get a starting point.
|
|
224
|
+
arr.push({ isOn: true, timeout: 1000 }); // Turn on for 1 second
|
|
225
|
+
arr.push({ isOn: false, timeout: 5000 }); // Turn off for 5 seconds
|
|
226
|
+
arr.push({ isOn: true, timeout: 1000 });
|
|
227
|
+
ptheme = sys.board.valueMaps.lightThemes.findItem('alpinewhite');
|
|
228
|
+
}
|
|
229
|
+
let count = theme.sequence - ptheme.sequence;
|
|
230
|
+
if (count < 0) count = count + 14;
|
|
231
|
+
for (let i = 0; i < count; i++) {
|
|
232
|
+
arr.push({ isOn: true, timeout: 200 });
|
|
233
|
+
arr.push({ isOn: false, timeout: 200 });
|
|
234
|
+
}
|
|
235
|
+
console.log(arr);
|
|
236
|
+
if (arr.length === 0) return new InterfaceServerResponse(200, 'Success');
|
|
237
|
+
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
238
|
+
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
239
|
+
if (!res.error) {
|
|
240
|
+
cstate.lightingTheme = ptheme.val;
|
|
241
|
+
cstate.isOn = true; // At this point the relay will be off but we want the process
|
|
242
|
+
// to assume that the relay state is not actually changing.
|
|
243
|
+
this._sequencing = false;
|
|
244
|
+
await this.setCircuitStateAsync(cstate, true, false);
|
|
245
|
+
}
|
|
246
|
+
return res;
|
|
247
|
+
}
|
|
248
|
+
protected async setColorLogicThemeAsync(cstate: CircuitState, theme: any): Promise<InterfaceServerResponse> {
|
|
249
|
+
let ptheme = sys.board.valueMaps.lightThemes.findItem(cstate.lightingTheme) || { val: 0, sequence: 0 };
|
|
250
|
+
// First check to see if we are on. If we are not then we need to emit our status as if we are initializing and busy.
|
|
251
|
+
let arr = [];
|
|
252
|
+
if (ptheme.val === 0) {
|
|
253
|
+
// We don't know our previous theme so we are going to sync the lights to get a starting point.
|
|
254
|
+
arr.push({ isOn: true, timeout: 1000 }); // Turn on for 1 second
|
|
255
|
+
arr.push({ isOn: false, timeout: 12000 }); // Turn off for 12 seconds
|
|
256
|
+
arr.push({ isOn: true, timeout: 1000 });
|
|
257
|
+
ptheme = sys.board.valueMaps.lightThemes.findItem('voodoolounge');
|
|
258
|
+
}
|
|
259
|
+
else if (!cstate.isOn) {
|
|
260
|
+
if (typeof this.timeOff === 'undefined' || new Date().getTime() - this.timeOff.getTime() > 15000) {
|
|
261
|
+
// We have been off for more than 15 seconds so we need to turn it on then wait for 17 seconds while the safety light processes.
|
|
262
|
+
arr.push({ isOn: true, timeout: 17000 }); // Crazy pants
|
|
263
|
+
}
|
|
264
|
+
else arr.push({ isOn: true, timeout: 1000 }); // Start with on
|
|
265
|
+
}
|
|
266
|
+
let count = theme.sequence - ptheme.sequence;
|
|
267
|
+
if (count < 0) count = count + 17;
|
|
268
|
+
for (let i = 0; i < count; i++) {
|
|
269
|
+
arr.push({ isOn: true, timeout: 200 }); // Use 200ms since @Crewski verified 200ms is reliable
|
|
270
|
+
arr.push({ isOn: false, timeout: 200 });
|
|
271
|
+
}
|
|
272
|
+
console.log(arr);
|
|
273
|
+
if (arr.length === 0) return new InterfaceServerResponse(200, 'Success');
|
|
274
|
+
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
275
|
+
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
276
|
+
if (!res.error) {
|
|
277
|
+
cstate.lightingTheme = ptheme.val;
|
|
278
|
+
cstate.isOn = true; // At this point the relay will be off but we want the process
|
|
279
|
+
// to assume that the relay state is not actually changing.
|
|
280
|
+
this._sequencing = false;
|
|
281
|
+
await this.setCircuitStateAsync(cstate, true, false);
|
|
282
|
+
}
|
|
283
|
+
return res;
|
|
284
|
+
}
|
|
285
|
+
// This method only dispatches to the proper light setting algorithm. Previously we assumed that simply switching on/off sequences the proper
|
|
286
|
+
// number of times was all there was but the nutcases who make these things must torture small animals.
|
|
287
|
+
public async setLightThemeAsync(theme: any) {
|
|
288
|
+
try {
|
|
289
|
+
this._sequencing = true;
|
|
290
|
+
let res = new InterfaceServerResponse(200, 'Success');
|
|
291
|
+
let arr = [];
|
|
292
|
+
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
293
|
+
let type = sys.board.valueMaps.circuitFunctions.transform(this.circuit.type);
|
|
294
|
+
// Now set the command state so that users do not get all button mashy.
|
|
295
|
+
cstate.action = sys.board.valueMaps.circuitActions.getValue('settheme');
|
|
296
|
+
cstate.emitEquipmentChange();
|
|
297
|
+
switch (type.name) {
|
|
298
|
+
case 'colorcascade':
|
|
299
|
+
case 'globrite':
|
|
300
|
+
case 'magicstream':
|
|
301
|
+
case 'intellibrite':
|
|
302
|
+
res = await this.setIntelliBriteThemeAsync(cstate, theme);
|
|
303
|
+
break;
|
|
304
|
+
case 'colorlogic':
|
|
305
|
+
res = await this.setColorLogicThemeAsync(cstate, theme);
|
|
306
|
+
break;
|
|
307
|
+
case 'watercolors':
|
|
308
|
+
res = await this.setWaterColorsThemeAsync(cstate, theme);
|
|
309
|
+
break;
|
|
310
|
+
case 'pooltone':
|
|
311
|
+
res = await this.setPoolToneThemeAsync(cstate, theme);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
cstate.action = 0;
|
|
315
|
+
// Make sure clients know that we are done.
|
|
316
|
+
cstate.emitEquipmentChange();
|
|
317
|
+
return res;
|
|
318
|
+
} catch (err) { logger.error(`Nixie: Error setting lighting theme ${this.id} - ${theme.desc}: ${err.message}`); }
|
|
319
|
+
finally { this._sequencing = false; }
|
|
320
|
+
}
|
|
321
|
+
public async sendOnOffSequenceAsync(count: number | { isOn: boolean, timeout: number }[], timeout?: number): Promise<InterfaceServerResponse> {
|
|
322
|
+
try {
|
|
323
|
+
|
|
324
|
+
this._sequencing = true;
|
|
325
|
+
let arr = [];
|
|
326
|
+
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
327
|
+
|
|
328
|
+
if (typeof count === 'number') {
|
|
329
|
+
if (cstate.isOn) arr.push({ isOn: false, timeout: 1000 });
|
|
330
|
+
let t = typeof timeout === 'undefined' ? 100 : timeout;
|
|
331
|
+
//arr.push({ isOn: false, timeout: t }); // This may not be needed but we always need to start from off.
|
|
332
|
+
//[{ isOn: true, timeout: 1000 }, { isOn: false, timeout: 1000 }]
|
|
333
|
+
for (let i = 0; i < count; i++) {
|
|
334
|
+
if (i < count - 1) {
|
|
335
|
+
arr.push({ isOn: true, timeout: t });
|
|
336
|
+
arr.push({ isOn: false, timeout: t });
|
|
337
|
+
}
|
|
338
|
+
else arr.push({ isOn: true, timeout: 1000 });
|
|
339
|
+
}
|
|
340
|
+
console.log(arr);
|
|
341
|
+
}
|
|
342
|
+
else arr = count;
|
|
343
|
+
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, arr, 60000);
|
|
344
|
+
// Even though we ended with on we need to make sure that the relay stays on now that we are done.
|
|
345
|
+
if (!res.error) {
|
|
346
|
+
this._sequencing = false;
|
|
347
|
+
await this.setCircuitStateAsync(cstate, true, false);
|
|
348
|
+
}
|
|
349
|
+
return res;
|
|
350
|
+
} catch (err) { logger.error(`Nixie: Error sending circuit sequence ${this.id}: ${count}`); }
|
|
351
|
+
finally { this._sequencing = false; }
|
|
352
|
+
}
|
|
353
|
+
public async setCircuitStateAsync(cstate: ICircuitState, val: boolean, scheduled: boolean = false): Promise<InterfaceServerResponse> {
|
|
354
|
+
try {
|
|
355
|
+
// Lets do some special processing here for service mode
|
|
356
|
+
if (state.mode !== 0 && val) {
|
|
357
|
+
// Always set the state to off if we are in service mode for bodies. Other circuits
|
|
358
|
+
// may actually be turned on but only if they are not one of the body circuits.
|
|
359
|
+
switch (sys.board.valueMaps.circuitFunctions.getName(this.circuit.type)) {
|
|
360
|
+
case 'pool':
|
|
361
|
+
case 'spa':
|
|
362
|
+
case 'chemrelay':
|
|
363
|
+
val = false;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (val !== cstate.isOn) {
|
|
368
|
+
logger.info(`NCP: Setting Circuit ${cstate.name} to ${val}`);
|
|
369
|
+
if (cstate.isOn && val) {
|
|
370
|
+
// We are already on so lets check the egg timer and shut it off if it has expired.
|
|
371
|
+
let eggOff = this.eggTimerOff;
|
|
372
|
+
if (typeof eggOff !== 'undefined' && eggOff.getTime() <= new Date().getTime()) val = false;
|
|
373
|
+
}
|
|
374
|
+
// Check to see if we should be on by poking the schedules.
|
|
375
|
+
}
|
|
376
|
+
if (utils.isNullOrEmpty(this.circuit.connectionId) || utils.isNullOrEmpty(this.circuit.deviceBinding)) {
|
|
377
|
+
if (val && val !== cstate.isOn) {
|
|
378
|
+
sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
|
|
379
|
+
}
|
|
380
|
+
else if (!val) {
|
|
381
|
+
if (cstate.manualPriorityActive) delayMgr.cancelManualPriorityDelay(cstate.id);
|
|
382
|
+
cstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
|
|
383
|
+
}
|
|
384
|
+
cstate.isOn = val;
|
|
385
|
+
return new InterfaceServerResponse(200, 'Success');
|
|
386
|
+
}
|
|
387
|
+
if (this._sequencing) return new InterfaceServerResponse(200, 'Success');
|
|
388
|
+
let res = await NixieEquipment.putDeviceService(this.circuit.connectionId, `/state/device/${this.circuit.deviceBinding}`, { isOn: val, latch: val ? 10000 : undefined });
|
|
389
|
+
if (res.status.code === 200) {
|
|
390
|
+
// Set this up so we can process our egg timer.
|
|
391
|
+
if (val && val !== cstate.isOn) {
|
|
392
|
+
sys.board.circuits.setEndTime(sys.circuits.getInterfaceById(cstate.id), cstate, val);
|
|
393
|
+
switch (sys.board.valueMaps.circuitFunctions.getName(this.circuit.type)) {
|
|
394
|
+
case 'colorlogic':
|
|
395
|
+
if (!this._sequencing) {
|
|
396
|
+
// We need a little bit of special time for ColorLogic circuits.
|
|
397
|
+
let timeDiff = typeof this.timeOff === 'undefined' ? 30000 : new Date().getTime() - this.timeOff.getTime();
|
|
398
|
+
//logger.info(`Resetting ColorLogic themes ${cstate.isOn}:${val} ${cstate.lightingTheme}... ${timeDiff}`);
|
|
399
|
+
if (timeDiff > 15000) {
|
|
400
|
+
// There is this wacko thing that the lights will come on white for 15 seconds
|
|
401
|
+
// so we need to make sure they don't try to advance the theme setting during this period. We will simply set this to a holding pattern for
|
|
402
|
+
// that timeframe.
|
|
403
|
+
cstate.action = sys.board.valueMaps.circuitActions.getValue('settheme');
|
|
404
|
+
let theme = cstate.lightingTheme;
|
|
405
|
+
cstate.lightingTheme = sys.board.valueMaps.lightThemes.getValue('cloudwhite');
|
|
406
|
+
cstate.startDelay = true;
|
|
407
|
+
setTimeout(() => { cstate.startDelay = false; cstate.action = 0; cstate.lightingTheme = theme; cstate.emitEquipmentChange(); }, 17000);
|
|
408
|
+
}
|
|
409
|
+
else if (timeDiff <= 10000) {
|
|
410
|
+
// If the user turns the light back on within 10 seconds. Surprise! You are forced into the next theme.
|
|
411
|
+
let thm = sys.board.valueMaps.lightThemes.get(cstate.lightingTheme);
|
|
412
|
+
let themes = this.circuit.getLightThemes();
|
|
413
|
+
cstate.lightingTheme = thm.sequence === 17 ? themes.find(elem => elem.sequence === 1).val : themes.find(elem => elem.sequence === thm.sequence + 1).val;
|
|
414
|
+
}
|
|
415
|
+
else if (timeDiff <= 15000) {
|
|
416
|
+
// If the user turns the light back on before 15 seconds expire then we are going to do voodoo. Switch the theme to voodoolounge.
|
|
417
|
+
cstate.lightingTheme = sys.board.valueMaps.lightThemes.getValue('voodoolounge');
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
else if (!val) {
|
|
424
|
+
delayMgr.cancelManualPriorityDelays();
|
|
425
|
+
cstate.manualPriorityActive = false; // if the delay was previously cancelled, still need to turn this off
|
|
426
|
+
}
|
|
427
|
+
if (!val && cstate.isOn) this.timeOff = new Timestamp();
|
|
428
|
+
cstate.isOn = val;
|
|
429
|
+
}
|
|
430
|
+
return res;
|
|
431
|
+
} catch (err) { logger.error(`Nixie: Error setting circuit state ${cstate.id}-${cstate.name} to ${val}`); }
|
|
432
|
+
}
|
|
433
|
+
public async checkCircuitEggTimerExpirationAsync(cstate: ICircuitState) {
|
|
434
|
+
// if circuit end time is past current time, either the schedule is finished
|
|
435
|
+
// (this should already be turned off) or the egg timer has expired
|
|
436
|
+
try {
|
|
437
|
+
if (!cstate.isActive || !cstate.isOn) return;
|
|
438
|
+
if (typeof cstate.endTime !== 'undefined') {
|
|
439
|
+
if (cstate.endTime.toDate() < new Timestamp().toDate()) {
|
|
440
|
+
await sys.board.circuits.setCircuitStateAsync(cstate.id, false);
|
|
441
|
+
cstate.emitEquipmentChange();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} catch (err) { logger.error(`Error syncing circuit: ${err}`); }
|
|
445
|
+
}
|
|
446
|
+
private async checkHardwareStatusAsync(connectionId: string, deviceBinding: string) {
|
|
447
|
+
try {
|
|
448
|
+
let dev = await NixieEquipment.getDeviceService(connectionId, `/status/device/${deviceBinding}`);
|
|
449
|
+
return dev;
|
|
450
|
+
} catch (err) { logger.error(`Nixie Circuit checkHardwareStatusAsync: ${err.message}`); return { hasFault: true } }
|
|
451
|
+
}
|
|
452
|
+
public async validateSetupAsync(circuit: Circuit, cstate: CircuitState) {
|
|
453
|
+
try {
|
|
454
|
+
if (typeof circuit.connectionId !== 'undefined' && circuit.connectionId !== ''
|
|
455
|
+
&& typeof circuit.deviceBinding !== 'undefined' && circuit.deviceBinding !== '') {
|
|
456
|
+
try {
|
|
457
|
+
let stat = await this.checkHardwareStatusAsync(circuit.connectionId, circuit.deviceBinding);
|
|
458
|
+
// If we have a status check the return.
|
|
459
|
+
cstate.commStatus = stat.hasFault ? 1 : 0;
|
|
460
|
+
} catch (err) { cstate.commStatus = 1; }
|
|
461
|
+
}
|
|
462
|
+
else
|
|
463
|
+
cstate.commStatus = 0;
|
|
464
|
+
// The validation will be different if the circuit is on or not. So lets get that information.
|
|
465
|
+
} catch (err) { logger.error(`Nixie Error checking Circuit Hardware ${this.circuit.name}: ${err.message}`); cstate.commStatus = 1; return Promise.reject(err); }
|
|
466
|
+
}
|
|
467
|
+
public async closeAsync() {
|
|
468
|
+
try {
|
|
469
|
+
let cstate = state.circuits.getItemById(this.circuit.id);
|
|
470
|
+
cstate.stopDelay = false;
|
|
471
|
+
cstate.startDelay = false;
|
|
472
|
+
await this.setCircuitStateAsync(cstate, false);
|
|
473
|
+
cstate.emitEquipmentChange();
|
|
474
|
+
}
|
|
475
|
+
catch (err) { logger.error(`Nixie Circuit closeAsync: ${err.message}`); return Promise.reject(err); }
|
|
476
|
+
}
|
|
477
|
+
public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
|
|
478
|
+
}
|