nodejs-poolcontroller 8.3.0 → 8.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.eslintrc.json +36 -36
  2. package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -84
  3. package/.github/ISSUE_TEMPLATE/2-docs.md +12 -12
  4. package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -28
  5. package/.github/ISSUE_TEMPLATE/config.yml +8 -8
  6. package/.github/copilot-instructions.md +63 -63
  7. package/.github/workflows/ghcr-publish.yml +67 -67
  8. package/AGENTS.md +597 -0
  9. package/CONTRIBUTING.md +74 -74
  10. package/Changelog +292 -284
  11. package/Dockerfile +62 -62
  12. package/Gruntfile.js +40 -40
  13. package/LICENSE +661 -661
  14. package/README.md +318 -309
  15. package/anslq25/MessagesMock.ts +221 -221
  16. package/anslq25/boards/MockBoardFactory.ts +49 -49
  17. package/anslq25/boards/MockEasyTouchBoard.ts +696 -696
  18. package/anslq25/boards/MockSystemBoard.ts +216 -216
  19. package/anslq25/chemistry/MockChlorinator.ts +98 -98
  20. package/anslq25/pumps/MockPump.ts +83 -83
  21. package/app.ts +115 -115
  22. package/config/Config.ts +0 -0
  23. package/config/VersionCheck.ts +0 -0
  24. package/controller/Constants.ts +809 -805
  25. package/controller/Equipment.ts +2688 -2664
  26. package/controller/Errors.ts +181 -181
  27. package/controller/Lockouts.ts +549 -549
  28. package/controller/State.ts +3738 -3701
  29. package/controller/boards/AquaLinkBoard.ts +1003 -1003
  30. package/controller/boards/BoardFactory.ts +53 -53
  31. package/controller/boards/EasyTouchBoard.ts +3202 -3202
  32. package/controller/boards/IntelliCenterBoard.ts +4393 -3899
  33. package/controller/boards/IntelliComBoard.ts +69 -69
  34. package/controller/boards/IntelliTouchBoard.ts +382 -382
  35. package/controller/boards/NixieBoard.ts +1944 -1944
  36. package/controller/boards/SunTouchBoard.ts +400 -400
  37. package/controller/boards/SystemBoard.ts +5268 -5268
  38. package/controller/comms/Comms.ts +1272 -1255
  39. package/controller/comms/ScreenLogic.ts +1665 -1665
  40. package/controller/comms/messages/Messages.ts +1433 -1406
  41. package/controller/comms/messages/config/ChlorinatorMessage.ts +5 -0
  42. package/controller/comms/messages/config/CircuitGroupMessage.ts +0 -0
  43. package/controller/comms/messages/config/CircuitMessage.ts +0 -0
  44. package/controller/comms/messages/config/ConfigMessage.ts +6 -0
  45. package/controller/comms/messages/config/CoverMessage.ts +0 -0
  46. package/controller/comms/messages/config/CustomNameMessage.ts +31 -31
  47. package/controller/comms/messages/config/EquipmentMessage.ts +216 -210
  48. package/controller/comms/messages/config/ExternalMessage.ts +96 -10
  49. package/controller/comms/messages/config/FeatureMessage.ts +0 -0
  50. package/controller/comms/messages/config/GeneralMessage.ts +0 -0
  51. package/controller/comms/messages/config/HeaterMessage.ts +0 -0
  52. package/controller/comms/messages/config/IntellichemMessage.ts +0 -0
  53. package/controller/comms/messages/config/OptionsMessage.ts +194 -174
  54. package/controller/comms/messages/config/PumpMessage.ts +0 -0
  55. package/controller/comms/messages/config/RemoteMessage.ts +0 -0
  56. package/controller/comms/messages/config/ScheduleMessage.ts +401 -390
  57. package/controller/comms/messages/config/SecurityMessage.ts +0 -0
  58. package/controller/comms/messages/config/ValveMessage.ts +0 -0
  59. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +0 -0
  60. package/controller/comms/messages/status/EquipmentStateMessage.ts +1158 -822
  61. package/controller/comms/messages/status/HeaterStateMessage.ts +135 -135
  62. package/controller/comms/messages/status/IntelliChemStateMessage.ts +448 -448
  63. package/controller/comms/messages/status/IntelliValveStateMessage.ts +36 -36
  64. package/controller/comms/messages/status/PumpStateMessage.ts +0 -0
  65. package/controller/comms/messages/status/RegalModbusStateMessage.ts +410 -410
  66. package/controller/comms/messages/status/VersionMessage.ts +103 -41
  67. package/controller/nixie/Nixie.ts +173 -173
  68. package/controller/nixie/NixieEquipment.ts +104 -104
  69. package/controller/nixie/bodies/Body.ts +120 -120
  70. package/controller/nixie/bodies/Filter.ts +135 -135
  71. package/controller/nixie/chemistry/ChemController.ts +2724 -2724
  72. package/controller/nixie/chemistry/ChemDoser.ts +806 -806
  73. package/controller/nixie/chemistry/Chlorinator.ts +367 -367
  74. package/controller/nixie/circuits/Circuit.ts +478 -478
  75. package/controller/nixie/heaters/Heater.ts +834 -834
  76. package/controller/nixie/pumps/Pump.ts +1193 -1193
  77. package/controller/nixie/schedules/Schedule.ts +401 -401
  78. package/controller/nixie/valves/Valve.ts +170 -170
  79. package/defaultConfig.json +352 -352
  80. package/docker-compose.yml +31 -31
  81. package/logger/DataLogger.ts +448 -448
  82. package/logger/Logger.ts +448 -436
  83. package/package.json +58 -58
  84. package/sendSocket.js +32 -32
  85. package/tsconfig.json +25 -25
  86. package/types/express-multer.d.ts +32 -32
  87. package/web/Server.ts +1937 -1927
  88. package/web/bindings/aqualinkD.json +559 -559
  89. package/web/bindings/influxDB.json +1066 -1066
  90. package/web/bindings/mqtt.json +721 -721
  91. package/web/bindings/mqttAlt.json +746 -746
  92. package/web/bindings/rulesManager.json +54 -54
  93. package/web/bindings/smartThings-Hubitat.json +31 -31
  94. package/web/bindings/valveRelays.json +20 -20
  95. package/web/bindings/vera.json +25 -25
  96. package/web/interfaces/baseInterface.ts +188 -188
  97. package/web/interfaces/httpInterface.ts +148 -148
  98. package/web/interfaces/influxInterface.ts +283 -283
  99. package/web/interfaces/mqttInterface.ts +695 -695
  100. package/web/interfaces/ruleInterface.ts +101 -87
  101. package/web/services/config/Config.ts +1063 -1053
  102. package/web/services/config/ConfigSocket.ts +0 -0
  103. package/web/services/state/State.ts +0 -0
  104. package/web/services/state/StateSocket.ts +0 -0
  105. package/web/services/utilities/Utilities.ts +233 -233
@@ -1,696 +1,696 @@
1
- import { logger } from "../../logger/Logger";
2
- import { Inbound, Outbound, Protocol } from "../../controller/comms/messages/Messages";
3
- import { MockSystemCommands, MockSystemBoard, MockCircuitCommands, MockScheduleCommands, MockHeaterCommands, MockValveCommands, MockRemoteCommands, MockPumpCommands } from "./MockSystemBoard";
4
- import { BodyTempState, state } from "../../controller/State";
5
- import { ControllerType, Heater, PoolSystem, PumpCircuit, sys } from "../../controller/Equipment";
6
- import { byteValueMap } from "../../controller/boards/SystemBoard";
7
- import { conn } from "../../controller/comms/Comms";
8
- import { Timestamp, utils } from "../../controller/Constants";
9
- export class MockEasyTouch extends MockSystemBoard {
10
- constructor(system: PoolSystem) {
11
- super(system);
12
- this.valueMaps.expansionBoards = new byteValueMap([
13
- [0, { name: 'ET28', part: 'ET2-8', desc: 'EasyTouch2 8', circuits: 8, shared: true }],
14
- [1, { name: 'ET28P', part: 'ET2-8P', desc: 'EasyTouch2 8P', circuits: 8, single: true, shared: false }],
15
- [2, { name: 'ET24', part: 'ET2-4', desc: 'EasyTouch2 4', circuits: 4, shared: true }],
16
- [3, { name: 'ET24P', part: 'ET2-4P', desc: 'EasyTouch2 4P', circuits: 4, single: true, shared: false }],
17
- [6, { name: 'ETPSL4', part: 'ET-PSL4', desc: 'EasyTouch PSL4', circuits: 4, features: 2, schedules: 4, pumps: 1, shared: true }],
18
- [7, { name: 'ETPL4', part: 'ET-PL4', desc: 'EasyTouch PL4', circuits: 4, features: 2, schedules: 4, pumps: 1, single: true, shared: false }],
19
- // EasyTouch 1 models all start at 128.
20
- [128, { name: 'ET8', part: 'ET-8', desc: 'EasyTouch 8', circuits: 8, shared: true }],
21
- [129, { name: 'ET8P', part: 'ET-8P', desc: 'EasyTouch 8', circuits: 8, single: true, shared: false }],
22
- [130, { name: 'ET4', part: 'ET-4', desc: 'EasyTouch 4', circuits: 4, shared: true }],
23
- [129, { name: 'ET4P', part: 'ET-4P', desc: 'EasyTouch 4P', circuits: 4, single: true, shared: false }]
24
- ]);
25
- }
26
- public system: EasyTouchMockSystemCommands = new EasyTouchMockSystemCommands(this);
27
- public circuits: EasyTouchMockCircuitCommands = new EasyTouchMockCircuitCommands(this);
28
- public schedules: EasyTouchMockScheduleCommands = new EasyTouchMockScheduleCommands(this);
29
- public heaters: EasyTouchMockHeaterCommands = new EasyTouchMockHeaterCommands(this);
30
- public valves: EasyTouchMockValveCommands = new EasyTouchMockValveCommands(this);
31
- public remotes: EasyTouchMockRemoteCommands = new EasyTouchMockRemoteCommands(this);
32
- public pumps: EasyTouchMockPumpCommands = new EasyTouchMockPumpCommands(this);
33
-
34
- }
35
-
36
- class EasyTouchMockSystemCommands extends MockSystemCommands {
37
- public async processDateTimeAsync(msg: Inbound) {
38
- try {
39
- let response: Outbound = Outbound.create({
40
- action: 5,
41
- portId: msg.portId,
42
- protocol: msg.protocol,
43
- dest: msg.source,
44
- source: 16 //msg.dest
45
- });
46
- response.appendPayloadBytes(0, 7);
47
- response.setPayloadByte(0, state.time.hours);
48
- response.setPayloadByte(1, state.time.minutes);
49
- response.setPayloadByte(2, Timestamp.dayOfWeek(state.time));
50
- response.setPayloadByte(3, state.time.date);
51
- response.setPayloadByte(4, state.time.month);
52
- response.setPayloadByte(5, state.time.year - 2000);
53
- response.setPayloadByte(6, sys.general.options.adjustDST ? 1 : 0);
54
- msg.isProcessed = true;
55
- await sys.anslq25Board.sendAsync(response);
56
- //conn.queueSendMessage(response);
57
- }
58
- catch (err) {
59
- logger.error(`ANSLQ25 error processing date/time. ${err.message}`);
60
- }
61
- }
62
- public async processCustomNameAsync(msg: Inbound): Promise<void> {
63
- try {
64
- let response: Outbound = Outbound.create({
65
- action: 10,
66
- portId: msg.portId,
67
- protocol: msg.protocol,
68
- dest: msg.source,
69
- source: 16 //msg.dest
70
- });
71
- response.appendPayloadByte(msg.payload[0]);
72
- let cname = sys.customNames.getItemById(msg.payload[0]).name;
73
- if (typeof cname === 'undefined') response.appendPayloadString(`CustomName${msg.payload[0]}`, 11);
74
- else
75
- response.appendPayloadString(cname, 11);
76
- msg.isProcessed = true;
77
- await sys.anslq25Board.sendAsync(response);
78
- }
79
- catch (err) {
80
- logger.error(`ANSLQ25 error processing custom name ${msg.payload[0]}. ${err.message}`);
81
- }
82
- }
83
- public async processSettingsAsync(msg: Inbound): Promise<void> {
84
- try {
85
- // 40/168/232
86
- let response: Outbound = Outbound.create({
87
- action: 40,
88
- portId: msg.portId,
89
- protocol: msg.protocol,
90
- dest: msg.source,
91
- source: 16 //msg.dest
92
- });
93
- response.appendPayloadBytes(0, 10);
94
- let chem = sys.chemControllers.getItemByAddress(144);
95
- if (chem.isActive) response.setPayloadByte(3, 0x01, 0);
96
- response.setPayloadByte(4, sys.general.options.manualHeat ? 1 : 0, 0);
97
- response.setPayloadByte(5, sys.general.options.manualPriority ? 1 : 0, 0);
98
- msg.isProcessed = true;
99
- await sys.anslq25Board.sendAsync(response);
100
- }
101
- catch (err) {
102
- logger.error(`ANSLQ25 error processing settings. ${err.message}`);
103
- }
104
- }
105
- public async sendAck(msg: Inbound) {
106
- /*
107
- * Per matching rules:
108
- * if (msgIn.source === msgOut.dest && msgIn.payload[0] === msgOut.action) return true;
109
- */
110
- let response: Outbound = Outbound.create({
111
- action: 1,
112
- portId: msg.portId,
113
- protocol: msg.protocol,
114
- dest: msg.source,
115
- source: 16 //msg.dest
116
- });
117
-
118
- response.appendPayloadByte(msg.action);
119
- msg.isProcessed = true;
120
- await sys.anslq25Board.sendAsync(response);
121
- // conn.queueSendMessage(response);
122
- }
123
- public async sendStatusAsync() {
124
- try {
125
- let msg = Outbound.create({
126
- portId: sys.anslq25.portId,
127
- protocol: Protocol.Broadcast,
128
- dest: 16,
129
- source: 15,
130
- action: 2
131
- });
132
-
133
- console.log(`send status command`);
134
- msg.appendPayloadBytes(0, 29);
135
- // to do: reverse engineer logic for model types
136
- // sys.equipment.model;
137
- // let mod = sys.board.valueMaps.expansionBoards.get(sys.equipment.modules.getItemById(0).type);
138
- let mt = sys.equipment.modules.getItemById(0);
139
- let model1 = mt.type
140
- msg.setPayloadByte(27, model1); // model1
141
- msg.setPayloadByte(28, 13); // model2
142
-
143
- // set time
144
- msg.setPayloadByte(0, state.time.hours);
145
- msg.setPayloadByte(1, state.time.minutes);
146
-
147
- // set mode
148
- msg.setPayloadByte(9, msg.setPayloadByte[9] | state.mode);
149
-
150
- // set units
151
- msg.setPayloadByte(9, msg[9] | state.temps.units);
152
-
153
- // set valves
154
- msg.setPayloadByte(10, state.valve);
155
-
156
- // set delay
157
- msg.setPayloadByte(12, msg[12] | (Math.max(state.delay, 0)));
158
-
159
- // set freeze
160
- if (state.freeze) msg.setPayloadByte(9, msg[9] | 0x08);
161
-
162
- // set circuits
163
- let circuits = state.circuits.get(true);
164
- let circuitId = 0;
165
- for (let i = 2; i <= circuits.length; i++) {
166
- for (let j = 0; j < 8; j++) {
167
-
168
- let circuit = circuits[circuitId];
169
- if (circuit.isActive && circuit.isOn) {
170
- msg.setPayloadByte(i, msg[i] & (1 >> j))
171
- }
172
- }
173
- circuitId++;
174
- }
175
- // set temps
176
- msg.setPayloadByte(14, state.temps.waterSensor1);
177
- msg.setPayloadByte(18, state.temps.air);
178
- let solar: Heater = sys.heaters.getItemById(2);
179
- if (solar.isActive) msg.setPayloadByte(19, state.temps.solar);
180
- if (sys.bodies.length > 2 || sys.equipment.dual) msg.setPayloadByte(15, state.temps.waterSensor2);
181
- // set body attributes
182
- if (sys.bodies.length > 0) {
183
- const tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
184
- if (tbody.isOn) {
185
- msg.setPayloadByte(2, msg[2] | 0x20);
186
- if (tbody.heatMode > 0) {
187
- let _heatStatus = sys.board.valueMaps.heatStatus.getName(tbody.heatStatus);
188
- if (tbody.heaterOptions.hybrid > 0) {
189
-
190
- if (_heatStatus === 'dual') msg.setPayloadByte(10, msg[10] | 0x14);
191
- else if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x10);
192
- else if (_heatStatus === 'hpheat') msg.setPayloadByte(10, msg[10] | 0x04);
193
- }
194
- else {
195
- if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x04);
196
- if (_heatStatus === 'cooling' || _heatStatus === 'solar') msg.setPayloadByte(10, msg[10] | 0x10);
197
- }
198
- }
199
- }
200
- else msg.setPayloadByte(10, 0);
201
- }
202
- if (sys.bodies.length > 1) {
203
- const tbody: BodyTempState = state.temps.bodies.getItemById(2, true);
204
- // const cbody: Body = sys.bodies.getItemById(2);
205
- let _heatStatus = sys.board.valueMaps.heatStatus.getName(tbody.heatStatus);
206
- if (tbody.isOn) msg.setPayloadByte(2, msg[2] | 0x01);
207
- msg.setPayloadByte(22, msg[22] | tbody.heatMode << 2);
208
- if (tbody.isOn) {
209
- if (tbody.heaterOptions.hybrid > 0) {
210
- let _heatStatus = sys.board.valueMaps.heatStatus.getName(tbody.heatStatus);
211
- if (tbody.heatMode > 0) {
212
- if (_heatStatus === 'dual') msg.setPayloadByte(10, msg[10] | 0x28);
213
- else if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x20);
214
- else if (_heatStatus === 'hpheat') msg.setPayloadByte(10, msg[10] | 0x08);
215
- }
216
- else {
217
- if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x28);
218
- if (_heatStatus === 'cooling' || _heatStatus === 'solar') msg.setPayloadByte(10, msg[10] | 0x20);
219
- }
220
- }
221
- }
222
- else msg.setPayloadByte(10, msg[10], 0);
223
- };
224
-
225
- // set temps -- 14 (water) and 15 (water2)
226
- msg.setPayloadByte(14, state.temps.waterSensor1, 0);
227
- if (sys.bodies.length > 2 || sys.equipment.dual) msg.setPayloadByte(15, state.temps.waterSensor2, 0);
228
- msg.setPayloadByte(18, state.temps.air, 0);
229
- msg.setPayloadByte(19, state.temps.solarSensor1, 0);
230
- if (sys.bodies.length > 2 || sys.equipment.dual)
231
- msg.setPayloadByte(17, state.temps.solarSensor2, 0);
232
- if ((sys.bodies.length > 2))
233
- msg.setPayloadByte(22, state.temps.solarSensor3, 0);
234
- if ((sys.bodies.length > 3))
235
- msg.setPayloadByte(23, state.temps.solarSensor4, 0);
236
-
237
- await sys.anslq25Board.sendAsync(msg);
238
- }
239
- catch (err) {
240
- logger.error(`Error sending ANSLQ25 status packet: ${err.message}`);
241
- }
242
- };
243
- }
244
-
245
- class EasyTouchMockCircuitCommands extends MockCircuitCommands {
246
- public async processCircuitAsync(msg: Inbound) {
247
- // example [255,0,255][165,33,16,34,139,5][17,14,209,0,0][2,120]
248
- // set circuit 17 to function 14 and name 209
249
- // response: [255,0,255][165,33,34,16,1,1][139][1,133]
250
- // request for circuit 2: [165,33,16,33,203,1],[2],[1,197]]
251
-
252
- try {
253
- let response: Outbound = Outbound.create({
254
- action: 11,
255
- portId: msg.portId,
256
- protocol: msg.protocol,
257
- dest: msg.source,
258
- source: 16 //msg.dest
259
- });
260
- let circuit = sys.circuits.getInterfaceById(msg.payload[0]);
261
- response.appendPayloadBytes(0, 5);
262
- response.setPayloadByte(0, circuit.id);
263
- if (circuit.id === 1) {
264
- response.setPayloadByte(1, 1 | (circuit.freeze ? 64 : 0), 0);
265
- response.setPayloadByte(2, 72, 53);
266
-
267
- }
268
- else if (circuit.id === 6) {
269
- response.setPayloadByte(1, 2 | (circuit.freeze ? 64 : 0), 0);
270
- response.setPayloadByte(2, 61, 53);
271
-
272
- }
273
- else {
274
- response.setPayloadByte(1, circuit.type | (circuit.freeze ? 64 : 0), 0);
275
- response.setPayloadByte(2, circuit.nameId, 53);
276
- }
277
- msg.isProcessed = true;
278
- await sys.anslq25Board.sendAsync(response);
279
- }
280
- catch (err) {
281
- logger.error(`ANSLQ25 error processing circuit ${msg.payload[0]}. ${err.message}`);
282
- }
283
- };
284
- public async processLightGroupAsync(msg: Inbound) {
285
- try {
286
- // 39/167/231
287
- // todo - when 25 packet length vs 32. May need to add.
288
- let response: Outbound = Outbound.create({
289
- action: 39,
290
- portId: msg.portId,
291
- protocol: msg.protocol,
292
- dest: msg.source,
293
- source: 16 //msg.dest
294
- });
295
- let lg = sys.lightGroups.getItemById(sys.board.equipmentIds.circuitGroups.start);
296
- response.appendPayloadBytes(0, 32);
297
- for (let byte = 0; byte <= 32; byte = byte + 4) {
298
- let circuit = sys.circuits.getInterfaceById(byte);
299
- response.setPayloadByte(byte, circuit.id, 0);
300
- let pair = byte + 1;
301
- const lgCircuit = lg.circuits.getItemByCircuitId(circuit.id);
302
- response.setPayloadByte(pair, Math.max((lgCircuit.position - 1), 0), 0);
303
- response.setPayloadByte(pair, response.payload[pair] | lgCircuit.color, 0);
304
- response.setPayloadByte(byte + 2, lgCircuit.swimDelay << 1, 0);
305
- }
306
- msg.isProcessed = true;
307
- await sys.anslq25Board.sendAsync(response);
308
- }
309
- catch (err) {
310
- logger.error(`ANSLQ25 error processing circuit ${msg.payload[0]}. ${err.message}`);
311
- }
312
- };
313
- }
314
-
315
- export class EasyTouchMockScheduleCommands extends MockScheduleCommands {
316
- public async processScheduleAsync(msg: Inbound) {
317
- // Sample packet
318
- // Request: 165,33,16,33,209,1,7,1,202
319
- // Response: [165,33,15,16,17,7][6,12,25,0,6,30,0][1,76]
320
- try {
321
- let response: Outbound = Outbound.create({
322
- action: 17,
323
- portId: msg.portId,
324
- protocol: msg.protocol,
325
- dest: msg.source,
326
- source: 16 //msg.dest
327
- });
328
- response.appendPayloadBytes(0, 7);
329
- let eggTimer = sys.eggTimers.getItemById(msg.payload[0]);
330
- let schedule = sys.schedules.getItemById(msg.payload[0]);
331
- if (eggTimer.isActive) {
332
- response.setPayloadByte(0, eggTimer.id);
333
- response.setPayloadByte(1, eggTimer.circuit);
334
- response.setPayloadByte(2, 25);
335
- response.setPayloadByte(4, eggTimer.runTime === 27 ? 27 : Math.floor(eggTimer.runTime / 60));
336
- response.setPayloadByte(5, eggTimer.runTime === 27 ? 0 : eggTimer.runTime - (Math.floor(eggTimer.runTime / 60) * 60));
337
- }
338
- else {
339
- response.setPayloadByte(0, schedule.id);
340
- response.setPayloadByte(1, schedule.circuit, 0);
341
- response.setPayloadByte(2, Math.floor(schedule.startTime / 60), 0);
342
- response.setPayloadByte(3, Math.floor(schedule.startTime / 60) - (Math.floor(schedule.startTime / 60) * 60), 0);
343
- response.setPayloadByte(4, schedule.scheduleType === 0 ? 0 : Math.floor(schedule.endTime / 60), 0);
344
- response.setPayloadByte(5, Math.floor(schedule.endTime / 60) - (Math.floor(schedule.endTime / 60) * 60), 0);
345
- response.setPayloadByte(6, schedule.scheduleDays, 0);
346
- }
347
- msg.isProcessed = true;
348
- await sys.anslq25Board.sendAsync(response);
349
- }
350
- catch (err) {
351
- logger.error(`ANSLQ25 error processing custom name ${msg.payload[0]}. ${err.message}`);
352
- }
353
- };
354
- }
355
-
356
- export class EasyTouchMockHeaterCommands extends MockHeaterCommands {
357
- public async processHeatModesAsync(msg: Inbound) {
358
- // IntelliTouch only. Heat status
359
- // [165,x,15,16,8,13],[75,75,64,87,101,11,0, 0 ,62 ,0 ,0 ,0 ,0] ,[2,190]
360
- // Heat Modes
361
- // 1 = Heater
362
- // 2 = Solar Preferred
363
- // 3 = Solar Only
364
- //[81, 81, 82, 85, 97, 7, 0, 0, 0, 100, 100, 4, 0][3, 87]
365
- // byte | val |
366
- // 0 | 81 | Water sensor 1
367
- // 1 | 81 | Unknown (Probably water sensor 2 on a D)
368
- // 2 | 82 | Air sensor
369
- // 3 | 85 | Body 1 setpoint
370
- // 4 | 97 | Body 2 setpoint
371
- // 5 | 7 | Body 1 & 2 heat mode. (0111) (Pool = 11 Solar only/Spa = 01 Heater)
372
- // 6 | 0 | Unknown (Water Sensor 3)
373
- // 7 | 0 | Unknown (Water Sensor 4)
374
- // 8 | 0 | Unknown -- Reserved air sensor
375
- // 9 | 100 | Unknown (Body 3 setpoint)
376
- // 10 | 100 | Unknown (Body 4 setpoint)
377
- // 11 | 4 | Unknown (Body 3 & 4 head mode. (0010) (Pool = 00 = Off/ 10 = Solar Preferred)
378
- // 12 | 0 | Unknown
379
- // There are two messages sent when the OCP tries to tse a heat mode in IntelliTouch. The first one on the action 136 is for the first 2 bodies and the second
380
- // is for the remaining 2 bodies. The second half of this message mirrors the values for the second 136 message.
381
-
382
- try {
383
- let response: Outbound = Outbound.create({
384
- action: 8,
385
- portId: msg.portId,
386
- protocol: msg.protocol,
387
- dest: msg.source,
388
- source: 16 //msg.dest
389
- });
390
- response.appendPayloadBytes(0, 13);
391
-
392
- const tbody1: BodyTempState = state.temps.bodies.getItemById(1);
393
- const tbody2: BodyTempState = state.temps.bodies.getItemById(2);
394
- response.setPayloadByte(0, state.temps.waterSensor1, 0);
395
- response.setPayloadByte(1, state.temps.waterSensor2, 0);
396
- response.setPayloadByte(2, state.temps.air, 0);
397
- response.setPayloadByte(3, tbody1.setPoint, 0);
398
- response.setPayloadByte(4, tbody2.setPoint, 0);
399
- const tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
400
- if (tbody.heaterOptions.hybrid > 0) {
401
- response.setPayloadByte(5, tbody2.heatMode << 2 | tbody1.heatMode, 0);
402
- }
403
- else {
404
- response.setPayloadByte(5, tbody1.heatMode << 2 | tbody2.heatMode, 0);
405
- }
406
- response.setPayloadByte(9, tbody1.coolSetpoint, 0);
407
- response.setPayloadByte(9, tbody2.coolSetpoint, 0);
408
- msg.isProcessed = true;
409
- await sys.anslq25Board.sendAsync(response);
410
- }
411
- catch (err) {
412
- logger.error(`ANSLQ25 error processing heat modes. ${err.message}`);
413
- }
414
- };
415
-
416
-
417
- public async processHeaterConfigAsync(msg: Inbound) {
418
- // 34/162/226
419
-
420
- try {
421
- let response: Outbound = Outbound.create({
422
- action: 34,
423
- portId: msg.portId,
424
- protocol: msg.protocol,
425
- dest: msg.source,
426
- source: 16 //msg.dest
427
- });
428
- response.appendPayloadBytes(0, 3);
429
- const tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
430
- if (tbody.heaterOptions.hybrid > 0) {
431
- response.setPayloadByte(1, 0x10);
432
- }
433
- else if (tbody.heaterOptions.heatpump > 0) {
434
- response.setPayloadByte(1, 0x20);
435
- let hpump = sys.heaters.getItemById(3);
436
- if (hpump.heatingEnabled) response.setPayloadByte(1, response.payload[1] | 0x01);
437
- if (hpump.coolingEnabled) response.setPayloadByte(1, response.payload[1] | 0x02);
438
- }
439
- else if (tbody.heaterOptions.solar > 0) {
440
- response.setPayloadByte(0, 0x01);
441
- let solar = sys.heaters.getItemById(2);
442
- if (solar.freeze) response.setPayloadByte(1, response.payload[1] | 0x80);
443
- if (solar.coolingEnabled) response.setPayloadByte(1, response.payload[1] | 0x20);
444
- response.setPayloadByte(2, (solar.startTempDelta - 3) << 1);
445
- response.setPayloadByte(2, response.payload[2] | (solar.stopTempDelta - 2) << 6);
446
-
447
- }
448
- msg.isProcessed = true;
449
- await sys.anslq25Board.sendAsync(response);
450
- }
451
- catch (err) {
452
- logger.error(`ANSLQ25 error processing heater config. ${err.message}`);
453
- }
454
- };
455
- }
456
-
457
- export class EasyTouchMockValveCommands extends MockValveCommands {
458
- public async processValveAssignmentsAsync(msg: Inbound) {
459
- try {
460
- // 29/157/221
461
- let response: Outbound = Outbound.create({
462
- action: 29,
463
- portId: msg.portId,
464
- protocol: msg.protocol,
465
- dest: msg.source,
466
- source: 16 //msg.dest
467
- });
468
- response.appendPayloadBytes(0, 24);
469
- response.setPayloadByte(1, 2); //constant
470
- for (let ndx = 4, id = 1; id <= sys.equipment.maxValves; ndx++) {
471
- let valve = sys.valves.getItemById(id);
472
- response.setPayloadByte(ndx, valve.circuit, 0);
473
- id++;
474
- }
475
- msg.isProcessed = true;
476
- await sys.anslq25Board.sendAsync(response);
477
- }
478
- catch (err) {
479
- logger.error(`ANSLQ25 error processing valve assignment packet. ${err.message}`);
480
- }
481
- };
482
- public async processValveOptionsAsync(msg: Inbound) {
483
- try {
484
- // 35/163/227
485
- let response: Outbound = Outbound.create({
486
- action: 35,
487
- portId: msg.portId,
488
- protocol: msg.protocol,
489
- dest: msg.source,
490
- source: 16 //msg.dest
491
- });
492
- response.appendPayloadBytes(0, 2);
493
- response.setPayloadByte(0, (sys.general.options.pumpDelay ? 128 : 0) | 4, 4);
494
- msg.isProcessed = true;
495
- await sys.anslq25Board.sendAsync(response);
496
- }
497
- catch (err) {
498
- logger.error(`ANSLQ25 error processing valve options packet. ${err.message}`);
499
- }
500
- };
501
- }
502
-
503
- export class EasyTouchMockRemoteCommands extends MockRemoteCommands {
504
- public async processIS4IS10RemoteAsync(msg: Inbound) {
505
- try {
506
- // 32/160/224
507
- let response: Outbound = Outbound.create({
508
- action: 32,
509
- portId: msg.portId,
510
- protocol: msg.protocol,
511
- dest: msg.source,
512
- source: 16 //msg.dest
513
- });
514
-
515
- response.appendPayloadBytes(0, 11);
516
- console.log(sys.remotes.length);
517
- for (let i = 0; i < sys.remotes.length; i++) {
518
-
519
- let remote = sys.remotes.getItemById(i + 1);
520
- response.setPayloadByte(0, i);
521
- response.setPayloadByte(1, remote.button1, 0);
522
- response.setPayloadByte(2, remote.button2, 0);
523
- response.setPayloadByte(3, remote.button3, 0);
524
- response.setPayloadByte(4, remote.button4, 0);
525
- response.setPayloadByte(5, remote.button5, 0);
526
- response.setPayloadByte(6, remote.button6, 0);
527
- response.setPayloadByte(7, remote.button7, 0);
528
- response.setPayloadByte(8, remote.button8, 0);
529
- response.setPayloadByte(9, remote.button9, 0);
530
- response.setPayloadByte(10, remote.button10, 0);
531
- }
532
- msg.isProcessed = true;
533
- await sys.anslq25Board.sendAsync(response);
534
- }
535
- catch (err) {
536
- logger.error(`ANSLQ25 error processing IS4/IS10 packet. ${err.message}`);
537
- }
538
- };
539
- public async processQuickTouchRemoteAsync(msg: Inbound) {
540
- try {
541
- // 33/161/225
542
- let response: Outbound = Outbound.create({
543
- action: 33,
544
- portId: msg.portId,
545
- protocol: msg.protocol,
546
- dest: msg.source,
547
- source: 16 //msg.dest
548
- });
549
- response.appendPayloadBytes(0, 4);
550
- let remote = sys.remotes.getItemById(6);
551
- response.setPayloadByte(0, remote.button1, 0);
552
- response.setPayloadByte(1, remote.button2, 0);
553
- response.setPayloadByte(2, remote.button3, 0);
554
- response.setPayloadByte(3, remote.button4, 0);
555
- msg.isProcessed = true;
556
- await sys.anslq25Board.sendAsync(response);
557
- }
558
- catch (err) {
559
- logger.error(`ANSLQ25 error processing quicktouch remote packet. ${err.message}`);
560
- }
561
- };
562
- public async processSpaCommandRemoteAsync(msg: Inbound) {
563
- try {
564
- // 22/150/214
565
- let response: Outbound = Outbound.create({
566
- action: 22,
567
- portId: msg.portId,
568
- protocol: msg.protocol,
569
- dest: msg.source,
570
- source: 16 //msg.dest
571
- });
572
- response.appendPayloadBytes(0, 16);
573
- let remote = sys.remotes.getItemById(7);
574
- response.setPayloadByte(5, remote.pumpId, 0);
575
- response.setPayloadByte(6, remote.stepSize, 0);
576
- msg.isProcessed = true;
577
- await sys.anslq25Board.sendAsync(response);
578
- }
579
- catch (err) {
580
- logger.error(`ANSLQ25 error processing spa command remote packet. ${err.message}`);
581
- }
582
- };
583
- }
584
-
585
- export class EasyTouchMockPumpCommands extends MockPumpCommands {
586
- public async processPumpConfigAsync(msg: Inbound) {
587
- try {
588
- // 24/152/212 and 27/155/215(?)
589
- // [255, 0, 255], [165, 33, 15, 16, 27, 46], [2, 6, 15, 2, 0, 1, 29, 11, 35, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 30, 55, 5, 10, 60, 5, 1, 50, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [3, 41]
590
- let response: Outbound = Outbound.create({
591
- action: 24,
592
- portId: msg.portId,
593
- protocol: msg.protocol,
594
- dest: msg.source,
595
- source: 16 //msg.dest
596
- });
597
- response.appendPayloadBytes(0, 46);
598
- let pump = sys.pumps.getItemById(msg.payload[0]);
599
- response.setPayloadByte(0, pump.id);
600
- response.setPayloadByte(1, pump.type, 0);
601
- switch (pump.type) {
602
- case 0: //none
603
- {
604
- break;
605
- }
606
- case 1: // vf
607
- {
608
- let pumpCircuits = pump.circuits.get();
609
- for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
610
- let pumpCircuit: PumpCircuit = pumpCircuits[circuitId];
611
- if (pumpCircuit.circuit > 0) {
612
- response.setPayloadByte(circuitId * 2 + 3, pumpCircuit.circuit, 0);
613
- response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.flow, 30);
614
- }
615
- response.setPayloadByte(1, pump.backgroundCircuit, 0);
616
- response.setPayloadByte(2, pump.filterSize / 1000, 0);
617
- response.setPayloadByte(3, pump.turnovers, 0);
618
- response.setPayloadByte(21, pump.manualFilterGPM, 0);
619
- response.setPayloadByte(22, pump.primingSpeed, 0);
620
- // response.setPayloadByte(23, pump.primingTime, 0);
621
- response.setPayloadByte(23, pump.primingTime | (pump.maxSystemTime << 4), 0);
622
- response.setPayloadByte(24, pump.maxPressureIncrease, 0);
623
- response.setPayloadByte(25, pump.backwashFlow, 0);
624
- response.setPayloadByte(26, pump.backwashTime, 0);
625
- response.setPayloadByte(27, pump.rinseTime, 0);
626
- response.setPayloadByte(28, pump.vacuumFlow, 0);
627
- response.setPayloadByte(30, pump.vacuumTime, 0);
628
-
629
- }
630
- break;
631
- }
632
- case 64: // vsf
633
- {
634
- let pumpCircuits = pump.circuits.get();
635
- for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
636
- let pumpCircuit: PumpCircuit = pumpCircuits[circuitId];
637
- if (pumpCircuit.circuit > 0) {
638
- response.setPayloadByte(4, pumpCircuit.units << circuitId - 1 | response.payload[4], response.payload[4]);
639
- if (pumpCircuit.units) {
640
- response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.flow, response.payload[4]);
641
-
642
- }
643
- else {
644
- response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.speed - (pumpCircuit.speed % 256) / 256, 0);
645
- response.setPayloadByte(circuitId + 21, pumpCircuit.speed % 256, 0);
646
- }
647
- }
648
- }
649
- break;
650
- }
651
- case 128: // vs
652
- case 169: //vs+svrs
653
- {
654
- let pumpCircuits = pump.circuits.get();
655
- for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
656
- let pumpCircuit: PumpCircuit = pumpCircuits[circuitId];
657
- if (pumpCircuit.circuit > 0) {
658
-
659
- response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.speed - (pumpCircuit.speed % 256) / 256, 0);
660
- response.setPayloadByte(circuitId + 21, pumpCircuit.speed % 256, 0);
661
- }
662
- }
663
- break;
664
- }
665
- }
666
- msg.isProcessed = true;
667
- await sys.anslq25Board.sendAsync(response);
668
- }
669
- catch (err) {
670
- logger.error(`ANSLQ25 error processing spa command remote packet. ${err.message}`);
671
- }
672
- };
673
- public async processHighSpeedCircuitsAsync(msg: Inbound) {
674
- try {
675
- // 30/158/222
676
- let response: Outbound = Outbound.create({
677
- action: 30,
678
- portId: msg.portId,
679
- protocol: msg.protocol,
680
- dest: msg.source,
681
- source: 16 //msg.dest
682
- });
683
- response.appendPayloadBytes(0, 16);
684
- let pump = sys.pumps.getDualSpeed();
685
- let pumpCircuits = pump.circuits.get();
686
- for (let i = 1; i <= pumpCircuits.length; i++) {
687
- response.setPayloadByte(i, pumpCircuits[i].circuit, 0);
688
- }
689
- msg.isProcessed = true;
690
- await sys.anslq25Board.sendAsync(response);
691
- }
692
- catch (err) {
693
- logger.error(`ANSLQ25 error processing spa command remote packet. ${err.message}`);
694
- }
695
- };
696
- };
1
+ import { logger } from "../../logger/Logger";
2
+ import { Inbound, Outbound, Protocol } from "../../controller/comms/messages/Messages";
3
+ import { MockSystemCommands, MockSystemBoard, MockCircuitCommands, MockScheduleCommands, MockHeaterCommands, MockValveCommands, MockRemoteCommands, MockPumpCommands } from "./MockSystemBoard";
4
+ import { BodyTempState, state } from "../../controller/State";
5
+ import { ControllerType, Heater, PoolSystem, PumpCircuit, sys } from "../../controller/Equipment";
6
+ import { byteValueMap } from "../../controller/boards/SystemBoard";
7
+ import { conn } from "../../controller/comms/Comms";
8
+ import { Timestamp, utils } from "../../controller/Constants";
9
+ export class MockEasyTouch extends MockSystemBoard {
10
+ constructor(system: PoolSystem) {
11
+ super(system);
12
+ this.valueMaps.expansionBoards = new byteValueMap([
13
+ [0, { name: 'ET28', part: 'ET2-8', desc: 'EasyTouch2 8', circuits: 8, shared: true }],
14
+ [1, { name: 'ET28P', part: 'ET2-8P', desc: 'EasyTouch2 8P', circuits: 8, single: true, shared: false }],
15
+ [2, { name: 'ET24', part: 'ET2-4', desc: 'EasyTouch2 4', circuits: 4, shared: true }],
16
+ [3, { name: 'ET24P', part: 'ET2-4P', desc: 'EasyTouch2 4P', circuits: 4, single: true, shared: false }],
17
+ [6, { name: 'ETPSL4', part: 'ET-PSL4', desc: 'EasyTouch PSL4', circuits: 4, features: 2, schedules: 4, pumps: 1, shared: true }],
18
+ [7, { name: 'ETPL4', part: 'ET-PL4', desc: 'EasyTouch PL4', circuits: 4, features: 2, schedules: 4, pumps: 1, single: true, shared: false }],
19
+ // EasyTouch 1 models all start at 128.
20
+ [128, { name: 'ET8', part: 'ET-8', desc: 'EasyTouch 8', circuits: 8, shared: true }],
21
+ [129, { name: 'ET8P', part: 'ET-8P', desc: 'EasyTouch 8', circuits: 8, single: true, shared: false }],
22
+ [130, { name: 'ET4', part: 'ET-4', desc: 'EasyTouch 4', circuits: 4, shared: true }],
23
+ [129, { name: 'ET4P', part: 'ET-4P', desc: 'EasyTouch 4P', circuits: 4, single: true, shared: false }]
24
+ ]);
25
+ }
26
+ public system: EasyTouchMockSystemCommands = new EasyTouchMockSystemCommands(this);
27
+ public circuits: EasyTouchMockCircuitCommands = new EasyTouchMockCircuitCommands(this);
28
+ public schedules: EasyTouchMockScheduleCommands = new EasyTouchMockScheduleCommands(this);
29
+ public heaters: EasyTouchMockHeaterCommands = new EasyTouchMockHeaterCommands(this);
30
+ public valves: EasyTouchMockValveCommands = new EasyTouchMockValveCommands(this);
31
+ public remotes: EasyTouchMockRemoteCommands = new EasyTouchMockRemoteCommands(this);
32
+ public pumps: EasyTouchMockPumpCommands = new EasyTouchMockPumpCommands(this);
33
+
34
+ }
35
+
36
+ class EasyTouchMockSystemCommands extends MockSystemCommands {
37
+ public async processDateTimeAsync(msg: Inbound) {
38
+ try {
39
+ let response: Outbound = Outbound.create({
40
+ action: 5,
41
+ portId: msg.portId,
42
+ protocol: msg.protocol,
43
+ dest: msg.source,
44
+ source: 16 //msg.dest
45
+ });
46
+ response.appendPayloadBytes(0, 7);
47
+ response.setPayloadByte(0, state.time.hours);
48
+ response.setPayloadByte(1, state.time.minutes);
49
+ response.setPayloadByte(2, Timestamp.dayOfWeek(state.time));
50
+ response.setPayloadByte(3, state.time.date);
51
+ response.setPayloadByte(4, state.time.month);
52
+ response.setPayloadByte(5, state.time.year - 2000);
53
+ response.setPayloadByte(6, sys.general.options.adjustDST ? 1 : 0);
54
+ msg.isProcessed = true;
55
+ await sys.anslq25Board.sendAsync(response);
56
+ //conn.queueSendMessage(response);
57
+ }
58
+ catch (err) {
59
+ logger.error(`ANSLQ25 error processing date/time. ${err.message}`);
60
+ }
61
+ }
62
+ public async processCustomNameAsync(msg: Inbound): Promise<void> {
63
+ try {
64
+ let response: Outbound = Outbound.create({
65
+ action: 10,
66
+ portId: msg.portId,
67
+ protocol: msg.protocol,
68
+ dest: msg.source,
69
+ source: 16 //msg.dest
70
+ });
71
+ response.appendPayloadByte(msg.payload[0]);
72
+ let cname = sys.customNames.getItemById(msg.payload[0]).name;
73
+ if (typeof cname === 'undefined') response.appendPayloadString(`CustomName${msg.payload[0]}`, 11);
74
+ else
75
+ response.appendPayloadString(cname, 11);
76
+ msg.isProcessed = true;
77
+ await sys.anslq25Board.sendAsync(response);
78
+ }
79
+ catch (err) {
80
+ logger.error(`ANSLQ25 error processing custom name ${msg.payload[0]}. ${err.message}`);
81
+ }
82
+ }
83
+ public async processSettingsAsync(msg: Inbound): Promise<void> {
84
+ try {
85
+ // 40/168/232
86
+ let response: Outbound = Outbound.create({
87
+ action: 40,
88
+ portId: msg.portId,
89
+ protocol: msg.protocol,
90
+ dest: msg.source,
91
+ source: 16 //msg.dest
92
+ });
93
+ response.appendPayloadBytes(0, 10);
94
+ let chem = sys.chemControllers.getItemByAddress(144);
95
+ if (chem.isActive) response.setPayloadByte(3, 0x01, 0);
96
+ response.setPayloadByte(4, sys.general.options.manualHeat ? 1 : 0, 0);
97
+ response.setPayloadByte(5, sys.general.options.manualPriority ? 1 : 0, 0);
98
+ msg.isProcessed = true;
99
+ await sys.anslq25Board.sendAsync(response);
100
+ }
101
+ catch (err) {
102
+ logger.error(`ANSLQ25 error processing settings. ${err.message}`);
103
+ }
104
+ }
105
+ public async sendAck(msg: Inbound) {
106
+ /*
107
+ * Per matching rules:
108
+ * if (msgIn.source === msgOut.dest && msgIn.payload[0] === msgOut.action) return true;
109
+ */
110
+ let response: Outbound = Outbound.create({
111
+ action: 1,
112
+ portId: msg.portId,
113
+ protocol: msg.protocol,
114
+ dest: msg.source,
115
+ source: 16 //msg.dest
116
+ });
117
+
118
+ response.appendPayloadByte(msg.action);
119
+ msg.isProcessed = true;
120
+ await sys.anslq25Board.sendAsync(response);
121
+ // conn.queueSendMessage(response);
122
+ }
123
+ public async sendStatusAsync() {
124
+ try {
125
+ let msg = Outbound.create({
126
+ portId: sys.anslq25.portId,
127
+ protocol: Protocol.Broadcast,
128
+ dest: 16,
129
+ source: 15,
130
+ action: 2
131
+ });
132
+
133
+ console.log(`send status command`);
134
+ msg.appendPayloadBytes(0, 29);
135
+ // to do: reverse engineer logic for model types
136
+ // sys.equipment.model;
137
+ // let mod = sys.board.valueMaps.expansionBoards.get(sys.equipment.modules.getItemById(0).type);
138
+ let mt = sys.equipment.modules.getItemById(0);
139
+ let model1 = mt.type
140
+ msg.setPayloadByte(27, model1); // model1
141
+ msg.setPayloadByte(28, 13); // model2
142
+
143
+ // set time
144
+ msg.setPayloadByte(0, state.time.hours);
145
+ msg.setPayloadByte(1, state.time.minutes);
146
+
147
+ // set mode
148
+ msg.setPayloadByte(9, msg.setPayloadByte[9] | state.mode);
149
+
150
+ // set units
151
+ msg.setPayloadByte(9, msg[9] | state.temps.units);
152
+
153
+ // set valves
154
+ msg.setPayloadByte(10, state.valve);
155
+
156
+ // set delay
157
+ msg.setPayloadByte(12, msg[12] | (Math.max(state.delay, 0)));
158
+
159
+ // set freeze
160
+ if (state.freeze) msg.setPayloadByte(9, msg[9] | 0x08);
161
+
162
+ // set circuits
163
+ let circuits = state.circuits.get(true);
164
+ let circuitId = 0;
165
+ for (let i = 2; i <= circuits.length; i++) {
166
+ for (let j = 0; j < 8; j++) {
167
+
168
+ let circuit = circuits[circuitId];
169
+ if (circuit.isActive && circuit.isOn) {
170
+ msg.setPayloadByte(i, msg[i] & (1 >> j))
171
+ }
172
+ }
173
+ circuitId++;
174
+ }
175
+ // set temps
176
+ msg.setPayloadByte(14, state.temps.waterSensor1);
177
+ msg.setPayloadByte(18, state.temps.air);
178
+ let solar: Heater = sys.heaters.getItemById(2);
179
+ if (solar.isActive) msg.setPayloadByte(19, state.temps.solar);
180
+ if (sys.bodies.length > 2 || sys.equipment.dual) msg.setPayloadByte(15, state.temps.waterSensor2);
181
+ // set body attributes
182
+ if (sys.bodies.length > 0) {
183
+ const tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
184
+ if (tbody.isOn) {
185
+ msg.setPayloadByte(2, msg[2] | 0x20);
186
+ if (tbody.heatMode > 0) {
187
+ let _heatStatus = sys.board.valueMaps.heatStatus.getName(tbody.heatStatus);
188
+ if (tbody.heaterOptions.hybrid > 0) {
189
+
190
+ if (_heatStatus === 'dual') msg.setPayloadByte(10, msg[10] | 0x14);
191
+ else if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x10);
192
+ else if (_heatStatus === 'hpheat') msg.setPayloadByte(10, msg[10] | 0x04);
193
+ }
194
+ else {
195
+ if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x04);
196
+ if (_heatStatus === 'cooling' || _heatStatus === 'solar') msg.setPayloadByte(10, msg[10] | 0x10);
197
+ }
198
+ }
199
+ }
200
+ else msg.setPayloadByte(10, 0);
201
+ }
202
+ if (sys.bodies.length > 1) {
203
+ const tbody: BodyTempState = state.temps.bodies.getItemById(2, true);
204
+ // const cbody: Body = sys.bodies.getItemById(2);
205
+ let _heatStatus = sys.board.valueMaps.heatStatus.getName(tbody.heatStatus);
206
+ if (tbody.isOn) msg.setPayloadByte(2, msg[2] | 0x01);
207
+ msg.setPayloadByte(22, msg[22] | tbody.heatMode << 2);
208
+ if (tbody.isOn) {
209
+ if (tbody.heaterOptions.hybrid > 0) {
210
+ let _heatStatus = sys.board.valueMaps.heatStatus.getName(tbody.heatStatus);
211
+ if (tbody.heatMode > 0) {
212
+ if (_heatStatus === 'dual') msg.setPayloadByte(10, msg[10] | 0x28);
213
+ else if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x20);
214
+ else if (_heatStatus === 'hpheat') msg.setPayloadByte(10, msg[10] | 0x08);
215
+ }
216
+ else {
217
+ if (_heatStatus === 'heater') msg.setPayloadByte(10, msg[10] | 0x28);
218
+ if (_heatStatus === 'cooling' || _heatStatus === 'solar') msg.setPayloadByte(10, msg[10] | 0x20);
219
+ }
220
+ }
221
+ }
222
+ else msg.setPayloadByte(10, msg[10], 0);
223
+ };
224
+
225
+ // set temps -- 14 (water) and 15 (water2)
226
+ msg.setPayloadByte(14, state.temps.waterSensor1, 0);
227
+ if (sys.bodies.length > 2 || sys.equipment.dual) msg.setPayloadByte(15, state.temps.waterSensor2, 0);
228
+ msg.setPayloadByte(18, state.temps.air, 0);
229
+ msg.setPayloadByte(19, state.temps.solarSensor1, 0);
230
+ if (sys.bodies.length > 2 || sys.equipment.dual)
231
+ msg.setPayloadByte(17, state.temps.solarSensor2, 0);
232
+ if ((sys.bodies.length > 2))
233
+ msg.setPayloadByte(22, state.temps.solarSensor3, 0);
234
+ if ((sys.bodies.length > 3))
235
+ msg.setPayloadByte(23, state.temps.solarSensor4, 0);
236
+
237
+ await sys.anslq25Board.sendAsync(msg);
238
+ }
239
+ catch (err) {
240
+ logger.error(`Error sending ANSLQ25 status packet: ${err.message}`);
241
+ }
242
+ };
243
+ }
244
+
245
+ class EasyTouchMockCircuitCommands extends MockCircuitCommands {
246
+ public async processCircuitAsync(msg: Inbound) {
247
+ // example [255,0,255][165,33,16,34,139,5][17,14,209,0,0][2,120]
248
+ // set circuit 17 to function 14 and name 209
249
+ // response: [255,0,255][165,33,34,16,1,1][139][1,133]
250
+ // request for circuit 2: [165,33,16,33,203,1],[2],[1,197]]
251
+
252
+ try {
253
+ let response: Outbound = Outbound.create({
254
+ action: 11,
255
+ portId: msg.portId,
256
+ protocol: msg.protocol,
257
+ dest: msg.source,
258
+ source: 16 //msg.dest
259
+ });
260
+ let circuit = sys.circuits.getInterfaceById(msg.payload[0]);
261
+ response.appendPayloadBytes(0, 5);
262
+ response.setPayloadByte(0, circuit.id);
263
+ if (circuit.id === 1) {
264
+ response.setPayloadByte(1, 1 | (circuit.freeze ? 64 : 0), 0);
265
+ response.setPayloadByte(2, 72, 53);
266
+
267
+ }
268
+ else if (circuit.id === 6) {
269
+ response.setPayloadByte(1, 2 | (circuit.freeze ? 64 : 0), 0);
270
+ response.setPayloadByte(2, 61, 53);
271
+
272
+ }
273
+ else {
274
+ response.setPayloadByte(1, circuit.type | (circuit.freeze ? 64 : 0), 0);
275
+ response.setPayloadByte(2, circuit.nameId, 53);
276
+ }
277
+ msg.isProcessed = true;
278
+ await sys.anslq25Board.sendAsync(response);
279
+ }
280
+ catch (err) {
281
+ logger.error(`ANSLQ25 error processing circuit ${msg.payload[0]}. ${err.message}`);
282
+ }
283
+ };
284
+ public async processLightGroupAsync(msg: Inbound) {
285
+ try {
286
+ // 39/167/231
287
+ // todo - when 25 packet length vs 32. May need to add.
288
+ let response: Outbound = Outbound.create({
289
+ action: 39,
290
+ portId: msg.portId,
291
+ protocol: msg.protocol,
292
+ dest: msg.source,
293
+ source: 16 //msg.dest
294
+ });
295
+ let lg = sys.lightGroups.getItemById(sys.board.equipmentIds.circuitGroups.start);
296
+ response.appendPayloadBytes(0, 32);
297
+ for (let byte = 0; byte <= 32; byte = byte + 4) {
298
+ let circuit = sys.circuits.getInterfaceById(byte);
299
+ response.setPayloadByte(byte, circuit.id, 0);
300
+ let pair = byte + 1;
301
+ const lgCircuit = lg.circuits.getItemByCircuitId(circuit.id);
302
+ response.setPayloadByte(pair, Math.max((lgCircuit.position - 1), 0), 0);
303
+ response.setPayloadByte(pair, response.payload[pair] | lgCircuit.color, 0);
304
+ response.setPayloadByte(byte + 2, lgCircuit.swimDelay << 1, 0);
305
+ }
306
+ msg.isProcessed = true;
307
+ await sys.anslq25Board.sendAsync(response);
308
+ }
309
+ catch (err) {
310
+ logger.error(`ANSLQ25 error processing circuit ${msg.payload[0]}. ${err.message}`);
311
+ }
312
+ };
313
+ }
314
+
315
+ export class EasyTouchMockScheduleCommands extends MockScheduleCommands {
316
+ public async processScheduleAsync(msg: Inbound) {
317
+ // Sample packet
318
+ // Request: 165,33,16,33,209,1,7,1,202
319
+ // Response: [165,33,15,16,17,7][6,12,25,0,6,30,0][1,76]
320
+ try {
321
+ let response: Outbound = Outbound.create({
322
+ action: 17,
323
+ portId: msg.portId,
324
+ protocol: msg.protocol,
325
+ dest: msg.source,
326
+ source: 16 //msg.dest
327
+ });
328
+ response.appendPayloadBytes(0, 7);
329
+ let eggTimer = sys.eggTimers.getItemById(msg.payload[0]);
330
+ let schedule = sys.schedules.getItemById(msg.payload[0]);
331
+ if (eggTimer.isActive) {
332
+ response.setPayloadByte(0, eggTimer.id);
333
+ response.setPayloadByte(1, eggTimer.circuit);
334
+ response.setPayloadByte(2, 25);
335
+ response.setPayloadByte(4, eggTimer.runTime === 27 ? 27 : Math.floor(eggTimer.runTime / 60));
336
+ response.setPayloadByte(5, eggTimer.runTime === 27 ? 0 : eggTimer.runTime - (Math.floor(eggTimer.runTime / 60) * 60));
337
+ }
338
+ else {
339
+ response.setPayloadByte(0, schedule.id);
340
+ response.setPayloadByte(1, schedule.circuit, 0);
341
+ response.setPayloadByte(2, Math.floor(schedule.startTime / 60), 0);
342
+ response.setPayloadByte(3, Math.floor(schedule.startTime / 60) - (Math.floor(schedule.startTime / 60) * 60), 0);
343
+ response.setPayloadByte(4, schedule.scheduleType === 0 ? 0 : Math.floor(schedule.endTime / 60), 0);
344
+ response.setPayloadByte(5, Math.floor(schedule.endTime / 60) - (Math.floor(schedule.endTime / 60) * 60), 0);
345
+ response.setPayloadByte(6, schedule.scheduleDays, 0);
346
+ }
347
+ msg.isProcessed = true;
348
+ await sys.anslq25Board.sendAsync(response);
349
+ }
350
+ catch (err) {
351
+ logger.error(`ANSLQ25 error processing custom name ${msg.payload[0]}. ${err.message}`);
352
+ }
353
+ };
354
+ }
355
+
356
+ export class EasyTouchMockHeaterCommands extends MockHeaterCommands {
357
+ public async processHeatModesAsync(msg: Inbound) {
358
+ // IntelliTouch only. Heat status
359
+ // [165,x,15,16,8,13],[75,75,64,87,101,11,0, 0 ,62 ,0 ,0 ,0 ,0] ,[2,190]
360
+ // Heat Modes
361
+ // 1 = Heater
362
+ // 2 = Solar Preferred
363
+ // 3 = Solar Only
364
+ //[81, 81, 82, 85, 97, 7, 0, 0, 0, 100, 100, 4, 0][3, 87]
365
+ // byte | val |
366
+ // 0 | 81 | Water sensor 1
367
+ // 1 | 81 | Unknown (Probably water sensor 2 on a D)
368
+ // 2 | 82 | Air sensor
369
+ // 3 | 85 | Body 1 setpoint
370
+ // 4 | 97 | Body 2 setpoint
371
+ // 5 | 7 | Body 1 & 2 heat mode. (0111) (Pool = 11 Solar only/Spa = 01 Heater)
372
+ // 6 | 0 | Unknown (Water Sensor 3)
373
+ // 7 | 0 | Unknown (Water Sensor 4)
374
+ // 8 | 0 | Unknown -- Reserved air sensor
375
+ // 9 | 100 | Unknown (Body 3 setpoint)
376
+ // 10 | 100 | Unknown (Body 4 setpoint)
377
+ // 11 | 4 | Unknown (Body 3 & 4 head mode. (0010) (Pool = 00 = Off/ 10 = Solar Preferred)
378
+ // 12 | 0 | Unknown
379
+ // There are two messages sent when the OCP tries to tse a heat mode in IntelliTouch. The first one on the action 136 is for the first 2 bodies and the second
380
+ // is for the remaining 2 bodies. The second half of this message mirrors the values for the second 136 message.
381
+
382
+ try {
383
+ let response: Outbound = Outbound.create({
384
+ action: 8,
385
+ portId: msg.portId,
386
+ protocol: msg.protocol,
387
+ dest: msg.source,
388
+ source: 16 //msg.dest
389
+ });
390
+ response.appendPayloadBytes(0, 13);
391
+
392
+ const tbody1: BodyTempState = state.temps.bodies.getItemById(1);
393
+ const tbody2: BodyTempState = state.temps.bodies.getItemById(2);
394
+ response.setPayloadByte(0, state.temps.waterSensor1, 0);
395
+ response.setPayloadByte(1, state.temps.waterSensor2, 0);
396
+ response.setPayloadByte(2, state.temps.air, 0);
397
+ response.setPayloadByte(3, tbody1.setPoint, 0);
398
+ response.setPayloadByte(4, tbody2.setPoint, 0);
399
+ const tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
400
+ if (tbody.heaterOptions.hybrid > 0) {
401
+ response.setPayloadByte(5, tbody2.heatMode << 2 | tbody1.heatMode, 0);
402
+ }
403
+ else {
404
+ response.setPayloadByte(5, tbody1.heatMode << 2 | tbody2.heatMode, 0);
405
+ }
406
+ response.setPayloadByte(9, tbody1.coolSetpoint, 0);
407
+ response.setPayloadByte(9, tbody2.coolSetpoint, 0);
408
+ msg.isProcessed = true;
409
+ await sys.anslq25Board.sendAsync(response);
410
+ }
411
+ catch (err) {
412
+ logger.error(`ANSLQ25 error processing heat modes. ${err.message}`);
413
+ }
414
+ };
415
+
416
+
417
+ public async processHeaterConfigAsync(msg: Inbound) {
418
+ // 34/162/226
419
+
420
+ try {
421
+ let response: Outbound = Outbound.create({
422
+ action: 34,
423
+ portId: msg.portId,
424
+ protocol: msg.protocol,
425
+ dest: msg.source,
426
+ source: 16 //msg.dest
427
+ });
428
+ response.appendPayloadBytes(0, 3);
429
+ const tbody: BodyTempState = state.temps.bodies.getItemById(1, true);
430
+ if (tbody.heaterOptions.hybrid > 0) {
431
+ response.setPayloadByte(1, 0x10);
432
+ }
433
+ else if (tbody.heaterOptions.heatpump > 0) {
434
+ response.setPayloadByte(1, 0x20);
435
+ let hpump = sys.heaters.getItemById(3);
436
+ if (hpump.heatingEnabled) response.setPayloadByte(1, response.payload[1] | 0x01);
437
+ if (hpump.coolingEnabled) response.setPayloadByte(1, response.payload[1] | 0x02);
438
+ }
439
+ else if (tbody.heaterOptions.solar > 0) {
440
+ response.setPayloadByte(0, 0x01);
441
+ let solar = sys.heaters.getItemById(2);
442
+ if (solar.freeze) response.setPayloadByte(1, response.payload[1] | 0x80);
443
+ if (solar.coolingEnabled) response.setPayloadByte(1, response.payload[1] | 0x20);
444
+ response.setPayloadByte(2, (solar.startTempDelta - 3) << 1);
445
+ response.setPayloadByte(2, response.payload[2] | (solar.stopTempDelta - 2) << 6);
446
+
447
+ }
448
+ msg.isProcessed = true;
449
+ await sys.anslq25Board.sendAsync(response);
450
+ }
451
+ catch (err) {
452
+ logger.error(`ANSLQ25 error processing heater config. ${err.message}`);
453
+ }
454
+ };
455
+ }
456
+
457
+ export class EasyTouchMockValveCommands extends MockValveCommands {
458
+ public async processValveAssignmentsAsync(msg: Inbound) {
459
+ try {
460
+ // 29/157/221
461
+ let response: Outbound = Outbound.create({
462
+ action: 29,
463
+ portId: msg.portId,
464
+ protocol: msg.protocol,
465
+ dest: msg.source,
466
+ source: 16 //msg.dest
467
+ });
468
+ response.appendPayloadBytes(0, 24);
469
+ response.setPayloadByte(1, 2); //constant
470
+ for (let ndx = 4, id = 1; id <= sys.equipment.maxValves; ndx++) {
471
+ let valve = sys.valves.getItemById(id);
472
+ response.setPayloadByte(ndx, valve.circuit, 0);
473
+ id++;
474
+ }
475
+ msg.isProcessed = true;
476
+ await sys.anslq25Board.sendAsync(response);
477
+ }
478
+ catch (err) {
479
+ logger.error(`ANSLQ25 error processing valve assignment packet. ${err.message}`);
480
+ }
481
+ };
482
+ public async processValveOptionsAsync(msg: Inbound) {
483
+ try {
484
+ // 35/163/227
485
+ let response: Outbound = Outbound.create({
486
+ action: 35,
487
+ portId: msg.portId,
488
+ protocol: msg.protocol,
489
+ dest: msg.source,
490
+ source: 16 //msg.dest
491
+ });
492
+ response.appendPayloadBytes(0, 2);
493
+ response.setPayloadByte(0, (sys.general.options.pumpDelay ? 128 : 0) | 4, 4);
494
+ msg.isProcessed = true;
495
+ await sys.anslq25Board.sendAsync(response);
496
+ }
497
+ catch (err) {
498
+ logger.error(`ANSLQ25 error processing valve options packet. ${err.message}`);
499
+ }
500
+ };
501
+ }
502
+
503
+ export class EasyTouchMockRemoteCommands extends MockRemoteCommands {
504
+ public async processIS4IS10RemoteAsync(msg: Inbound) {
505
+ try {
506
+ // 32/160/224
507
+ let response: Outbound = Outbound.create({
508
+ action: 32,
509
+ portId: msg.portId,
510
+ protocol: msg.protocol,
511
+ dest: msg.source,
512
+ source: 16 //msg.dest
513
+ });
514
+
515
+ response.appendPayloadBytes(0, 11);
516
+ console.log(sys.remotes.length);
517
+ for (let i = 0; i < sys.remotes.length; i++) {
518
+
519
+ let remote = sys.remotes.getItemById(i + 1);
520
+ response.setPayloadByte(0, i);
521
+ response.setPayloadByte(1, remote.button1, 0);
522
+ response.setPayloadByte(2, remote.button2, 0);
523
+ response.setPayloadByte(3, remote.button3, 0);
524
+ response.setPayloadByte(4, remote.button4, 0);
525
+ response.setPayloadByte(5, remote.button5, 0);
526
+ response.setPayloadByte(6, remote.button6, 0);
527
+ response.setPayloadByte(7, remote.button7, 0);
528
+ response.setPayloadByte(8, remote.button8, 0);
529
+ response.setPayloadByte(9, remote.button9, 0);
530
+ response.setPayloadByte(10, remote.button10, 0);
531
+ }
532
+ msg.isProcessed = true;
533
+ await sys.anslq25Board.sendAsync(response);
534
+ }
535
+ catch (err) {
536
+ logger.error(`ANSLQ25 error processing IS4/IS10 packet. ${err.message}`);
537
+ }
538
+ };
539
+ public async processQuickTouchRemoteAsync(msg: Inbound) {
540
+ try {
541
+ // 33/161/225
542
+ let response: Outbound = Outbound.create({
543
+ action: 33,
544
+ portId: msg.portId,
545
+ protocol: msg.protocol,
546
+ dest: msg.source,
547
+ source: 16 //msg.dest
548
+ });
549
+ response.appendPayloadBytes(0, 4);
550
+ let remote = sys.remotes.getItemById(6);
551
+ response.setPayloadByte(0, remote.button1, 0);
552
+ response.setPayloadByte(1, remote.button2, 0);
553
+ response.setPayloadByte(2, remote.button3, 0);
554
+ response.setPayloadByte(3, remote.button4, 0);
555
+ msg.isProcessed = true;
556
+ await sys.anslq25Board.sendAsync(response);
557
+ }
558
+ catch (err) {
559
+ logger.error(`ANSLQ25 error processing quicktouch remote packet. ${err.message}`);
560
+ }
561
+ };
562
+ public async processSpaCommandRemoteAsync(msg: Inbound) {
563
+ try {
564
+ // 22/150/214
565
+ let response: Outbound = Outbound.create({
566
+ action: 22,
567
+ portId: msg.portId,
568
+ protocol: msg.protocol,
569
+ dest: msg.source,
570
+ source: 16 //msg.dest
571
+ });
572
+ response.appendPayloadBytes(0, 16);
573
+ let remote = sys.remotes.getItemById(7);
574
+ response.setPayloadByte(5, remote.pumpId, 0);
575
+ response.setPayloadByte(6, remote.stepSize, 0);
576
+ msg.isProcessed = true;
577
+ await sys.anslq25Board.sendAsync(response);
578
+ }
579
+ catch (err) {
580
+ logger.error(`ANSLQ25 error processing spa command remote packet. ${err.message}`);
581
+ }
582
+ };
583
+ }
584
+
585
+ export class EasyTouchMockPumpCommands extends MockPumpCommands {
586
+ public async processPumpConfigAsync(msg: Inbound) {
587
+ try {
588
+ // 24/152/212 and 27/155/215(?)
589
+ // [255, 0, 255], [165, 33, 15, 16, 27, 46], [2, 6, 15, 2, 0, 1, 29, 11, 35, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 0, 30, 30, 55, 5, 10, 60, 5, 1, 50, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [3, 41]
590
+ let response: Outbound = Outbound.create({
591
+ action: 24,
592
+ portId: msg.portId,
593
+ protocol: msg.protocol,
594
+ dest: msg.source,
595
+ source: 16 //msg.dest
596
+ });
597
+ response.appendPayloadBytes(0, 46);
598
+ let pump = sys.pumps.getItemById(msg.payload[0]);
599
+ response.setPayloadByte(0, pump.id);
600
+ response.setPayloadByte(1, pump.type, 0);
601
+ switch (pump.type) {
602
+ case 0: //none
603
+ {
604
+ break;
605
+ }
606
+ case 1: // vf
607
+ {
608
+ let pumpCircuits = pump.circuits.get();
609
+ for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
610
+ let pumpCircuit: PumpCircuit = pumpCircuits[circuitId];
611
+ if (pumpCircuit.circuit > 0) {
612
+ response.setPayloadByte(circuitId * 2 + 3, pumpCircuit.circuit, 0);
613
+ response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.flow, 30);
614
+ }
615
+ response.setPayloadByte(1, pump.backgroundCircuit, 0);
616
+ response.setPayloadByte(2, pump.filterSize / 1000, 0);
617
+ response.setPayloadByte(3, pump.turnovers, 0);
618
+ response.setPayloadByte(21, pump.manualFilterGPM, 0);
619
+ response.setPayloadByte(22, pump.primingSpeed, 0);
620
+ // response.setPayloadByte(23, pump.primingTime, 0);
621
+ response.setPayloadByte(23, pump.primingTime | (pump.maxSystemTime << 4), 0);
622
+ response.setPayloadByte(24, pump.maxPressureIncrease, 0);
623
+ response.setPayloadByte(25, pump.backwashFlow, 0);
624
+ response.setPayloadByte(26, pump.backwashTime, 0);
625
+ response.setPayloadByte(27, pump.rinseTime, 0);
626
+ response.setPayloadByte(28, pump.vacuumFlow, 0);
627
+ response.setPayloadByte(30, pump.vacuumTime, 0);
628
+
629
+ }
630
+ break;
631
+ }
632
+ case 64: // vsf
633
+ {
634
+ let pumpCircuits = pump.circuits.get();
635
+ for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
636
+ let pumpCircuit: PumpCircuit = pumpCircuits[circuitId];
637
+ if (pumpCircuit.circuit > 0) {
638
+ response.setPayloadByte(4, pumpCircuit.units << circuitId - 1 | response.payload[4], response.payload[4]);
639
+ if (pumpCircuit.units) {
640
+ response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.flow, response.payload[4]);
641
+
642
+ }
643
+ else {
644
+ response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.speed - (pumpCircuit.speed % 256) / 256, 0);
645
+ response.setPayloadByte(circuitId + 21, pumpCircuit.speed % 256, 0);
646
+ }
647
+ }
648
+ }
649
+ break;
650
+ }
651
+ case 128: // vs
652
+ case 169: //vs+svrs
653
+ {
654
+ let pumpCircuits = pump.circuits.get();
655
+ for (let circuitId = 1; circuitId <= sys.board.valueMaps.pumpTypes.get(pump.type).maxCircuits; circuitId++) {
656
+ let pumpCircuit: PumpCircuit = pumpCircuits[circuitId];
657
+ if (pumpCircuit.circuit > 0) {
658
+
659
+ response.setPayloadByte(circuitId * 2 + 4, pumpCircuit.speed - (pumpCircuit.speed % 256) / 256, 0);
660
+ response.setPayloadByte(circuitId + 21, pumpCircuit.speed % 256, 0);
661
+ }
662
+ }
663
+ break;
664
+ }
665
+ }
666
+ msg.isProcessed = true;
667
+ await sys.anslq25Board.sendAsync(response);
668
+ }
669
+ catch (err) {
670
+ logger.error(`ANSLQ25 error processing spa command remote packet. ${err.message}`);
671
+ }
672
+ };
673
+ public async processHighSpeedCircuitsAsync(msg: Inbound) {
674
+ try {
675
+ // 30/158/222
676
+ let response: Outbound = Outbound.create({
677
+ action: 30,
678
+ portId: msg.portId,
679
+ protocol: msg.protocol,
680
+ dest: msg.source,
681
+ source: 16 //msg.dest
682
+ });
683
+ response.appendPayloadBytes(0, 16);
684
+ let pump = sys.pumps.getDualSpeed();
685
+ let pumpCircuits = pump.circuits.get();
686
+ for (let i = 1; i <= pumpCircuits.length; i++) {
687
+ response.setPayloadByte(i, pumpCircuits[i].circuit, 0);
688
+ }
689
+ msg.isProcessed = true;
690
+ await sys.anslq25Board.sendAsync(response);
691
+ }
692
+ catch (err) {
693
+ logger.error(`ANSLQ25 error processing spa command remote packet. ${err.message}`);
694
+ }
695
+ };
696
+ };