nodejs-poolcontroller 7.7.0 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +26 -35
- package/Changelog +22 -0
- package/README.md +7 -3
- package/anslq25/MessagesMock.ts +218 -0
- package/anslq25/boards/MockBoardFactory.ts +50 -0
- package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
- package/anslq25/boards/MockSystemBoard.ts +217 -0
- package/anslq25/chemistry/MockChlorinator.ts +75 -0
- package/anslq25/pumps/MockPump.ts +84 -0
- package/app.ts +10 -14
- package/config/Config.ts +13 -9
- package/config/VersionCheck.ts +6 -2
- package/controller/Constants.ts +58 -25
- package/controller/Equipment.ts +225 -41
- package/controller/Errors.ts +2 -1
- package/controller/Lockouts.ts +34 -2
- package/controller/State.ts +491 -48
- package/controller/boards/AquaLinkBoard.ts +6 -3
- package/controller/boards/BoardFactory.ts +5 -1
- package/controller/boards/EasyTouchBoard.ts +1971 -1751
- package/controller/boards/IntelliCenterBoard.ts +1311 -1688
- package/controller/boards/IntelliComBoard.ts +7 -1
- package/controller/boards/IntelliTouchBoard.ts +153 -42
- package/controller/boards/NixieBoard.ts +209 -66
- package/controller/boards/SunTouchBoard.ts +393 -0
- package/controller/boards/SystemBoard.ts +1862 -1543
- package/controller/comms/Comms.ts +539 -138
- package/controller/comms/ScreenLogic.ts +1663 -0
- package/controller/comms/messages/Messages.ts +242 -60
- package/controller/comms/messages/config/ChlorinatorMessage.ts +4 -3
- package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
- package/controller/comms/messages/config/CircuitMessage.ts +81 -13
- package/controller/comms/messages/config/ConfigMessage.ts +3 -1
- package/controller/comms/messages/config/CoverMessage.ts +2 -1
- package/controller/comms/messages/config/CustomNameMessage.ts +2 -1
- package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
- package/controller/comms/messages/config/ExternalMessage.ts +33 -3
- package/controller/comms/messages/config/FeatureMessage.ts +2 -1
- package/controller/comms/messages/config/GeneralMessage.ts +2 -1
- package/controller/comms/messages/config/HeaterMessage.ts +3 -1
- package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
- package/controller/comms/messages/config/OptionsMessage.ts +12 -6
- package/controller/comms/messages/config/PumpMessage.ts +9 -12
- package/controller/comms/messages/config/RemoteMessage.ts +80 -13
- package/controller/comms/messages/config/ScheduleMessage.ts +43 -3
- package/controller/comms/messages/config/SecurityMessage.ts +2 -1
- package/controller/comms/messages/config/ValveMessage.ts +43 -26
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -7
- package/controller/comms/messages/status/EquipmentStateMessage.ts +93 -20
- package/controller/comms/messages/status/HeaterStateMessage.ts +24 -5
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +7 -4
- package/controller/comms/messages/status/IntelliValveStateMessage.ts +2 -1
- package/controller/comms/messages/status/PumpStateMessage.ts +72 -4
- package/controller/comms/messages/status/VersionMessage.ts +2 -1
- package/controller/nixie/Nixie.ts +15 -4
- package/controller/nixie/NixieEquipment.ts +1 -0
- package/controller/nixie/chemistry/ChemController.ts +300 -129
- package/controller/nixie/chemistry/ChemDoser.ts +806 -0
- package/controller/nixie/chemistry/Chlorinator.ts +133 -129
- package/controller/nixie/circuits/Circuit.ts +171 -30
- package/controller/nixie/heaters/Heater.ts +337 -173
- package/controller/nixie/pumps/Pump.ts +264 -236
- package/controller/nixie/schedules/Schedule.ts +9 -3
- package/defaultConfig.json +46 -5
- package/logger/Logger.ts +38 -9
- package/package.json +13 -9
- package/web/Server.ts +235 -122
- package/web/bindings/aqualinkD.json +114 -59
- package/web/bindings/homeassistant.json +437 -0
- package/web/bindings/influxDB.json +15 -0
- package/web/bindings/mqtt.json +28 -9
- package/web/bindings/mqttAlt.json +15 -0
- package/web/interfaces/baseInterface.ts +58 -7
- package/web/interfaces/httpInterface.ts +5 -2
- package/web/interfaces/influxInterface.ts +9 -2
- package/web/interfaces/mqttInterface.ts +234 -74
- package/web/interfaces/ruleInterface.ts +87 -0
- package/web/services/config/Config.ts +140 -33
- package/web/services/config/ConfigSocket.ts +2 -1
- package/web/services/state/State.ts +144 -3
- package/web/services/state/StateSocket.ts +65 -14
- package/web/services/utilities/Utilities.ts +189 -1
|
@@ -0,0 +1,393 @@
|
|
|
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 extend from 'extend';
|
|
19
|
+
import { EventEmitter } from 'events';
|
|
20
|
+
import { EasyTouchBoard, TouchConfigQueue, GetTouchConfigCategories, TouchCircuitCommands } from './EasyTouchBoard';
|
|
21
|
+
import { sys, PoolSystem, Circuit, ICircuit } from '../Equipment';
|
|
22
|
+
import { byteValueMap, EquipmentIdRange } from './SystemBoard';
|
|
23
|
+
import { state, ICircuitState } from '../State';
|
|
24
|
+
import { logger } from '../../logger/Logger';
|
|
25
|
+
import { conn } from '../comms/Comms';
|
|
26
|
+
import { Outbound } from "../comms/messages/Messages";
|
|
27
|
+
import { InvalidEquipmentIdError } from "../Errors";
|
|
28
|
+
import { utils } from "../Constants";
|
|
29
|
+
|
|
30
|
+
export class SunTouchBoard extends EasyTouchBoard {
|
|
31
|
+
constructor(system: PoolSystem) {
|
|
32
|
+
super(system); // graph chain to EasyTouchBoard constructor.
|
|
33
|
+
this.valueMaps.expansionBoards = new byteValueMap([
|
|
34
|
+
[41, { name: 'shared', part: '520820', desc: 'Pool and Spa controller', bodies: 2, valves: 4, circuits: 5, single: false, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }],
|
|
35
|
+
[40, { name: 'stsingle', part: '520819', desc: 'Pool or Spa controller', bodies: 2, valves: 4, circuits: 5, single: true, shared: true, dual: false, features: 4, chlorinators: 1, chemControllers: 1 }]
|
|
36
|
+
]);
|
|
37
|
+
this._statusInterval = -1;
|
|
38
|
+
this.equipmentIds.circuits = new EquipmentIdRange(1, 6);
|
|
39
|
+
this.equipmentIds.features = new EquipmentIdRange(7, 10);
|
|
40
|
+
this.equipmentIds.virtualCircuits = new EquipmentIdRange(128, 136);
|
|
41
|
+
this.equipmentIds.circuitGroups = new EquipmentIdRange(192, function () { return this.start + sys.equipment.maxCircuitGroups - 1; });
|
|
42
|
+
this.equipmentIds.circuits.start = 1;
|
|
43
|
+
this.equipmentIds.circuits.isInRange = (id: number) => { return [1, 2, 3, 4, 6].includes(id); };
|
|
44
|
+
this.equipmentIds.features.isInRange = (id: number) => { return [7, 8, 9, 10].includes(id); };
|
|
45
|
+
if (typeof sys.configVersion.equipment === 'undefined') { sys.configVersion.equipment = 0; }
|
|
46
|
+
this.valueMaps.heatSources = new byteValueMap([
|
|
47
|
+
[0, { name: 'off', desc: 'Off' }],
|
|
48
|
+
[32, { name: 'nochange', desc: 'No Change' }]
|
|
49
|
+
]);
|
|
50
|
+
this.valueMaps.heatStatus = new byteValueMap([
|
|
51
|
+
[0, { name: 'off', desc: 'Off' }],
|
|
52
|
+
[1, { name: 'heater', desc: 'Heater' }],
|
|
53
|
+
[2, { name: 'cooling', desc: 'Cooling' }],
|
|
54
|
+
[3, { name: 'solar', desc: 'Solar' }],
|
|
55
|
+
[4, { name: 'hpheat', desc: 'Heatpump' }],
|
|
56
|
+
[5, { name: 'dual', desc: 'Dual' }]
|
|
57
|
+
]);
|
|
58
|
+
this.valueMaps.circuitFunctions = new byteValueMap([
|
|
59
|
+
[0, { name: 'generic', desc: 'Generic' }],
|
|
60
|
+
[1, { name: 'spa', desc: 'Spa', hasHeatSource: true, body: 2 }],
|
|
61
|
+
[2, { name: 'pool', desc: 'Pool', hasHeatSource: true, body: 1 }],
|
|
62
|
+
[5, { name: 'mastercleaner', desc: 'Master Cleaner', body: 1 }],
|
|
63
|
+
[7, { name: 'light', desc: 'Light', isLight: true }],
|
|
64
|
+
[9, { name: 'samlight', desc: 'SAM Light', isLight: true }],
|
|
65
|
+
[10, { name: 'sallight', desc: 'SAL Light', isLight: true }],
|
|
66
|
+
[11, { name: 'photongen', desc: 'Photon Gen', isLight: true }],
|
|
67
|
+
[12, { name: 'colorwheel', desc: 'Color Wheel', isLight: true }],
|
|
68
|
+
[13, { name: 'valve', desc: 'Valve' }],
|
|
69
|
+
[14, { name: 'spillway', desc: 'Spillway' }],
|
|
70
|
+
[15, { name: 'floorcleaner', desc: 'Floor Cleaner', body: 1 }], // This circuit function does not seem to exist in IntelliTouch.
|
|
71
|
+
[19, { name: 'notused', desc: 'Not Used' }],
|
|
72
|
+
[63, { name: 'cleaner', desc: 'Cleaner' }],
|
|
73
|
+
|
|
74
|
+
]);
|
|
75
|
+
this.valueMaps.virtualCircuits = new byteValueMap([
|
|
76
|
+
[20, { name: 'solar', desc: 'Solar', assignableToPumpCircuit: true }],
|
|
77
|
+
[129, { name: 'poolspa', desc: 'Pool/Spa' }],
|
|
78
|
+
[130, { name: 'poolHeater', desc: 'Pool Heater', assignableToPumpCircuit: true }],
|
|
79
|
+
[131, { name: 'spaHeater', desc: 'Spa Heater', assignableToPumpCircuit: true }],
|
|
80
|
+
[132, { name: 'freeze', desc: 'Freeze', assignableToPumpCircuit: true }],
|
|
81
|
+
[258, { name: 'anyHeater', desc: 'Any Heater' }]
|
|
82
|
+
]);
|
|
83
|
+
this.valueMaps.circuitNames = new byteValueMap([
|
|
84
|
+
[3, { name: 'aux1', desc: 'AUX 1' }],
|
|
85
|
+
[4, { name: 'aux2', desc: 'AUX 2' }],
|
|
86
|
+
[5, { name: 'aux3', desc: 'AUX 3' }],
|
|
87
|
+
[6, { name: 'feature1', desc: 'FEATURE 1' }],
|
|
88
|
+
[7, { name: 'feature2', desc: 'FEATURE 2' }],
|
|
89
|
+
[8, { name: 'feature3', desc: 'FEATURE 3' }],
|
|
90
|
+
[9, { name: 'feature4', desc: 'FEATURE 4' }],
|
|
91
|
+
[61, { name: 'pool', desc: 'Pool' }],
|
|
92
|
+
[72, { name: 'spa', desc: 'Spa' }]
|
|
93
|
+
]);
|
|
94
|
+
this._configQueue = new SunTouchConfigQueue();
|
|
95
|
+
}
|
|
96
|
+
public initExpansionModules(byte1: number, byte2: number) {
|
|
97
|
+
console.log(`Pentair SunTouch System Detected!`);
|
|
98
|
+
sys.equipment.model = 'Suntouch';
|
|
99
|
+
|
|
100
|
+
// Initialize the installed personality board.
|
|
101
|
+
let mt = this.valueMaps.expansionBoards.transform(byte1); // Only have one example of SunTouch and it is a single body system (40).
|
|
102
|
+
let mod = sys.equipment.modules.getItemById(0, true);
|
|
103
|
+
if (mod.name !== mt.name) {
|
|
104
|
+
logger.info(`Clearing SunTouch configuration...`);
|
|
105
|
+
sys.bodies.removeItemById(1);
|
|
106
|
+
sys.bodies.removeItemById(2);
|
|
107
|
+
sys.bodies.removeItemById(3);
|
|
108
|
+
sys.bodies.removeItemById(4);
|
|
109
|
+
sys.circuits.clear(0);
|
|
110
|
+
sys.circuits.removeItemById(1);
|
|
111
|
+
sys.circuits.removeItemById(6);
|
|
112
|
+
sys.features.clear(0);
|
|
113
|
+
state.circuits.clear();
|
|
114
|
+
state.temps.clear();
|
|
115
|
+
sys.filters.clear(0);
|
|
116
|
+
state.filters.clear();
|
|
117
|
+
}
|
|
118
|
+
mod.name = mt.name;
|
|
119
|
+
mod.desc = mt.desc;
|
|
120
|
+
mod.type = byte1;
|
|
121
|
+
mod.part = mt.part;
|
|
122
|
+
let eq = sys.equipment;
|
|
123
|
+
let md = mod.get();
|
|
124
|
+
eq.maxBodies = md.bodies = typeof mt.bodies !== 'undefined' ? mt.bodies : mt.shared ? 2 : 1;
|
|
125
|
+
eq.maxCircuits = md.circuits = typeof mt.circuits !== 'undefined' ? mt.circuits : 3;
|
|
126
|
+
eq.maxFeatures = md.features = typeof mt.features !== 'undefined' ? mt.features : 0
|
|
127
|
+
eq.maxValves = md.valves = typeof mt.valves !== 'undefined' ? mt.valves : 2;
|
|
128
|
+
eq.maxPumps = md.maxPumps = typeof mt.pumps !== 'undefined' ? mt.pumps : 2;
|
|
129
|
+
eq.shared = mt.shared || false;
|
|
130
|
+
eq.dual = mt.dual || false;
|
|
131
|
+
eq.single = mt.single || false;
|
|
132
|
+
eq.maxChlorinators = md.chlorinators = 1;
|
|
133
|
+
eq.maxChemControllers = md.chemControllers = 1;
|
|
134
|
+
eq.maxCustomNames = 0;
|
|
135
|
+
eq.maxSchedules = 6;
|
|
136
|
+
if (sys.equipment.single) {
|
|
137
|
+
sys.board.valueMaps.circuitNames.merge([[61, { name: 'pool', desc: 'LO-Temp' }], [72, { name: 'spa', desc: 'HI-Temp' }]]);
|
|
138
|
+
sys.board.valueMaps.circuitFunctions.merge([[1, { name: 'pool', desc: 'LO-Temp', hasHeatSource: true }], [2, { name: 'spa', desc: 'HI-Temp', hasHeatSource: true }]]);
|
|
139
|
+
sys.board.valueMaps.virtualCircuits.merge([[130, { name: 'poolHeater', desc: 'LO-Temp Heater' }], [131, { name: 'spaHeater', desc: 'HI-Temp Heater' }]]);
|
|
140
|
+
sys.board.valueMaps.bodyTypes.merge([[0, { name: 'pool', desc: 'LO-Temp' }], [1, { name: 'spa', desc: 'HI-Temp' }]]);
|
|
141
|
+
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
sys.board.valueMaps.circuitNames.merge([[61, { name: 'pool', desc: 'Pool' }], [72, { name: 'spa', desc: 'Spa' }]]);
|
|
145
|
+
sys.board.valueMaps.circuitFunctions.merge([[1, { name: 'pool', desc: 'Pool', hasHeatsource: true }], [2, { name: 'spa', desc: 'Pool', hasHeatSource: true }]]);
|
|
146
|
+
sys.board.valueMaps.virtualCircuits.merge([[130, { name: 'poolHeater', desc: 'Pool Heater' }], [131, { name: 'spaHeater', desc: 'Spa Heater' }]]);
|
|
147
|
+
sys.board.valueMaps.bodyTypes.merge([[0, { name: 'pool', desc: 'Pool' }], [1, { name: 'spa', desc: 'Spa' }]]);
|
|
148
|
+
}
|
|
149
|
+
// Calculate out the invalid ids.
|
|
150
|
+
sys.board.equipmentIds.invalidIds.set([]);
|
|
151
|
+
// SunTouch bit mapping for circuits and features
|
|
152
|
+
// Bit Mask Circuit/Feature id
|
|
153
|
+
// 1 = 0x01 Spa 1
|
|
154
|
+
// 2 = 0x02 Aux 1 2
|
|
155
|
+
// 3 = 0x04 Aux 2 3
|
|
156
|
+
// 4 = 0x08 Aux 3 4
|
|
157
|
+
// 5 = 0x10 Feature 1 7
|
|
158
|
+
// 6 = 0x20 Pool 6
|
|
159
|
+
// 7 = 0x40 Feature 2 8
|
|
160
|
+
// 8 = 0x80 Feature 3 9
|
|
161
|
+
// 9 = 0x01 Feature 4 10
|
|
162
|
+
sys.board.equipmentIds.invalidIds.merge([5]);
|
|
163
|
+
state.equipment.model = sys.equipment.model = 'SunTouch';
|
|
164
|
+
sys.equipment.setEquipmentIds();
|
|
165
|
+
this.initBodyDefaults();
|
|
166
|
+
state.emitControllerChange();
|
|
167
|
+
}
|
|
168
|
+
public initBodyDefaults() {
|
|
169
|
+
// Initialize the bodies. We will need these very soon.
|
|
170
|
+
for (let i = 1; i <= sys.equipment.maxBodies; i++) {
|
|
171
|
+
// Add in the bodies for the configuration. These need to be set.
|
|
172
|
+
let cbody = sys.bodies.getItemById(i, true);
|
|
173
|
+
let tbody = state.temps.bodies.getItemById(i, true);
|
|
174
|
+
cbody.isActive = true;
|
|
175
|
+
tbody.circuit = cbody.circuit = i === 1 ? 1 : 6;
|
|
176
|
+
tbody.type = cbody.type = i - 1; // This will set the first body to pool/Lo-Temp and the second body to spa/Hi-Temp.
|
|
177
|
+
if (typeof cbody.name === 'undefined') {
|
|
178
|
+
if (sys.equipment.single) {
|
|
179
|
+
tbody.name = cbody.name = i === 1 ? 'LO' : 'HI';
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
let bt = sys.board.valueMaps.bodyTypes.transform(cbody.type);
|
|
183
|
+
tbody.name = cbody.name = bt.desc;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
let c = sys.circuits.getItemById(tbody.circuit, true, { isActive: false });
|
|
187
|
+
c.master = 0;
|
|
188
|
+
let cstate = state.circuits.getItemById(c.id, true);
|
|
189
|
+
cstate.type = c.type = tbody.circuit === 6 ? sys.board.valueMaps.circuitFunctions.encode('pool') : sys.board.valueMaps.circuitFunctions.encode('spa');
|
|
190
|
+
let name = sys.board.valueMaps.circuitNames.transform(c.id === 6 ? 61 : 72);
|
|
191
|
+
cstate.nameId = c.nameId = name.val;
|
|
192
|
+
// Check to see if the body circuit exists. We are going to create these so that they start
|
|
193
|
+
// out with the proper type.
|
|
194
|
+
if (!c.isActive) {
|
|
195
|
+
cstate.showInFeatures = c.showInFeatures = false;
|
|
196
|
+
c.isActive = cstate.isActive = true;
|
|
197
|
+
console.log(name);
|
|
198
|
+
cstate.name = c.name = name.desc;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
sys.bodies.removeItemById(3);
|
|
202
|
+
sys.bodies.removeItemById(4);
|
|
203
|
+
state.temps.bodies.removeItemById(3);
|
|
204
|
+
state.temps.bodies.removeItemById(4);
|
|
205
|
+
sys.board.heaters.initTempSensors();
|
|
206
|
+
sys.general.options.clockMode = sys.general.options.clockMode || 12;
|
|
207
|
+
sys.general.options.clockSource = sys.general.options.clockSource || 'manual';
|
|
208
|
+
// We are going to intialize the pool circuits
|
|
209
|
+
let filter = sys.filters.getItemById(1, true);
|
|
210
|
+
if (typeof filter.name === 'undefined') filter.name = 'Filter';
|
|
211
|
+
state.filters.getItemById(1, true).name = filter.name;
|
|
212
|
+
}
|
|
213
|
+
public circuits: SunTouchCircuitCommands = new SunTouchCircuitCommands(this);
|
|
214
|
+
|
|
215
|
+
}
|
|
216
|
+
class SunTouchConfigQueue extends TouchConfigQueue {
|
|
217
|
+
public queueChanges() {
|
|
218
|
+
this.reset();
|
|
219
|
+
logger.info(`Requesting ${sys.controllerType} configuration`);
|
|
220
|
+
// Config categories that do nothing
|
|
221
|
+
// 195 - [0-2]
|
|
222
|
+
// 196 - [0-2]
|
|
223
|
+
// 198 - [0-2]
|
|
224
|
+
// 199 - [0-2]
|
|
225
|
+
// 201 - [0-2]
|
|
226
|
+
// 202 - [0-2] - Custom Names
|
|
227
|
+
// 204 - [0-2]
|
|
228
|
+
// 205 - [0-2]
|
|
229
|
+
// 206 - [0-2]
|
|
230
|
+
// 207 - [0-2]
|
|
231
|
+
// 208 - [0-2]
|
|
232
|
+
// 209 - [0-10] - This returns invalid data about schedules. It is simply not correct
|
|
233
|
+
// 212 - [0-2]
|
|
234
|
+
// 213 - [0-2]
|
|
235
|
+
// 214 - [0]
|
|
236
|
+
// 215 - [0-2]
|
|
237
|
+
// 216 - [0-4] - This does not return anything about the pumps
|
|
238
|
+
// 218 - [0-2]
|
|
239
|
+
// 219 - [0-2]
|
|
240
|
+
// 220 - [0-2]
|
|
241
|
+
// 223 - [0-2]
|
|
242
|
+
// 224 - [1-2]
|
|
243
|
+
// 226 - [0]
|
|
244
|
+
// 228 - [0-2]
|
|
245
|
+
// 229 - [0-2]
|
|
246
|
+
// 230 - [0-2]
|
|
247
|
+
// 231 - [0-2]
|
|
248
|
+
// 233 - [0-2]
|
|
249
|
+
// 234 - [0-2]
|
|
250
|
+
// 235 - [0-2]
|
|
251
|
+
// 236 - [0-2]
|
|
252
|
+
// 237 - [0-2]
|
|
253
|
+
// 238 - [0-2]
|
|
254
|
+
// 239 - [0-2]
|
|
255
|
+
// 240 - [0-2]
|
|
256
|
+
// 241 - [0-2]
|
|
257
|
+
// 242 - [0-2]
|
|
258
|
+
// 243 - [0-2]
|
|
259
|
+
// 244 - [0-2]
|
|
260
|
+
// 245 - [0-2]
|
|
261
|
+
// 246 - [0-2]
|
|
262
|
+
// 247 - [0-2]
|
|
263
|
+
// 248 - [0-2]
|
|
264
|
+
// 249 - [0-2]
|
|
265
|
+
// 250 - [0-2]
|
|
266
|
+
// 251 - [0-2]
|
|
267
|
+
|
|
268
|
+
this.queueItems(GetTouchConfigCategories.version); // 252
|
|
269
|
+
this.queueItems(GetTouchConfigCategories.dateTime, [0]); //197
|
|
270
|
+
this.queueItems(GetTouchConfigCategories.heatTemperature, [0]); // 200
|
|
271
|
+
//this.queueRange(GetTouchConfigCategories.customNames, 0, sys.equipment.maxCustomNames - 1); 202 SunTouch does not appear to support custom names. No responses
|
|
272
|
+
this.queueItems(GetTouchConfigCategories.solarHeatPump, [0]); // 208
|
|
273
|
+
this.queueRange(GetTouchConfigCategories.circuits, 1, sys.board.equipmentIds.features.end); // 203 circuits & Features
|
|
274
|
+
//this.queueRange(GetTouchConfigCategories.schedules, 1, sys.equipment.maxSchedules); // 209 This return is worthless in SunTouch
|
|
275
|
+
this.queueItems(GetTouchConfigCategories.delays, [0]); // 227
|
|
276
|
+
this.queueItems(GetTouchConfigCategories.settings, [0]); // 232
|
|
277
|
+
this.queueItems(GetTouchConfigCategories.intellifloSpaSideRemotes, [0]); // 225 QuickTouch
|
|
278
|
+
this.queueItems(GetTouchConfigCategories.valves, [0]); // 221
|
|
279
|
+
|
|
280
|
+
// Check for these positions to see if we can get it to spit out all the schedules.
|
|
281
|
+
this.queueItems(222, [0]); // First 2 schedules. This request ignores the payload and does not return additional items.
|
|
282
|
+
this.queueItems(211, [0]);
|
|
283
|
+
this.queueItems(19, [0]); // If we send this request it will respond with a valid 147. The correct request however should be 211.
|
|
284
|
+
//this.queueRange(GetTouchConfigCategories.circuitGroups, 0, sys.equipment.maxFeatures - 1); SunTouch does not support macros
|
|
285
|
+
this.queueItems(GetTouchConfigCategories.intellichlor, [0]); // 217
|
|
286
|
+
//let test = [195, 196, 208, 214, 218, 219, 220, 226, 228, 229, 230, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251];
|
|
287
|
+
//for (let i = 0; i < test.length; i++) {
|
|
288
|
+
// let cat = test[i];
|
|
289
|
+
// this.queueRange(cat, 0, 2);
|
|
290
|
+
//}
|
|
291
|
+
|
|
292
|
+
if (this.remainingItems > 0) {
|
|
293
|
+
var self = this;
|
|
294
|
+
setTimeout(() => { self.processNext(); }, 50);
|
|
295
|
+
} else state.status = 1;
|
|
296
|
+
state.emitControllerChange();
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
class SunTouchCircuitCommands extends TouchCircuitCommands {
|
|
301
|
+
public async setCircuitStateAsync(id: number, val: boolean, ignoreDelays?: boolean): Promise<ICircuitState> {
|
|
302
|
+
if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError('Circuit or Feature id not valid', id, 'Circuit'));
|
|
303
|
+
let c = sys.circuits.getInterfaceById(id);
|
|
304
|
+
if (c.master !== 0) return await super.setCircuitStateAsync(id, val);
|
|
305
|
+
if (id === 192 || c.type === 3) return await sys.board.circuits.setLightGroupThemeAsync(id - 191, val ? 1 : 0);
|
|
306
|
+
if (id >= 192) return await sys.board.circuits.setCircuitGroupStateAsync(id, val);
|
|
307
|
+
|
|
308
|
+
// for some dumb reason, if the spa is on and the pool circuit is desired to be on,
|
|
309
|
+
// it will ignore the packet.
|
|
310
|
+
// We can override that by emulating a click to turn off the spa instead of turning
|
|
311
|
+
// on the pool
|
|
312
|
+
if (sys.equipment.maxBodies > 1 && id === 6 && val && state.circuits.getItemById(1).isOn) {
|
|
313
|
+
id = 1;
|
|
314
|
+
val = false;
|
|
315
|
+
}
|
|
316
|
+
let mappedId = id;
|
|
317
|
+
if (id === 7) mappedId = 5;
|
|
318
|
+
else if (id > 6) mappedId = id - 1;
|
|
319
|
+
let cstate = state.circuits.getInterfaceById(id);
|
|
320
|
+
let out = Outbound.create({
|
|
321
|
+
action: 134,
|
|
322
|
+
payload: [mappedId, val ? 1 : 0],
|
|
323
|
+
retries: 3,
|
|
324
|
+
response: true,
|
|
325
|
+
scope: `circuitState${id}`
|
|
326
|
+
});
|
|
327
|
+
await out.sendAsync();
|
|
328
|
+
sys.board.circuits.setEndTime(c, cstate, val);
|
|
329
|
+
cstate.isOn = val;
|
|
330
|
+
state.emitEquipmentChanges();
|
|
331
|
+
return cstate;
|
|
332
|
+
|
|
333
|
+
}
|
|
334
|
+
public async setCircuitAsync(data: any): Promise<ICircuit> {
|
|
335
|
+
try {
|
|
336
|
+
// example [255,0,255][165,33,16,34,139,5][17,14,209,0,0][2,120]
|
|
337
|
+
// set circuit 17 to function 14 and name 209
|
|
338
|
+
// response: [255,0,255][165,33,34,16,1,1][139][1,133]
|
|
339
|
+
let id = parseInt(data.id, 10);
|
|
340
|
+
if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError('Circuit Id is invalid', data.id, 'Feature'));
|
|
341
|
+
if (id >= 255 || data.master === 1) return super.setCircuitAsync(data);
|
|
342
|
+
let circuit = sys.circuits.getInterfaceById(id);
|
|
343
|
+
// Alright check to see if we are adding a nixie circuit.
|
|
344
|
+
if (id === -1 || circuit.master !== 0) {
|
|
345
|
+
let circ = await super.setCircuitAsync(data);
|
|
346
|
+
return circ;
|
|
347
|
+
}
|
|
348
|
+
let typeByte = parseInt(data.type, 10) || circuit.type || sys.board.valueMaps.circuitFunctions.getValue('generic');
|
|
349
|
+
let nameByte = circuit.nameId; // You cannot change the Name Id in SunTouch.
|
|
350
|
+
if (typeof data.nameId !== 'undefined') nameByte = data.nameId;
|
|
351
|
+
let mappedId = id;
|
|
352
|
+
if (id === 7) mappedId = 5;
|
|
353
|
+
else if (id > 6) mappedId = id - 1;
|
|
354
|
+
|
|
355
|
+
let out = Outbound.create({
|
|
356
|
+
action: 139,
|
|
357
|
+
payload: [mappedId, typeByte | (utils.makeBool(data.freeze) ? 64 : 0), nameByte, 0, 0],
|
|
358
|
+
retries: 3,
|
|
359
|
+
response: true
|
|
360
|
+
});
|
|
361
|
+
await out.sendAsync();
|
|
362
|
+
circuit = sys.circuits.getInterfaceById(data.id);
|
|
363
|
+
let cstate = state.circuits.getInterfaceById(data.id);
|
|
364
|
+
circuit.nameId = cstate.nameId = nameByte;
|
|
365
|
+
circuit.name = typeof data.name !== 'undefined' ? data.name.toString() : circuit.name;
|
|
366
|
+
circuit.showInFeatures = cstate.showInFeatures = typeof data.showInFeatures !== 'undefined' ? data.showInFeatures : circuit.showInFeatures;
|
|
367
|
+
circuit.freeze = typeof data.freeze !== 'undefined' ? utils.makeBool(data.freeze) : circuit.freeze;
|
|
368
|
+
circuit.type = cstate.type = typeByte;
|
|
369
|
+
circuit.eggTimer = typeof data.eggTimer !== 'undefined' ? parseInt(data.eggTimer, 10) : circuit.eggTimer || 720;
|
|
370
|
+
circuit.dontStop = (typeof data.dontStop !== 'undefined') ? utils.makeBool(data.dontStop) : circuit.eggTimer === 1620;
|
|
371
|
+
cstate.isActive = circuit.isActive = true;
|
|
372
|
+
circuit.master = 0;
|
|
373
|
+
let eggTimer = sys.eggTimers.find(elem => elem.circuit === parseInt(data.id, 10));
|
|
374
|
+
try {
|
|
375
|
+
if (circuit.eggTimer === 720) {
|
|
376
|
+
if (typeof eggTimer !== 'undefined') await sys.board.schedules.deleteEggTimerAsync({ id: eggTimer.id });
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
await sys.board.schedules.setEggTimerAsync({ id: typeof eggTimer !== 'undefined' ? eggTimer.id : -1, runTime: circuit.eggTimer, dontStop: circuit.dontStop, circuit: circuit.id });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
// fail silently if there are no slots to fill in the schedules
|
|
384
|
+
logger.info(`Cannot set/delete eggtimer on circuit ${circuit.id}. Error: ${err.message}`);
|
|
385
|
+
circuit.eggTimer = 720;
|
|
386
|
+
circuit.dontStop = false;
|
|
387
|
+
}
|
|
388
|
+
state.emitEquipmentChanges();
|
|
389
|
+
return circuit;
|
|
390
|
+
}
|
|
391
|
+
catch (err) { logger.error(`setCircuitAsync error setting circuit ${JSON.stringify(data)}: ${err}`); return Promise.reject(err); }
|
|
392
|
+
}
|
|
393
|
+
}
|