nodejs-poolcontroller 7.5.1 → 7.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/1-bug-report.yml +84 -0
- package/.github/ISSUE_TEMPLATE/2-docs.md +12 -0
- package/.github/ISSUE_TEMPLATE/3-proposal.md +28 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/Changelog +19 -0
- package/Dockerfile +3 -3
- package/README.md +13 -8
- package/app.ts +1 -1
- package/config/Config.ts +38 -2
- package/config/VersionCheck.ts +27 -12
- package/controller/Constants.ts +2 -1
- package/controller/Equipment.ts +193 -9
- package/controller/Errors.ts +10 -0
- package/controller/Lockouts.ts +503 -0
- package/controller/State.ts +269 -64
- package/controller/boards/AquaLinkBoard.ts +1000 -0
- package/controller/boards/BoardFactory.ts +4 -0
- package/controller/boards/EasyTouchBoard.ts +468 -144
- package/controller/boards/IntelliCenterBoard.ts +466 -307
- package/controller/boards/IntelliTouchBoard.ts +37 -5
- package/controller/boards/NixieBoard.ts +671 -141
- package/controller/boards/SystemBoard.ts +1397 -641
- package/controller/comms/Comms.ts +462 -362
- package/controller/comms/messages/Messages.ts +174 -30
- package/controller/comms/messages/config/ChlorinatorMessage.ts +6 -3
- package/controller/comms/messages/config/CircuitMessage.ts +1 -0
- package/controller/comms/messages/config/ExternalMessage.ts +10 -8
- package/controller/comms/messages/config/HeaterMessage.ts +141 -29
- package/controller/comms/messages/config/OptionsMessage.ts +9 -2
- package/controller/comms/messages/config/PumpMessage.ts +53 -35
- package/controller/comms/messages/config/ScheduleMessage.ts +33 -25
- package/controller/comms/messages/config/ValveMessage.ts +2 -2
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +38 -86
- package/controller/comms/messages/status/EquipmentStateMessage.ts +59 -23
- package/controller/comms/messages/status/HeaterStateMessage.ts +57 -3
- package/controller/comms/messages/status/IntelliChemStateMessage.ts +56 -8
- package/controller/comms/messages/status/PumpStateMessage.ts +23 -1
- package/controller/nixie/Nixie.ts +1 -1
- package/controller/nixie/bodies/Body.ts +3 -0
- package/controller/nixie/chemistry/ChemController.ts +164 -51
- package/controller/nixie/chemistry/Chlorinator.ts +137 -88
- package/controller/nixie/circuits/Circuit.ts +51 -19
- package/controller/nixie/heaters/Heater.ts +241 -31
- package/controller/nixie/pumps/Pump.ts +488 -206
- package/controller/nixie/schedules/Schedule.ts +91 -35
- package/controller/nixie/valves/Valve.ts +1 -1
- package/defaultConfig.json +20 -0
- package/package.json +21 -21
- package/web/Server.ts +94 -49
- package/web/bindings/aqualinkD.json +505 -0
- package/web/bindings/influxDB.json +71 -1
- package/web/bindings/mqtt.json +98 -39
- package/web/bindings/mqttAlt.json +59 -1
- package/web/interfaces/baseInterface.ts +1 -0
- package/web/interfaces/httpInterface.ts +23 -2
- package/web/interfaces/influxInterface.ts +45 -10
- package/web/interfaces/mqttInterface.ts +114 -54
- package/web/services/config/Config.ts +55 -132
- package/web/services/state/State.ts +81 -4
- package/web/services/state/StateSocket.ts +4 -4
- package/web/services/utilities/Utilities.ts +8 -6
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -52
- package/config copy.json +0 -300
- package/issue_template.md +0 -52
|
@@ -17,13 +17,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
17
17
|
import * as extend from 'extend';
|
|
18
18
|
import { EventEmitter } from 'events';
|
|
19
19
|
import { SystemBoard, byteValueMap, byteValueMaps, ConfigQueue, ConfigRequest, CircuitCommands, FeatureCommands, ChlorinatorCommands, PumpCommands, BodyCommands, ScheduleCommands, HeaterCommands, EquipmentIdRange, ValveCommands, SystemCommands, ChemControllerCommands } from './SystemBoard';
|
|
20
|
-
import { PoolSystem, Body, Schedule, Pump, ConfigVersion, sys, Heater, ICircuitGroup, LightGroupCircuit, LightGroup, ExpansionPanel, ExpansionModule, ExpansionModuleCollection, Valve, General, Options, Location, Owner, ICircuit, Feature, CircuitGroup, ChemController, TempSensorCollection } from '../Equipment';
|
|
20
|
+
import { PoolSystem, Body, Schedule, Pump, ConfigVersion, sys, Heater, ICircuitGroup, LightGroupCircuit, LightGroup, ExpansionPanel, ExpansionModule, ExpansionModuleCollection, Valve, General, Options, Location, Owner, ICircuit, Feature, CircuitGroup, ChemController, TempSensorCollection, Chlorinator } from '../Equipment';
|
|
21
21
|
import { Protocol, Outbound, Inbound, Message, Response } from '../comms/messages/Messages';
|
|
22
22
|
import { conn } from '../comms/Comms';
|
|
23
23
|
import { logger } from '../../logger/Logger';
|
|
24
24
|
import { state, ChlorinatorState, LightGroupState, VirtualCircuitState, ICircuitState, BodyTempState, CircuitGroupState, ICircuitGroupState, ChemControllerState } from '../State';
|
|
25
25
|
import { utils } from '../../controller/Constants';
|
|
26
|
-
import { InvalidEquipmentIdError, InvalidEquipmentDataError, EquipmentNotFoundError, MessageError } from '../Errors';
|
|
26
|
+
import { InvalidEquipmentIdError, InvalidEquipmentDataError, EquipmentNotFoundError, MessageError, InvalidOperationError } from '../Errors';
|
|
27
27
|
import { ncp } from '../nixie/Nixie';
|
|
28
28
|
export class IntelliCenterBoard extends SystemBoard {
|
|
29
29
|
public needsConfigChanges: boolean = false;
|
|
@@ -47,26 +47,28 @@ export class IntelliCenterBoard extends SystemBoard {
|
|
|
47
47
|
this.valueMaps.circuitFunctions = new byteValueMap([
|
|
48
48
|
[0, { name: 'generic', desc: 'Generic' }],
|
|
49
49
|
[1, { name: 'spillway', desc: 'Spillway' }],
|
|
50
|
-
[2, { name: 'mastercleaner', desc: 'Master Cleaner' }],
|
|
50
|
+
[2, { name: 'mastercleaner', desc: 'Master Cleaner', body: 1 }],
|
|
51
51
|
[3, { name: 'chemrelay', desc: 'Chem Relay' }],
|
|
52
52
|
[4, { name: 'light', desc: 'Light', isLight: true }],
|
|
53
|
-
[5, { name: 'intellibrite', desc: 'Intellibrite', isLight: true }],
|
|
54
|
-
[6, { name: 'globrite', desc: 'GloBrite', isLight: true }],
|
|
53
|
+
[5, { name: 'intellibrite', desc: 'Intellibrite', isLight: true, theme: 'intellibrite' }],
|
|
54
|
+
[6, { name: 'globrite', desc: 'GloBrite', isLight: true, theme: 'intellibrite' }],
|
|
55
55
|
[7, { name: 'globritewhite', desc: 'GloBrite White', isLight: true }],
|
|
56
|
-
[8, { name: 'magicstream', desc: 'Magicstream', isLight: true }],
|
|
56
|
+
[8, { name: 'magicstream', desc: 'Magicstream', isLight: true, theme: 'intellibrite' }],
|
|
57
57
|
[9, { name: 'dimmer', desc: 'Dimmer', isLight: true }],
|
|
58
|
-
[10, { name: 'colorcascade', desc: 'ColorCascade', isLight: true }],
|
|
59
|
-
[11, { name: 'mastercleaner2', desc: 'Master Cleaner 2' }],
|
|
60
|
-
[12, { name: 'pool', desc: 'Pool', hasHeatSource: true }],
|
|
61
|
-
[13, { name: 'spa', desc: 'Spa', hasHeatSource: true }]
|
|
58
|
+
[10, { name: 'colorcascade', desc: 'ColorCascade', isLight: true, theme: 'intellibrite' }],
|
|
59
|
+
[11, { name: 'mastercleaner2', desc: 'Master Cleaner 2', body: 2 }],
|
|
60
|
+
[12, { name: 'pool', desc: 'Pool', hasHeatSource: true, body: 1 }],
|
|
61
|
+
[13, { name: 'spa', desc: 'Spa', hasHeatSource: true, body: 2 }]
|
|
62
62
|
]);
|
|
63
63
|
this.valueMaps.pumpTypes = new byteValueMap([
|
|
64
|
-
[1, { name: 'ss', desc: 'Single Speed', maxCircuits: 0, hasAddress: false, hasBody:true }],
|
|
65
|
-
[2, { name: 'ds', desc: 'Two Speed', maxCircuits: 8, hasAddress: false, hasBody:true }],
|
|
64
|
+
[1, { name: 'ss', desc: 'Single Speed', maxCircuits: 0, hasAddress: false, hasBody: true }],
|
|
65
|
+
[2, { name: 'ds', desc: 'Two Speed', maxCircuits: 8, hasAddress: false, hasBody: true }],
|
|
66
66
|
[3, { name: 'vs', desc: 'Intelliflo VS', maxPrimingTime: 6, minSpeed: 450, maxSpeed: 3450, maxCircuits: 8, hasAddress: true }],
|
|
67
67
|
[4, { name: 'vsf', desc: 'Intelliflo VSF', minSpeed: 450, maxSpeed: 3450, minFlow: 15, maxFlow: 130, maxCircuits: 8, hasAddress: true }],
|
|
68
68
|
[5, { name: 'vf', desc: 'Intelliflo VF', maxPrimingTime: 6, minFlow: 15, maxFlow: 130, maxCircuits: 8, hasAddress: true }],
|
|
69
|
-
[100, {name: 'sf', desc: 'SuperFlo VS', hasAddress: false, maxCircuits: 8, maxRelays: 4, equipmentMaster: 1}]
|
|
69
|
+
[100, { name: 'sf', desc: 'SuperFlo VS', hasAddress: false, maxCircuits: 8, maxRelays: 4, equipmentMaster: 1, maxSpeeds: 4, relays: [{ id: 1, name: 'Program #1' }, { id: 2, name: 'Program #2' }, { id: 3, name: 'Program #3' }, { id: 4, name: 'Program #4' }] }],
|
|
70
|
+
[101, { name: 'hwrly', desc: 'Hayward Relay VS', hasAddress: false, maxCircuits: 8, maxRelays: 4, equipmentMaster: 1, maxSpeeds: 8, relays: [{ id: 1, name: 'Step #1' }, { id: 2, name: 'Step #2' }, { id: 3, name: 'Step #3' }, { id: 4, name: 'Pump On' }] }],
|
|
71
|
+
[102, { name: 'hwvs', desc: 'Hayward Eco/TriStar VS', minSpeed: 450, maxSpeed: 3450, maxCircuits: 8, hasAddress: true, equipmentMaster: 1 }]
|
|
70
72
|
]);
|
|
71
73
|
// RSG - same as systemBoard definition; can delete.
|
|
72
74
|
this.valueMaps.heatModes = new byteValueMap([
|
|
@@ -91,12 +93,12 @@ export class IntelliCenterBoard extends SystemBoard {
|
|
|
91
93
|
]);
|
|
92
94
|
this.valueMaps.heaterTypes = new byteValueMap([
|
|
93
95
|
[1, { name: 'gas', desc: 'Gas Heater', hasAddress: false }],
|
|
94
|
-
[2, { name: 'solar', desc: 'Solar Heater', hasAddress: false, hasCoolSetpoint: true }],
|
|
95
|
-
[3, { name: 'heatpump', desc: 'Heat Pump', hasAddress: true }],
|
|
96
|
-
[4, { name: 'ultratemp', desc: 'UltraTemp', hasAddress: true, hasCoolSetpoint: true }],
|
|
96
|
+
[2, { name: 'solar', desc: 'Solar Heater', hasAddress: false, hasCoolSetpoint: true, hasPreference: true }],
|
|
97
|
+
[3, { name: 'heatpump', desc: 'Heat Pump', hasAddress: true, hasPreference: true }],
|
|
98
|
+
[4, { name: 'ultratemp', desc: 'UltraTemp', hasAddress: true, hasCoolSetpoint: true, hasPreference: true }],
|
|
97
99
|
[5, { name: 'hybrid', desc: 'Hybrid', hasAddress: true }],
|
|
98
|
-
[6, { name: '
|
|
99
|
-
[7, { name: '
|
|
100
|
+
[6, { name: 'mastertemp', desc: 'MasterTemp', hasAddress: true }],
|
|
101
|
+
[7, { name: 'maxetherm', desc: 'Max-E-Therm', hasAddress: true }],
|
|
100
102
|
]);
|
|
101
103
|
|
|
102
104
|
|
|
@@ -133,7 +135,7 @@ export class IntelliCenterBoard extends SystemBoard {
|
|
|
133
135
|
[4, { name: 'i10P', part: '521993Z', desc: 'i10P Personality Card', bodies: 1, valves: 2, circuits: 10, shared: false, dual: false, chlorinators: 1, chemControllers: 1 }], // This is a guess
|
|
134
136
|
[5, { name: 'i10PS', part: '521873Z', desc: 'i10PS Personality Card', bodies: 2, valves: 4, circuits: 11, shared: true, dual: false, chlorinators: 1, chemControllers: 1 }],
|
|
135
137
|
[6, { name: 'i10x', part: '522997Z', desc: 'i10x Expansion Module', circuits: 10 }],
|
|
136
|
-
[7, { name: 'i10D', part: '523029Z', desc: 'i10D Personality Card', bodies: 2, valves: 2, circuits: 11, shared: false, dual: true, chlorinators: 1, chemControllers:
|
|
138
|
+
[7, { name: 'i10D', part: '523029Z', desc: 'i10D Personality Card', bodies: 2, valves: 2, circuits: 11, shared: false, dual: true, chlorinators: 1, chemControllers: 2 }], // We have witnessed this in the wild
|
|
137
139
|
[8, { name: 'Valve Exp', part: '522440', desc: 'Valve Expansion Module', valves: 6 }],
|
|
138
140
|
[9, { name: 'A/D Module', part: '522039', desc: 'A/D Cover Module', covers: 2 }], // Finally have a user with one of these
|
|
139
141
|
[10, { name: 'iChlor Mux', part: '522719', desc: 'iChlor MUX Card', chlorinators: 3 }], // This is a guess
|
|
@@ -173,20 +175,49 @@ export class IntelliCenterBoard extends SystemBoard {
|
|
|
173
175
|
[2, { name: 'sunset', desc: 'Sunset' }]
|
|
174
176
|
]);
|
|
175
177
|
this.valueMaps.lightThemes = new byteValueMap([
|
|
176
|
-
[0, { name: 'white', desc: 'White', sequence: 11 }],
|
|
177
|
-
[1, { name: 'green', desc: 'Green', sequence: 9 }],
|
|
178
|
-
[2, { name: 'blue', desc: 'Blue', sequence: 8 }],
|
|
179
|
-
[3, { name: 'magenta', desc: 'Magenta', sequence: 12 }],
|
|
180
|
-
[4, { name: 'red', desc: 'Red', sequence: 10 }],
|
|
181
|
-
[5, { name: 'sam', desc: 'SAm Mode', sequence: 1 }],
|
|
182
|
-
[6, { name: 'party', desc: 'Party', sequence: 2 }],
|
|
183
|
-
[7, { name: 'romance', desc: 'Romance', sequence: 3 }],
|
|
184
|
-
[8, { name: 'caribbean', desc: 'Caribbean', sequence: 4 }],
|
|
185
|
-
[9, { name: 'american', desc: 'American', sequence: 5 }],
|
|
186
|
-
[10, { name: 'sunset', desc: 'Sunset', sequence: 6 }],
|
|
187
|
-
[11, { name: 'royal', desc: 'Royal', sequence: 7 }],
|
|
178
|
+
[0, { name: 'white', desc: 'White', sequence: 11, types:['intellibrite', 'magicstream'] }],
|
|
179
|
+
[1, { name: 'green', desc: 'Green', sequence: 9, types: ['intellibrite', 'magicstream'] }],
|
|
180
|
+
[2, { name: 'blue', desc: 'Blue', sequence: 8, types: ['intellibrite', 'magicstream'] }],
|
|
181
|
+
[3, { name: 'magenta', desc: 'Magenta', sequence: 12, types: ['intellibrite', 'magicstream'] }],
|
|
182
|
+
[4, { name: 'red', desc: 'Red', sequence: 10, types: ['intellibrite', 'magicstream'] }],
|
|
183
|
+
[5, { name: 'sam', desc: 'SAm Mode', sequence: 1, types: ['intellibrite', 'magicstream'] }],
|
|
184
|
+
[6, { name: 'party', desc: 'Party', sequence: 2, types: ['intellibrite', 'magicstream'] }],
|
|
185
|
+
[7, { name: 'romance', desc: 'Romance', sequence: 3, types: ['intellibrite', 'magicstream'] }],
|
|
186
|
+
[8, { name: 'caribbean', desc: 'Caribbean', sequence: 4, types: ['intellibrite', 'magicstream'] }],
|
|
187
|
+
[9, { name: 'american', desc: 'American', sequence: 5, types: ['intellibrite', 'magicstream'] }],
|
|
188
|
+
[10, { name: 'sunset', desc: 'Sunset', sequence: 6, types: ['intellibrite', 'magicstream'] }],
|
|
189
|
+
[11, { name: 'royal', desc: 'Royal', sequence: 7, types: ['intellibrite', 'magicstream'] }],
|
|
188
190
|
[255, { name: 'none', desc: 'None' }]
|
|
189
191
|
]);
|
|
192
|
+
this.valueMaps.lightGroupCommands = new byteValueMap([
|
|
193
|
+
[1, { name: 'colorsync', desc: 'Sync', types: ['intellibrite'], command: 'colorSync', message: 'Synchronizing' }],
|
|
194
|
+
[2, { name: 'colorset', desc: 'Set', types: ['intellibrite'], command: 'colorSet', message: 'Sequencing Set Operation' }],
|
|
195
|
+
[3, { name: 'colorswim', desc: 'Swim', types: ['intellibrite'], command: 'colorSwim', message: 'Sequencing Swim Operation' }],
|
|
196
|
+
[12, { name: 'colorhold', desc: 'Hold', types: ['intellibrite', 'magicstream'], command: 'colorHold', message: 'Saving Current Colors', sequence: 13 }],
|
|
197
|
+
[13, { name: 'colorrecall', desc: 'Recall', types: ['intellibrite', 'magicstream'], command: 'colorRecall', message: 'Recalling Saved Colors', sequence: 14 }]
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
this.valueMaps.lightCommands = new byteValueMap([
|
|
201
|
+
[12, { name: 'colorhold', desc: 'Hold', types: ['intellibrite'], sequence: 13 }],
|
|
202
|
+
[13, { name: 'colorrecall', desc: 'Recall', types: ['intellibrite'], sequence: 14 }],
|
|
203
|
+
[15, {
|
|
204
|
+
name: 'lightthumper', desc: 'Thumper', types: ['magicstream'], command: 'lightThumper', message: 'Toggling Thumper',
|
|
205
|
+
sequence: [ // Cycle party mode 3 times.
|
|
206
|
+
{ isOn: false, timeout: 100 },
|
|
207
|
+
{ isOn: true, timeout: 100 },
|
|
208
|
+
{ isOn: false, timeout: 100 },
|
|
209
|
+
{ isOn: true, timeout: 5000 },
|
|
210
|
+
{ isOn: false, timeout: 100 },
|
|
211
|
+
{ isOn: true, timeout: 100 },
|
|
212
|
+
{ isOn: false, timeout: 100 },
|
|
213
|
+
{ isOn: true, timeout: 5000 },
|
|
214
|
+
{ isOn: false, timeout: 100 },
|
|
215
|
+
{ isOn: true, timeout: 100 },
|
|
216
|
+
{ isOn: false, timeout: 100 },
|
|
217
|
+
{ isOn: true, timeout: 1000 },
|
|
218
|
+
]
|
|
219
|
+
}]
|
|
220
|
+
]);
|
|
190
221
|
this.valueMaps.lightColors = new byteValueMap([
|
|
191
222
|
[0, { name: 'white', desc: 'White' }],
|
|
192
223
|
[16, { name: 'lightgreen', desc: 'Light Green' }],
|
|
@@ -241,7 +272,6 @@ export class IntelliCenterBoard extends SystemBoard {
|
|
|
241
272
|
public heaters: IntelliCenterHeaterCommands = new IntelliCenterHeaterCommands(this);
|
|
242
273
|
public valves: IntelliCenterValveCommands = new IntelliCenterValveCommands(this);
|
|
243
274
|
public chemControllers: IntelliCenterChemControllerCommands = new IntelliCenterChemControllerCommands(this);
|
|
244
|
-
|
|
245
275
|
public reloadConfig() {
|
|
246
276
|
//sys.resetSystem();
|
|
247
277
|
sys.configVersion.clear();
|
|
@@ -569,7 +599,7 @@ export class IntelliCenterBoard extends SystemBoard {
|
|
|
569
599
|
}
|
|
570
600
|
public get commandSourceAddress(): number { return Message.pluginAddress; }
|
|
571
601
|
public get commandDestAddress(): number { return 15; }
|
|
572
|
-
public static getAckResponse(action: number): Response { return Response.create({ dest: sys.board.commandSourceAddress, action: 1, payload: [action] }); }
|
|
602
|
+
public static getAckResponse(action: number, source?: number, dest?: number): Response { return Response.create({ source: source, dest: dest || sys.board.commandSourceAddress, action: 1, payload: [action] }); }
|
|
573
603
|
}
|
|
574
604
|
class IntelliCenterConfigRequest extends ConfigRequest {
|
|
575
605
|
constructor(cat: number, ver: number, items?: number[], oncomplete?: Function) {
|
|
@@ -640,14 +670,14 @@ class IntelliCenterConfigQueue extends ConfigQueue {
|
|
|
640
670
|
response: Response.create({ dest:-1, action: 30, payload: [this.curr.category, itm], callback: () => { self.processNext(out); } })
|
|
641
671
|
});
|
|
642
672
|
logger.verbose(`Requesting config for: ${ConfigCategories[this.curr.category]} - Item: ${itm}`);
|
|
643
|
-
setTimeout(conn.queueSendMessage, 50
|
|
673
|
+
setTimeout(() => { conn.queueSendMessage(out) }, 50);
|
|
644
674
|
} else {
|
|
645
675
|
// Now that we are done check the configuration a final time. If we have anything outstanding
|
|
646
676
|
// it will get picked up.
|
|
647
677
|
state.status = 1;
|
|
648
678
|
this.curr = null;
|
|
649
679
|
this._processing = false;
|
|
650
|
-
if (this._failed) setTimeout(
|
|
680
|
+
if (this._failed) setTimeout(() => { sys.checkConfiguration(); }, 100);
|
|
651
681
|
logger.info(`Configuration Complete`);
|
|
652
682
|
sys.board.heaters.updateHeaterServices();
|
|
653
683
|
state.cleanupState();
|
|
@@ -752,24 +782,41 @@ class IntelliCenterConfigQueue extends ConfigQueue {
|
|
|
752
782
|
this.maybeQueueItems(curr.general, ver.general, ConfigCategories.general, [0, 1, 2, 3, 4, 5, 6, 7]);
|
|
753
783
|
this.maybeQueueItems(curr.covers, ver.covers, ConfigCategories.covers, [0, 1]);
|
|
754
784
|
if (this.compareVersions(curr.schedules, ver.schedules)) {
|
|
755
|
-
|
|
785
|
+
// Alright we used to think we could rely on the schedule start time as the trigger that identifies an active schedule. However, active
|
|
786
|
+
// schedules are actually determined by looking at the schedule type messages[8-10].
|
|
787
|
+
let req = new IntelliCenterConfigRequest(ConfigCategories.schedules, ver.schedules, [8, 9, 10], function (req: IntelliCenterConfigRequest) {
|
|
756
788
|
let maxSchedId = sys.schedules.getMaxId();
|
|
757
789
|
req.fillRange(5, 5 + Math.min(Math.ceil(maxSchedId / 40), 7)); // Circuits
|
|
758
|
-
req.fillRange(8, 8 + Math.min(Math.ceil(maxSchedId / 40), 10)); // Flags
|
|
759
790
|
req.fillRange(11, 11 + Math.min(Math.ceil(maxSchedId / 40), 13)); // Schedule days bitmask
|
|
760
|
-
req.fillRange(
|
|
761
|
-
req.fillRange(17, 17 + Math.min(Math.ceil(maxSchedId / 40), 19)); // Unknown (one byte per schedule)
|
|
762
|
-
req.fillRange(20, 20 + Math.min(Math.ceil(maxSchedId / 40), 22)); // Unknown (one byte per schedule)
|
|
791
|
+
req.fillRange(0, Math.min(Math.ceil(maxSchedId / 40), 4)); // Start Time
|
|
763
792
|
req.fillRange(23, 23 + Math.min(Math.ceil(maxSchedId / 20), 26)); // End Time
|
|
793
|
+
req.fillRange(14, 14 + Math.min(Math.ceil(maxSchedId / 40), 16)); // Start Month
|
|
794
|
+
req.fillRange(17, 17 + Math.min(Math.ceil(maxSchedId / 40), 19)); // Start Day
|
|
795
|
+
req.fillRange(20, 20 + Math.min(Math.ceil(maxSchedId / 40), 22)); // Start Year
|
|
764
796
|
req.fillRange(28, 28 + Math.min(Math.ceil(maxSchedId / 40), 30)); // Heat Mode
|
|
765
797
|
req.fillRange(31, 31 + Math.min(Math.ceil(maxSchedId / 40), 33)); // Heat Mode
|
|
766
798
|
req.fillRange(34, 34 + Math.min(Math.ceil(maxSchedId / 40), 36)); // Heat Mode
|
|
767
799
|
});
|
|
800
|
+
// DEPRECATED: 12-26-21 This was the old order of fetching the schedule. This did not work properly with start times of midnight since the start time of 0
|
|
801
|
+
// was previously being used to determine whether the schedule was active. The schedule/time type messages are now being used.
|
|
802
|
+
//let req = new IntelliCenterConfigRequest(ConfigCategories.schedules, ver.schedules, [0, 1, 2, 3, 4], function (req: IntelliCenterConfigRequest) {
|
|
803
|
+
// let maxSchedId = sys.schedules.getMaxId();
|
|
804
|
+
// req.fillRange(5, 5 + Math.min(Math.ceil(maxSchedId / 40), 7)); // Circuits
|
|
805
|
+
// req.fillRange(8, 8 + Math.min(Math.ceil(maxSchedId / 40), 10)); // Flags
|
|
806
|
+
// req.fillRange(11, 11 + Math.min(Math.ceil(maxSchedId / 40), 13)); // Schedule days bitmask
|
|
807
|
+
// req.fillRange(14, 14 + Math.min(Math.ceil(maxSchedId / 40), 16)); // Unknown (one byte per schedule)
|
|
808
|
+
// req.fillRange(17, 17 + Math.min(Math.ceil(maxSchedId / 40), 19)); // Unknown (one byte per schedule)
|
|
809
|
+
// req.fillRange(20, 20 + Math.min(Math.ceil(maxSchedId / 40), 22)); // Unknown (one byte per schedule)
|
|
810
|
+
// req.fillRange(23, 23 + Math.min(Math.ceil(maxSchedId / 20), 26)); // End Time
|
|
811
|
+
// req.fillRange(28, 28 + Math.min(Math.ceil(maxSchedId / 40), 30)); // Heat Mode
|
|
812
|
+
// req.fillRange(31, 31 + Math.min(Math.ceil(maxSchedId / 40), 33)); // Heat Mode
|
|
813
|
+
// req.fillRange(34, 34 + Math.min(Math.ceil(maxSchedId / 40), 36)); // Heat Mode
|
|
814
|
+
//});
|
|
768
815
|
this.push(req);
|
|
769
816
|
}
|
|
770
817
|
this.maybeQueueItems(curr.systemState, ver.systemState, ConfigCategories.systemState, [0]);
|
|
771
818
|
logger.info(`Queued ${this.remainingItems} configuration items`);
|
|
772
|
-
if (this.remainingItems > 0) setTimeout(
|
|
819
|
+
if (this.remainingItems > 0) setTimeout(() => { self.processNext(); }, 50);
|
|
773
820
|
else {
|
|
774
821
|
this._processing = false;
|
|
775
822
|
if (this._newRequest) {
|
|
@@ -1292,7 +1339,7 @@ class IntelliCenterSystemCommands extends SystemCommands {
|
|
|
1292
1339
|
let out = Outbound.create({
|
|
1293
1340
|
action: 168,
|
|
1294
1341
|
retries: 5,
|
|
1295
|
-
payload: [12, 0,
|
|
1342
|
+
payload: [12, 0, 13, parseInt(obj.timeZone, 10)],
|
|
1296
1343
|
response: IntelliCenterBoard.getAckResponse(168),
|
|
1297
1344
|
onComplete: (err, msg) => {
|
|
1298
1345
|
if (err) reject(err);
|
|
@@ -1956,15 +2003,83 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
|
|
|
1956
2003
|
}
|
|
1957
2004
|
catch (err) { return Promise.reject(err); }
|
|
1958
2005
|
}
|
|
1959
|
-
public
|
|
2006
|
+
public async runLightGroupCommandAsync(obj: any): Promise<ICircuitState> {
|
|
2007
|
+
// Do all our validation.
|
|
2008
|
+
try {
|
|
2009
|
+
let id = parseInt(obj.id, 10);
|
|
2010
|
+
let cmd = typeof obj.command !== 'undefined' ? sys.board.valueMaps.lightGroupCommands.findItem(obj.command) : { val: 0, name: 'undefined' };
|
|
2011
|
+
if (cmd.val === 0) return Promise.reject(new InvalidOperationError(`Light group command ${cmd.name} does not exist`, 'runLightGroupCommandAsync'));
|
|
2012
|
+
if (isNaN(id)) return Promise.reject(new InvalidOperationError(`Light group ${id} does not exist`, 'runLightGroupCommandAsync'));
|
|
2013
|
+
let grp = sys.lightGroups.getItemById(id);
|
|
2014
|
+
let nop = sys.board.valueMaps.circuitActions.getValue(cmd.name);
|
|
2015
|
+
let sgrp = state.lightGroups.getItemById(grp.id);
|
|
2016
|
+
sgrp.action = nop;
|
|
2017
|
+
sgrp.emitEquipmentChange();
|
|
2018
|
+
switch (cmd.name) {
|
|
2019
|
+
case 'colorset':
|
|
2020
|
+
await this.sequenceLightGroupAsync(id, 'colorset');
|
|
2021
|
+
break;
|
|
2022
|
+
case 'colorswim':
|
|
2023
|
+
await this.sequenceLightGroupAsync(id, 'colorswim');
|
|
2024
|
+
break;
|
|
2025
|
+
case 'colorhold':
|
|
2026
|
+
await this.setLightGroupThemeAsync(id, 12);
|
|
2027
|
+
break;
|
|
2028
|
+
case 'colorrecall':
|
|
2029
|
+
await this.setLightGroupThemeAsync(id, 13);
|
|
2030
|
+
break;
|
|
2031
|
+
case 'lightthumper':
|
|
2032
|
+
break;
|
|
2033
|
+
}
|
|
2034
|
+
sgrp.action = 0;
|
|
2035
|
+
sgrp.emitEquipmentChange();
|
|
2036
|
+
return sgrp;
|
|
2037
|
+
}
|
|
2038
|
+
catch (err) { return Promise.reject(`Error runLightGroupCommandAsync ${err.message}`); }
|
|
2039
|
+
}
|
|
2040
|
+
public async runLightCommandAsync(obj: any): Promise<ICircuitState> {
|
|
2041
|
+
// Do all our validation.
|
|
2042
|
+
try {
|
|
2043
|
+
let id = parseInt(obj.id, 10);
|
|
2044
|
+
let cmd = typeof obj.command !== 'undefined' ? sys.board.valueMaps.lightCommands.findItem(obj.command) : { val: 0, name: 'undefined' };
|
|
2045
|
+
if (cmd.val === 0) return Promise.reject(new InvalidOperationError(`Light command ${cmd.name} does not exist`, 'runLightCommandAsync'));
|
|
2046
|
+
if (isNaN(id)) return Promise.reject(new InvalidOperationError(`Light ${id} does not exist`, 'runLightCommandAsync'));
|
|
2047
|
+
let circ = sys.circuits.getItemById(id);
|
|
2048
|
+
if (!circ.isActive) return Promise.reject(new InvalidOperationError(`Light circuit #${id} is not active`, 'runLightCommandAsync'));
|
|
2049
|
+
let type = sys.board.valueMaps.circuitFunctions.transform(circ.type);
|
|
2050
|
+
if (!type.isLight) return Promise.reject(new InvalidOperationError(`Circuit #${id} is not a light`, 'runLightCommandAsync'));
|
|
2051
|
+
let nop = sys.board.valueMaps.circuitActions.getValue(cmd.name);
|
|
2052
|
+
let slight = state.circuits.getItemById(circ.id);
|
|
2053
|
+
slight.action = nop;
|
|
2054
|
+
slight.emitEquipmentChange();
|
|
2055
|
+
switch (cmd.name) {
|
|
2056
|
+
case 'colorhold':
|
|
2057
|
+
await this.setLightThemeAsync(id, 12);
|
|
2058
|
+
break;
|
|
2059
|
+
case 'colorrecall':
|
|
2060
|
+
await this.setLightThemeAsync(id, 13);
|
|
2061
|
+
break;
|
|
2062
|
+
case 'lightthumper':
|
|
2063
|
+
// I do not know how to trigger the thumper.
|
|
2064
|
+
break;
|
|
2065
|
+
}
|
|
2066
|
+
slight.action = 0;
|
|
2067
|
+
slight.emitEquipmentChange();
|
|
2068
|
+
return slight;
|
|
2069
|
+
}
|
|
2070
|
+
catch (err) { return Promise.reject(`Error runLightCommandAsync ${err.message}`); }
|
|
2071
|
+
}
|
|
2072
|
+
public async sequenceLightGroupAsync(id: number, operation: string): Promise<LightGroupState> {
|
|
1960
2073
|
let sgroup = state.lightGroups.getItemById(id);
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
let
|
|
2074
|
+
try {
|
|
2075
|
+
if (!sgroup.isActive) return Promise.reject(new InvalidEquipmentIdError(`An active light group could not be found with id ${id}`, id, 'lightGroup'));
|
|
2076
|
+
let cmd = sys.board.valueMaps.lightGroupCommands.findItem(operation.toLowerCase());
|
|
1964
2077
|
let ndx = id - sys.board.equipmentIds.circuitGroups.start;
|
|
1965
2078
|
let byteNdx = Math.floor(ndx / 4);
|
|
1966
2079
|
let bitNdx = (ndx * 2) - (byteNdx * 8);
|
|
2080
|
+
let out = this.createCircuitStateMessage(id, true);
|
|
1967
2081
|
let byte = out.payload[28 + byteNdx];
|
|
2082
|
+
|
|
1968
2083
|
// Each light group is represented by two bits on the status byte. There are 3 status bytes that give us only 12 of the 16 on the config stream but the 168 message
|
|
1969
2084
|
// does acutally send 4 so all are represented there.
|
|
1970
2085
|
// [10] = Set
|
|
@@ -1973,46 +2088,96 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
|
|
|
1973
2088
|
// [11] = No sequencing underway.
|
|
1974
2089
|
// In the end we are only trying to impact the specific bits in the middle of the byte that represent
|
|
1975
2090
|
// the light group we are dealing with.
|
|
1976
|
-
switch (
|
|
1977
|
-
case
|
|
2091
|
+
switch (cmd.name) {
|
|
2092
|
+
case 'colorsync':
|
|
1978
2093
|
byte &= ((0xFC << bitNdx) | (0xFF >> (8 - bitNdx)));
|
|
1979
2094
|
break;
|
|
1980
|
-
case
|
|
2095
|
+
case 'colorset':
|
|
1981
2096
|
byte &= ((0xFE << bitNdx) | (0xFF >> (8 - bitNdx)));
|
|
1982
2097
|
break;
|
|
1983
|
-
case
|
|
2098
|
+
case 'colorswim':
|
|
1984
2099
|
byte &= ((0xFD << bitNdx) | (0xFF >> (8 - bitNdx)));
|
|
1985
2100
|
break;
|
|
2101
|
+
default:
|
|
2102
|
+
return Promise.reject(new InvalidOperationError(`Invalid Light Group Sequence ${operation}`, 'sequenceLightGroupAsync'));
|
|
1986
2103
|
}
|
|
1987
|
-
|
|
2104
|
+
sgroup.emitEquipmentChange();
|
|
1988
2105
|
out.payload[28 + byteNdx] = byte;
|
|
1989
|
-
|
|
2106
|
+
// So now we have all the info we need to sequence the group.
|
|
2107
|
+
await new Promise((resolve, reject) => {
|
|
1990
2108
|
out.retries = 5;
|
|
1991
2109
|
out.response = IntelliCenterBoard.getAckResponse(168);
|
|
1992
2110
|
out.onComplete = (err, msg) => {
|
|
1993
2111
|
if (!err) {
|
|
1994
|
-
sgroup.action =
|
|
2112
|
+
sgroup.action = sys.board.valueMaps.circuitActions.getValue(cmd.name);
|
|
1995
2113
|
state.emitEquipmentChanges();
|
|
1996
2114
|
resolve(sgroup);
|
|
1997
2115
|
}
|
|
1998
|
-
else
|
|
2116
|
+
else {
|
|
2117
|
+
sgroup.action = 0;
|
|
2118
|
+
reject(err);
|
|
2119
|
+
}
|
|
1999
2120
|
};
|
|
2000
2121
|
conn.queueSendMessage(out);
|
|
2001
2122
|
});
|
|
2002
|
-
|
|
2003
|
-
return Promise.
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2123
|
+
return sgroup;
|
|
2124
|
+
} catch (err) { return Promise.reject(new InvalidOperationError(`Error Sequencing Light Group: ${err.message}`, 'sequenceLightGroupAsync')); }
|
|
2125
|
+
//let nop = sys.board.valueMaps.circuitActions.getValue(operation);
|
|
2126
|
+
//if (nop > 0) {
|
|
2127
|
+
// let out = this.createCircuitStateMessage(id, true);
|
|
2128
|
+
// let ndx = id - sys.board.equipmentIds.circuitGroups.start;
|
|
2129
|
+
// let byteNdx = Math.floor(ndx / 4);
|
|
2130
|
+
// let bitNdx = (ndx * 2) - (byteNdx * 8);
|
|
2131
|
+
// let byte = out.payload[28 + byteNdx];
|
|
2132
|
+
// // Each light group is represented by two bits on the status byte. There are 3 status bytes that give us only 12 of the 16 on the config stream but the 168 message
|
|
2133
|
+
// // does acutally send 4 so all are represented there.
|
|
2134
|
+
// // [10] = Set
|
|
2135
|
+
// // [01] = Swim
|
|
2136
|
+
// // [00] = Sync
|
|
2137
|
+
// // [11] = No sequencing underway.
|
|
2138
|
+
// // In the end we are only trying to impact the specific bits in the middle of the byte that represent
|
|
2139
|
+
// // the light group we are dealing with.
|
|
2140
|
+
// switch (nop) {
|
|
2141
|
+
// case 1: // Sync
|
|
2142
|
+
// byte &= ((0xFC << bitNdx) | (0xFF >> (8 - bitNdx)));
|
|
2143
|
+
// break;
|
|
2144
|
+
// case 2: // Color Set
|
|
2145
|
+
// byte &= ((0xFE << bitNdx) | (0xFF >> (8 - bitNdx)));
|
|
2146
|
+
// break;
|
|
2147
|
+
// case 3: // Color Swim
|
|
2148
|
+
// byte &= ((0xFD << bitNdx) | (0xFF >> (8 - bitNdx)));
|
|
2149
|
+
// break;
|
|
2150
|
+
// }
|
|
2151
|
+
// console.log({ groupNdx: ndx, action: nop, byteNdx: byteNdx, bitNdx: bitNdx, byte: byte })
|
|
2152
|
+
// out.payload[28 + byteNdx] = byte;
|
|
2153
|
+
// return new Promise<LightGroupState>((resolve, reject) => {
|
|
2154
|
+
// out.retries = 5;
|
|
2155
|
+
// out.response = IntelliCenterBoard.getAckResponse(168);
|
|
2156
|
+
// out.onComplete = (err, msg) => {
|
|
2157
|
+
// if (!err) {
|
|
2158
|
+
// sgroup.action = nop;
|
|
2159
|
+
// state.emitEquipmentChanges();
|
|
2160
|
+
// resolve(sgroup);
|
|
2161
|
+
// }
|
|
2162
|
+
// else reject(err);
|
|
2163
|
+
// };
|
|
2164
|
+
// conn.queueSendMessage(out);
|
|
2165
|
+
// });
|
|
2166
|
+
//}
|
|
2167
|
+
//return Promise.resolve(sgroup);
|
|
2015
2168
|
}
|
|
2169
|
+
// 12-01-21 RKS: This has been deprecated. This allows for multiple vendor light themes driven by the metadata on the valuemaps.
|
|
2170
|
+
//public getLightThemes(type: number): any[] {
|
|
2171
|
+
// switch (type) {
|
|
2172
|
+
// case 5: // Intellibrite
|
|
2173
|
+
// case 6: // Globrite
|
|
2174
|
+
// case 8: // Magicstream
|
|
2175
|
+
// case 10: // ColorCascade
|
|
2176
|
+
// return sys.board.valueMaps.lightThemes.toArray();
|
|
2177
|
+
// default:
|
|
2178
|
+
// return [];
|
|
2179
|
+
// }
|
|
2180
|
+
//}
|
|
2016
2181
|
private async verifyVersionAsync(): Promise<boolean> {
|
|
2017
2182
|
return new Promise<boolean>((resolve, reject) => {
|
|
2018
2183
|
let out = Outbound.create({
|
|
@@ -2072,7 +2237,7 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
|
|
|
2072
2237
|
conn.queueSendMessage(out);
|
|
2073
2238
|
});
|
|
2074
2239
|
}
|
|
2075
|
-
public async setCircuitStateAsync(id: number, val: boolean): Promise<ICircuitState> {
|
|
2240
|
+
public async setCircuitStateAsync(id: number, val: boolean, ignoreDelays?: boolean): Promise<ICircuitState> {
|
|
2076
2241
|
let c = sys.circuits.getInterfaceById(id);
|
|
2077
2242
|
if (c.master !== 0) return await super.setCircuitStateAsync(id, val);
|
|
2078
2243
|
// As of 1.047 there is a sequence to this.
|
|
@@ -2164,6 +2329,30 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
|
|
|
2164
2329
|
}
|
|
2165
2330
|
catch (err) { return Promise.reject(err); }
|
|
2166
2331
|
}
|
|
2332
|
+
public async setColorHoldAsync(id: number): Promise<ICircuitState> {
|
|
2333
|
+
let circuit = sys.circuits.getInterfaceById(id);
|
|
2334
|
+
if (circuit.master === 1) return await super.setColorHoldAsync(id);
|
|
2335
|
+
try {
|
|
2336
|
+
if (sys.board.equipmentIds.circuitGroups.isInRange(id)) {
|
|
2337
|
+
await this.setLightGroupThemeAsync(id, 12);
|
|
2338
|
+
return Promise.resolve(state.lightGroups.getItemById(id));
|
|
2339
|
+
}
|
|
2340
|
+
return await this.setLightThemeAsync(id, 12);
|
|
2341
|
+
}
|
|
2342
|
+
catch (err) { return Promise.reject(err); }
|
|
2343
|
+
}
|
|
2344
|
+
public async setColorRecallAsync(id: number): Promise<ICircuitState> {
|
|
2345
|
+
let circuit = sys.circuits.getInterfaceById(id);
|
|
2346
|
+
if (circuit.master === 1) return await super.setColorHoldAsync(id);
|
|
2347
|
+
try {
|
|
2348
|
+
if (sys.board.equipmentIds.circuitGroups.isInRange(id)) {
|
|
2349
|
+
await this.setLightGroupThemeAsync(id, 13);
|
|
2350
|
+
return Promise.resolve(state.lightGroups.getItemById(id));
|
|
2351
|
+
}
|
|
2352
|
+
return await this.setLightThemeAsync(id, 13);
|
|
2353
|
+
}
|
|
2354
|
+
catch (err) { return Promise.reject(err); }
|
|
2355
|
+
}
|
|
2167
2356
|
public async setLightThemeAsync(id: number, theme: number): Promise<ICircuitState> {
|
|
2168
2357
|
let circuit = sys.circuits.getInterfaceById(id);
|
|
2169
2358
|
if (circuit.master === 1) return await super.setLightThemeAsync(id, theme);
|
|
@@ -2179,6 +2368,7 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
|
|
|
2179
2368
|
action: 168, payload: [1, 0, id - 1, circuit.type, circuit.freeze ? 1 : 0, circuit.showInFeatures ? 1 : 0,
|
|
2180
2369
|
theme, Math.floor(circuit.eggTimer / 60), circuit.eggTimer - ((Math.floor(circuit.eggTimer) / 60) * 60), circuit.dontStop ? 1 : 0]
|
|
2181
2370
|
});
|
|
2371
|
+
cstate.action = sys.board.valueMaps.circuitActions.getValue('lighttheme');
|
|
2182
2372
|
out.response = IntelliCenterBoard.getAckResponse(168);
|
|
2183
2373
|
out.retries = 5;
|
|
2184
2374
|
await new Promise<void>((resolve, reject) => {
|
|
@@ -2191,6 +2381,7 @@ class IntelliCenterCircuitCommands extends CircuitCommands {
|
|
|
2191
2381
|
else {
|
|
2192
2382
|
reject(err);
|
|
2193
2383
|
}
|
|
2384
|
+
cstate.action = 0;
|
|
2194
2385
|
};
|
|
2195
2386
|
out.appendPayloadString(circuit.name, 16);
|
|
2196
2387
|
conn.queueSendMessage(out);
|
|
@@ -2479,20 +2670,18 @@ class IntelliCenterFeatureCommands extends FeatureCommands {
|
|
|
2479
2670
|
class IntelliCenterChlorinatorCommands extends ChlorinatorCommands {
|
|
2480
2671
|
public async setChlorAsync(obj: any): Promise<ChlorinatorState> {
|
|
2481
2672
|
let id = parseInt(obj.id, 10);
|
|
2673
|
+
// Bail out right away if this is not controlled by the OCP.
|
|
2674
|
+
if (typeof obj.master !== 'undefined' && parseInt(obj.master, 10) !== 0) return super.setChlorAsync(obj);
|
|
2482
2675
|
let isAdd = false;
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
chlor.master = utils.makeBool(obj.master) ? 1 : 0;
|
|
2487
|
-
// Calculate an id for the chlorinator. The messed up part is that if a chlorinator is not attached to the OCP, its address
|
|
2488
|
-
// cannot be set by the MUX. This will have to wait.
|
|
2676
|
+
if (isNaN(id) || id <= 0) {
|
|
2677
|
+
// We are adding so we need to see if there is another chlorinator that is not external.
|
|
2678
|
+
if (sys.chlorinators.count(elem => elem.master !== 2) > sys.equipment.maxChlorinators) return Promise.reject(new InvalidEquipmentDataError(`The max number of chlorinators has been exceeded you may only add ${sys.equipment.maxChlorinators}`, 'chlorinator', sys.equipment.maxChlorinators));
|
|
2489
2679
|
id = 1;
|
|
2680
|
+
isAdd = true;
|
|
2490
2681
|
}
|
|
2682
|
+
let chlor = sys.chlorinators.getItemById(id);
|
|
2683
|
+
if (chlor.master !== 0 && !isAdd) return super.setChlorAsync(obj);
|
|
2491
2684
|
|
|
2492
|
-
//let chlor = extend(true, {}, sys.chlorinators.getItemById(id).get(), obj);
|
|
2493
|
-
// If this is a virtual chlorinator then go to the base class and handle it from there.
|
|
2494
|
-
if (chlor.master === 1) return super.setChlorAsync(obj);
|
|
2495
|
-
if (typeof chlor.master === 'undefined') chlor.master = 0;
|
|
2496
2685
|
let name = obj.name || chlor.name || 'IntelliChlor' + id;
|
|
2497
2686
|
let superChlorHours = parseInt(obj.superChlorHours, 10);
|
|
2498
2687
|
if (typeof obj.superChlorinate !== 'undefined') obj.superChlor = utils.makeBool(obj.superChlorinate);
|
|
@@ -2505,8 +2694,7 @@ class IntelliCenterChlorinatorCommands extends ChlorinatorCommands {
|
|
|
2505
2694
|
let poolSetpoint = typeof obj.poolSetpoint !== 'undefined' ? parseInt(obj.poolSetpoint, 10) : chlor.poolSetpoint;
|
|
2506
2695
|
let spaSetpoint = typeof obj.spaSetpoint !== 'undefined' ? parseInt(obj.spaSetpoint, 10) : chlor.spaSetpoint;
|
|
2507
2696
|
if (poolSetpoint === 0) console.log(obj);
|
|
2508
|
-
|
|
2509
|
-
let model = typeof obj.model !== 'undefined' ? obj.model : chlor.model;
|
|
2697
|
+
let model = typeof obj.model !== 'undefined' ? sys.board.valueMaps.chlorinatorModel.encode(obj.model) : chlor.model || 0;
|
|
2510
2698
|
let chlorType = typeof obj.type !== 'undefined' ? sys.board.valueMaps.chlorinatorType.encode(obj.type) : chlor.type || 0;
|
|
2511
2699
|
if (isAdd) {
|
|
2512
2700
|
if (isNaN(poolSetpoint)) poolSetpoint = 50;
|
|
@@ -2523,10 +2711,12 @@ class IntelliCenterChlorinatorCommands extends ChlorinatorCommands {
|
|
|
2523
2711
|
if (typeof obj.disabled !== 'undefined') chlor.disabled = utils.makeBool(obj.disabled);
|
|
2524
2712
|
if (typeof chlor.body === 'undefined') chlor.body = obj.body || 32;
|
|
2525
2713
|
// Verify the data.
|
|
2526
|
-
let body = sys.board.bodies.mapBodyAssociation(chlor.body);
|
|
2714
|
+
let body = sys.board.bodies.mapBodyAssociation(typeof obj.body === 'undefined' ? chlor.body || 0 : obj.body);
|
|
2527
2715
|
if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Chlorinator body association is not valid: ${chlor.body}`, 'chlorinator', chlor.body));
|
|
2528
2716
|
if (poolSetpoint > 100 || poolSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator poolSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.poolSetpoint));
|
|
2529
2717
|
if (spaSetpoint > 100 || spaSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator spaSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.spaSetpoint));
|
|
2718
|
+
let portId = typeof obj.portId !== 'undefined' ? parseInt(obj.portId, 10) : chlor.portId;
|
|
2719
|
+
if (portId !== chlor.portId && sys.chlorinators.count(elem => elem.id !== chlor.id && elem.portId === portId && elem.master !== 2) > 0) return Promise.reject(new InvalidEquipmentDataError(`Another chlorinator is installed on port #${portId}. Only one chlorinator can be installed per port.`, 'Chlorinator', portId));
|
|
2530
2720
|
if (typeof obj.ignoreSaltReading !== 'undefined') chlor.ignoreSaltReading = utils.makeBool(obj.ignoreSaltReading);
|
|
2531
2721
|
return new Promise<ChlorinatorState>((resolve, reject) => {
|
|
2532
2722
|
let out = Outbound.create({
|
|
@@ -2542,8 +2732,10 @@ class IntelliCenterChlorinatorCommands extends ChlorinatorCommands {
|
|
|
2542
2732
|
else {
|
|
2543
2733
|
let schlor = state.chlorinators.getItemById(id, true);
|
|
2544
2734
|
let cchlor = sys.chlorinators.getItemById(id, true);
|
|
2735
|
+
chlor.master = 0;
|
|
2736
|
+
schlor.body = chlor.body = body.val;
|
|
2545
2737
|
chlor.disabled = disabled;
|
|
2546
|
-
chlor.model = model;
|
|
2738
|
+
schlor.model = chlor.model = model;
|
|
2547
2739
|
schlor.type = chlor.type = chlorType;
|
|
2548
2740
|
chlor.name = schlor.name = name;
|
|
2549
2741
|
chlor.isDosing = isDosing;
|
|
@@ -2759,10 +2951,11 @@ class IntelliCenterPumpCommands extends PumpCommands {
|
|
|
2759
2951
|
let speed = parseInt(c.speed, 10);
|
|
2760
2952
|
let flow = parseInt(c.flow, 10);
|
|
2761
2953
|
let circuit = i < type.maxCircuits ? parseInt(c.circuit, 10) : 256;
|
|
2762
|
-
let units
|
|
2763
|
-
if (
|
|
2764
|
-
if (type.name === 'vs') units =
|
|
2765
|
-
else
|
|
2954
|
+
let units;
|
|
2955
|
+
if (type.name === 'vf') units = sys.board.valueMaps.pumpUnits.getValue('gpm');
|
|
2956
|
+
else if (type.name === 'vs') units = sys.board.valueMaps.pumpUnits.getValue('rpm');
|
|
2957
|
+
else units = sys.board.valueMaps.pumpUnits.encode(c.units);
|
|
2958
|
+
if (isNaN(units)) units = sys.board.valueMaps.pumpUnits.getValue('rpm');
|
|
2766
2959
|
outc.setPayloadByte(i + 18, circuit - 1, circ.circuit - 1);
|
|
2767
2960
|
if (typeof type.minSpeed !== 'undefined' && (parseInt(c.units, 10) === 0 || isNaN(parseInt(c.units, 10)))) {
|
|
2768
2961
|
outc.setPayloadByte(i + 26, 0); // Set to rpm
|
|
@@ -2898,6 +3091,9 @@ class IntelliCenterPumpCommands extends PumpCommands {
|
|
|
2898
3091
|
// We now need to get the type for the pump. If the incoming data doesn't include it then we need to
|
|
2899
3092
|
// get it from the current pump configuration.
|
|
2900
3093
|
let pump = sys.pumps.getItemById(id, false);
|
|
3094
|
+
// Check to see if this happens to be a Nixie Pump.
|
|
3095
|
+
if (pump.master === 1) return super.deletePumpAsync(data);
|
|
3096
|
+
|
|
2901
3097
|
if (typeof pump.type === 'undefined') return Promise.reject(new InvalidEquipmentIdError(`Pump #${data.id} does not exist in configuration`, data.id, 'Schedule'));
|
|
2902
3098
|
let outc = Outbound.create({ action: 168, payload: [4, 0, id - 1, 0, 0, id + 95] });
|
|
2903
3099
|
outc.appendPayloadInt(450); // 6
|
|
@@ -2943,6 +3139,94 @@ class IntelliCenterPumpCommands extends PumpCommands {
|
|
|
2943
3139
|
}
|
|
2944
3140
|
}
|
|
2945
3141
|
class IntelliCenterBodyCommands extends BodyCommands {
|
|
3142
|
+
private bodyHeatSettings: {
|
|
3143
|
+
processing: boolean,
|
|
3144
|
+
bytes: number[],
|
|
3145
|
+
body1: { heatMode: number, heatSetpoint: number, coolSetpoint: number },
|
|
3146
|
+
body2: { heatMode: number, heatSetpoint: number, coolSetpoint: number }
|
|
3147
|
+
};
|
|
3148
|
+
private async queueBodyHeatSettings(bodyId?: number, byte?: number, data?: any): Promise<Boolean> {
|
|
3149
|
+
if (typeof this.bodyHeatSettings === 'undefined') {
|
|
3150
|
+
let body1 = sys.bodies.getItemById(1);
|
|
3151
|
+
let body2 = sys.bodies.getItemById(2);
|
|
3152
|
+
this.bodyHeatSettings = {
|
|
3153
|
+
processing: false,
|
|
3154
|
+
bytes: [],
|
|
3155
|
+
body1: { heatMode: body1.heatMode || 1, heatSetpoint: body1.heatSetpoint || 78, coolSetpoint: body1.coolSetpoint || 100 },
|
|
3156
|
+
body2: { heatMode: body2.heatMode || 1, heatSetpoint: body2.heatSetpoint || 78, coolSetpoint: body2.coolSetpoint || 100 }
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
let bhs = this.bodyHeatSettings;
|
|
3160
|
+
if (typeof data !== 'undefined' && typeof bodyId !== 'undefined' && bodyId > 0) {
|
|
3161
|
+
let body = bodyId === 2 ? bhs.body2 : bhs.body1;
|
|
3162
|
+
if (!bhs.bytes.includes(byte) && byte) bhs.bytes.push(byte);
|
|
3163
|
+
if (typeof data.heatSetpoint !== 'undefined') body.heatSetpoint = data.heatSetpoint;
|
|
3164
|
+
if (typeof data.coolSetpoint !== 'undefined') body.coolSetpoint = data.coolSetpoint;
|
|
3165
|
+
if (typeof data.heatMode !== 'undefined') body.heatMode = data.heatMode;
|
|
3166
|
+
}
|
|
3167
|
+
if (!bhs.processing && bhs.bytes.length > 0) {
|
|
3168
|
+
bhs.processing = true;
|
|
3169
|
+
let byte2 = bhs.bytes.shift();
|
|
3170
|
+
let fnToByte = function (num) { return num < 0 ? Math.abs(num) | 0x80 : Math.abs(num) || 0; };
|
|
3171
|
+
let payload = [0, 0, byte2, 1,
|
|
3172
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('water1')),
|
|
3173
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('solar1')),
|
|
3174
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('air')),
|
|
3175
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('water2')),
|
|
3176
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('solar2')),
|
|
3177
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('water3')),
|
|
3178
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('solar3')),
|
|
3179
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('water4')),
|
|
3180
|
+
fnToByte(sys.equipment.tempSensors.getCalibration('solar4')),
|
|
3181
|
+
0,
|
|
3182
|
+
0x10 | (sys.general.options.clockMode === 24 ? 0x40 : 0x00) | (sys.general.options.adjustDST ? 0x80 : 0x00) | (sys.general.options.clockSource === 'internet' ? 0x20 : 0x00),
|
|
3183
|
+
89, 27, 110, 3, 0, 0,
|
|
3184
|
+
bhs.body1.heatSetpoint, bhs.body1.coolSetpoint, bhs.body2.heatSetpoint, bhs.body2.coolSetpoint, bhs.body1.heatMode, bhs.body2.heatMode, 0, 0, 15,
|
|
3185
|
+
sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0
|
|
3186
|
+
];
|
|
3187
|
+
return new Promise<boolean>((resolve, reject) => {
|
|
3188
|
+
let out = Outbound.create({
|
|
3189
|
+
action: 168,
|
|
3190
|
+
payload: payload,
|
|
3191
|
+
retries: 2,
|
|
3192
|
+
response: IntelliCenterBoard.getAckResponse(168),
|
|
3193
|
+
onComplete: (err, msg) => {
|
|
3194
|
+
bhs.processing = false;
|
|
3195
|
+
if (err) reject(err);
|
|
3196
|
+
else {
|
|
3197
|
+
let body1 = sys.bodies.getItemById(1);
|
|
3198
|
+
let sbody1 = state.temps.bodies.getItemById(1);
|
|
3199
|
+
body1.heatMode = sbody1.heatMode = bhs.body1.heatMode;
|
|
3200
|
+
body1.heatSetpoint = sbody1.heatSetpoint = bhs.body1.heatSetpoint;
|
|
3201
|
+
body1.coolSetpoint = sbody1.coolSetpoint = bhs.body1.coolSetpoint;
|
|
3202
|
+
if (sys.equipment.dual || sys.equipment.shared) {
|
|
3203
|
+
let body2 = sys.bodies.getItemById(2);
|
|
3204
|
+
let sbody2 = state.temps.bodies.getItemById(2);
|
|
3205
|
+
body2.heatMode = sbody2.heatMode = bhs.body2.heatMode;
|
|
3206
|
+
body2.heatSetpoint = sbody2.heatSetpoint = bhs.body2.heatSetpoint;
|
|
3207
|
+
body2.coolSetpoint = sbody2.coolSetpoint = bhs.body2.coolSetpoint;
|
|
3208
|
+
}
|
|
3209
|
+
state.emitEquipmentChanges();
|
|
3210
|
+
resolve(true);
|
|
3211
|
+
}
|
|
3212
|
+
}
|
|
3213
|
+
});
|
|
3214
|
+
conn.queueSendMessage(out);
|
|
3215
|
+
});
|
|
3216
|
+
}
|
|
3217
|
+
else {
|
|
3218
|
+
// Try every second to re-try if we have a bunch at once.
|
|
3219
|
+
if (bhs.bytes.length > 0) {
|
|
3220
|
+
setTimeout(async () => {
|
|
3221
|
+
try {
|
|
3222
|
+
await this.queueBodyHeatSettings();
|
|
3223
|
+
} catch (err) { logger.error(`Error sending queued body setpoint message: ${err.message}`); }
|
|
3224
|
+
}, 3000);
|
|
3225
|
+
}
|
|
3226
|
+
else bhs.processing = false;
|
|
3227
|
+
return true;
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
2946
3230
|
public async setBodyAsync(obj: any): Promise<Body> {
|
|
2947
3231
|
let byte = 0;
|
|
2948
3232
|
let id = parseInt(obj.id, 10);
|
|
@@ -3013,40 +3297,53 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3013
3297
|
});
|
|
3014
3298
|
}
|
|
3015
3299
|
}
|
|
3300
|
+
if (typeof obj.showInDashBoard !== 'undefined') {
|
|
3301
|
+
let sbody = state.temps.bodies.getItemById(id, false);
|
|
3302
|
+
body.showInDashboard = sbody.showInDashboard = utils.makeBool(obj.showInDashboard);
|
|
3303
|
+
}
|
|
3016
3304
|
return Promise.resolve(body);
|
|
3017
3305
|
}
|
|
3018
3306
|
catch (err) { return Promise.reject(err); }
|
|
3019
3307
|
}
|
|
3020
3308
|
public async setHeatModeAsync(body: Body, mode: number): Promise<BodyTempState> {
|
|
3309
|
+
let modes = sys.board.bodies.getHeatModes(body.id);
|
|
3310
|
+
if (typeof modes.find(elem => elem.val === mode) === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Cannot set heat mode to ${mode} since this is not a valid mode for the ${body.name}`, 'Body', mode));
|
|
3311
|
+
await this.queueBodyHeatSettings(body.id, body.id === 2 ? 23 : 22, { heatMode: mode });
|
|
3312
|
+
return state.temps.bodies.getItemById(body.id);
|
|
3313
|
+
/*
|
|
3314
|
+
|
|
3315
|
+
let byte2 = 22;
|
|
3316
|
+
let body1 = sys.bodies.getItemById(1);
|
|
3317
|
+
let body2 = sys.bodies.getItemById(2);
|
|
3318
|
+
|
|
3319
|
+
let heat1 = body1.heatSetpoint || 78;
|
|
3320
|
+
let cool1 = body1.coolSetpoint || 100;
|
|
3321
|
+
let heat2 = body2.heatSetpoint || 78;
|
|
3322
|
+
let cool2 = body2.coolSetpoint || 103;
|
|
3323
|
+
|
|
3324
|
+
let mode1 = body1.heatMode || 1;
|
|
3325
|
+
let mode2 = body2.heatMode || 1;
|
|
3326
|
+
let bitopts = 0;
|
|
3327
|
+
if (sys.general.options.clockSource) bitopts += 32;
|
|
3328
|
+
if (sys.general.options.clockMode === 24) bitopts += 64;
|
|
3329
|
+
if (sys.general.options.adjustDST) bitopts += 128;
|
|
3330
|
+
|
|
3331
|
+
switch (body.id) {
|
|
3332
|
+
case 1:
|
|
3333
|
+
byte2 = 22;
|
|
3334
|
+
mode1 = mode;
|
|
3335
|
+
break;
|
|
3336
|
+
case 2:
|
|
3337
|
+
byte2 = 23;
|
|
3338
|
+
mode2 = mode;
|
|
3339
|
+
break;
|
|
3340
|
+
}
|
|
3021
3341
|
return new Promise<BodyTempState>((resolve, reject) => {
|
|
3022
|
-
const self = this;
|
|
3023
|
-
let byte2 = 18;
|
|
3024
|
-
let mode1 = sys.bodies.getItemById(1).setPoint || 100;
|
|
3025
|
-
let mode2 = sys.bodies.getItemById(2).setPoint || 100;
|
|
3026
|
-
let mode3 = sys.bodies.getItemById(3).setPoint || 100;
|
|
3027
|
-
let mode4 = sys.bodies.getItemById(4).setPoint || 100;
|
|
3028
|
-
switch (body.id) {
|
|
3029
|
-
case 1:
|
|
3030
|
-
byte2 = 22;
|
|
3031
|
-
mode1 = mode;
|
|
3032
|
-
break;
|
|
3033
|
-
case 2:
|
|
3034
|
-
byte2 = 23;
|
|
3035
|
-
mode2 = mode;
|
|
3036
|
-
break;
|
|
3037
|
-
case 3:
|
|
3038
|
-
byte2 = 24;
|
|
3039
|
-
mode3 = mode;
|
|
3040
|
-
break;
|
|
3041
|
-
case 4:
|
|
3042
|
-
byte2 = 25;
|
|
3043
|
-
mode4 = mode;
|
|
3044
|
-
break;
|
|
3045
|
-
}
|
|
3046
3342
|
let out = Outbound.create({
|
|
3047
3343
|
action: 168,
|
|
3048
|
-
payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0,
|
|
3049
|
-
,
|
|
3344
|
+
payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, bitopts, 89, 27, 110, 3, 0, 0,
|
|
3345
|
+
heat1, cool1, heat2, cool2, mode1, mode2, 0, 0, 15,
|
|
3346
|
+
sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0],
|
|
3050
3347
|
retries: 5,
|
|
3051
3348
|
response: IntelliCenterBoard.getAckResponse(168),
|
|
3052
3349
|
onComplete: (err, msg) => {
|
|
@@ -3062,36 +3359,36 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3062
3359
|
})
|
|
3063
3360
|
conn.queueSendMessage(out);
|
|
3064
3361
|
});
|
|
3362
|
+
*/
|
|
3065
3363
|
}
|
|
3066
3364
|
public async setHeatSetpointAsync(body: Body, setPoint: number): Promise<BodyTempState> {
|
|
3365
|
+
if (typeof setPoint === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Cannot set heat setpoint to undefined for the ${body.name}`, 'Body', setPoint));
|
|
3366
|
+
else if (setPoint < 0 || setPoint > 110) return Promise.reject(new InvalidEquipmentDataError(`Cannot set heat setpoint to ${setPoint} for the ${body.name}`, 'Body', setPoint));
|
|
3367
|
+
await this.queueBodyHeatSettings(body.id, body.id === 2 ? 20 : 18, { heatSetpoint: setPoint });
|
|
3368
|
+
return state.temps.bodies.getItemById(body.id);
|
|
3369
|
+
/*
|
|
3067
3370
|
let byte2 = 18;
|
|
3068
3371
|
let body1 = sys.bodies.getItemById(1);
|
|
3069
3372
|
let body2 = sys.bodies.getItemById(2);
|
|
3070
|
-
let body3 = sys.bodies.getItemById(3);
|
|
3071
|
-
let body4 = sys.bodies.getItemById(4);
|
|
3072
3373
|
|
|
3073
|
-
let
|
|
3074
|
-
let
|
|
3075
|
-
let
|
|
3076
|
-
let
|
|
3374
|
+
let heat1 = body1.heatSetpoint || 78;
|
|
3375
|
+
let cool1 = body1.coolSetpoint || 100;
|
|
3376
|
+
let heat2 = body2.heatSetpoint || 78;
|
|
3377
|
+
let cool2 = body2.coolSetpoint || 103;
|
|
3077
3378
|
switch (body.id) {
|
|
3078
3379
|
case 1:
|
|
3079
3380
|
byte2 = 18;
|
|
3080
|
-
|
|
3381
|
+
heat1 = setPoint;
|
|
3081
3382
|
break;
|
|
3082
3383
|
case 2:
|
|
3083
3384
|
byte2 = 20;
|
|
3084
|
-
|
|
3085
|
-
break;
|
|
3086
|
-
case 3:
|
|
3087
|
-
byte2 = 19;
|
|
3088
|
-
temp3 = setPoint;
|
|
3089
|
-
break;
|
|
3090
|
-
case 4:
|
|
3091
|
-
byte2 = 21;
|
|
3092
|
-
temp4 = setPoint;
|
|
3385
|
+
heat2 = setPoint;
|
|
3093
3386
|
break;
|
|
3094
3387
|
}
|
|
3388
|
+
let bitopts = 0;
|
|
3389
|
+
if (sys.general.options.clockSource) bitopts += 32;
|
|
3390
|
+
if (sys.general.options.clockMode === 24) bitopts += 64;
|
|
3391
|
+
if (sys.general.options.adjustDST) bitopts += 128;
|
|
3095
3392
|
// 6 15 17 18 21 22 24 25
|
|
3096
3393
|
//[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 89, 100, 98, 100, 0, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][5, 243]
|
|
3097
3394
|
//[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 235, 27, 167, 1, 0, 0, 89, 81, 98, 103, 5, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][6, 48]
|
|
@@ -3099,8 +3396,8 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3099
3396
|
action: 168,
|
|
3100
3397
|
response: IntelliCenterBoard.getAckResponse(168),
|
|
3101
3398
|
retries: 5,
|
|
3102
|
-
payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0,
|
|
3103
|
-
|
|
3399
|
+
payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, bitopts, 89, 27, 110, 3, 0, 0,
|
|
3400
|
+
heat1, cool1, heat2, cool2, body1.heatMode || 1, body2.heatMode || 1, 0, 0, 15,
|
|
3104
3401
|
sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0]
|
|
3105
3402
|
});
|
|
3106
3403
|
return new Promise<BodyTempState>((resolve, reject) => {
|
|
@@ -3114,25 +3411,22 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3114
3411
|
};
|
|
3115
3412
|
conn.queueSendMessage(out);
|
|
3116
3413
|
});
|
|
3414
|
+
*/
|
|
3117
3415
|
}
|
|
3118
3416
|
public async setCoolSetpointAsync(body: Body, setPoint: number): Promise<BodyTempState> {
|
|
3119
|
-
|
|
3417
|
+
if (typeof setPoint === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Cannot set cooling setpoint to undefined for the ${body.name}`, 'Body', setPoint));
|
|
3418
|
+
else if (setPoint < 0 || setPoint > 110) return Promise.reject(new InvalidEquipmentDataError(`Cannot set cooling setpoint to ${setPoint} for the ${body.name}`, 'Body', setPoint));
|
|
3419
|
+
await this.queueBodyHeatSettings(body.id, body.id === 2 ? 21 : 19, { coolSetpoint: setPoint });
|
|
3420
|
+
return state.temps.bodies.getItemById(body.id);
|
|
3421
|
+
/*
|
|
3120
3422
|
let byte2 = 19;
|
|
3121
3423
|
let body1 = sys.bodies.getItemById(1);
|
|
3122
3424
|
let body2 = sys.bodies.getItemById(2);
|
|
3123
|
-
let body3 = sys.bodies.getItemById(3);
|
|
3124
|
-
let body4 = sys.bodies.getItemById(3);
|
|
3125
3425
|
|
|
3126
|
-
let
|
|
3127
|
-
let cool1 =
|
|
3128
|
-
let
|
|
3129
|
-
let cool2 =
|
|
3130
|
-
|
|
3131
|
-
//Them
|
|
3132
|
-
//[165, 63, 15, 16, 168, 41][0, 0, 19, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 59, 30, 5, 5, 0, 0, 90, 102, 98, 81, 3, 1, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][4, 129]
|
|
3133
|
-
//Us
|
|
3134
|
-
//[165, 63, 15, 33, 168, 40][0, 0, 19, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 5, 5, 0, 0, 90, 103, 98, 81, 3, 1, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0][5, 249]
|
|
3135
|
-
//[165, 63, 15, 33, 168, 40][0, 0, 19, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 90, 103, 98, 81, 3, 1, 0, 0, 15, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][5, 249]
|
|
3426
|
+
let heat1 = body1.heatSetpoint || 78;
|
|
3427
|
+
let cool1 = body1.coolSetpoint || 100;
|
|
3428
|
+
let heat2 = body2.heatSetpoint || 78;
|
|
3429
|
+
let cool2 = body2.coolSetpoint || 103;
|
|
3136
3430
|
switch (body.id) {
|
|
3137
3431
|
case 1:
|
|
3138
3432
|
byte2 = 19;
|
|
@@ -3143,6 +3437,10 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3143
3437
|
cool2 = setPoint;
|
|
3144
3438
|
break;
|
|
3145
3439
|
}
|
|
3440
|
+
let bitopts = 0;
|
|
3441
|
+
if (sys.general.options.clockSource) bitopts += 32;
|
|
3442
|
+
if (sys.general.options.clockMode === 24) bitopts += 64;
|
|
3443
|
+
if (sys.general.options.adjustDST) bitopts += 128;
|
|
3146
3444
|
// 6 15 17 18 21 22 24 25
|
|
3147
3445
|
//[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 176, 89, 27, 110, 3, 0, 0, 89, 100, 98, 100, 0, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][5, 243]
|
|
3148
3446
|
//[255, 0, 255][165, 63, 15, 16, 168, 41][0, 0, 18, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 235, 27, 167, 1, 0, 0, 89, 81, 98, 103, 5, 0, 0, 0, 15, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0][6, 48]
|
|
@@ -3150,8 +3448,8 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3150
3448
|
action: 168,
|
|
3151
3449
|
response: IntelliCenterBoard.getAckResponse(168),
|
|
3152
3450
|
retries: 5,
|
|
3153
|
-
payload: [0, 0, byte2, 1, 0, 0,
|
|
3154
|
-
|
|
3451
|
+
payload: [0, 0, byte2, 1, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, bitopts, 89, 27, 110, 3, 0, 0,
|
|
3452
|
+
heat1, cool1, heat2, cool2, body1.heatMode || 1, body2.heatMode || 1, 0, 0, 15,
|
|
3155
3453
|
sys.general.options.pumpDelay ? 1 : 0, sys.general.options.cooldownDelay ? 1 : 0, 0, 100, 0, 0, 0, 0, sys.general.options.manualPriority ? 1 : 0, sys.general.options.manualHeat ? 1 : 0, 0]
|
|
3156
3454
|
});
|
|
3157
3455
|
return new Promise<BodyTempState>((resolve, reject) => {
|
|
@@ -3165,6 +3463,7 @@ class IntelliCenterBodyCommands extends BodyCommands {
|
|
|
3165
3463
|
};
|
|
3166
3464
|
conn.queueSendMessage(out);
|
|
3167
3465
|
});
|
|
3466
|
+
*/
|
|
3168
3467
|
}
|
|
3169
3468
|
}
|
|
3170
3469
|
class IntelliCenterScheduleCommands extends ScheduleCommands {
|
|
@@ -3299,7 +3598,7 @@ class IntelliCenterScheduleCommands extends ScheduleCommands {
|
|
|
3299
3598
|
, startDate.getMonth() + 1
|
|
3300
3599
|
, startDate.getDay() || 0
|
|
3301
3600
|
, startDate.getFullYear() - 2000
|
|
3302
|
-
,
|
|
3601
|
+
, 0 // This changed to 0 to mean no change in 1.047
|
|
3303
3602
|
, 78
|
|
3304
3603
|
, 100
|
|
3305
3604
|
],
|
|
@@ -3349,9 +3648,8 @@ class IntelliCenterHeaterCommands extends HeaterCommands {
|
|
|
3349
3648
|
if (isNaN(id)) return reject(new InvalidEquipmentIdError('Heater Id is not valid.', obj.id, 'Heater'));
|
|
3350
3649
|
let heater: Heater;
|
|
3351
3650
|
if (id <= 0) {
|
|
3352
|
-
// We are adding a heater. In this case
|
|
3353
|
-
|
|
3354
|
-
id = vheaters.length + 1;
|
|
3651
|
+
// We are adding a heater. In this case we need to find the first id slot that is empty.
|
|
3652
|
+
id = sys.heaters.getNextEquipmentId(new EquipmentIdRange(1, 16));
|
|
3355
3653
|
}
|
|
3356
3654
|
heater = sys.heaters.getItemById(id, false);
|
|
3357
3655
|
let type = 0;
|
|
@@ -3551,7 +3849,7 @@ class IntelliCenterHeaterCommands extends HeaterCommands {
|
|
|
3551
3849
|
|
|
3552
3850
|
sys.board.valueMaps.heatModes = new byteValueMap([[1, { name: 'off', desc: 'Off' }]]);
|
|
3553
3851
|
if (gasHeaterInstalled) sys.board.valueMaps.heatModes.merge([[2, { name: 'heater', desc: 'Heater' }]]);
|
|
3554
|
-
if (mastertempInstalled) sys.board.valueMaps.heatModes.merge([11, { name: 'mtheater', desc: 'MasterTemp' }]);
|
|
3852
|
+
if (mastertempInstalled) sys.board.valueMaps.heatModes.merge([[11, { name: 'mtheater', desc: 'MasterTemp' }]]);
|
|
3555
3853
|
if (solarInstalled && (gasHeaterInstalled || heatPumpInstalled || mastertempInstalled)) sys.board.valueMaps.heatModes.merge([[3, { name: 'solar', desc: 'Solar Only' }], [4, { name: 'solarpref', desc: 'Solar Preferred' }]]);
|
|
3556
3854
|
else if (solarInstalled) sys.board.valueMaps.heatModes.merge([[3, { name: 'solar', desc: 'Solar' }]]);
|
|
3557
3855
|
if (ultratempInstalled && (gasHeaterInstalled || heatPumpInstalled || mastertempInstalled)) sys.board.valueMaps.heatModes.merge([[5, { name: 'ultratemp', desc: 'UltraTemp Only' }], [6, { name: 'ultratemppref', desc: 'UltraTemp Pref' }]]);
|
|
@@ -3661,24 +3959,24 @@ export class IntelliCenterChemControllerCommands extends ChemControllerCommands
|
|
|
3661
3959
|
}
|
|
3662
3960
|
if (isNaN(pHSetpoint) || pHSetpoint > type.ph.max || pHSetpoint < type.ph.min) Promise.reject(new InvalidEquipmentDataError(`Invalid pH setpoint`, 'ph.setpoint', pHSetpoint));
|
|
3663
3961
|
if (isNaN(orpSetpoint) || orpSetpoint > type.orp.max || orpSetpoint < type.orp.min) Promise.reject(new InvalidEquipmentDataError(`Invalid orp setpoint`, 'orp.setpoint', orpSetpoint));
|
|
3664
|
-
let phTolerance = typeof data.ph.tolerance !== 'undefined' ? data.ph.tolerance : chem.ph.tolerance;
|
|
3665
|
-
let orpTolerance = typeof data.orp.tolerance !== 'undefined' ? data.orp.tolerance : chem.orp.tolerance;
|
|
3666
|
-
if (typeof data.ph.tolerance !== 'undefined') {
|
|
3962
|
+
let phTolerance = typeof data.ph !== 'undefined' && typeof data.ph.tolerance !== 'undefined' ? data.ph.tolerance : chem.ph.tolerance;
|
|
3963
|
+
let orpTolerance = typeof data.orp !== 'undefined' && typeof data.orp.tolerance !== 'undefined' ? data.orp.tolerance : chem.orp.tolerance;
|
|
3964
|
+
if (typeof data.ph !== 'undefined' && typeof data.ph.tolerance !== 'undefined') {
|
|
3667
3965
|
if (typeof data.ph.tolerance.enabled !== 'undefined') phTolerance.enabled = utils.makeBool(data.ph.tolerance.enabled);
|
|
3668
3966
|
if (typeof data.ph.tolerance.low !== 'undefined') phTolerance.low = parseFloat(data.ph.tolerance.low);
|
|
3669
3967
|
if (typeof data.ph.tolerance.high !== 'undefined') phTolerance.high = parseFloat(data.ph.tolerance.high);
|
|
3670
3968
|
if (isNaN(phTolerance.low)) phTolerance.low = type.ph.min;
|
|
3671
3969
|
if (isNaN(phTolerance.high)) phTolerance.high = type.ph.max;
|
|
3672
3970
|
}
|
|
3673
|
-
if (typeof data.orp.tolerance !== 'undefined') {
|
|
3971
|
+
if (typeof data.orp !== 'undefined' && typeof data.orp.tolerance !== 'undefined') {
|
|
3674
3972
|
if (typeof data.orp.tolerance.enabled !== 'undefined') orpTolerance.enabled = utils.makeBool(data.orp.tolerance.enabled);
|
|
3675
3973
|
if (typeof data.orp.tolerance.low !== 'undefined') orpTolerance.low = parseFloat(data.orp.tolerance.low);
|
|
3676
3974
|
if (typeof data.orp.tolerance.high !== 'undefined') orpTolerance.high = parseFloat(data.orp.tolerance.high);
|
|
3677
3975
|
if (isNaN(orpTolerance.low)) orpTolerance.low = type.orp.min;
|
|
3678
3976
|
if (isNaN(orpTolerance.high)) orpTolerance.high = type.orp.max;
|
|
3679
3977
|
}
|
|
3680
|
-
let phEnabled = typeof data.ph.enabled !== 'undefined' ? utils.makeBool(data.ph.enabled) : chem.ph.enabled;
|
|
3681
|
-
let orpEnabled = typeof data.orp.enabled !== 'undefined' ? utils.makeBool(data.orp.enabled) : chem.orp.enabled;
|
|
3978
|
+
let phEnabled = typeof data.ph !== 'undefined' && typeof data.ph.enabled !== 'undefined' ? utils.makeBool(data.ph.enabled) : chem.ph.enabled;
|
|
3979
|
+
let orpEnabled = typeof data.orp !== 'undefined' && typeof data.orp.enabled !== 'undefined' ? utils.makeBool(data.orp.enabled) : chem.orp.enabled;
|
|
3682
3980
|
let siCalcType = typeof data.siCalcType !== 'undefined' ? sys.board.valueMaps.siCalcTypes.encode(data.siCalcType, 0) : chem.siCalcType;
|
|
3683
3981
|
|
|
3684
3982
|
let saltLevel = (state.chlorinators.length > 0) ? state.chlorinators.getItemById(1).saltLevel || 1000 : 1000
|
|
@@ -3686,14 +3984,18 @@ export class IntelliCenterChemControllerCommands extends ChemControllerCommands
|
|
|
3686
3984
|
chem.orp.tank.capacity = 6;
|
|
3687
3985
|
let acidTankLevel = typeof data.ph !== 'undefined' && typeof data.ph.tank !== 'undefined' && typeof data.ph.tank.level !== 'undefined' ? parseInt(data.ph.tank.level, 10) : schem.ph.tank.level;
|
|
3688
3986
|
let orpTankLevel = typeof data.orp !== 'undefined' && typeof data.orp.tank !== 'undefined' && typeof data.orp.tank.level !== 'undefined' ? parseInt(data.orp.tank.level, 10) : schem.orp.tank.level;
|
|
3987
|
+
//Them
|
|
3988
|
+
//[255, 0, 255][165, 63, 15, 16, 168, 20][8, 0, 0, 32, 1, 144, 1, 248, 2, 144, 1, 1, 1, 29, 0, 0, 0, 100, 0, 0][4, 135]
|
|
3989
|
+
//Us
|
|
3990
|
+
//[255, 0, 255][165, 0, 15, 33, 168, 20][8, 0, 0, 32, 1, 144, 1, 248, 2, 144, 1, 1, 1, 33, 0, 0, 0, 100, 0, 0][4, 93]
|
|
3689
3991
|
return new Promise<ChemController>((resolve, reject) => {
|
|
3690
3992
|
let out = Outbound.create({
|
|
3691
|
-
protocol: Protocol.
|
|
3993
|
+
protocol: Protocol.Broadcast,
|
|
3692
3994
|
action: 168,
|
|
3693
3995
|
payload: [],
|
|
3694
3996
|
retries: 3, // We are going to try 4 times.
|
|
3695
3997
|
response: IntelliCenterBoard.getAckResponse(168),
|
|
3696
|
-
onAbort: () => { },
|
|
3998
|
+
//onAbort: () => { },
|
|
3697
3999
|
onComplete: (err) => {
|
|
3698
4000
|
if (err) reject(err);
|
|
3699
4001
|
else {
|
|
@@ -3728,172 +4030,29 @@ export class IntelliCenterChemControllerCommands extends ChemControllerCommands
|
|
|
3728
4030
|
}
|
|
3729
4031
|
}
|
|
3730
4032
|
});
|
|
4033
|
+
|
|
3731
4034
|
//[8, 0, chem.id - 1, body.val, 1, chem.address, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0]
|
|
3732
|
-
out.insertPayloadBytes(0, 0,
|
|
4035
|
+
out.insertPayloadBytes(0, 0, 20);
|
|
3733
4036
|
out.setPayloadByte(0, 8);
|
|
3734
4037
|
out.setPayloadByte(1, 0);
|
|
3735
4038
|
out.setPayloadByte(2, chem.id - 1);
|
|
3736
4039
|
out.setPayloadByte(3, body.val);
|
|
3737
|
-
out.setPayloadByte(4, 1);
|
|
4040
|
+
out.setPayloadByte(4, acidTankLevel + 1);
|
|
3738
4041
|
out.setPayloadByte(5, address);
|
|
3739
4042
|
out.setPayloadByte(6, 1);
|
|
3740
4043
|
out.setPayloadInt(7, Math.round(pHSetpoint * 100), 700);
|
|
3741
4044
|
out.setPayloadInt(9, orpSetpoint, 400);
|
|
3742
4045
|
out.setPayloadByte(11, 1);
|
|
3743
4046
|
out.setPayloadByte(12, 1);
|
|
4047
|
+
//out.setPayloadByte(11, acidTankLevel + 1, 1);
|
|
4048
|
+
//out.setPayloadByte(12, orpTankLevel + 1, 1);
|
|
4049
|
+
|
|
3744
4050
|
out.setPayloadInt(13, calciumHardness, 25);
|
|
3745
4051
|
out.setPayloadInt(15, cyanuricAcid, 0);
|
|
3746
4052
|
out.setPayloadInt(17, alkalinity, 25);
|
|
3747
4053
|
conn.queueSendMessage(out);
|
|
3748
4054
|
});
|
|
3749
4055
|
}
|
|
3750
|
-
|
|
3751
|
-
//protected async setIntelliChemStateAsync(data: any): Promise<ChemControllerState> {
|
|
3752
|
-
// try {
|
|
3753
|
-
// // This is a protected method so the id will always be valid if we made it here. Do
|
|
3754
|
-
// // one more check since we cannot lock a thread.
|
|
3755
|
-
// let chem = sys.chemControllers.find(elem => elem.id === data.id);
|
|
3756
|
-
// if (typeof chem === 'undefined') return Promise.reject(`A valid IntelliChem controller could not be found at id ${data.id}`);
|
|
3757
|
-
// // If we are virtual send it back to the SystemBoard for processing.
|
|
3758
|
-
// if (chem.master !== 0) return super.setIntelliChemStateAsync(data);
|
|
3759
|
-
// let address = typeof data.address !== 'undefined' ? parseInt(data.address, 10) : chem.address;
|
|
3760
|
-
// if (typeof address === 'undefined' || isNaN(address) || (address < 144 || address > 158)) return Promise.reject(new InvalidEquipmentDataError(`Invalid IntelliChem address`, 'chemController', address));
|
|
3761
|
-
// let pHSetpoint = typeof data.ph !== 'undefined' && typeof data.ph.setpoint !== 'undefined' ? parseFloat(data.ph.setpoint) : chem.ph.setpoint;
|
|
3762
|
-
// let orpSetpoint = typeof data.orp !== 'undefined' && typeof data.orp.setpoint !== 'undefined' ? parseInt(data.orp.setpoint, 10) : chem.orp.setpoint;
|
|
3763
|
-
// let calciumHardness = typeof data.calciumHardness !== 'undefined' ? parseInt(data.calciumHardness, 10) : chem.calciumHardness;
|
|
3764
|
-
// let cyanuricAcid = typeof data.cyanuricAcid !== 'undefined' ? parseInt(data.cyanuricAcid, 10) : chem.cyanuricAcid;
|
|
3765
|
-
// let alkalinity = typeof data.alkalinity !== 'undefined' ? parseInt(data.alkalinity, 10) : chem.alkalinity;
|
|
3766
|
-
// let body = sys.board.bodies.mapBodyAssociation(typeof data.body === 'undefined' ? chem.body : data.body);
|
|
3767
|
-
// if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Invalid body assignment`, 'chemController', data.body || chem.body));
|
|
3768
|
-
// // Do a final validation pass so we dont send this off in a mess.
|
|
3769
|
-
// if (isNaN(pHSetpoint)) return Promise.reject(new InvalidEquipmentDataError(`Invalid pH Setpoint`, 'chemController', pHSetpoint));
|
|
3770
|
-
// if (isNaN(orpSetpoint)) return Promise.reject(new InvalidEquipmentDataError(`Invalid orp Setpoint`, 'chemController', orpSetpoint));
|
|
3771
|
-
// if (isNaN(calciumHardness)) return Promise.reject(new InvalidEquipmentDataError(`Invalid calcium hardness`, 'chemController', calciumHardness));
|
|
3772
|
-
// if (isNaN(cyanuricAcid)) return Promise.reject(new InvalidEquipmentDataError(`Invalid cyanuric acid`, 'chemController', cyanuricAcid));
|
|
3773
|
-
// if (isNaN(alkalinity)) return Promise.reject(new InvalidEquipmentDataError(`Invalid alkalinity`, 'chemController', alkalinity));
|
|
3774
|
-
// return new Promise<ChemControllerState>(async (resolve, reject) => {
|
|
3775
|
-
// let out = Outbound.create({
|
|
3776
|
-
// action: 168,
|
|
3777
|
-
// response: IntelliCenterBoard.getAckResponse(168),
|
|
3778
|
-
// retries: 3,
|
|
3779
|
-
// payload: [8, 0, chem.id - 1, body.val, 1, chem.address, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
3780
|
-
// onComplete: (err) => {
|
|
3781
|
-
// if (err) { reject(err); }
|
|
3782
|
-
// else {
|
|
3783
|
-
// let cstate = state.chemControllers.getItemById(chem.id, true);
|
|
3784
|
-
// chem.isActive = true;
|
|
3785
|
-
// chem.isVirtual = false;
|
|
3786
|
-
// //chem.address = address;
|
|
3787
|
-
// chem.body = body;
|
|
3788
|
-
// chem.calciumHardness = calciumHardness;
|
|
3789
|
-
// chem.orp.setpoint = cstate.orp.setpoint = orpSetpoint;
|
|
3790
|
-
// chem.ph.setpoint = cstate.ph.setpoint = pHSetpoint;
|
|
3791
|
-
// chem.cyanuricAcid = cyanuricAcid;
|
|
3792
|
-
// chem.alkalinity = alkalinity;
|
|
3793
|
-
// chem.type = 2;
|
|
3794
|
-
// chem.name = typeof chem.name === 'undefined' ? `IntelliChem ${chem.id}` : chem.name;
|
|
3795
|
-
// chem.ph.tank.capacity = chem.orp.tank.capacity = 6;
|
|
3796
|
-
// chem.ph.tank.units = chem.orp.tank.units = '';
|
|
3797
|
-
// cstate.body = chem.body;
|
|
3798
|
-
// cstate.address = chem.address;
|
|
3799
|
-
// cstate.name = chem.name;
|
|
3800
|
-
// cstate.type = chem.type;
|
|
3801
|
-
// cstate.isActive = chem.isActive;
|
|
3802
|
-
// resolve(cstate);
|
|
3803
|
-
// }
|
|
3804
|
-
// }
|
|
3805
|
-
// });
|
|
3806
|
-
// out.setPayloadInt(7, Math.round(pHSetpoint * 100), 700);
|
|
3807
|
-
// out.setPayloadInt(9, orpSetpoint, 400);
|
|
3808
|
-
// out.setPayloadInt(13, calciumHardness, 25);
|
|
3809
|
-
// out.setPayloadInt(15, cyanuricAcid, 0);
|
|
3810
|
-
// out.setPayloadInt(17, alkalinity, 25);
|
|
3811
|
-
// conn.queueSendMessage(out);
|
|
3812
|
-
// });
|
|
3813
|
-
// }
|
|
3814
|
-
// catch (err) { return Promise.reject(err); }
|
|
3815
|
-
//}
|
|
3816
|
-
//protected async setIntelliChemAsync(data: any): Promise<ChemController> {
|
|
3817
|
-
// try {
|
|
3818
|
-
// // This is a protected method so the id will always be valid if we made it here. Do
|
|
3819
|
-
// // one more check since we cannot lock a thread.
|
|
3820
|
-
// let chem = sys.chemControllers.find(elem => elem.id === data.id);
|
|
3821
|
-
// let ichemType = sys.board.valueMaps.chemControllerTypes.encode('intellichem');
|
|
3822
|
-
// if (typeof chem === 'undefined') {
|
|
3823
|
-
// // We are adding an IntelliChem. Check to see how many intellichems we have.
|
|
3824
|
-
// let arr = sys.chemControllers.toArray();
|
|
3825
|
-
// let count = 0;
|
|
3826
|
-
// for (let i = 0; i < arr.length; i++) {
|
|
3827
|
-
// let cc: ChemController = arr[i];
|
|
3828
|
-
// if (cc.type === ichemType) count++;
|
|
3829
|
-
// }
|
|
3830
|
-
// if (count >= sys.equipment.maxChemControllers) return Promise.reject(new InvalidEquipmentDataError(`The max number of IntelliChem controllers has been reached: ${sys.equipment.maxChemControllers}`, 'chemController', sys.equipment.maxChemControllers));
|
|
3831
|
-
// let id = (sys.chemControllers.getMaxId() || 0) + 1;
|
|
3832
|
-
// chem = sys.chemControllers.getItemById(id);
|
|
3833
|
-
// }
|
|
3834
|
-
// let address = typeof data.address !== 'undefined' ? parseInt(data.address, 10) : chem.address;
|
|
3835
|
-
// if (typeof address === 'undefined' || isNaN(address) || (address < 144 || address > 158)) return Promise.reject(new InvalidEquipmentDataError(`Invalid IntelliChem address`, 'chemController', address));
|
|
3836
|
-
// if (typeof sys.chemControllers.find(elem => elem.id !== data.id && elem.type === ichemType && elem.address === address) !== 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Invalid IntelliChem address: Address is used on another IntelliChem`, 'chemController', address));
|
|
3837
|
-
// let pHSetpoint = typeof data.ph.setpoint !== 'undefined' ? parseFloat(data.ph.setpoint) : chem.ph.setpoint;
|
|
3838
|
-
// let orpSetpoint = typeof data.orp.setpoint !== 'undefined' ? parseInt(data.orp.setpoint, 10) : chem.orp.setpoint;
|
|
3839
|
-
// let calciumHardness = typeof data.calciumHardness !== 'undefined' ? parseInt(data.calciumHardness, 10) : chem.calciumHardness;
|
|
3840
|
-
// let cyanuricAcid = typeof data.cyanuricAcid !== 'undefined' ? parseInt(data.cyanuricAcid, 10) : chem.cyanuricAcid;
|
|
3841
|
-
// let alkalinity = typeof data.alkalinity !== 'undefined' ? parseInt(data.alkalinity, 10) : chem.alkalinity;
|
|
3842
|
-
// let body = sys.board.bodies.mapBodyAssociation(typeof data.body === 'undefined' ? chem.body : data.body);
|
|
3843
|
-
// let name = typeof data.name === 'undefined' ? chem.name || `IntelliChem ${chem.id}` : data.name;
|
|
3844
|
-
// if (typeof body === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Invalid body assignment`, 'chemController', data.body || chem.body));
|
|
3845
|
-
// // Do a final validation pass so we dont send this off in a mess.
|
|
3846
|
-
// if (isNaN(address)) return Promise.reject(new InvalidEquipmentDataError(`Invalid address ${data.address}`, 'chemController', data.address));
|
|
3847
|
-
// if (isNaN(pHSetpoint)) return Promise.reject(new InvalidEquipmentDataError(`Invalid pH Setpoint`, 'chemController', pHSetpoint));
|
|
3848
|
-
// if (isNaN(orpSetpoint)) return Promise.reject(new InvalidEquipmentDataError(`Invalid orp Setpoint`, 'chemController', orpSetpoint));
|
|
3849
|
-
// if (isNaN(calciumHardness)) return Promise.reject(new InvalidEquipmentDataError(`Invalid calcium hardness`, 'chemController', calciumHardness));
|
|
3850
|
-
// if (isNaN(cyanuricAcid)) return Promise.reject(new InvalidEquipmentDataError(`Invalid cyanuric acid`, 'chemController', cyanuricAcid));
|
|
3851
|
-
// if (isNaN(alkalinity)) return Promise.reject(new InvalidEquipmentDataError(`Invalid alkalinity`, 'chemController', alkalinity));
|
|
3852
|
-
|
|
3853
|
-
// return new Promise<ChemController>(async (resolve, reject) => {
|
|
3854
|
-
// let out = Outbound.create({
|
|
3855
|
-
// action: 168,
|
|
3856
|
-
// response: IntelliCenterBoard.getAckResponse(168),
|
|
3857
|
-
// retries: 3,
|
|
3858
|
-
// payload: [8, 0, chem.id - 1, body.val, 1, address, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
|
|
3859
|
-
// onComplete: (err) => {
|
|
3860
|
-
// if (err) { reject(err); }
|
|
3861
|
-
// else {
|
|
3862
|
-
// chem = sys.chemControllers.getItemById(chem.id, true);
|
|
3863
|
-
// let cstate = state.chemControllers.getItemById(chem.id, true);
|
|
3864
|
-
// chem.master = sys.board.equipmentMaster;
|
|
3865
|
-
// chem.isActive = true;
|
|
3866
|
-
// chem.isVirtual = false;
|
|
3867
|
-
// chem.address = address;
|
|
3868
|
-
// chem.body = body;
|
|
3869
|
-
// chem.calciumHardness = calciumHardness;
|
|
3870
|
-
// chem.orp.setpoint = cstate.orp.setpoint = orpSetpoint;
|
|
3871
|
-
// chem.ph.setpoint = cstate.ph.setpoint = pHSetpoint;
|
|
3872
|
-
// chem.cyanuricAcid = cyanuricAcid;
|
|
3873
|
-
// chem.alkalinity = alkalinity;
|
|
3874
|
-
// chem.type = 2;
|
|
3875
|
-
// chem.name = name;
|
|
3876
|
-
// chem.ph.tank.capacity = chem.orp.tank.capacity = 6;
|
|
3877
|
-
// chem.ph.tank.units = chem.orp.tank.units = '';
|
|
3878
|
-
// cstate.body = chem.body;
|
|
3879
|
-
// cstate.address = chem.address;
|
|
3880
|
-
// cstate.name = chem.name;
|
|
3881
|
-
// cstate.type = chem.type;
|
|
3882
|
-
// cstate.isActive = chem.isActive;
|
|
3883
|
-
// resolve(chem);
|
|
3884
|
-
// }
|
|
3885
|
-
// }
|
|
3886
|
-
// });
|
|
3887
|
-
// out.setPayloadInt(7, Math.round(pHSetpoint * 100), 700);
|
|
3888
|
-
// out.setPayloadInt(9, Math.floor(orpSetpoint), 400);
|
|
3889
|
-
// out.setPayloadInt(13, Math.floor(calciumHardness), 25);
|
|
3890
|
-
// out.setPayloadInt(15, Math.floor(cyanuricAcid), 0);
|
|
3891
|
-
// out.setPayloadInt(17, Math.floor(alkalinity), 25);
|
|
3892
|
-
// conn.queueSendMessage(out);
|
|
3893
|
-
// });
|
|
3894
|
-
// }
|
|
3895
|
-
// catch (err) { return Promise.reject(err); }
|
|
3896
|
-
//}
|
|
3897
4056
|
public async deleteChemControllerAsync(data: any): Promise<ChemController> {
|
|
3898
4057
|
let id = typeof data.id !== 'undefined' ? parseInt(data.id, 10) : -1;
|
|
3899
4058
|
if (typeof id === 'undefined' || isNaN(id)) return Promise.reject(new InvalidEquipmentIdError(`Invalid Chem Controller Id`, id, 'chemController'));
|