nodejs-poolcontroller 8.0.1 → 8.0.2
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/.docker/Dockerfile.armv6 +29 -0
- package/.docker/Dockerfile.armv7 +29 -0
- package/.docker/Dockerfile.linux +62 -0
- package/.docker/Dockerfile.windows +43 -0
- package/.docker/docker-compose.yml +47 -0
- package/.docker/ecosystem.config.js +35 -0
- package/.github/workflows/docker-publish-njsPC-linux.yml +81 -0
- package/.github/workflows/docker-publish-njsPC-windows.yml +41 -0
- package/Dockerfile +4 -3
- package/README.md +4 -1
- package/config/Config.ts +1 -1
- package/controller/Constants.ts +164 -67
- package/controller/Equipment.ts +78 -18
- package/controller/Lockouts.ts +15 -0
- package/controller/State.ts +280 -7
- package/controller/boards/EasyTouchBoard.ts +225 -101
- package/controller/boards/IntelliCenterBoard.ts +67 -18
- package/controller/boards/IntelliTouchBoard.ts +2 -4
- package/controller/boards/NixieBoard.ts +84 -27
- package/controller/boards/SunTouchBoard.ts +8 -2
- package/controller/boards/SystemBoard.ts +3259 -3242
- package/controller/comms/ScreenLogic.ts +47 -44
- package/controller/comms/messages/Messages.ts +4 -4
- package/controller/comms/messages/config/ChlorinatorMessage.ts +10 -3
- package/controller/comms/messages/config/ExternalMessage.ts +4 -1
- package/controller/comms/messages/config/PumpMessage.ts +8 -7
- package/controller/comms/messages/config/RemoteMessage.ts +48 -43
- package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -2
- package/controller/comms/messages/status/EquipmentStateMessage.ts +9 -4
- package/controller/nixie/NixieEquipment.ts +1 -1
- package/controller/nixie/bodies/Body.ts +1 -1
- package/controller/nixie/chemistry/ChemController.ts +37 -28
- package/controller/nixie/circuits/Circuit.ts +36 -0
- package/controller/nixie/heaters/Heater.ts +24 -5
- package/controller/nixie/pumps/Pump.ts +155 -97
- package/controller/nixie/schedules/Schedule.ts +207 -126
- package/defaultConfig.json +3 -3
- package/logger/DataLogger.ts +7 -7
- package/package.json +2 -2
- package/sendSocket.js +32 -0
- package/web/Server.ts +17 -11
- package/web/bindings/homeassistant.json +2 -2
- package/web/interfaces/mqttInterface.ts +18 -18
- package/web/services/config/Config.ts +34 -1
- package/web/services/state/State.ts +10 -3
- package/web/services/state/StateSocket.ts +7 -3
- package/web/services/utilities/Utilities.ts +3 -3
|
@@ -21,7 +21,7 @@ import * as extend from 'extend';
|
|
|
21
21
|
import { logger } from '../../logger/Logger';
|
|
22
22
|
import { conn } from '../comms/Comms';
|
|
23
23
|
import { Message, Outbound, Protocol, Response } from '../comms/messages/Messages';
|
|
24
|
-
import { Timestamp, utils } from '../Constants';
|
|
24
|
+
import { ControllerType, Timestamp, utils } from '../Constants';
|
|
25
25
|
import { Body, ChemController, ConfigVersion, CustomName, EggTimer, Feature, Heater, ICircuit, LightGroup, LightGroupCircuit, Options, PoolSystem, Pump, Schedule, sys, Valve } from '../Equipment';
|
|
26
26
|
import { InvalidEquipmentDataError, InvalidEquipmentIdError, InvalidOperationError } from '../Errors';
|
|
27
27
|
import { ncp } from "../nixie/Nixie";
|
|
@@ -174,7 +174,7 @@ export class EasyTouchBoard extends SystemBoard {
|
|
|
174
174
|
this.valueMaps.pumpTypes = new byteValueMap([
|
|
175
175
|
[1, { name: 'vf', desc: 'Intelliflo VF', maxPrimingTime: 6, minFlow: 15, maxFlow: 130, flowStepSize: 1, maxCircuits: 8, hasAddress: true }],
|
|
176
176
|
[64, { name: 'vsf', desc: 'Intelliflo VSF', minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, minFlow: 15, maxFlow: 130, flowStepSize: 1, maxCircuits: 8, hasAddress: true }],
|
|
177
|
-
[65, { name: 'ds', desc: 'Two-Speed', maxCircuits:
|
|
177
|
+
[65, { name: 'ds', desc: 'Two-Speed', maxCircuits: 4, hasAddress: false, hasBody: true }],
|
|
178
178
|
[128, { name: 'vs', desc: 'Intelliflo VS', maxPrimingTime: 10, minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, maxCircuits: 8, hasAddress: true }],
|
|
179
179
|
[169, { name: 'vssvrs', desc: 'IntelliFlo VS+SVRS', maxPrimingTime: 6, minSpeed: 450, maxSpeed: 3450, speedStepSize: 10, maxCircuits: 8, hasAddress: true }],
|
|
180
180
|
[257, { name: 'ss', desc: 'Single Speed', maxCircuits: 0, hasAddress: false, hasBody: true, equipmentMaster: 1, maxRelays: 1, relays: [{ id: 1, name: 'Pump On/Off' }] }],
|
|
@@ -348,6 +348,49 @@ export class EasyTouchBoard extends SystemBoard {
|
|
|
348
348
|
[131, { name: 'ET4P', part: 'ET-4P', desc: 'EasyTouch 4P', circuits: 4, single: true, shared: false }]
|
|
349
349
|
]);
|
|
350
350
|
}
|
|
351
|
+
public initValves(eq) {
|
|
352
|
+
if (typeof sys.valves.find((v) => v.id === 1) === 'undefined') {
|
|
353
|
+
let valve = sys.valves.getItemById(1, true);
|
|
354
|
+
valve.isIntake = false;
|
|
355
|
+
valve.isReturn = false;
|
|
356
|
+
valve.type = 0;
|
|
357
|
+
valve.master = 0;
|
|
358
|
+
valve.isActive = true;
|
|
359
|
+
valve.name = 'Valve A';
|
|
360
|
+
logger.info(`Initializing EasyTouch Valve A`);
|
|
361
|
+
|
|
362
|
+
}
|
|
363
|
+
if (typeof sys.valves.find((v) => v.id === 2) === 'undefined') {
|
|
364
|
+
let valve = sys.valves.getItemById(2, true);
|
|
365
|
+
valve.isIntake = false;
|
|
366
|
+
valve.isReturn = false;
|
|
367
|
+
valve.type = 0;
|
|
368
|
+
valve.master = 0;
|
|
369
|
+
valve.isActive = true;
|
|
370
|
+
valve.name = 'Valve B';
|
|
371
|
+
logger.info(`Initializing EasyTouch Valve B`);
|
|
372
|
+
}
|
|
373
|
+
if (eq.intakeReturnValves) {
|
|
374
|
+
logger.info(`Initializing EasyTouch Intake/Return Valves`);
|
|
375
|
+
let valve = sys.valves.getItemById(3, true);
|
|
376
|
+
valve.isIntake = true;
|
|
377
|
+
valve.isReturn = false;
|
|
378
|
+
valve.circuit = 6;
|
|
379
|
+
valve.type = 0;
|
|
380
|
+
valve.master = 0;
|
|
381
|
+
valve.isActive = true;
|
|
382
|
+
valve.name = 'Intake';
|
|
383
|
+
|
|
384
|
+
valve = sys.valves.getItemById(4, true);
|
|
385
|
+
valve.isIntake = false;
|
|
386
|
+
valve.isReturn = true;
|
|
387
|
+
valve.circuit = 6;
|
|
388
|
+
valve.type = 0;
|
|
389
|
+
valve.master = 0;
|
|
390
|
+
valve.isActive = true;
|
|
391
|
+
valve.name = 'Return';
|
|
392
|
+
}
|
|
393
|
+
}
|
|
351
394
|
public initHeaterDefaults() {
|
|
352
395
|
sys.board.heaters.updateHeaterServices();
|
|
353
396
|
// RKS: 03-03-22 This is not correct. As it turns out there is a case where the only heater installed is not
|
|
@@ -428,7 +471,7 @@ export class EasyTouchBoard extends SystemBoard {
|
|
|
428
471
|
eq.maxChlorinators = md.chlorinators = 1;
|
|
429
472
|
eq.maxChemControllers = md.chemControllers = 1;
|
|
430
473
|
eq.maxCustomNames = 10;
|
|
431
|
-
eq.intakeReturnValves = md.intakeReturnValves = typeof mt.intakeReturnValves !== 'undefined' ? mt.intakeReturnValves :
|
|
474
|
+
eq.intakeReturnValves = md.intakeReturnValves = typeof mt.intakeReturnValves !== 'undefined' ? mt.intakeReturnValves : mt.shared;
|
|
432
475
|
// Calculate out the invalid ids.
|
|
433
476
|
sys.board.equipmentIds.invalidIds.set([]);
|
|
434
477
|
if (!eq.shared) sys.board.equipmentIds.invalidIds.merge([1]);
|
|
@@ -438,6 +481,7 @@ export class EasyTouchBoard extends SystemBoard {
|
|
|
438
481
|
state.equipment.controllerType = 'easytouch';
|
|
439
482
|
this.initBodyDefaults();
|
|
440
483
|
this.initHeaterDefaults();
|
|
484
|
+
this.initValves(eq);
|
|
441
485
|
sys.board.bodies.initFilters();
|
|
442
486
|
sys.equipment.shared ? sys.board.equipmentIds.circuits.start = 1 : sys.board.equipmentIds.circuits.start = 2;
|
|
443
487
|
(async () => {
|
|
@@ -677,7 +721,8 @@ export class TouchScheduleCommands extends ScheduleCommands {
|
|
|
677
721
|
let schedDays = sys.board.schedules.transformDays(typeof data.scheduleDays !== 'undefined' ? data.scheduleDays : sched.scheduleDays || 255); // default to all days
|
|
678
722
|
let changeHeatSetpoint = typeof (data.changeHeatSetpoint !== 'undefined') ? utils.makeBool(data.changeHeatSetpoint) : sched.changeHeatSetpoint;
|
|
679
723
|
let display = typeof data.display !== 'undefined' ? data.display : sched.display || 0;
|
|
680
|
-
|
|
724
|
+
let endTimeOffset = typeof data.endTimeOffset !== 'undefined' ? data.endTimeOffset : sched.endTimeOffset;
|
|
725
|
+
let startTimeOffset = typeof data.startTimeOffset !== 'undefined' ? data.startTimeOffset : sched.startTimeOffset;
|
|
681
726
|
// Ensure all the defaults.
|
|
682
727
|
// if (isNaN(startDate.getTime())) startDate = new Date();
|
|
683
728
|
if (typeof startTime === 'undefined') startTime = 480; // 8am
|
|
@@ -722,10 +767,10 @@ export class TouchScheduleCommands extends ScheduleCommands {
|
|
|
722
767
|
if (state.heliotrope.isCalculated) {
|
|
723
768
|
const sunrise = state.heliotrope.sunrise.getHours() * 60 + state.heliotrope.sunrise.getMinutes();
|
|
724
769
|
const sunset = state.heliotrope.sunset.getHours() * 60 + state.heliotrope.sunset.getMinutes();
|
|
725
|
-
if (startTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunrise')) startTime = sunrise;
|
|
726
|
-
else if (startTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunset')) startTime = sunset;
|
|
727
|
-
if (endTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunrise')) endTime = sunrise;
|
|
728
|
-
else if (endTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunset')) endTime = sunset;
|
|
770
|
+
if (startTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunrise')) startTime = (sunrise + startTimeOffset);
|
|
771
|
+
else if (startTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunset')) startTime = (sunset + startTimeOffset);
|
|
772
|
+
if (endTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunrise')) endTime = (sunrise + endTimeOffset);
|
|
773
|
+
else if (endTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunset')) endTime = (sunset + endTimeOffset);
|
|
729
774
|
}
|
|
730
775
|
|
|
731
776
|
|
|
@@ -769,6 +814,8 @@ export class TouchScheduleCommands extends ScheduleCommands {
|
|
|
769
814
|
sched.endTimeType = ssched.endTimeType = endTimeType;
|
|
770
815
|
sched.isActive = ssched.isActive = true;
|
|
771
816
|
ssched.display = sched.display = display;
|
|
817
|
+
sched.startTimeOffset = ssched.startTimeOffset = startTimeOffset;
|
|
818
|
+
sched.endTimeOffset = ssched.endTimeOffset = endTimeOffset;
|
|
772
819
|
ssched.emitEquipmentChange();
|
|
773
820
|
// For good measure russ is sending out a config request for
|
|
774
821
|
// the schedule in question. If there was a failure on the
|
|
@@ -925,19 +972,19 @@ export class TouchScheduleCommands extends ScheduleCommands {
|
|
|
925
972
|
let sUpdated = false;
|
|
926
973
|
let sched = sys.schedules.getItemByIndex(i);
|
|
927
974
|
if (sched.startTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunrise') && sched.startTime !== sunrise) {
|
|
928
|
-
sched.startTime = sunrise;
|
|
975
|
+
sched.startTime = sunrise + (sched.startTimeOffset || 0);
|
|
929
976
|
anyUpdated = sUpdated = true;
|
|
930
977
|
}
|
|
931
978
|
else if (sched.startTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunset') && sched.startTime !== sunset) {
|
|
932
|
-
sched.startTime = sunset;
|
|
979
|
+
sched.startTime = sunset + (sched.startTimeOffset || 0);
|
|
933
980
|
anyUpdated = sUpdated = true;
|
|
934
981
|
}
|
|
935
982
|
if (sched.endTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunrise') && sched.endTime !== sunrise) {
|
|
936
|
-
sched.endTime = sunrise;
|
|
983
|
+
sched.endTime = sunrise + (sched.endTimeOffset || 0);
|
|
937
984
|
anyUpdated = sUpdated = true;
|
|
938
985
|
}
|
|
939
986
|
else if (sched.endTimeType === sys.board.valueMaps.scheduleTimeTypes.getValue('sunset') && sched.endTime !== sunset) {
|
|
940
|
-
sched.endTime = sunset;
|
|
987
|
+
sched.endTime = sunset + (sched.endTimeOffset || 0);
|
|
941
988
|
anyUpdated = sUpdated = true;
|
|
942
989
|
}
|
|
943
990
|
if (sUpdated) {
|
|
@@ -1919,14 +1966,18 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
|
|
|
1919
1966
|
if (typeof superChlorinate === 'undefined') superChlorinate = utils.makeBool(chlor.superChlor);
|
|
1920
1967
|
}
|
|
1921
1968
|
if (typeof obj.disabled !== 'undefined') chlor.disabled = utils.makeBool(obj.disabled);
|
|
1969
|
+
if (typeof obj.body !== 'undefined') chlor.body = parseInt(obj.body, 10);
|
|
1922
1970
|
if (typeof chlor.body === 'undefined') chlor.body = parseInt(obj.body, 10) || 32;
|
|
1923
1971
|
// Verify the data.
|
|
1924
|
-
let
|
|
1925
|
-
|
|
1972
|
+
let bdy = sys.board.bodies.mapBodyAssociation(chlor.body || 0);
|
|
1973
|
+
let body;
|
|
1974
|
+
if (typeof bdy === 'undefined') {
|
|
1926
1975
|
if (sys.equipment.shared) body = 32;
|
|
1927
1976
|
else if (!sys.equipment.dual) body = 0;
|
|
1928
1977
|
else return Promise.reject(new InvalidEquipmentDataError(`Chlorinator body association is not valid: ${body}`, 'chlorinator', body));
|
|
1929
1978
|
}
|
|
1979
|
+
else
|
|
1980
|
+
body = bdy.val;
|
|
1930
1981
|
if (poolSetpoint > 100 || poolSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator poolSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.poolSetpoint));
|
|
1931
1982
|
if (spaSetpoint > 100 || spaSetpoint < 0) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator spaSetpoint is out of range: ${chlor.poolSetpoint}`, 'chlorinator', chlor.spaSetpoint));
|
|
1932
1983
|
if (typeof obj.ignoreSaltReading !== 'undefined') chlor.ignoreSaltReading = utils.makeBool(obj.ignoreSaltReading);
|
|
@@ -1988,7 +2039,7 @@ class TouchChlorinatorCommands extends ChlorinatorCommands {
|
|
|
1988
2039
|
let id = parseInt(obj.id, 10);
|
|
1989
2040
|
if (isNaN(id)) return Promise.reject(new InvalidEquipmentDataError(`Chlorinator id is not valid: ${obj.id}`, 'chlorinator', obj.id));
|
|
1990
2041
|
let chlor = sys.chlorinators.getItemById(id);
|
|
1991
|
-
if (chlor.master
|
|
2042
|
+
if (chlor.master >= 1) return await super.deleteChlorAsync(obj);
|
|
1992
2043
|
if (sl.enabled) {
|
|
1993
2044
|
await sl.chlor.setChlorEnabledAsync(false);
|
|
1994
2045
|
}
|
|
@@ -2026,11 +2077,25 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2026
2077
|
//}
|
|
2027
2078
|
// RKS: 05-20-22 This was moved out of systemBoard it does not belong there and probably should not
|
|
2028
2079
|
// be called in any current form since it was not being called as part of a message result.
|
|
2029
|
-
private setType(pump: Pump, pumpType: number) {
|
|
2080
|
+
private async setType(pump: Pump, pumpType: number) {
|
|
2030
2081
|
// if we are changing pump types, need to clear out circuits
|
|
2031
2082
|
// and props that aren't for this pump type
|
|
2032
2083
|
let _id = pump.id;
|
|
2033
2084
|
if (pump.type !== pumpType || pumpType === 0) {
|
|
2085
|
+
if (pump.type === 65) { // This used to be a two-speed pump. We need to remove the high speed circuits.
|
|
2086
|
+
let outc = Outbound.create({
|
|
2087
|
+
action: 158,
|
|
2088
|
+
retries: 2,
|
|
2089
|
+
response: Response.create({ action: 1, payload: [158] })
|
|
2090
|
+
});
|
|
2091
|
+
outc.appendPayloadBytes(0, 16);
|
|
2092
|
+
if (sys.controllerType !== ControllerType.IntelliTouch) {
|
|
2093
|
+
outc.setPayloadByte(4, 1);
|
|
2094
|
+
outc.setPayloadByte(5, 72);
|
|
2095
|
+
outc.setPayloadByte(13, 2);
|
|
2096
|
+
}
|
|
2097
|
+
await outc.sendAsync();
|
|
2098
|
+
}
|
|
2034
2099
|
let _p = pump.get(true);
|
|
2035
2100
|
sys.pumps.removeItemById(_id);
|
|
2036
2101
|
pump = sys.pumps.getItemById(_id, true);
|
|
@@ -2054,7 +2119,6 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2054
2119
|
spump.emitData('pumpExt', spump.getExtended());
|
|
2055
2120
|
}
|
|
2056
2121
|
}
|
|
2057
|
-
|
|
2058
2122
|
public async setPumpAsync(data: any, send: boolean = true): Promise<Pump> {
|
|
2059
2123
|
// Rules regarding Pumps in *Touch
|
|
2060
2124
|
// In *Touch there are basically three classifications of pumps. These include those under control of RS485, Dual Speed, and Single Speed.
|
|
@@ -2083,28 +2147,26 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2083
2147
|
if (type.equipmentMaster > 0 || data.master > 0) return await super.setPumpAsync(data);
|
|
2084
2148
|
data.master = 0;
|
|
2085
2149
|
if (typeof data.type === 'undefined' || isNaN(ntype) || typeof type.name === 'undefined') return Promise.reject(new InvalidEquipmentDataError('You must supply a pump type when creating a new pump', 'Pump', data));
|
|
2150
|
+
// Do not rely on the address for the id. This will not work overall since more than
|
|
2151
|
+
// one pump type that exists in the ET and IT realm.
|
|
2152
|
+
id = sys.pumps.getNextEquipmentId(new EquipmentIdRange(1, 255));
|
|
2086
2153
|
if (type.name === 'ds') {
|
|
2087
|
-
id = 9;
|
|
2088
2154
|
if (sys.pumps.find(elem => elem.type === ntype)) return Promise.reject(new InvalidEquipmentDataError(`You may add only one ${type.desc} pump`, 'Pump', data));
|
|
2089
2155
|
}
|
|
2090
2156
|
else if (type.name === 'ss') {
|
|
2091
|
-
id = 10;
|
|
2092
2157
|
if (sys.pumps.find(elem => elem.type === ntype)) return Promise.reject(new InvalidEquipmentDataError(`You may add only one ${type.desc} pump`, 'Pump', data));
|
|
2093
2158
|
}
|
|
2094
|
-
else if (type.name === 'none')
|
|
2159
|
+
else if (type.name === 'none') // This is a mis-guided relic. None should simply not exist.
|
|
2160
|
+
return Promise.reject(new InvalidEquipmentDataError('You must supply a valid type when adding a pump.', 'Pump', data));
|
|
2095
2161
|
else {
|
|
2096
|
-
// Under most circumstances the id will = the address minus 95.
|
|
2097
2162
|
if (typeof data.address !== 'undefined') {
|
|
2098
2163
|
data.address = parseInt(data.address, 10);
|
|
2099
2164
|
if (isNaN(data.address)) return Promise.reject(new InvalidEquipmentDataError(`You must supply a valid pump address to add a ${type.desc} pump.`, 'Pump', data));
|
|
2100
|
-
id = data.address - 95;
|
|
2101
2165
|
// Make sure it doesn't already exist.
|
|
2102
2166
|
if (sys.pumps.find(elem => elem.address === data.address)) return Promise.reject(new InvalidEquipmentDataError(`A pump already exists at address ${data.address - 95}`, 'Pump', data));
|
|
2103
2167
|
}
|
|
2104
2168
|
else {
|
|
2105
2169
|
if (typeof id === 'undefined') return Promise.reject(new InvalidEquipmentDataError(`You may not add another ${type.desc} pump. Max number of pumps exceeded.`, 'Pump', data));
|
|
2106
|
-
id = sys.pumps.getNextEquipmentId(sys.board.equipmentIds.pumps);
|
|
2107
|
-
data.address = id + 95;
|
|
2108
2170
|
}
|
|
2109
2171
|
}
|
|
2110
2172
|
isAdd = true;
|
|
@@ -2112,24 +2174,42 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2112
2174
|
}
|
|
2113
2175
|
else {
|
|
2114
2176
|
pump = sys.pumps.getItemById(id, false);
|
|
2115
|
-
if (data.master > 0 || pump.master > 0) return await super.setPumpAsync(data);
|
|
2116
2177
|
data.master = 0;
|
|
2117
2178
|
ntype = typeof data.type === 'undefined' ? pump.type : parseInt(data.type, 10);
|
|
2118
2179
|
if (isNaN(ntype)) return Promise.reject(new InvalidEquipmentDataError(`Pump type ${data.type} is not valid`, 'Pump', data));
|
|
2119
2180
|
type = sys.board.valueMaps.pumpTypes.transform(ntype);
|
|
2181
|
+
data.master = type.name in ['ss', 'hwvs', 'hwrly', 'sf'] ? 1 : 0;
|
|
2182
|
+
if (data.master > 0 || pump.master > 0) return await super.setPumpAsync(data);
|
|
2183
|
+
|
|
2120
2184
|
// changing type? clear out all props and add as new
|
|
2121
2185
|
if (ntype !== pump.type) {
|
|
2122
2186
|
isAdd = true;
|
|
2123
|
-
this.setType(pump, ntype);
|
|
2187
|
+
await this.setType(pump, ntype);
|
|
2124
2188
|
pump = sys.pumps.getItemById(id, false); // refetch pump with new value
|
|
2125
2189
|
}
|
|
2126
2190
|
}
|
|
2127
2191
|
// Validate all the ids since in *Touch the address is determined from the id.
|
|
2192
|
+
// RKS: 05-07-23 - The pump address is no longer determined by the id. This was such a twisted effort to try to match these to the id.
|
|
2193
|
+
// while the order of the pump coming out of the config messages determine the address the id is not relevant since *Touch panels should simply
|
|
2194
|
+
// search for the address using byte index + 96. All of this was becoming too much.
|
|
2128
2195
|
if (!isAdd) isAdd = sys.pumps.find(elem => elem.id === id) === undefined;
|
|
2196
|
+
if (type.name === 'ss') {
|
|
2197
|
+
data.master = 1; // All single speed pumps are Nixie controlled.
|
|
2198
|
+
return await super.setPumpAsync(data);
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
if (isAdd) {
|
|
2202
|
+
// The ids for dual speed and single speed pumps have been mixed up in the past. Originally, the code relied on ss id === 10 and ds === 9. However, when the screenLogic
|
|
2203
|
+
// code was added these got switched and essentially invalidated all ds and ss pumps. The way this works now is that the single ds pump for ET and IT will be determined by
|
|
2204
|
+
// type and the id will not matter.
|
|
2205
|
+
data.master = 0;
|
|
2206
|
+
if (type.name === 'ds' && typeof sys.pumps.find(x => x.type === 65) !== 'undefined') return Promise.reject(new InvalidEquipmentDataError(`Only one Two-speed pump may be added`, 'Pump', data));
|
|
2207
|
+
else if (sys.pumps.count(x => x.type in [1, 64, 128, 169]) >= sys.equipment.maxPumps) return Promise.reject(new InvalidEquipmentDataError(`The maximum number of variable pumps have been exceeded for this panel. ${type.desc}`, 'Pump', data));
|
|
2208
|
+
}
|
|
2129
2209
|
// Now lets validate the ids related to the type.
|
|
2130
|
-
if (id ===
|
|
2131
|
-
else if (id ===
|
|
2132
|
-
else if (id > sys.equipment.maxPumps) return Promise.reject(new InvalidEquipmentDataError(`The id for a ${type.desc} must be less than ${sys.equipment.maxPumps}`, 'Pump', data));
|
|
2210
|
+
//if (id === 10 && type.name !== 'ds') return Promise.reject(new InvalidEquipmentDataError(`The id for a Two-speed pump must be 9`, 'Pump', data));
|
|
2211
|
+
//else if (id === 9 && type.name !== 'ss') return Promise.reject(new InvalidEquipmentDataError(`The id for a Single-Speed pump must be 10`, 'Pump', data));
|
|
2212
|
+
//else if (id > sys.equipment.maxPumps) return Promise.reject(new InvalidEquipmentDataError(`The id for a ${type.desc} must be less than ${sys.equipment.maxPumps}`, 'Pump', data));
|
|
2133
2213
|
|
|
2134
2214
|
// Need to do a check here if we are clearing out the circuits; id data.circuits === []
|
|
2135
2215
|
// extend will keep the original array
|
|
@@ -2180,34 +2260,60 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2180
2260
|
// We will not be sending message for ss type pumps.
|
|
2181
2261
|
if (type.name === 'ss') {
|
|
2182
2262
|
// The OCP doesn't deal with single speed pumps. Simply add it to the config.
|
|
2183
|
-
|
|
2184
|
-
pump.set(pump);
|
|
2263
|
+
pump.circuits.clear();
|
|
2185
2264
|
let spump = state.pumps.getItemById(id, true);
|
|
2186
2265
|
for (let prop in spump) {
|
|
2187
2266
|
if (typeof data[prop] !== 'undefined') spump[prop] = data[prop];
|
|
2188
2267
|
}
|
|
2189
|
-
|
|
2268
|
+
spump.type = pump.type = type.val;
|
|
2269
|
+
pump.model = typeof data.model === 'undefined' ? sys.board.valueMaps.pumpSSModels.encode(data.model) : pump.model || 0;
|
|
2190
2270
|
spump.emitEquipmentChange();
|
|
2191
|
-
|
|
2271
|
+
|
|
2272
|
+
return pump;
|
|
2192
2273
|
}
|
|
2193
2274
|
else if (type.name === 'ds') {
|
|
2194
2275
|
// We are going to set all the high speed circuits.
|
|
2195
2276
|
// RSG: TODO I don't know what the message is to set the high speed circuits. The following should
|
|
2196
2277
|
// be moved into the onComplete for the outbound message to set high speed circuits.
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2278
|
+
// RKS: 05-06-23 - Send the message for high speed circuits.
|
|
2279
|
+
let outc = Outbound.create({
|
|
2280
|
+
action: 158,
|
|
2281
|
+
retries: 2,
|
|
2282
|
+
response: Response.create({ action: 1, payload: [158] })
|
|
2283
|
+
});
|
|
2284
|
+
outc.appendPayloadBytes(0, 16);
|
|
2285
|
+
if (sys.controllerType !== ControllerType.IntelliTouch) {
|
|
2286
|
+
outc.setPayloadByte(4, 1);
|
|
2287
|
+
outc.setPayloadByte(5, 72);
|
|
2288
|
+
outc.setPayloadByte(13, 2);
|
|
2200
2289
|
}
|
|
2201
|
-
let
|
|
2202
|
-
for (let
|
|
2203
|
-
|
|
2290
|
+
let maxCircuits = typeof data.circuits !== 'undefined' ? Math.min(data.circuits.length, sys.controllerType === ControllerType.IntelliTouch ? 8 : 4) : 0;
|
|
2291
|
+
for (let i = 0; i < maxCircuits; i++) {
|
|
2292
|
+
outc.setPayloadByte(i, data.circuits[i].circuit, 0);
|
|
2204
2293
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2294
|
+
// So lets send the message for setting the high speed circuits.
|
|
2295
|
+
if (await outc.sendAsync()) {
|
|
2296
|
+
let spump = state.pumps.getItemById(id, true);
|
|
2297
|
+
pump.model = typeof data.model === 'undefined' ? sys.board.valueMaps.pumpDSModels.encode(data.model) : pump.model || 0;
|
|
2298
|
+
let circs = [];
|
|
2299
|
+
// Lets do this to rearrange cases where 0s are sent.
|
|
2300
|
+
for (let i = 0; i < maxCircuits; i++) {
|
|
2301
|
+
if (data.circuits[i].circuit > 0) {
|
|
2302
|
+
circs.push(data.circuits[i].circuit);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
pump.circuits.clear();
|
|
2306
|
+
for (let i = 0; i < circs.length; i++) {
|
|
2307
|
+
pump.circuits.getItemById(i + 1, true, { id: i + 1, circuit: circs[i] });
|
|
2308
|
+
}
|
|
2309
|
+
spump.emitEquipmentChange();
|
|
2310
|
+
}
|
|
2311
|
+
return pump;
|
|
2207
2312
|
}
|
|
2208
2313
|
else {
|
|
2209
2314
|
let arrCircuits = [];
|
|
2210
|
-
|
|
2315
|
+
// Look to see if there is another pump at the same address.
|
|
2316
|
+
if (typeof sys.pumps.find(x => x.id !== id && x.address === data.address) !== 'undefined') return Promise.reject(new InvalidEquipmentDataError(`A pump already exists at address ${data.address - 95}`, 'Pump', data));
|
|
2211
2317
|
if (isVersion1) {
|
|
2212
2318
|
if (data.address !== 96) return Promise.reject(new InvalidEquipmentDataError(`EasyTouch Version 1 controllers only support VS pumps at the first address`, 'Pump', data));
|
|
2213
2319
|
if (type.name !== 'vs') return Promise.reject(new InvalidEquipmentDataError(`EasyTouch Version 1 controllers only support VS pump types. ${type.desc} pumps are not supported`, 'Pump', data));
|
|
@@ -2250,7 +2356,7 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2250
2356
|
data.circuits = arrCircuits;
|
|
2251
2357
|
|
|
2252
2358
|
if (send) {
|
|
2253
|
-
return new Promise<Pump>(async (resolve, reject) => {
|
|
2359
|
+
return await new Promise<Pump>(async (resolve, reject) => {
|
|
2254
2360
|
outc.onComplete = (err, msg) => {
|
|
2255
2361
|
if (err) reject(err);
|
|
2256
2362
|
else {
|
|
@@ -2268,10 +2374,12 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2268
2374
|
retries: 2,
|
|
2269
2375
|
response: true
|
|
2270
2376
|
});
|
|
2271
|
-
|
|
2377
|
+
(async () => { pumpConfigRequest.sendAsync(); })();
|
|
2378
|
+
//conn.queueSendMessage(pumpConfigRequest);
|
|
2272
2379
|
}
|
|
2273
2380
|
};
|
|
2274
2381
|
await outc.sendAsync();
|
|
2382
|
+
return pump;
|
|
2275
2383
|
});
|
|
2276
2384
|
}
|
|
2277
2385
|
}
|
|
@@ -2428,32 +2536,26 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2428
2536
|
return Promise.resolve(pump);
|
|
2429
2537
|
}
|
|
2430
2538
|
else if (send) {
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
});
|
|
2452
|
-
conn.queueSendMessage(pumpConfigRequest);
|
|
2453
|
-
}
|
|
2454
|
-
};
|
|
2455
|
-
await outc.sendAsync();
|
|
2456
|
-
});
|
|
2539
|
+
if (await outc.sendAsync()) {
|
|
2540
|
+
pump = sys.pumps.getItemById(id, true);
|
|
2541
|
+
// RKS: 05-20-22 Boooh to this if the payload does not include its
|
|
2542
|
+
// circuits we have just destroyed the pump definition. So I added code to
|
|
2543
|
+
// make sure that the data is complete.
|
|
2544
|
+
pump.set(data); // Sets all the data back to the pump.
|
|
2545
|
+
let spump = state.pumps.getItemById(id, true);
|
|
2546
|
+
spump.isActive = pump.isActive = true;
|
|
2547
|
+
spump.name = pump.name;
|
|
2548
|
+
spump.type = pump.type;
|
|
2549
|
+
spump.emitEquipmentChange();
|
|
2550
|
+
const pumpConfigRequest = Outbound.create({
|
|
2551
|
+
action: 216,
|
|
2552
|
+
payload: [pump.id],
|
|
2553
|
+
retries: 2,
|
|
2554
|
+
response: true
|
|
2555
|
+
});
|
|
2556
|
+
await pumpConfigRequest.sendAsync();
|
|
2557
|
+
return pump;
|
|
2558
|
+
}
|
|
2457
2559
|
}
|
|
2458
2560
|
}
|
|
2459
2561
|
}
|
|
@@ -2548,30 +2650,50 @@ class TouchPumpCommands extends PumpCommands {
|
|
|
2548
2650
|
if (isNaN(id)) return Promise.reject(new InvalidEquipmentIdError(`deletePumpAsync: Pump ${id} is not valid.`, 0, `pump`));
|
|
2549
2651
|
let pump = sys.pumps.getItemById(id, false);
|
|
2550
2652
|
if (pump.master === 1) return super.deletePumpAsync(data);
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
resolve(sys.pumps.getItemById(id, false));
|
|
2564
|
-
const pumpConfigRequest = Outbound.create({
|
|
2565
|
-
action: 216,
|
|
2566
|
-
payload: [id],
|
|
2567
|
-
retries: 2,
|
|
2568
|
-
response: true
|
|
2569
|
-
});
|
|
2570
|
-
conn.queueSendMessage(pumpConfigRequest);
|
|
2571
|
-
}
|
|
2572
|
-
};
|
|
2653
|
+
else if (pump.type === 65) { // Dual speed pump.
|
|
2654
|
+
let outc = Outbound.create({
|
|
2655
|
+
action: 158,
|
|
2656
|
+
retries: 2,
|
|
2657
|
+
response: Response.create({ action: 1, payload: [158] })
|
|
2658
|
+
});
|
|
2659
|
+
outc.appendPayloadBytes(0, 16);
|
|
2660
|
+
if (sys.controllerType !== ControllerType.IntelliTouch) {
|
|
2661
|
+
outc.setPayloadByte(4, 1);
|
|
2662
|
+
outc.setPayloadByte(5, 72);
|
|
2663
|
+
outc.setPayloadByte(13, 2);
|
|
2664
|
+
}
|
|
2573
2665
|
await outc.sendAsync();
|
|
2574
|
-
|
|
2666
|
+
pump.isActive = false;
|
|
2667
|
+
sys.pumps.removeItemById(id);
|
|
2668
|
+
state.pumps.removeItemById(id);
|
|
2669
|
+
return pump;
|
|
2670
|
+
}
|
|
2671
|
+
else {
|
|
2672
|
+
const outc = Outbound.create({
|
|
2673
|
+
action: 155,
|
|
2674
|
+
payload: [id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
2675
|
+
retries: 2,
|
|
2676
|
+
response: true
|
|
2677
|
+
});
|
|
2678
|
+
return new Promise<Pump>(async (resolve, reject) => {
|
|
2679
|
+
outc.onComplete = (err, msg) => {
|
|
2680
|
+
if (err) reject(err);
|
|
2681
|
+
else {
|
|
2682
|
+
sys.pumps.removeItemById(id);
|
|
2683
|
+
state.pumps.removeItemById(id);
|
|
2684
|
+
resolve(sys.pumps.getItemById(id, false));
|
|
2685
|
+
const pumpConfigRequest = Outbound.create({
|
|
2686
|
+
action: 216,
|
|
2687
|
+
payload: [id],
|
|
2688
|
+
retries: 2,
|
|
2689
|
+
response: true
|
|
2690
|
+
});
|
|
2691
|
+
(async () => { await pumpConfigRequest.sendAsync(); });
|
|
2692
|
+
}
|
|
2693
|
+
};
|
|
2694
|
+
await outc.sendAsync();
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2575
2697
|
}
|
|
2576
2698
|
}
|
|
2577
2699
|
class TouchHeaterCommands extends HeaterCommands {
|
|
@@ -2807,15 +2929,15 @@ class TouchHeaterCommands extends HeaterCommands {
|
|
|
2807
2929
|
// 2 = Gas Heater
|
|
2808
2930
|
// 3 = Hybrid
|
|
2809
2931
|
// 16 = Dual
|
|
2810
|
-
sys.board.valueMaps.heatModes.set(1, { name: '
|
|
2811
|
-
sys.board.valueMaps.heatModes.set(2, { name: '
|
|
2812
|
-
sys.board.valueMaps.heatModes.set(3, { name: '
|
|
2813
|
-
sys.board.valueMaps.heatModes.set(16, { name: '
|
|
2932
|
+
sys.board.valueMaps.heatModes.set(1, { name: 'hybheatpump', desc: 'Heat Pump' });
|
|
2933
|
+
sys.board.valueMaps.heatModes.set(2, { name: 'hybheat', desc: 'Gas Heat' });
|
|
2934
|
+
sys.board.valueMaps.heatModes.set(3, { name: 'hybhybrid', desc: 'Hybrid' });
|
|
2935
|
+
sys.board.valueMaps.heatModes.set(16, { name: 'hybdual', desc: 'Dual Heat' });
|
|
2814
2936
|
|
|
2815
|
-
sys.board.valueMaps.heatSources.set(2, { name: '
|
|
2816
|
-
sys.board.valueMaps.heatSources.set(5, { name: '
|
|
2817
|
-
sys.board.valueMaps.heatSources.set(20, { name: '
|
|
2818
|
-
sys.board.valueMaps.heatSources.set(21, { name: '
|
|
2937
|
+
sys.board.valueMaps.heatSources.set(2, { name: 'hybheat', desc: 'Gas Heat' });
|
|
2938
|
+
sys.board.valueMaps.heatSources.set(5, { name: 'hybhybrid', desc: 'Hybrid' });
|
|
2939
|
+
sys.board.valueMaps.heatSources.set(20, { name: 'hybdual', desc: 'Dual Heat' });
|
|
2940
|
+
sys.board.valueMaps.heatSources.set(21, { name: 'hybheatpump', desc: 'Heat Pump' });
|
|
2819
2941
|
}
|
|
2820
2942
|
else {
|
|
2821
2943
|
if (gasHeaterInstalled) {
|
|
@@ -2863,6 +2985,8 @@ class TouchHeaterCommands extends HeaterCommands {
|
|
|
2863
2985
|
let body = sys.bodies.getItemByIndex(i);
|
|
2864
2986
|
let btemp = state.temps.bodies.getItemById(body.id, body.isActive !== false);
|
|
2865
2987
|
let opts = sys.board.heaters.getInstalledHeaterTypes(body.id);
|
|
2988
|
+
btemp.data.heatMode = sys.board.valueMaps.heatModes.transform(btemp.heatMode);
|
|
2989
|
+
btemp.data.heatStatus = sys.board.valueMaps.heatStatus.transform(btemp.heatStatus);
|
|
2866
2990
|
btemp.heaterOptions = opts;
|
|
2867
2991
|
}
|
|
2868
2992
|
this.setActiveTempSensors();
|