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.
- 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 -0
- package/.github/workflows/ghcr-publish.yml +67 -0
- package/AGENTS.md +597 -0
- package/CONTRIBUTING.md +74 -74
- package/Changelog +292 -257
- package/Dockerfile +62 -19
- package/Gruntfile.js +40 -40
- package/LICENSE +661 -661
- package/README.md +318 -191
- 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 +57 -7
- package/config/VersionCheck.ts +63 -35
- 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 -3690
- 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 -1929
- package/controller/boards/SunTouchBoard.ts +400 -400
- package/controller/boards/SystemBoard.ts +5268 -5268
- package/controller/comms/Comms.ts +1272 -1214
- package/controller/comms/ScreenLogic.ts +1665 -1665
- package/controller/comms/messages/Messages.ts +1433 -1243
- 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 +411 -0
- 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 +1194 -996
- package/controller/nixie/schedules/Schedule.ts +401 -401
- package/controller/nixie/valves/Valve.ts +170 -170
- package/defaultConfig.json +352 -347
- package/docker-compose.yml +32 -0
- package/logger/DataLogger.ts +448 -448
- package/logger/Logger.ts +448 -436
- package/package.json +58 -60
- package/sendSocket.js +32 -32
- package/tsconfig.json +25 -25
- package/types/express-multer.d.ts +32 -0
- 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
- package/.github/workflows/docker-publish-njsPC-linux.yml +0 -50
|
@@ -1,1054 +1,1064 @@
|
|
|
1
|
-
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
-
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
-
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
4
|
-
|
|
5
|
-
This program is free software: you can redistribute it and/or modify
|
|
6
|
-
it under the terms of the GNU Affero General Public License as
|
|
7
|
-
published by the Free Software Foundation, either version 3 of the
|
|
8
|
-
License, or (at your option) any later version.
|
|
9
|
-
|
|
10
|
-
This program is distributed in the hope that it will be useful,
|
|
11
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
-
GNU Affero General Public License for more details.
|
|
14
|
-
|
|
15
|
-
You should have received a copy of the GNU Affero General Public License
|
|
16
|
-
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
import * as fs from "fs";
|
|
19
|
-
import * as path from "path";
|
|
20
|
-
import * as express from "express";
|
|
21
|
-
import * as extend from 'extend';
|
|
22
|
-
import * as multer from 'multer';
|
|
23
|
-
import { sys, LightGroup, ControllerType, Pump, Valve, Body, General, Circuit, ICircuit, Feature, CircuitGroup, CustomNameCollection, Schedule, Chlorinator, Heater, Screenlogic } from "../../../controller/Equipment";
|
|
24
|
-
import { config } from "../../../config/Config";
|
|
25
|
-
import { logger } from "../../../logger/Logger";
|
|
26
|
-
import { utils } from "../../../controller/Constants";
|
|
27
|
-
import { ServiceProcessError } from "../../../controller/Errors";
|
|
28
|
-
import { state } from "../../../controller/State";
|
|
29
|
-
import { stopPacketCaptureAsync, startPacketCapture } from '../../../app';
|
|
30
|
-
import { conn } from "../../../controller/comms/Comms";
|
|
31
|
-
import { webApp, BackupFile, RestoreFile } from "../../Server";
|
|
32
|
-
import { release } from "os";
|
|
33
|
-
import { ScreenLogicComms, sl } from "../../../controller/comms/ScreenLogic";
|
|
34
|
-
import { screenlogic } from "node-screenlogic";
|
|
35
|
-
|
|
36
|
-
export class ConfigRoute {
|
|
37
|
-
public static initRoutes(app: express.Application) {
|
|
38
|
-
app.get('/config/body/:body/heatModes', (req, res) => {
|
|
39
|
-
return res.status(200).send(sys.bodies.getItemById(parseInt(req.params.body, 10)).getHeatModes());
|
|
40
|
-
});
|
|
41
|
-
app.get('/config/circuit/names', (req, res) => {
|
|
42
|
-
let circuitNames = sys.board.circuits.getCircuitNames();
|
|
43
|
-
return res.status(200).send(circuitNames);
|
|
44
|
-
});
|
|
45
|
-
app.get('/config/circuit/references', (req, res) => {
|
|
46
|
-
let circuits = typeof req.query.circuits === 'undefined' || utils.makeBool(req.query.circuits);
|
|
47
|
-
let features = typeof req.query.features === 'undefined' || utils.makeBool(req.query.features);
|
|
48
|
-
let groups = typeof req.query.features === 'undefined' || utils.makeBool(req.query.groups);
|
|
49
|
-
let virtual = typeof req.query.virtual === 'undefined' || utils.makeBool(req.query.virtual);
|
|
50
|
-
return res.status(200).send(sys.board.circuits.getCircuitReferences(circuits, features, virtual, groups));
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
/******* CONFIGURATION PICK LISTS/REFERENCES and VALIDATION PARAMETERS *********/
|
|
54
|
-
/// Returns an object that contains the general options for setting up the panel.
|
|
55
|
-
app.get('/config/options/general', (req, res) => {
|
|
56
|
-
let opts = {
|
|
57
|
-
countries: sys.board.valueMaps.countries.toArray(),
|
|
58
|
-
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
59
|
-
timeZones: sys.board.valueMaps.timeZones.toArray(),
|
|
60
|
-
clockSources: sys.board.valueMaps.clockSources.toArray(),
|
|
61
|
-
clockModes: sys.board.valueMaps.clockModes.toArray(),
|
|
62
|
-
pool: sys.general.get(true),
|
|
63
|
-
sensors: sys.board.system.getSensors(),
|
|
64
|
-
systemUnits: sys.board.valueMaps.systemUnits.toArray()
|
|
65
|
-
};
|
|
66
|
-
return res.status(200).send(opts);
|
|
67
|
-
});
|
|
68
|
-
app.get('/config/options/rs485', async (req, res, next) => {
|
|
69
|
-
try {
|
|
70
|
-
let opts = { ports: [], local: [], screenlogic: {} }
|
|
71
|
-
let cfg = config.getSection('controller');
|
|
72
|
-
for (let section in cfg) {
|
|
73
|
-
if (section.startsWith('comms')) {
|
|
74
|
-
let cport = extend(true, { enabled: false, netConnect: false, mock: false }, cfg[section]);
|
|
75
|
-
let port = conn.findPortById(cport.portId || 0);
|
|
76
|
-
if (typeof cport.type === 'undefined'){
|
|
77
|
-
cport.type = cport.netConnect ? 'netConnect' : cport.mock ? 'mock' : 'local'
|
|
78
|
-
}
|
|
79
|
-
if (typeof port !== 'undefined') cport.stats = port.stats;
|
|
80
|
-
if (port.portId === 0 && port.type === 'screenlogic') {
|
|
81
|
-
cport.screenlogic.stats = sl.stats;
|
|
82
|
-
}
|
|
83
|
-
opts.ports.push(cport);
|
|
84
|
-
}
|
|
85
|
-
// if (section.startsWith('screenlogic')){
|
|
86
|
-
// let screenlogic = cfg[section];
|
|
87
|
-
// screenlogic.types = [{ val: 'local', name: 'Local', desc: 'Local Screenlogic' }, { val: 'remote', name: 'Remote', desc: 'Remote Screenlogic' }];
|
|
88
|
-
// screenlogic.stats = sl.stats;
|
|
89
|
-
// opts.screenlogic = screenlogic;
|
|
90
|
-
// }
|
|
91
|
-
}
|
|
92
|
-
opts.local = await conn.getLocalPortsAsync() || [];
|
|
93
|
-
return res.status(200).send(opts);
|
|
94
|
-
} catch (err) { next(err); }
|
|
95
|
-
});
|
|
96
|
-
// app.get('/config/options/screenlogic', async (req, res, next) => {
|
|
97
|
-
// try {
|
|
98
|
-
// let cfg = config.getSection('controller.screenlogic');
|
|
99
|
-
// let data = {
|
|
100
|
-
// cfg,
|
|
101
|
-
// types: [{ val: 'local', name: 'Local', desc: 'Local Screenlogic' }, { val: 'remote', name: 'Remote', desc: 'Remote Screenlogic' }]
|
|
102
|
-
// }
|
|
103
|
-
// return res.status(200).send(data);
|
|
104
|
-
// } catch (err) { next(err); }
|
|
105
|
-
// });
|
|
106
|
-
app.get('/config/options/screenlogic/search', async (req, res, next) => {
|
|
107
|
-
try {
|
|
108
|
-
let localUnits = await ScreenLogicComms.searchAsync();
|
|
109
|
-
return res.status(200).send(localUnits);
|
|
110
|
-
} catch (err) { next(err); }
|
|
111
|
-
});
|
|
112
|
-
app.get('/config/options/circuits', async (req, res, next) => {
|
|
113
|
-
try {
|
|
114
|
-
let opts = {
|
|
115
|
-
maxCircuits: sys.equipment.maxCircuits,
|
|
116
|
-
equipmentIds: sys.equipment.equipmentIds.circuits,
|
|
117
|
-
invalidIds: sys.board.equipmentIds.invalidIds.get(),
|
|
118
|
-
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
119
|
-
functions: sys.board.circuits.getCircuitFunctions(),
|
|
120
|
-
circuits: sys.circuits.get(),
|
|
121
|
-
controllerType: sys.controllerType,
|
|
122
|
-
servers: await sys.ncp.getREMServers()
|
|
123
|
-
};
|
|
124
|
-
return res.status(200).send(opts);
|
|
125
|
-
} catch (err) { next(err); }
|
|
126
|
-
});
|
|
127
|
-
app.get('/config/options/circuitGroups', (req, res) => {
|
|
128
|
-
let opts = {
|
|
129
|
-
maxCircuitGroups: sys.equipment.maxCircuitGroups,
|
|
130
|
-
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
131
|
-
circuits: sys.board.circuits.getCircuitReferences(true, true, false),
|
|
132
|
-
circuitGroups: sys.circuitGroups.get(),
|
|
133
|
-
circuitStates: sys.board.valueMaps.groupCircuitStates.toArray()
|
|
134
|
-
};
|
|
135
|
-
return res.status(200).send(opts);
|
|
136
|
-
});
|
|
137
|
-
app.get('/config/options/lightGroups', (req, res) => {
|
|
138
|
-
let opts = {
|
|
139
|
-
maxLightGroups: sys.equipment.maxLightGroups,
|
|
140
|
-
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
141
|
-
themes: sys.board.circuits.getLightThemes(),
|
|
142
|
-
colors: sys.board.valueMaps.lightColors.toArray(),
|
|
143
|
-
circuits: sys.board.circuits.getLightReferences(),
|
|
144
|
-
lightGroups: sys.lightGroups.get(),
|
|
145
|
-
functions: sys.board.circuits.getCircuitFunctions()
|
|
146
|
-
};
|
|
147
|
-
return res.status(200).send(opts);
|
|
148
|
-
});
|
|
149
|
-
app.get('/config/options/features', (req, res) => {
|
|
150
|
-
let opts = {
|
|
151
|
-
maxFeatures: sys.equipment.maxFeatures,
|
|
152
|
-
invalidIds: sys.board.equipmentIds.invalidIds.get(),
|
|
153
|
-
equipmentIds: sys.equipment.equipmentIds.features,
|
|
154
|
-
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
155
|
-
functions: sys.board.features.getFeatureFunctions(),
|
|
156
|
-
features: sys.features.get()
|
|
157
|
-
};
|
|
158
|
-
return res.status(200).send(opts);
|
|
159
|
-
});
|
|
160
|
-
app.get('/config/options/bodies', (req, res) => {
|
|
161
|
-
let opts = {
|
|
162
|
-
maxBodies: sys.equipment.maxBodies,
|
|
163
|
-
bodyTypes: sys.board.valueMaps.bodies.toArray(),
|
|
164
|
-
bodies: sys.bodies.get(),
|
|
165
|
-
capacityUnits: sys.board.valueMaps.volumeUnits.toArray()
|
|
166
|
-
};
|
|
167
|
-
return res.status(200).send(opts);
|
|
168
|
-
});
|
|
169
|
-
app.get('/config/options/valves', async (req, res, next) => {
|
|
170
|
-
try {
|
|
171
|
-
let opts = {
|
|
172
|
-
maxValves: sys.equipment.maxValves,
|
|
173
|
-
valveTypes: sys.board.valueMaps.valveTypes.toArray(),
|
|
174
|
-
circuits: sys.board.circuits.getCircuitReferences(true, true, true),
|
|
175
|
-
valves: sys.valves.get(),
|
|
176
|
-
servers: await sys.ncp.getREMServers()
|
|
177
|
-
};
|
|
178
|
-
opts.circuits.unshift({ id: 256, name: 'Unassigned', type: 0, equipmentType: 'circuit' });
|
|
179
|
-
return res.status(200).send(opts);
|
|
180
|
-
} catch (err) { next(err); }
|
|
181
|
-
});
|
|
182
|
-
app.get('/config/options/pumps', async (req, res, next) => {
|
|
183
|
-
try {
|
|
184
|
-
let opts: any = {
|
|
185
|
-
maxPumps: sys.equipment.maxPumps,
|
|
186
|
-
pumpUnits: sys.board.valueMaps.pumpUnits.toArray(),
|
|
187
|
-
pumpTypes: sys.board.valueMaps.pumpTypes.toArray(),
|
|
188
|
-
models: {
|
|
189
|
-
ss: sys.board.valueMaps.pumpSSModels.toArray(),
|
|
190
|
-
ds: sys.board.valueMaps.pumpDSModels.toArray(),
|
|
191
|
-
vs: sys.board.valueMaps.pumpVSModels.toArray(),
|
|
192
|
-
vf: sys.board.valueMaps.pumpVSModels.toArray(),
|
|
193
|
-
vsf: sys.board.valueMaps.pumpVSFModels.toArray(),
|
|
194
|
-
vssvrs: sys.board.valueMaps.pumpVSSVRSModels.toArray()
|
|
195
|
-
},
|
|
196
|
-
circuits: sys.board.circuits.getCircuitReferences(true, true, true, true),
|
|
197
|
-
bodies: sys.board.valueMaps.pumpBodies.toArray(),
|
|
198
|
-
pumps: sys.pumps.get(),
|
|
199
|
-
servers: await sys.ncp.getREMServers(),
|
|
200
|
-
rs485ports: await conn.listInstalledPorts()
|
|
201
|
-
};
|
|
202
|
-
// RKS: Why do we need the circuit names? We have the circuits. Is this so
|
|
203
|
-
// that we can name the pump. I thought that *Touch uses the pump type as the name
|
|
204
|
-
// plus a number.
|
|
205
|
-
// RG: because I need the name/val of the Not Used circuit for displaying pumpCircuits that are
|
|
206
|
-
// empty. EG A pump circuit can be not used even if all the circuits are used.
|
|
207
|
-
if (sys.controllerType !== ControllerType.IntelliCenter) {
|
|
208
|
-
opts.circuitNames = sys.board.circuits.getCircuitNames().filter(c => c.name === 'notused');
|
|
209
|
-
}
|
|
210
|
-
return res.status(200).send(opts);
|
|
211
|
-
} catch (err) { next(err); }
|
|
212
|
-
});
|
|
213
|
-
app.get('/config/options/schedules', async (req, res, next) => {
|
|
214
|
-
try {
|
|
215
|
-
let opts = {
|
|
216
|
-
maxSchedules: sys.equipment.maxSchedules,
|
|
217
|
-
tempUnits: sys.board.valueMaps.tempUnits.transform(state.temps.units),
|
|
218
|
-
scheduleTimeTypes: sys.board.valueMaps.scheduleTimeTypes.toArray(),
|
|
219
|
-
scheduleTypes: sys.board.valueMaps.scheduleTypes.toArray(),
|
|
220
|
-
scheduleDays: sys.board.valueMaps.scheduleDays.toArray(),
|
|
221
|
-
heatSources: sys.board.valueMaps.heatSources.toArray(),
|
|
222
|
-
circuits: sys.board.circuits.getCircuitReferences(true, true, false, true),
|
|
223
|
-
schedules: sys.schedules.get(),
|
|
224
|
-
clockMode: sys.general.options.clockMode || 12,
|
|
225
|
-
displayTypes: sys.board.valueMaps.scheduleDisplayTypes.toArray(),
|
|
226
|
-
bodies: [],
|
|
227
|
-
eggTimers: sys.eggTimers.get() // needed for *Touch to not overwrite real schedules
|
|
228
|
-
};
|
|
229
|
-
// Now get all the body heat sources.
|
|
230
|
-
for (let i = 0; i < sys.bodies.length; i++) {
|
|
231
|
-
let body = sys.bodies.getItemByIndex(i);
|
|
232
|
-
opts.bodies.push({ id: body.id, circuit: body.circuit, name: body.name, alias: body.alias, heatSources: sys.board.bodies.getHeatSources(body.id) });
|
|
233
|
-
}
|
|
234
|
-
return res.status(200).send(opts);
|
|
235
|
-
} catch (err) { next(err); }
|
|
236
|
-
});
|
|
237
|
-
app.get('/config/options/heaters', async (req, res, next) => {
|
|
238
|
-
try {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
});
|
|
384
|
-
app.get('/
|
|
385
|
-
let opts = {
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
return res.status(200).send(opts);
|
|
389
|
-
});
|
|
390
|
-
app.get('/app/
|
|
391
|
-
let opts =
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
});
|
|
451
|
-
app.put('/config/
|
|
452
|
-
try {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
return res.status(200).send(
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
//
|
|
619
|
-
//
|
|
620
|
-
//
|
|
621
|
-
//
|
|
622
|
-
//
|
|
623
|
-
//
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
let
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
app.get('/config/
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
return res.status(200).send(
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
app.
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
//
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
});
|
|
927
|
-
app.get('/app/config/
|
|
928
|
-
try {
|
|
929
|
-
let
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
if (typeof
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
await webApp.
|
|
972
|
-
return res.status(200).send(opts);
|
|
973
|
-
} catch (err) { next(err); }
|
|
974
|
-
|
|
975
|
-
});
|
|
976
|
-
app.put('/app/config/
|
|
977
|
-
try {
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1
|
+
/* nodejs-poolController. An application to control pool equipment.
|
|
2
|
+
Copyright (C) 2016, 2017, 2018, 2019, 2020, 2021, 2022.
|
|
3
|
+
Russell Goldin, tagyoureit. russ.goldin@gmail.com
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as
|
|
7
|
+
published by the Free Software Foundation, either version 3 of the
|
|
8
|
+
License, or (at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from "fs";
|
|
19
|
+
import * as path from "path";
|
|
20
|
+
import * as express from "express";
|
|
21
|
+
import * as extend from 'extend';
|
|
22
|
+
import * as multer from 'multer';
|
|
23
|
+
import { sys, LightGroup, ControllerType, Pump, Valve, Body, General, Circuit, ICircuit, Feature, CircuitGroup, CustomNameCollection, Schedule, Chlorinator, Heater, Screenlogic } from "../../../controller/Equipment";
|
|
24
|
+
import { config } from "../../../config/Config";
|
|
25
|
+
import { logger } from "../../../logger/Logger";
|
|
26
|
+
import { utils } from "../../../controller/Constants";
|
|
27
|
+
import { ServiceProcessError } from "../../../controller/Errors";
|
|
28
|
+
import { state } from "../../../controller/State";
|
|
29
|
+
import { stopPacketCaptureAsync, startPacketCapture } from '../../../app';
|
|
30
|
+
import { conn } from "../../../controller/comms/Comms";
|
|
31
|
+
import { webApp, BackupFile, RestoreFile } from "../../Server";
|
|
32
|
+
import { release } from "os";
|
|
33
|
+
import { ScreenLogicComms, sl } from "../../../controller/comms/ScreenLogic";
|
|
34
|
+
import { screenlogic } from "node-screenlogic";
|
|
35
|
+
|
|
36
|
+
export class ConfigRoute {
|
|
37
|
+
public static initRoutes(app: express.Application) {
|
|
38
|
+
app.get('/config/body/:body/heatModes', (req, res) => {
|
|
39
|
+
return res.status(200).send(sys.bodies.getItemById(parseInt(req.params.body, 10)).getHeatModes());
|
|
40
|
+
});
|
|
41
|
+
app.get('/config/circuit/names', (req, res) => {
|
|
42
|
+
let circuitNames = sys.board.circuits.getCircuitNames();
|
|
43
|
+
return res.status(200).send(circuitNames);
|
|
44
|
+
});
|
|
45
|
+
app.get('/config/circuit/references', (req, res) => {
|
|
46
|
+
let circuits = typeof req.query.circuits === 'undefined' || utils.makeBool(req.query.circuits);
|
|
47
|
+
let features = typeof req.query.features === 'undefined' || utils.makeBool(req.query.features);
|
|
48
|
+
let groups = typeof req.query.features === 'undefined' || utils.makeBool(req.query.groups);
|
|
49
|
+
let virtual = typeof req.query.virtual === 'undefined' || utils.makeBool(req.query.virtual);
|
|
50
|
+
return res.status(200).send(sys.board.circuits.getCircuitReferences(circuits, features, virtual, groups));
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
/******* CONFIGURATION PICK LISTS/REFERENCES and VALIDATION PARAMETERS *********/
|
|
54
|
+
/// Returns an object that contains the general options for setting up the panel.
|
|
55
|
+
app.get('/config/options/general', (req, res) => {
|
|
56
|
+
let opts = {
|
|
57
|
+
countries: sys.board.valueMaps.countries.toArray(),
|
|
58
|
+
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
59
|
+
timeZones: sys.board.valueMaps.timeZones.toArray(),
|
|
60
|
+
clockSources: sys.board.valueMaps.clockSources.toArray(),
|
|
61
|
+
clockModes: sys.board.valueMaps.clockModes.toArray(),
|
|
62
|
+
pool: sys.general.get(true),
|
|
63
|
+
sensors: sys.board.system.getSensors(),
|
|
64
|
+
systemUnits: sys.board.valueMaps.systemUnits.toArray()
|
|
65
|
+
};
|
|
66
|
+
return res.status(200).send(opts);
|
|
67
|
+
});
|
|
68
|
+
app.get('/config/options/rs485', async (req, res, next) => {
|
|
69
|
+
try {
|
|
70
|
+
let opts = { ports: [], local: [], screenlogic: {} }
|
|
71
|
+
let cfg = config.getSection('controller');
|
|
72
|
+
for (let section in cfg) {
|
|
73
|
+
if (section.startsWith('comms')) {
|
|
74
|
+
let cport = extend(true, { enabled: false, netConnect: false, mock: false }, cfg[section]);
|
|
75
|
+
let port = conn.findPortById(cport.portId || 0);
|
|
76
|
+
if (typeof cport.type === 'undefined'){
|
|
77
|
+
cport.type = cport.netConnect ? 'netConnect' : cport.mock ? 'mock' : 'local'
|
|
78
|
+
}
|
|
79
|
+
if (typeof port !== 'undefined') cport.stats = port.stats;
|
|
80
|
+
if (port.portId === 0 && port.type === 'screenlogic') {
|
|
81
|
+
cport.screenlogic.stats = sl.stats;
|
|
82
|
+
}
|
|
83
|
+
opts.ports.push(cport);
|
|
84
|
+
}
|
|
85
|
+
// if (section.startsWith('screenlogic')){
|
|
86
|
+
// let screenlogic = cfg[section];
|
|
87
|
+
// screenlogic.types = [{ val: 'local', name: 'Local', desc: 'Local Screenlogic' }, { val: 'remote', name: 'Remote', desc: 'Remote Screenlogic' }];
|
|
88
|
+
// screenlogic.stats = sl.stats;
|
|
89
|
+
// opts.screenlogic = screenlogic;
|
|
90
|
+
// }
|
|
91
|
+
}
|
|
92
|
+
opts.local = await conn.getLocalPortsAsync() || [];
|
|
93
|
+
return res.status(200).send(opts);
|
|
94
|
+
} catch (err) { next(err); }
|
|
95
|
+
});
|
|
96
|
+
// app.get('/config/options/screenlogic', async (req, res, next) => {
|
|
97
|
+
// try {
|
|
98
|
+
// let cfg = config.getSection('controller.screenlogic');
|
|
99
|
+
// let data = {
|
|
100
|
+
// cfg,
|
|
101
|
+
// types: [{ val: 'local', name: 'Local', desc: 'Local Screenlogic' }, { val: 'remote', name: 'Remote', desc: 'Remote Screenlogic' }]
|
|
102
|
+
// }
|
|
103
|
+
// return res.status(200).send(data);
|
|
104
|
+
// } catch (err) { next(err); }
|
|
105
|
+
// });
|
|
106
|
+
app.get('/config/options/screenlogic/search', async (req, res, next) => {
|
|
107
|
+
try {
|
|
108
|
+
let localUnits = await ScreenLogicComms.searchAsync();
|
|
109
|
+
return res.status(200).send(localUnits);
|
|
110
|
+
} catch (err) { next(err); }
|
|
111
|
+
});
|
|
112
|
+
app.get('/config/options/circuits', async (req, res, next) => {
|
|
113
|
+
try {
|
|
114
|
+
let opts = {
|
|
115
|
+
maxCircuits: sys.equipment.maxCircuits,
|
|
116
|
+
equipmentIds: sys.equipment.equipmentIds.circuits,
|
|
117
|
+
invalidIds: sys.board.equipmentIds.invalidIds.get(),
|
|
118
|
+
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
119
|
+
functions: sys.board.circuits.getCircuitFunctions(),
|
|
120
|
+
circuits: sys.circuits.get(),
|
|
121
|
+
controllerType: sys.controllerType,
|
|
122
|
+
servers: await sys.ncp.getREMServers()
|
|
123
|
+
};
|
|
124
|
+
return res.status(200).send(opts);
|
|
125
|
+
} catch (err) { next(err); }
|
|
126
|
+
});
|
|
127
|
+
app.get('/config/options/circuitGroups', (req, res) => {
|
|
128
|
+
let opts = {
|
|
129
|
+
maxCircuitGroups: sys.equipment.maxCircuitGroups,
|
|
130
|
+
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
131
|
+
circuits: sys.board.circuits.getCircuitReferences(true, true, false),
|
|
132
|
+
circuitGroups: sys.circuitGroups.get(),
|
|
133
|
+
circuitStates: sys.board.valueMaps.groupCircuitStates.toArray()
|
|
134
|
+
};
|
|
135
|
+
return res.status(200).send(opts);
|
|
136
|
+
});
|
|
137
|
+
app.get('/config/options/lightGroups', (req, res) => {
|
|
138
|
+
let opts = {
|
|
139
|
+
maxLightGroups: sys.equipment.maxLightGroups,
|
|
140
|
+
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
141
|
+
themes: sys.board.circuits.getLightThemes(),
|
|
142
|
+
colors: sys.board.valueMaps.lightColors.toArray(),
|
|
143
|
+
circuits: sys.board.circuits.getLightReferences(),
|
|
144
|
+
lightGroups: sys.lightGroups.get(),
|
|
145
|
+
functions: sys.board.circuits.getCircuitFunctions()
|
|
146
|
+
};
|
|
147
|
+
return res.status(200).send(opts);
|
|
148
|
+
});
|
|
149
|
+
app.get('/config/options/features', (req, res) => {
|
|
150
|
+
let opts = {
|
|
151
|
+
maxFeatures: sys.equipment.maxFeatures,
|
|
152
|
+
invalidIds: sys.board.equipmentIds.invalidIds.get(),
|
|
153
|
+
equipmentIds: sys.equipment.equipmentIds.features,
|
|
154
|
+
equipmentNames: sys.board.circuits.getCircuitNames(),
|
|
155
|
+
functions: sys.board.features.getFeatureFunctions(),
|
|
156
|
+
features: sys.features.get()
|
|
157
|
+
};
|
|
158
|
+
return res.status(200).send(opts);
|
|
159
|
+
});
|
|
160
|
+
app.get('/config/options/bodies', (req, res) => {
|
|
161
|
+
let opts = {
|
|
162
|
+
maxBodies: sys.equipment.maxBodies,
|
|
163
|
+
bodyTypes: sys.board.valueMaps.bodies.toArray(),
|
|
164
|
+
bodies: sys.bodies.get(),
|
|
165
|
+
capacityUnits: sys.board.valueMaps.volumeUnits.toArray()
|
|
166
|
+
};
|
|
167
|
+
return res.status(200).send(opts);
|
|
168
|
+
});
|
|
169
|
+
app.get('/config/options/valves', async (req, res, next) => {
|
|
170
|
+
try {
|
|
171
|
+
let opts = {
|
|
172
|
+
maxValves: sys.equipment.maxValves,
|
|
173
|
+
valveTypes: sys.board.valueMaps.valveTypes.toArray(),
|
|
174
|
+
circuits: sys.board.circuits.getCircuitReferences(true, true, true),
|
|
175
|
+
valves: sys.valves.get(),
|
|
176
|
+
servers: await sys.ncp.getREMServers()
|
|
177
|
+
};
|
|
178
|
+
opts.circuits.unshift({ id: 256, name: 'Unassigned', type: 0, equipmentType: 'circuit' });
|
|
179
|
+
return res.status(200).send(opts);
|
|
180
|
+
} catch (err) { next(err); }
|
|
181
|
+
});
|
|
182
|
+
app.get('/config/options/pumps', async (req, res, next) => {
|
|
183
|
+
try {
|
|
184
|
+
let opts: any = {
|
|
185
|
+
maxPumps: sys.equipment.maxPumps,
|
|
186
|
+
pumpUnits: sys.board.valueMaps.pumpUnits.toArray(),
|
|
187
|
+
pumpTypes: sys.board.valueMaps.pumpTypes.toArray(),
|
|
188
|
+
models: {
|
|
189
|
+
ss: sys.board.valueMaps.pumpSSModels.toArray(),
|
|
190
|
+
ds: sys.board.valueMaps.pumpDSModels.toArray(),
|
|
191
|
+
vs: sys.board.valueMaps.pumpVSModels.toArray(),
|
|
192
|
+
vf: sys.board.valueMaps.pumpVSModels.toArray(),
|
|
193
|
+
vsf: sys.board.valueMaps.pumpVSFModels.toArray(),
|
|
194
|
+
vssvrs: sys.board.valueMaps.pumpVSSVRSModels.toArray()
|
|
195
|
+
},
|
|
196
|
+
circuits: sys.board.circuits.getCircuitReferences(true, true, true, true),
|
|
197
|
+
bodies: sys.board.valueMaps.pumpBodies.toArray(),
|
|
198
|
+
pumps: sys.pumps.get(),
|
|
199
|
+
servers: await sys.ncp.getREMServers(),
|
|
200
|
+
rs485ports: await conn.listInstalledPorts()
|
|
201
|
+
};
|
|
202
|
+
// RKS: Why do we need the circuit names? We have the circuits. Is this so
|
|
203
|
+
// that we can name the pump. I thought that *Touch uses the pump type as the name
|
|
204
|
+
// plus a number.
|
|
205
|
+
// RG: because I need the name/val of the Not Used circuit for displaying pumpCircuits that are
|
|
206
|
+
// empty. EG A pump circuit can be not used even if all the circuits are used.
|
|
207
|
+
if (sys.controllerType !== ControllerType.IntelliCenter) {
|
|
208
|
+
opts.circuitNames = sys.board.circuits.getCircuitNames().filter(c => c.name === 'notused');
|
|
209
|
+
}
|
|
210
|
+
return res.status(200).send(opts);
|
|
211
|
+
} catch (err) { next(err); }
|
|
212
|
+
});
|
|
213
|
+
app.get('/config/options/schedules', async (req, res, next) => {
|
|
214
|
+
try {
|
|
215
|
+
let opts = {
|
|
216
|
+
maxSchedules: sys.equipment.maxSchedules,
|
|
217
|
+
tempUnits: sys.board.valueMaps.tempUnits.transform(state.temps.units),
|
|
218
|
+
scheduleTimeTypes: sys.board.valueMaps.scheduleTimeTypes.toArray(),
|
|
219
|
+
scheduleTypes: sys.board.valueMaps.scheduleTypes.toArray(),
|
|
220
|
+
scheduleDays: sys.board.valueMaps.scheduleDays.toArray(),
|
|
221
|
+
heatSources: sys.board.valueMaps.heatSources.toArray(),
|
|
222
|
+
circuits: sys.board.circuits.getCircuitReferences(true, true, false, true),
|
|
223
|
+
schedules: sys.schedules.get(),
|
|
224
|
+
clockMode: sys.general.options.clockMode || 12,
|
|
225
|
+
displayTypes: sys.board.valueMaps.scheduleDisplayTypes.toArray(),
|
|
226
|
+
bodies: [],
|
|
227
|
+
eggTimers: sys.eggTimers.get() // needed for *Touch to not overwrite real schedules
|
|
228
|
+
};
|
|
229
|
+
// Now get all the body heat sources.
|
|
230
|
+
for (let i = 0; i < sys.bodies.length; i++) {
|
|
231
|
+
let body = sys.bodies.getItemByIndex(i);
|
|
232
|
+
opts.bodies.push({ id: body.id, circuit: body.circuit, name: body.name, alias: body.alias, heatSources: sys.board.bodies.getHeatSources(body.id) });
|
|
233
|
+
}
|
|
234
|
+
return res.status(200).send(opts);
|
|
235
|
+
} catch (err) { next(err); }
|
|
236
|
+
});
|
|
237
|
+
app.get('/config/options/heaters', async (req, res, next) => {
|
|
238
|
+
try {
|
|
239
|
+
// Ensure heat mode/source valueMaps reflect the *current board* before returning picklists.
|
|
240
|
+
// Without this, startup can expose generic defaults until the first status/config packets arrive.
|
|
241
|
+
sys.board.heaters.updateHeaterServices();
|
|
242
|
+
let opts = {
|
|
243
|
+
tempUnits: sys.board.valueMaps.tempUnits.transform(state.temps.units),
|
|
244
|
+
bodies: sys.board.bodies.getBodyAssociations(),
|
|
245
|
+
maxHeaters: sys.equipment.maxHeaters,
|
|
246
|
+
heaters: sys.heaters.get(),
|
|
247
|
+
heaterTypes: sys.board.valueMaps.heaterTypes.toArray(),
|
|
248
|
+
// Align with `/config/body/:id/heatModes` (body picklist). This ensures any board-specific
|
|
249
|
+
// filtering (e.g. IntelliCenter v3 preferred-mode suppression) is reflected consistently.
|
|
250
|
+
// Future improvement should return valid modes per body.
|
|
251
|
+
heatModes: sys.board.bodies.getHeatModes(1),
|
|
252
|
+
coolDownDelay: sys.general.options.cooldownDelay,
|
|
253
|
+
servers: [],
|
|
254
|
+
rs485ports: await conn.listInstalledPorts()
|
|
255
|
+
};
|
|
256
|
+
// We only need the servers data when the controller is a Nixie controller. We don't need to
|
|
257
|
+
// wait for this information if we are dealing with an OCP.
|
|
258
|
+
if (sys.controllerType === ControllerType.Nixie) opts.servers = await sys.ncp.getREMServers();
|
|
259
|
+
return res.status(200).send(opts);
|
|
260
|
+
} catch (err) { next(err); }
|
|
261
|
+
});
|
|
262
|
+
app.get('/config/options/customNames', (req, res) => {
|
|
263
|
+
let opts = {
|
|
264
|
+
maxCustomNames: sys.equipment.maxCustomNames,
|
|
265
|
+
customNames: sys.customNames.get()
|
|
266
|
+
};
|
|
267
|
+
return res.status(200).send(opts);
|
|
268
|
+
});
|
|
269
|
+
app.get('/config/options/chemControllers', async (req, res, next) => {
|
|
270
|
+
try {
|
|
271
|
+
let remServers = await sys.ncp.getREMServers();
|
|
272
|
+
let alarms = {
|
|
273
|
+
flow: sys.board.valueMaps.chemControllerAlarms.toArray().filter(el => [0, 1].includes(el.val)),
|
|
274
|
+
pH: sys.board.valueMaps.chemControllerAlarms.toArray().filter(el => [0, 2, 4].includes(el.val)),
|
|
275
|
+
orp: sys.board.valueMaps.chemControllerAlarms.toArray().filter(el => [0, 8, 16].includes(el.val)),
|
|
276
|
+
pHTank: sys.board.valueMaps.chemControllerAlarms.toArray().filter(el => [0, 32].includes(el.val)),
|
|
277
|
+
orpTank: sys.board.valueMaps.chemControllerAlarms.toArray().filter(el => [0, 64].includes(el.val)),
|
|
278
|
+
probeFault: sys.board.valueMaps.chemControllerAlarms.toArray().filter(el => [0, 128].includes(el.val))
|
|
279
|
+
}
|
|
280
|
+
let warnings = {
|
|
281
|
+
waterChemistry: sys.board.valueMaps.chemControllerWarnings.toArray().filter(el => [0, 1, 2].includes(el.val)),
|
|
282
|
+
pHLockout: sys.board.valueMaps.chemControllerLimits.toArray().filter(el => [0, 1].includes(el.val)),
|
|
283
|
+
pHDailyLimitReached: sys.board.valueMaps.chemControllerLimits.toArray().filter(el => [0, 2].includes(el.val)),
|
|
284
|
+
orpDailyLimitReached: sys.board.valueMaps.chemControllerLimits.toArray().filter(el => [0, 4].includes(el.val)),
|
|
285
|
+
invalidSetup: sys.board.valueMaps.chemControllerWarnings.toArray().filter(el => [0, 8].includes(el.val)),
|
|
286
|
+
chlorinatorCommsError: sys.board.valueMaps.chemControllerWarnings.toArray().filter(el => [0, 16].includes(el.val)),
|
|
287
|
+
}
|
|
288
|
+
let opts = {
|
|
289
|
+
types: sys.board.valueMaps.chemControllerTypes.toArray(),
|
|
290
|
+
bodies: sys.board.bodies.getBodyAssociations(),
|
|
291
|
+
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
292
|
+
status: sys.board.valueMaps.chemControllerStatus.toArray(),
|
|
293
|
+
pumpTypes: sys.board.valueMaps.chemPumpTypes.toArray(),
|
|
294
|
+
phSupplyTypes: sys.board.valueMaps.phSupplyTypes.toArray(),
|
|
295
|
+
volumeUnits: sys.board.valueMaps.volumeUnits.toArray(),
|
|
296
|
+
dosingMethods: sys.board.valueMaps.chemDosingMethods.toArray(),
|
|
297
|
+
chlorDosingMethods: sys.board.valueMaps.chemChlorDosingMethods.toArray(),
|
|
298
|
+
orpProbeTypes: sys.board.valueMaps.chemORPProbeTypes.toArray(),
|
|
299
|
+
phProbeTypes: sys.board.valueMaps.chemPhProbeTypes.toArray(),
|
|
300
|
+
flowSensorTypes: sys.board.valueMaps.flowSensorTypes.toArray(),
|
|
301
|
+
acidTypes: sys.board.valueMaps.acidTypes.toArray(),
|
|
302
|
+
remServers,
|
|
303
|
+
dosingStatus: sys.board.valueMaps.chemControllerDosingStatus.toArray(),
|
|
304
|
+
siCalcTypes: sys.board.valueMaps.siCalcTypes.toArray(),
|
|
305
|
+
alarms,
|
|
306
|
+
warnings,
|
|
307
|
+
// waterFlow: sys.board.valueMaps.chemControllerWaterFlow.toArray(), // remove
|
|
308
|
+
controllers: sys.chemControllers.get(),
|
|
309
|
+
maxChemControllers: sys.equipment.maxChemControllers,
|
|
310
|
+
doserTypes: sys.board.valueMaps.chemDoserTypes.toArray(),
|
|
311
|
+
chlorinators: sys.chlorinators.get(),
|
|
312
|
+
};
|
|
313
|
+
return res.status(200).send(opts);
|
|
314
|
+
}
|
|
315
|
+
catch (err) { next(err); }
|
|
316
|
+
});
|
|
317
|
+
app.get('/config/options/chemDosers', async (req, res, next) => {
|
|
318
|
+
try {
|
|
319
|
+
let remServers = await sys.ncp.getREMServers();
|
|
320
|
+
let opts = {
|
|
321
|
+
bodies: sys.board.bodies.getBodyAssociations(),
|
|
322
|
+
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
323
|
+
status: sys.board.valueMaps.chemDoserStatus.toArray(),
|
|
324
|
+
pumpTypes: sys.board.valueMaps.chemPumpTypes.toArray(),
|
|
325
|
+
volumeUnits: sys.board.valueMaps.volumeUnits.toArray(),
|
|
326
|
+
flowSensorTypes: sys.board.valueMaps.flowSensorTypes.toArray(),
|
|
327
|
+
remServers,
|
|
328
|
+
dosingStatus: sys.board.valueMaps.chemDoserDosingStatus.toArray(),
|
|
329
|
+
dosers: sys.chemDosers.get(),
|
|
330
|
+
doserTypes: sys.board.valueMaps.chemDoserTypes.toArray(),
|
|
331
|
+
maxChemDosers: sys.equipment.maxChemDosers
|
|
332
|
+
};
|
|
333
|
+
return res.status(200).send(opts);
|
|
334
|
+
}
|
|
335
|
+
catch (err) { next(err); }
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
app.get('/config/options/rem', async (req, res, next) => {
|
|
339
|
+
try {
|
|
340
|
+
let opts = {
|
|
341
|
+
servers: await sys.ncp.getREMServers()
|
|
342
|
+
}
|
|
343
|
+
return res.status(200).send(opts);
|
|
344
|
+
} catch (err) { next(err); }
|
|
345
|
+
});
|
|
346
|
+
app.get('/config/options/controllerType', async (req, res, next) => {
|
|
347
|
+
try {
|
|
348
|
+
let opts = {
|
|
349
|
+
controllerType: sys.controllerType,
|
|
350
|
+
type: state.controllerState,
|
|
351
|
+
equipment: sys.equipment.get(),
|
|
352
|
+
controllerTypes: sys.getAvailableControllerTypes()
|
|
353
|
+
}
|
|
354
|
+
return res.status(200).send(opts);
|
|
355
|
+
} catch (err) { next(err); }
|
|
356
|
+
});
|
|
357
|
+
app.get('/config/options/anslq25ControllerType', async (req, res, next) => {
|
|
358
|
+
try {
|
|
359
|
+
let opts = {
|
|
360
|
+
// controllerType: typeof sys.anslq25.controllerType === 'undefined' ? '' : sys.anslq25.controllerType,
|
|
361
|
+
// model: typeof sys.anslq25.model === 'undefined' ? '' : sys.anslq25.model,
|
|
362
|
+
// equipment: sys.equipment.get(),
|
|
363
|
+
...sys.anslq25.get(true),
|
|
364
|
+
controllerTypes: sys.getAvailableControllerTypes(['easytouch', 'intellitouch', 'intellicenter']),
|
|
365
|
+
rs485ports: await conn.listInstalledPorts()
|
|
366
|
+
}
|
|
367
|
+
return res.status(200).send(opts);
|
|
368
|
+
} catch (err) { next(err); }
|
|
369
|
+
});
|
|
370
|
+
app.get('/config/options/chlorinators', async (req, res, next) => {
|
|
371
|
+
try {
|
|
372
|
+
let opts = {
|
|
373
|
+
types: sys.board.valueMaps.chlorinatorType.toArray(),
|
|
374
|
+
bodies: sys.board.bodies.getBodyAssociations(),
|
|
375
|
+
chlorinators: sys.chlorinators.get(),
|
|
376
|
+
maxChlorinators: sys.equipment.maxChlorinators,
|
|
377
|
+
models: sys.board.valueMaps.chlorinatorModel.toArray(),
|
|
378
|
+
equipmentMasters: sys.board.valueMaps.equipmentMaster.toArray(),
|
|
379
|
+
rs485ports: await conn.listInstalledPorts()
|
|
380
|
+
};
|
|
381
|
+
return res.status(200).send(opts);
|
|
382
|
+
} catch (err) { next(err); }
|
|
383
|
+
});
|
|
384
|
+
app.get('/config/options/dateTime', (req, res) => {
|
|
385
|
+
let opts = {
|
|
386
|
+
dow: sys.board.system.getDOW()
|
|
387
|
+
}
|
|
388
|
+
return res.status(200).send(opts);
|
|
389
|
+
});
|
|
390
|
+
app.get('/app/options/logger', (req, res) => {
|
|
391
|
+
let opts = {
|
|
392
|
+
logger: config.getSection('log')
|
|
393
|
+
}
|
|
394
|
+
return res.status(200).send(opts);
|
|
395
|
+
});
|
|
396
|
+
app.get('/app/all/', (req, res) => {
|
|
397
|
+
let opts = config.getSection();
|
|
398
|
+
return res.status(200).send(opts);
|
|
399
|
+
});
|
|
400
|
+
app.get('/config/options/tempSensors', (req, res) => {
|
|
401
|
+
let opts = {
|
|
402
|
+
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
403
|
+
sensors: sys.board.system.getSensors()
|
|
404
|
+
};
|
|
405
|
+
return res.status(200).send(opts);
|
|
406
|
+
});
|
|
407
|
+
app.get('/config/options/filters', async (req, res, next) => {
|
|
408
|
+
try {
|
|
409
|
+
let opts = {
|
|
410
|
+
types: sys.board.valueMaps.filterTypes.toArray(),
|
|
411
|
+
bodies: sys.board.bodies.getBodyAssociations(),
|
|
412
|
+
filters: sys.filters.get(),
|
|
413
|
+
areaUnits: sys.board.valueMaps.areaUnits.toArray(),
|
|
414
|
+
pressureUnits: sys.board.valueMaps.pressureUnits.toArray(),
|
|
415
|
+
circuits: sys.board.circuits.getCircuitReferences(true, true, true, false),
|
|
416
|
+
servers: []
|
|
417
|
+
};
|
|
418
|
+
if (sys.controllerType === ControllerType.Nixie) opts.servers = await sys.ncp.getREMServers();
|
|
419
|
+
return res.status(200).send(opts);
|
|
420
|
+
} catch (err) { next(err); }
|
|
421
|
+
});
|
|
422
|
+
/******* END OF CONFIGURATION PICK LISTS/REFERENCES AND VALIDATION ***********/
|
|
423
|
+
/******* ENDPOINTS FOR MODIFYING THE OUTDOOR CONTROL PANEL SETTINGS **********/
|
|
424
|
+
app.put('/config/rem', async (req, res, next) => {
|
|
425
|
+
try {
|
|
426
|
+
// RSG: this is problematic because we now enable multiple rem type interfaces that may not be called REM.
|
|
427
|
+
// This is now also a dupe of PUT /app/interface and should be consolidated
|
|
428
|
+
// config.setSection('web.interfaces.rem', req.body);
|
|
429
|
+
config.setInterface(req.body);
|
|
430
|
+
}
|
|
431
|
+
catch (err) { next(err); }
|
|
432
|
+
})
|
|
433
|
+
app.put('/config/tempSensors', async (req, res, next) => {
|
|
434
|
+
try {
|
|
435
|
+
await sys.board.system.setTempSensorsAsync(req.body);
|
|
436
|
+
let opts = {
|
|
437
|
+
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
438
|
+
sensors: sys.board.system.getSensors()
|
|
439
|
+
};
|
|
440
|
+
return res.status(200).send(opts);
|
|
441
|
+
}
|
|
442
|
+
catch (err) { next(err); }
|
|
443
|
+
});
|
|
444
|
+
app.put('/config/filter', async (req, res, next) => {
|
|
445
|
+
try {
|
|
446
|
+
let sfilter = await sys.board.filters.setFilterAsync(req.body);
|
|
447
|
+
return res.status(200).send(sfilter.get(true));
|
|
448
|
+
}
|
|
449
|
+
catch (err) { next(err); }
|
|
450
|
+
});
|
|
451
|
+
app.put('/config/controllerType', async (req, res, next) => {
|
|
452
|
+
try {
|
|
453
|
+
let controller = await sys.board.setControllerType(req.body);
|
|
454
|
+
return res.status(200).send(controller.get(true));
|
|
455
|
+
} catch (err) { next(err); }
|
|
456
|
+
});
|
|
457
|
+
app.put('/config/anslq25ControllerType', async (req, res, next) => {
|
|
458
|
+
try {
|
|
459
|
+
// sys.anslq25ControllerType
|
|
460
|
+
await sys.anslq25Board.setAnslq25Async(req.body);
|
|
461
|
+
return res.status(200).send(sys.anslq25.get(true));
|
|
462
|
+
} catch (err) { next(err); }
|
|
463
|
+
});
|
|
464
|
+
app.delete('/config/filter', async (req, res, next) => {
|
|
465
|
+
try {
|
|
466
|
+
let sfilter = await sys.board.filters.deleteFilterAsync(req.body);
|
|
467
|
+
return res.status(200).send(sfilter.get(true));
|
|
468
|
+
}
|
|
469
|
+
catch (err) { next(err); }
|
|
470
|
+
});
|
|
471
|
+
app.put('/config/general', async (req, res, next) => {
|
|
472
|
+
// Change the options for the pool.
|
|
473
|
+
try {
|
|
474
|
+
let rc = await sys.board.system.setGeneralAsync(req.body);
|
|
475
|
+
let opts = {
|
|
476
|
+
countries: sys.board.valueMaps.countries.toArray(),
|
|
477
|
+
tempUnits: sys.board.valueMaps.tempUnits.toArray(),
|
|
478
|
+
timeZones: sys.board.valueMaps.timeZones.toArray(),
|
|
479
|
+
clockSources: sys.board.valueMaps.clockSources.toArray(),
|
|
480
|
+
clockModes: sys.board.valueMaps.clockModes.toArray(),
|
|
481
|
+
pool: sys.general.get(true),
|
|
482
|
+
sensors: sys.board.system.getSensors()
|
|
483
|
+
};
|
|
484
|
+
return res.status(200).send(opts);
|
|
485
|
+
}
|
|
486
|
+
catch (err) { next(err); }
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
app.put('/config/valve', async (req, res, next) => {
|
|
490
|
+
// Update a valve.
|
|
491
|
+
try {
|
|
492
|
+
let valve = await sys.board.valves.setValveAsync(req.body);
|
|
493
|
+
return res.status(200).send((valve).get(true));
|
|
494
|
+
}
|
|
495
|
+
catch (err) { next(err); }
|
|
496
|
+
});
|
|
497
|
+
app.delete('/config/valve', async (req, res, next) => {
|
|
498
|
+
// Update a valve.
|
|
499
|
+
try {
|
|
500
|
+
let valve = await sys.board.valves.deleteValveAsync(req.body);
|
|
501
|
+
return res.status(200).send((valve).get(true));
|
|
502
|
+
}
|
|
503
|
+
catch (err) { next(err); }
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
app.put('/config/body', async (req, res, next) => {
|
|
507
|
+
// Change the body attributes.
|
|
508
|
+
try {
|
|
509
|
+
let body = await sys.board.bodies.setBodyAsync(req.body);
|
|
510
|
+
return res.status(200).send((body).get(true));
|
|
511
|
+
}
|
|
512
|
+
catch (err) { next(err); }
|
|
513
|
+
});
|
|
514
|
+
app.put('/config/circuit', async (req, res, next) => {
|
|
515
|
+
// add/update a circuit
|
|
516
|
+
try {
|
|
517
|
+
let circuit = await sys.board.circuits.setCircuitAsync(req.body);
|
|
518
|
+
return res.status(200).send((circuit).get(true));
|
|
519
|
+
}
|
|
520
|
+
catch (err) { next(err); }
|
|
521
|
+
});
|
|
522
|
+
app.delete('/config/circuit', async (req, res, next) => {
|
|
523
|
+
// delete a circuit
|
|
524
|
+
try {
|
|
525
|
+
let circuit = await sys.board.circuits.deleteCircuitAsync(req.body);
|
|
526
|
+
return res.status(200).send((circuit).get(true));
|
|
527
|
+
}
|
|
528
|
+
catch (err) { next(err); }
|
|
529
|
+
});
|
|
530
|
+
app.put('/config/feature', async (req, res, next) => {
|
|
531
|
+
// add/update a feature
|
|
532
|
+
try {
|
|
533
|
+
let feature = await sys.board.features.setFeatureAsync(req.body);
|
|
534
|
+
return res.status(200).send((feature).get(true));
|
|
535
|
+
}
|
|
536
|
+
catch (err) { next(err); }
|
|
537
|
+
});
|
|
538
|
+
app.delete('/config/feature', async (req, res, next) => {
|
|
539
|
+
// delete a feature
|
|
540
|
+
try {
|
|
541
|
+
let feature = await sys.board.features.deleteFeatureAsync(req.body);
|
|
542
|
+
return res.status(200).send((feature).get(true));
|
|
543
|
+
}
|
|
544
|
+
catch (err) { next(err); }
|
|
545
|
+
});
|
|
546
|
+
app.put('/config/circuitGroup', async (req, res, next) => {
|
|
547
|
+
// add/update a circuitGroup
|
|
548
|
+
try {
|
|
549
|
+
let group = await sys.board.circuits.setCircuitGroupAsync(req.body);
|
|
550
|
+
return res.status(200).send((group).get(true));
|
|
551
|
+
}
|
|
552
|
+
catch (err) { next(err); }
|
|
553
|
+
});
|
|
554
|
+
app.delete('/config/circuitGroup', async (req, res, next) => {
|
|
555
|
+
try {
|
|
556
|
+
let group = await sys.board.circuits.deleteCircuitGroupAsync(req.body);
|
|
557
|
+
return res.status(200).send((group).get(true));
|
|
558
|
+
}
|
|
559
|
+
catch (err) { next(err); }
|
|
560
|
+
});
|
|
561
|
+
app.put('/config/lightGroup', async (req, res, next) => {
|
|
562
|
+
try {
|
|
563
|
+
let group = await sys.board.circuits.setLightGroupAsync(req.body);
|
|
564
|
+
return res.status(200).send((group).get(true));
|
|
565
|
+
}
|
|
566
|
+
catch (err) { next(err); }
|
|
567
|
+
});
|
|
568
|
+
app.delete('/config/lightGroup', async (req, res, next) => {
|
|
569
|
+
try {
|
|
570
|
+
let group = await sys.board.circuits.deleteLightGroupAsync(req.body);
|
|
571
|
+
return res.status(200).send((group).get(true));
|
|
572
|
+
}
|
|
573
|
+
catch (err) { next(err); }
|
|
574
|
+
});
|
|
575
|
+
app.put('/config/pump', async (req, res, next) => {
|
|
576
|
+
// Change the pump attributes. This will add the pump if it doesn't exist, set
|
|
577
|
+
// any affiliated circuits and maintain all attribututes of the pump.
|
|
578
|
+
// RSG: Caveat - you have to send none or all of the pump circuits or any not included be deleted.
|
|
579
|
+
try {
|
|
580
|
+
let pump = await sys.board.pumps.setPumpAsync(req.body);
|
|
581
|
+
return res.status(200).send((pump).get(true));
|
|
582
|
+
}
|
|
583
|
+
catch (err) { next(err); }
|
|
584
|
+
});
|
|
585
|
+
app.put('/config/pumpCircuit', async (req, res, next) => {
|
|
586
|
+
try {
|
|
587
|
+
let pmpId = parseInt(req.body.pumpId, 10);
|
|
588
|
+
let circId = parseInt(req.body.circuitId, 10);
|
|
589
|
+
let pmp: Pump;
|
|
590
|
+
if (isNaN(pmpId)) {
|
|
591
|
+
let pmpAddress = parseInt(req.body.address, 10);
|
|
592
|
+
if (!isNaN(pmpAddress)) pmp = sys.pumps.find(x => x.address === pmpAddress);
|
|
593
|
+
}
|
|
594
|
+
else
|
|
595
|
+
pmp = sys.pumps.find(x => x.id === pmpId);
|
|
596
|
+
if (typeof pmp === 'undefined') throw new ServiceProcessError(`Pump not found`, '/config/pumpCircuit', 'Set circuit speed');
|
|
597
|
+
let data = pmp.get(true);
|
|
598
|
+
let c = typeof data.circuits !== 'undefined' && typeof data.circuits.find !== 'undefined' ? data.circuits.find(x => x.circuit === circId) : undefined;
|
|
599
|
+
if (typeof c === 'undefined') throw new ServiceProcessError(`Circuit not found`, '/config/pumpCircuit', 'Set circuit speed');
|
|
600
|
+
if (typeof req.body.speed !== 'undefined') {
|
|
601
|
+
let speed = parseInt(req.body.speed, 10);
|
|
602
|
+
if (isNaN(speed)) throw new ServiceProcessError(`Invalid circuit speed supplied`, '/config/pumpCircuit', 'Set circuit speed');
|
|
603
|
+
c.speed = speed;
|
|
604
|
+
}
|
|
605
|
+
else if (typeof req.body.flow !== 'undefined') {
|
|
606
|
+
let flow = parseInt(req.body.flow, 10);
|
|
607
|
+
if (isNaN(flow)) throw new ServiceProcessError(`Invalid circuit flow supplied`, '/config/pumpCircuit', 'Set circuit flow');
|
|
608
|
+
c.flow = flow;
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
throw new ServiceProcessError(`You must supply a target flow or speed`, '/config/pumpCircuit', 'Set circuit flow');
|
|
612
|
+
}
|
|
613
|
+
await sys.board.pumps.setPumpAsync(data);
|
|
614
|
+
return res.status(200).send((pmp).get(true));
|
|
615
|
+
} catch (err) { next(err); }
|
|
616
|
+
|
|
617
|
+
});
|
|
618
|
+
// RKS: 05-20-22 This is a remnant of the old web ui. It is not called and the setType method needed to go away.
|
|
619
|
+
//app.delete('/config/pump/:pumpId', async (req, res, next) => {
|
|
620
|
+
// try {
|
|
621
|
+
// let pump = sys.pumps.getItemById(parseInt(req.params.pumpId, 10));
|
|
622
|
+
// await sys.board.pumps.deletePumpAsync()
|
|
623
|
+
// if (pump.type === 0) {
|
|
624
|
+
// return res.status(500).send(`Pump ${pump.id} not active`);
|
|
625
|
+
// }
|
|
626
|
+
// pump.setType(0);
|
|
627
|
+
// return res.status(200).send('OK');
|
|
628
|
+
// } catch (err) { next(err); }
|
|
629
|
+
//});
|
|
630
|
+
app.delete('/config/pump', async (req, res, next) => {
|
|
631
|
+
try {
|
|
632
|
+
let pump = await sys.board.pumps.deletePumpAsync(req.body);
|
|
633
|
+
return res.status(200).send((pump).get(true));
|
|
634
|
+
}
|
|
635
|
+
catch (err) { next(err); }
|
|
636
|
+
});
|
|
637
|
+
app.put('/config/customNames', async (req, res, next) => {
|
|
638
|
+
try {
|
|
639
|
+
let names = await sys.board.system.setCustomNamesAsync(req.body);
|
|
640
|
+
return res.status(200).send(names.get());
|
|
641
|
+
}
|
|
642
|
+
catch (err) { next(err); }
|
|
643
|
+
});
|
|
644
|
+
app.put('/config/customName', async (req, res, next) => {
|
|
645
|
+
try {
|
|
646
|
+
let name = await sys.board.system.setCustomNameAsync(req.body);
|
|
647
|
+
return res.status(200).send(name.get(true));
|
|
648
|
+
}
|
|
649
|
+
catch (err) { next(err); }
|
|
650
|
+
});
|
|
651
|
+
app.get('/config/schedule/:id', (req, res) => {
|
|
652
|
+
let schedId = parseInt(req.params.id || '0', 10);
|
|
653
|
+
let sched = sys.schedules.getItemById(schedId).get(true);
|
|
654
|
+
return res.status(200).send(sched);
|
|
655
|
+
});
|
|
656
|
+
app.put('/config/schedule', async (req, res, next) => {
|
|
657
|
+
try {
|
|
658
|
+
let sched = await sys.board.schedules.setScheduleAsync(req.body);
|
|
659
|
+
return res.status(200).send((sched as Schedule).get(true));
|
|
660
|
+
}
|
|
661
|
+
catch (err) { next(err); }
|
|
662
|
+
});
|
|
663
|
+
app.delete('/config/schedule', async (req, res, next) => {
|
|
664
|
+
try {
|
|
665
|
+
let sched = await sys.board.schedules.deleteScheduleAsync(req.body);
|
|
666
|
+
return res.status(200).send((sched as Schedule).get(true));
|
|
667
|
+
}
|
|
668
|
+
catch (err) {
|
|
669
|
+
//console.log(`Error deleting schedule... ${err}`);
|
|
670
|
+
next(err);
|
|
671
|
+
}
|
|
672
|
+
});
|
|
673
|
+
app.put('/config/chlorinator', async (req, res, next) => {
|
|
674
|
+
try {
|
|
675
|
+
let chlor = await sys.board.chlorinator.setChlorAsync(req.body);
|
|
676
|
+
return res.status(200).send(sys.chlorinators.getItemById(chlor.id).get(true));
|
|
677
|
+
}
|
|
678
|
+
catch (err) { next(err); }
|
|
679
|
+
});
|
|
680
|
+
app.delete('/config/chlorinator', async (req, res, next) => {
|
|
681
|
+
try {
|
|
682
|
+
let chlor = await sys.board.chlorinator.deleteChlorAsync(req.body);
|
|
683
|
+
return res.status(200).send(chlor.get(true));
|
|
684
|
+
}
|
|
685
|
+
catch (err) { next(err); }
|
|
686
|
+
});
|
|
687
|
+
app.put('/config/heater', async (req, res, next) => {
|
|
688
|
+
try {
|
|
689
|
+
let heater = await sys.board.heaters.setHeaterAsync(req.body);
|
|
690
|
+
return res.status(200).send(sys.heaters.getItemById(heater.id).get(true));
|
|
691
|
+
}
|
|
692
|
+
catch (err) { next(err); }
|
|
693
|
+
});
|
|
694
|
+
app.delete('/config/heater', async (req, res, next) => {
|
|
695
|
+
try {
|
|
696
|
+
let heater = await sys.board.heaters.deleteHeaterAsync(req.body);
|
|
697
|
+
return res.status(200).send((heater as Heater).get(true));
|
|
698
|
+
}
|
|
699
|
+
catch (err) { next(err); }
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
/***** END OF ENDPOINTS FOR MODIFYINC THE OUTDOOR CONTROL PANEL SETTINGS *****/
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
app.get('/config/circuits/names', (req, res) => {
|
|
707
|
+
let circuitNames = sys.board.circuits.getCircuitNames();
|
|
708
|
+
return res.status(200).send(circuitNames);
|
|
709
|
+
});
|
|
710
|
+
app.get('/config/circuit/functions', (req, res) => {
|
|
711
|
+
let circuitFunctions = sys.board.circuits.getCircuitFunctions();
|
|
712
|
+
return res.status(200).send(circuitFunctions);
|
|
713
|
+
});
|
|
714
|
+
app.get('/config/features/functions', (req, res) => {
|
|
715
|
+
let featureFunctions = sys.board.features.getFeatureFunctions();
|
|
716
|
+
return res.status(200).send(featureFunctions);
|
|
717
|
+
});
|
|
718
|
+
app.get('/config/circuit/:id', (req, res) => {
|
|
719
|
+
// todo: need getInterfaceById.get() in case features are requested here
|
|
720
|
+
// todo: it seems to make sense to combine with /state/circuit/:id as they both have similiar/overlapping info
|
|
721
|
+
return res.status(200).send(sys.circuits.getItemById(parseInt(req.params.id, 10)).get());
|
|
722
|
+
});
|
|
723
|
+
app.get('/config/circuit/:id/lightThemes', (req, res) => {
|
|
724
|
+
let circuit = sys.circuits.getInterfaceById(parseInt(req.params.id, 10));
|
|
725
|
+
let themes = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightThemes(circuit.type) : [];
|
|
726
|
+
return res.status(200).send(themes);
|
|
727
|
+
});
|
|
728
|
+
app.get('/config/circuit/:id/lightCommands', (req, res) => {
|
|
729
|
+
let circuit = sys.circuits.getInterfaceById(parseInt(req.params.id, 10));
|
|
730
|
+
let commands = typeof circuit !== 'undefined' && typeof circuit.getLightThemes === 'function' ? circuit.getLightCommands(circuit.type) : [];
|
|
731
|
+
return res.status(200).send(commands);
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
app.get('/config/chlorinator/:id', (req, res) => {
|
|
735
|
+
return res.status(200).send(sys.chlorinators.getItemById(parseInt(req.params.id, 10)).get());
|
|
736
|
+
});
|
|
737
|
+
app.get('/config/chlorinators/search', async (req, res, next) => {
|
|
738
|
+
// Change the options for the pool.
|
|
739
|
+
try {
|
|
740
|
+
//await sys.board.virtualChlorinatorController.search();
|
|
741
|
+
return res.status(200).send(sys.chlorinators.getItemById(1).get());
|
|
742
|
+
}
|
|
743
|
+
catch (err) {
|
|
744
|
+
next(err);
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
app.put('/config/dateTime', async (req, res, next) => {
|
|
748
|
+
try {
|
|
749
|
+
let time = await sys.updateControllerDateTimeAsync(req.body);
|
|
750
|
+
return res.status(200).send(time);
|
|
751
|
+
}
|
|
752
|
+
catch (err) { next(err); }
|
|
753
|
+
});
|
|
754
|
+
app.get('/config/lightGroups/themes', (req, res) => {
|
|
755
|
+
// RSG: is this and /config/circuit/:id/lightThemes both needed?
|
|
756
|
+
let grp = sys.lightGroups.getItemById(parseInt(req.body.id, 10));
|
|
757
|
+
return res.status(200).send(grp.getLightThemes());
|
|
758
|
+
});
|
|
759
|
+
app.get('/config/lightGroups/commands', (req, res) => {
|
|
760
|
+
let grp = sys.lightGroups.getItemById(parseInt(req.body.id, 10));
|
|
761
|
+
return res.status(200).send(grp.getLightCommands());
|
|
762
|
+
});
|
|
763
|
+
app.get('/config/lightGroup/:id', (req, res) => {
|
|
764
|
+
// if (sys.controllerType === ControllerType.IntelliCenter) {
|
|
765
|
+
let grp = sys.lightGroups.getItemById(parseInt(req.params.id, 10));
|
|
766
|
+
return res.status(200).send(grp.getExtended());
|
|
767
|
+
// }
|
|
768
|
+
// else
|
|
769
|
+
// return res.status(200).send(sys.intellibrite.getExtended());
|
|
770
|
+
});
|
|
771
|
+
app.get('/config/lightGroup/colors', (req, res) => {
|
|
772
|
+
return res.status(200).send(sys.board.valueMaps.lightColors.toArray());
|
|
773
|
+
});
|
|
774
|
+
app.put('/config/lightGroup/:id/setColors', async (req, res, next) => {
|
|
775
|
+
try {
|
|
776
|
+
let id = parseInt(req.params.id, 10);
|
|
777
|
+
let grp = extend(true, { id: id }, req.body);
|
|
778
|
+
await sys.board.circuits.setLightGroupAttribsAsync(grp);
|
|
779
|
+
return res.status(200).send(sys.lightGroups.getItemById(id).getExtended());
|
|
780
|
+
}
|
|
781
|
+
catch (err) { next(err); }
|
|
782
|
+
});
|
|
783
|
+
app.get('/config/intellibrite/themes', (req, res) => {
|
|
784
|
+
return res.status(200).send(sys.board.circuits.getLightThemes(16));
|
|
785
|
+
});
|
|
786
|
+
app.get('/config/circuitGroup/:id', (req, res) => {
|
|
787
|
+
let grp = sys.circuitGroups.getItemById(parseInt(req.params.id, 10));
|
|
788
|
+
return res.status(200).send(grp.getExtended());
|
|
789
|
+
});
|
|
790
|
+
/* app.get('/config/chemController/search', async (req, res, next) => {
|
|
791
|
+
// Change the options for the pool.
|
|
792
|
+
try {
|
|
793
|
+
let result = await sys.board.virtualChemControllers.search();
|
|
794
|
+
return res.status(200).send(result);
|
|
795
|
+
}
|
|
796
|
+
catch (err) {
|
|
797
|
+
next(err);
|
|
798
|
+
}
|
|
799
|
+
}); */
|
|
800
|
+
app.put('/config/chemController', async (req, res, next) => {
|
|
801
|
+
try {
|
|
802
|
+
let chem = await sys.board.chemControllers.setChemControllerAsync(req.body);
|
|
803
|
+
return res.status(200).send(chem.get());
|
|
804
|
+
}
|
|
805
|
+
catch (err) { next(err); }
|
|
806
|
+
});
|
|
807
|
+
app.put('/config/chemDoser', async (req, res, next) => {
|
|
808
|
+
try {
|
|
809
|
+
let doser = await sys.board.chemDosers.setChemDoserAsync(req.body);
|
|
810
|
+
return res.status(200).send(doser.get());
|
|
811
|
+
}
|
|
812
|
+
catch (err) { next(err); }
|
|
813
|
+
|
|
814
|
+
});
|
|
815
|
+
app.put('/config/chemController/calibrateDose', async (req, res, next) => {
|
|
816
|
+
try {
|
|
817
|
+
let schem = await sys.board.chemControllers.calibrateDoseAsync(req.body);
|
|
818
|
+
return res.status(200).send(schem.getExtended());
|
|
819
|
+
}
|
|
820
|
+
catch (err) { next(err); }
|
|
821
|
+
});
|
|
822
|
+
app.put('/config/chemDoser/calibrateDose', async (req, res, next) => {
|
|
823
|
+
try {
|
|
824
|
+
let schem = await sys.board.chemDosers.calibrateDoseAsync(req.body);
|
|
825
|
+
return res.status(200).send(schem.getExtended());
|
|
826
|
+
}
|
|
827
|
+
catch (err) { next(err); }
|
|
828
|
+
});
|
|
829
|
+
app.put('/config/chemController/feed', async (req, res, next) => {
|
|
830
|
+
try {
|
|
831
|
+
let chem = await sys.board.chemControllers.setChemControllerAsync(req.body);
|
|
832
|
+
return res.status(200).send(chem.get());
|
|
833
|
+
}
|
|
834
|
+
catch (err) { next(err); }
|
|
835
|
+
});
|
|
836
|
+
app.delete('/config/chemController', async (req, res, next) => {
|
|
837
|
+
try {
|
|
838
|
+
let chem = await sys.board.chemControllers.deleteChemControllerAsync(req.body);
|
|
839
|
+
return res.status(200).send(chem.get());
|
|
840
|
+
}
|
|
841
|
+
catch (err) { next(err); }
|
|
842
|
+
});
|
|
843
|
+
app.delete('/config/chemDoser', async (req, res, next) => {
|
|
844
|
+
try {
|
|
845
|
+
let doser = await sys.board.chemDosers.deleteChemDoserAsync(req.body);
|
|
846
|
+
return res.status(200).send(doser.get());
|
|
847
|
+
}
|
|
848
|
+
catch (err) { next(err); }
|
|
849
|
+
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
/* app.get('/config/intellibrite', (req, res) => {
|
|
853
|
+
return res.status(200).send(sys.intellibrite.getExtended());
|
|
854
|
+
});
|
|
855
|
+
app.get('/config/intellibrite/colors', (req, res) => {
|
|
856
|
+
return res.status(200).send(sys.board.valueMaps.lightColors.toArray());
|
|
857
|
+
});
|
|
858
|
+
app.put('/config/intellibrite/setColors', (req, res) => {
|
|
859
|
+
let grp = extend(true, { id: 0 }, req.body);
|
|
860
|
+
sys.board.circuits.setIntelliBriteColors(new LightGroup(grp));
|
|
861
|
+
return res.status(200).send('OK');
|
|
862
|
+
}); */
|
|
863
|
+
app.get('/config', (req, res) => {
|
|
864
|
+
return res.status(200).send(sys.getSection('all'));
|
|
865
|
+
});
|
|
866
|
+
app.get('/config/:section', (req, res) => {
|
|
867
|
+
return res.status(200).send(sys.getSection(req.params.section));
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
|
|
871
|
+
/******* ENDPOINTS FOR MANAGING THE poolController APPLICATION *********/
|
|
872
|
+
app.put('/app/logger/setOptions', (req, res) => {
|
|
873
|
+
logger.setOptions(req.body);
|
|
874
|
+
return res.status(200).send(logger.options);
|
|
875
|
+
});
|
|
876
|
+
app.put('/app/logger/clearMessages', (req, res) => {
|
|
877
|
+
logger.clearMessages();
|
|
878
|
+
return res.status(200).send('OK');
|
|
879
|
+
});
|
|
880
|
+
app.get('/app/messages/broadcast/actions', (req, res) => {
|
|
881
|
+
return res.status(200).send(sys.board.valueMaps.msgBroadcastActions.toArray());
|
|
882
|
+
});
|
|
883
|
+
app.put('/app/config/reload', (req, res) => {
|
|
884
|
+
sys.board.reloadConfig();
|
|
885
|
+
return res.status(200).send('OK');
|
|
886
|
+
});
|
|
887
|
+
app.put('/app/interface', async (req, res, next) => {
|
|
888
|
+
try {
|
|
889
|
+
let iface = await webApp.updateServerInterface(req.body);
|
|
890
|
+
return res.status(200).send(iface);
|
|
891
|
+
}
|
|
892
|
+
catch (err) { next(err); }
|
|
893
|
+
});
|
|
894
|
+
app.put('/app/rs485Port', async (req, res, next) => {
|
|
895
|
+
try {
|
|
896
|
+
let port = await conn.setPortAsync(req.body);
|
|
897
|
+
return res.status(200).send(port);
|
|
898
|
+
}
|
|
899
|
+
catch (err) { next(err); }
|
|
900
|
+
});
|
|
901
|
+
// app.put('/app/screenlogic', async (req, res, next) => {
|
|
902
|
+
// try {
|
|
903
|
+
// let screenlogic = await sl.setScreenlogicAsync(req.body);
|
|
904
|
+
// return res.status(200).send(screenlogic);
|
|
905
|
+
// }
|
|
906
|
+
// catch (err) { next(err); }
|
|
907
|
+
// });
|
|
908
|
+
app.delete('/app/rs485Port', async (req, res, next) => {
|
|
909
|
+
try {
|
|
910
|
+
let port = await conn.deleteAuxPort(req.body);
|
|
911
|
+
return res.status(200).send(port);
|
|
912
|
+
}
|
|
913
|
+
catch (err) { next(err); }
|
|
914
|
+
});
|
|
915
|
+
app.get('/app/config/startPacketCapture', async (req, res, next) => {
|
|
916
|
+
try {
|
|
917
|
+
await startPacketCapture(true);
|
|
918
|
+
return res.status(200).send('OK');
|
|
919
|
+
} catch (err) { next(err); }
|
|
920
|
+
});
|
|
921
|
+
app.get('/app/config/startPacketCaptureWithoutReset', async (req, res, next) => {
|
|
922
|
+
try {
|
|
923
|
+
await startPacketCapture(false);
|
|
924
|
+
return res.status(200).send('OK');
|
|
925
|
+
} catch (err) { next(err); }
|
|
926
|
+
});
|
|
927
|
+
app.get('/app/config/stopPacketCapture', async (req, res, next) => {
|
|
928
|
+
try {
|
|
929
|
+
let file = await stopPacketCaptureAsync();
|
|
930
|
+
res.download(file);
|
|
931
|
+
}
|
|
932
|
+
catch (err) { next(err); }
|
|
933
|
+
});
|
|
934
|
+
app.get('/app/config/:section', (req, res) => {
|
|
935
|
+
return res.status(200).send(config.getSection(req.params.section));
|
|
936
|
+
});
|
|
937
|
+
app.get('/app/config/options/backup', async (req, res, next) => {
|
|
938
|
+
try {
|
|
939
|
+
let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [] });
|
|
940
|
+
let servers = await sys.ncp.getREMServers();
|
|
941
|
+
if (typeof servers !== 'undefined') {
|
|
942
|
+
// Just in case somebody deletes the backup section and doesn't put it back properly.
|
|
943
|
+
for (let i = 0; i < servers.length; i++) {
|
|
944
|
+
let srv = servers[i];
|
|
945
|
+
if (typeof opts.servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.push({ name: srv.name, uuid: srv.uuid, backup: false, host: srv.interface.options.host });
|
|
946
|
+
}
|
|
947
|
+
for (let i = opts.servers.length - 1; i >= 0; i--) {
|
|
948
|
+
let srv = opts.servers[i];
|
|
949
|
+
if (typeof servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.splice(i, 1);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
if (typeof opts.servers === 'undefined') opts.servers = [];
|
|
953
|
+
return res.status(200).send(opts);
|
|
954
|
+
} catch (err) { next(err); }
|
|
955
|
+
});
|
|
956
|
+
app.get('/app/config/options/restore', async (req, res, next) => {
|
|
957
|
+
try {
|
|
958
|
+
let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [], backupFiles: [] });
|
|
959
|
+
let servers = await sys.ncp.getREMServers();
|
|
960
|
+
if (typeof servers !== 'undefined') {
|
|
961
|
+
for (let i = 0; i < servers.length; i++) {
|
|
962
|
+
let srv = servers[i];
|
|
963
|
+
if (typeof opts.servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.push({ name: srv.name, uuid: srv.uuid, backup: false });
|
|
964
|
+
}
|
|
965
|
+
for (let i = opts.servers.length - 1; i >= 0; i--) {
|
|
966
|
+
let srv = opts.servers[i];
|
|
967
|
+
if (typeof servers.find(elem => elem.uuid === srv.uuid) === 'undefined') opts.servers.splice(i, 1);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
if (typeof opts.servers === 'undefined') opts.servers = [];
|
|
971
|
+
opts.backupFiles = await webApp.readBackupFiles();
|
|
972
|
+
return res.status(200).send(opts);
|
|
973
|
+
} catch (err) { next(err); }
|
|
974
|
+
|
|
975
|
+
});
|
|
976
|
+
app.put('/app/config/options/backup', async (req, res, next) => {
|
|
977
|
+
try {
|
|
978
|
+
config.setSection('controller.backups', req.body);
|
|
979
|
+
let opts = config.getSection('controller.backups', { automatic: false, interval: { days: 30, hours: 0 }, keepCount: 5, servers: [] });
|
|
980
|
+
webApp.autoBackup = utils.makeBool(opts.automatic);
|
|
981
|
+
await webApp.checkAutoBackup();
|
|
982
|
+
return res.status(200).send(opts);
|
|
983
|
+
} catch (err) { next(err); }
|
|
984
|
+
|
|
985
|
+
});
|
|
986
|
+
app.put('/app/config/createBackup', async (req, res, next) => {
|
|
987
|
+
try {
|
|
988
|
+
let ret = await webApp.backupServer(req.body);
|
|
989
|
+
res.download(ret.filePath);
|
|
990
|
+
}
|
|
991
|
+
catch (err) { next(err); }
|
|
992
|
+
});
|
|
993
|
+
app.delete('/app/backup/file', async (req, res, next) => {
|
|
994
|
+
try {
|
|
995
|
+
let opts = req.body;
|
|
996
|
+
fs.unlinkSync(opts.filePath);
|
|
997
|
+
return res.status(200).send(opts);
|
|
998
|
+
}
|
|
999
|
+
catch (err) { next(err); }
|
|
1000
|
+
});
|
|
1001
|
+
app.post('/app/backup/file', async (req, res, next) => {
|
|
1002
|
+
try {
|
|
1003
|
+
let file = multer({
|
|
1004
|
+
limits: { fileSize: 1000000 },
|
|
1005
|
+
storage: multer.memoryStorage()
|
|
1006
|
+
}).single('backupFile');
|
|
1007
|
+
file(req, res, async (err) => {
|
|
1008
|
+
try {
|
|
1009
|
+
if (err) { next(err); }
|
|
1010
|
+
else {
|
|
1011
|
+
// Validate the incoming data and save it off only if it is valid.
|
|
1012
|
+
let bf = await BackupFile.fromBuffer(req.file.originalname, req.file.buffer);
|
|
1013
|
+
if (typeof bf === 'undefined') {
|
|
1014
|
+
err = new ServiceProcessError(`Invalid backup file: ${req.file.originalname}`, 'POST: app/backup/file', 'extractBackupOptions');
|
|
1015
|
+
next(err);
|
|
1016
|
+
}
|
|
1017
|
+
else {
|
|
1018
|
+
if (fs.existsSync(bf.filePath))
|
|
1019
|
+
return next(new ServiceProcessError(`File already exists ${req.file.originalname}`, 'POST: app/backup/file', 'writeFile'));
|
|
1020
|
+
else {
|
|
1021
|
+
try {
|
|
1022
|
+
fs.writeFileSync(bf.filePath, new Uint8Array(req.file.buffer));
|
|
1023
|
+
} catch (e) { logger.error(`Error writing backup file ${e.message}`); }
|
|
1024
|
+
}
|
|
1025
|
+
return res.status(200).send(bf);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
} catch (e) {
|
|
1029
|
+
err = new ServiceProcessError(`Error uploading file: ${e.message}`, 'POST: app/backup/file', 'uploadFile');
|
|
1030
|
+
next(err);
|
|
1031
|
+
logger.error(`Error uploading file ${e.message}`);
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
} catch (err) { next(err); }
|
|
1035
|
+
});
|
|
1036
|
+
app.put('/app/restore/validate', async (req, res, next) => {
|
|
1037
|
+
try {
|
|
1038
|
+
// Validate all the restore options.
|
|
1039
|
+
let opts = req.body;
|
|
1040
|
+
let ctx = await webApp.validateRestore(opts);
|
|
1041
|
+
return res.status(200).send(ctx);
|
|
1042
|
+
} catch (err) { next(err); }
|
|
1043
|
+
});
|
|
1044
|
+
app.put('/app/restore/file', async (req, res, next) => {
|
|
1045
|
+
try {
|
|
1046
|
+
let opts = req.body;
|
|
1047
|
+
let results = await webApp.restoreServers(opts);
|
|
1048
|
+
return res.status(200).send(results);
|
|
1049
|
+
} catch (err) { next(err); }
|
|
1050
|
+
});
|
|
1051
|
+
app.put('/app/anslq25', async(req, res, next) => {
|
|
1052
|
+
try {
|
|
1053
|
+
await sys.anslq25Board.setAnslq25Async(req.body);
|
|
1054
|
+
return res.status(200).send(sys.anslq25.get(true));
|
|
1055
|
+
} catch (err) { next(err); }
|
|
1056
|
+
});
|
|
1057
|
+
app.delete('/app/anslq25', async(req, res, next) => {
|
|
1058
|
+
try {
|
|
1059
|
+
await sys.anslq25Board.deleteAnslq25Async(req.body);
|
|
1060
|
+
return res.status(200).send(sys.anslq25.get(true));
|
|
1061
|
+
} catch (err) { next(err); }
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1054
1064
|
}
|