nodejs-poolcontroller 7.6.1 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/.eslintrc.json +36 -45
  2. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
  3. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
  4. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  6. package/CONTRIBUTING.md +74 -74
  7. package/Changelog +242 -215
  8. package/Dockerfile +17 -17
  9. package/Gruntfile.js +40 -40
  10. package/LICENSE +661 -661
  11. package/README.md +195 -191
  12. package/anslq25/MessagesMock.ts +218 -0
  13. package/anslq25/boards/MockBoardFactory.ts +50 -0
  14. package/anslq25/boards/MockEasyTouchBoard.ts +696 -0
  15. package/anslq25/boards/MockSystemBoard.ts +217 -0
  16. package/anslq25/chemistry/MockChlorinator.ts +75 -0
  17. package/anslq25/pumps/MockPump.ts +84 -0
  18. package/app.ts +10 -14
  19. package/config/Config.ts +26 -8
  20. package/config/VersionCheck.ts +8 -4
  21. package/controller/Constants.ts +59 -25
  22. package/controller/Equipment.ts +2667 -2459
  23. package/controller/Errors.ts +181 -180
  24. package/controller/Lockouts.ts +534 -436
  25. package/controller/State.ts +596 -77
  26. package/controller/boards/AquaLinkBoard.ts +1003 -0
  27. package/controller/boards/BoardFactory.ts +53 -45
  28. package/controller/boards/EasyTouchBoard.ts +3079 -2653
  29. package/controller/boards/IntelliCenterBoard.ts +3821 -4230
  30. package/controller/boards/IntelliComBoard.ts +69 -63
  31. package/controller/boards/IntelliTouchBoard.ts +384 -241
  32. package/controller/boards/NixieBoard.ts +1871 -1675
  33. package/controller/boards/SunTouchBoard.ts +393 -0
  34. package/controller/boards/SystemBoard.ts +5244 -4697
  35. package/controller/comms/Comms.ts +905 -541
  36. package/controller/comms/ScreenLogic.ts +1663 -0
  37. package/controller/comms/messages/Messages.ts +382 -54
  38. package/controller/comms/messages/config/ChlorinatorMessage.ts +8 -4
  39. package/controller/comms/messages/config/CircuitGroupMessage.ts +5 -2
  40. package/controller/comms/messages/config/CircuitMessage.ts +82 -13
  41. package/controller/comms/messages/config/ConfigMessage.ts +3 -1
  42. package/controller/comms/messages/config/CoverMessage.ts +2 -1
  43. package/controller/comms/messages/config/CustomNameMessage.ts +31 -30
  44. package/controller/comms/messages/config/EquipmentMessage.ts +5 -1
  45. package/controller/comms/messages/config/ExternalMessage.ts +33 -3
  46. package/controller/comms/messages/config/FeatureMessage.ts +2 -1
  47. package/controller/comms/messages/config/GeneralMessage.ts +2 -1
  48. package/controller/comms/messages/config/HeaterMessage.ts +145 -11
  49. package/controller/comms/messages/config/IntellichemMessage.ts +2 -1
  50. package/controller/comms/messages/config/OptionsMessage.ts +16 -27
  51. package/controller/comms/messages/config/PumpMessage.ts +62 -47
  52. package/controller/comms/messages/config/RemoteMessage.ts +80 -13
  53. package/controller/comms/messages/config/ScheduleMessage.ts +390 -347
  54. package/controller/comms/messages/config/SecurityMessage.ts +2 -1
  55. package/controller/comms/messages/config/ValveMessage.ts +44 -27
  56. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +44 -91
  57. package/controller/comms/messages/status/EquipmentStateMessage.ts +139 -30
  58. package/controller/comms/messages/status/HeaterStateMessage.ts +135 -86
  59. package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -445
  60. package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -35
  61. package/controller/comms/messages/status/PumpStateMessage.ts +92 -2
  62. package/controller/comms/messages/status/VersionMessage.ts +2 -1
  63. package/controller/nixie/Nixie.ts +173 -162
  64. package/controller/nixie/NixieEquipment.ts +104 -103
  65. package/controller/nixie/bodies/Body.ts +120 -120
  66. package/controller/nixie/bodies/Filter.ts +135 -135
  67. package/controller/nixie/chemistry/ChemController.ts +2682 -2498
  68. package/controller/nixie/chemistry/ChemDoser.ts +806 -0
  69. package/controller/nixie/chemistry/Chlorinator.ts +367 -314
  70. package/controller/nixie/circuits/Circuit.ts +402 -248
  71. package/controller/nixie/heaters/Heater.ts +815 -649
  72. package/controller/nixie/pumps/Pump.ts +934 -661
  73. package/controller/nixie/schedules/Schedule.ts +319 -257
  74. package/controller/nixie/valves/Valve.ts +170 -170
  75. package/defaultConfig.json +346 -286
  76. package/logger/DataLogger.ts +448 -448
  77. package/logger/Logger.ts +38 -9
  78. package/package.json +60 -56
  79. package/tsconfig.json +25 -25
  80. package/web/Server.ts +275 -117
  81. package/web/bindings/aqualinkD.json +560 -0
  82. package/web/bindings/homeassistant.json +437 -0
  83. package/web/bindings/influxDB.json +1066 -1021
  84. package/web/bindings/mqtt.json +721 -654
  85. package/web/bindings/mqttAlt.json +746 -684
  86. package/web/bindings/rulesManager.json +54 -54
  87. package/web/bindings/smartThings-Hubitat.json +31 -31
  88. package/web/bindings/valveRelays.json +20 -20
  89. package/web/bindings/vera.json +25 -25
  90. package/web/interfaces/baseInterface.ts +188 -136
  91. package/web/interfaces/httpInterface.ts +148 -124
  92. package/web/interfaces/influxInterface.ts +283 -245
  93. package/web/interfaces/mqttInterface.ts +695 -475
  94. package/web/interfaces/ruleInterface.ts +87 -0
  95. package/web/services/config/Config.ts +177 -49
  96. package/web/services/config/ConfigSocket.ts +2 -1
  97. package/web/services/state/State.ts +154 -3
  98. package/web/services/state/StateSocket.ts +69 -18
  99. package/web/services/utilities/Utilities.ts +232 -42
  100. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
  101. package/config copy.json +0 -300
  102. package/issue_template.md +0 -52
@@ -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
+ }