nodejs-poolcontroller 8.0.0 → 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 +79 -18
- package/controller/Lockouts.ts +15 -0
- package/controller/State.ts +280 -7
- package/controller/boards/EasyTouchBoard.ts +226 -102
- 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 +60 -57
- 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 +4 -3
- package/logger/DataLogger.ts +7 -7
- package/package.json +3 -3
- 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
package/controller/State.ts
CHANGED
|
@@ -23,10 +23,11 @@ import * as util from 'util';
|
|
|
23
23
|
import { logger } from '../logger/Logger';
|
|
24
24
|
import { webApp } from '../web/Server';
|
|
25
25
|
import { ControllerType, Timestamp, utils, Heliotrope } from './Constants';
|
|
26
|
-
import { sys, Chemical, ChemController, ChemicalTank, ChemicalPump } from './Equipment';
|
|
26
|
+
import { sys, Chemical, ChemController, ChemicalTank, ChemicalPump, Schedule } from './Equipment';
|
|
27
27
|
import { versionCheck } from '../config/VersionCheck';
|
|
28
28
|
import { DataLogger, DataLoggerEntry } from '../logger/DataLogger';
|
|
29
29
|
import { delayMgr } from './Lockouts';
|
|
30
|
+
import { time } from 'console';
|
|
30
31
|
|
|
31
32
|
export class State implements IState {
|
|
32
33
|
statePath: string;
|
|
@@ -212,6 +213,8 @@ export class State implements IState {
|
|
|
212
213
|
model: sys.equipment.model,
|
|
213
214
|
sunrise: self.data.sunrise || '',
|
|
214
215
|
sunset: self.data.sunset || '',
|
|
216
|
+
nextSunrise: self.data.nextSunrise || '',
|
|
217
|
+
nextSunset: self.data.nextSunset || '',
|
|
215
218
|
alias: sys.general.alias,
|
|
216
219
|
freeze: utils.makeBool(self.data.freeze),
|
|
217
220
|
valveMode: self.data.valveMode || {},
|
|
@@ -283,7 +286,10 @@ export class State implements IState {
|
|
|
283
286
|
}
|
|
284
287
|
}
|
|
285
288
|
}
|
|
286
|
-
public get time(): Timestamp {
|
|
289
|
+
public get time(): Timestamp {
|
|
290
|
+
if (typeof this._dt === 'undefined' || !this._dt.isValid) this._dt = new Timestamp(new Date());
|
|
291
|
+
return this._dt;
|
|
292
|
+
}
|
|
287
293
|
public get mode(): number { return typeof (this.data.mode) !== 'undefined' ? this.data.mode.val : -1; }
|
|
288
294
|
public set mode(val: number) {
|
|
289
295
|
let m = sys.board.valueMaps.panelModes.transform(val);
|
|
@@ -365,8 +371,16 @@ export class State implements IState {
|
|
|
365
371
|
EqStateCollection.removeNullIds(sdata.chemControllers);
|
|
366
372
|
EqStateCollection.removeNullIds(sdata.chemDosers);
|
|
367
373
|
EqStateCollection.removeNullIds(sdata.filters);
|
|
374
|
+
// Initialize the schedules.
|
|
375
|
+
if (typeof sdata.schedules !== 'undefined') {
|
|
376
|
+
for (let i = 0; i < sdata.schedules.length; i++) {
|
|
377
|
+
let ssched = sdata.schedules[i];
|
|
378
|
+
ssched.manualPriorityActive = ssched.isOn = ssched.triggered = false;
|
|
379
|
+
if (typeof ssched.scheduleTime !== 'undefined') ssched.scheduleTime.calculated = false;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
368
382
|
var self = this;
|
|
369
|
-
let pnlTime = typeof sdata.time !== 'undefined' ? new Date(sdata.time) : new Date();
|
|
383
|
+
let pnlTime = typeof sdata.time !== 'undefined' && sdata.time !== '' ? new Date(sdata.time) : new Date();
|
|
370
384
|
if (isNaN(pnlTime.getTime())) pnlTime = new Date();
|
|
371
385
|
this._dt = new Timestamp(pnlTime);
|
|
372
386
|
this._dt.milliseconds = 0;
|
|
@@ -384,6 +398,10 @@ export class State implements IState {
|
|
|
384
398
|
let times = self.heliotrope.calculatedTimes;
|
|
385
399
|
self.data.sunrise = times.isValid ? Timestamp.toISOLocal(times.sunrise) : '';
|
|
386
400
|
self.data.sunset = times.isValid ? Timestamp.toISOLocal(times.sunset) : '';
|
|
401
|
+
self.data.nextSunrise = times.isValid ? Timestamp.toISOLocal(times.nextSunrise) : '';
|
|
402
|
+
self.data.nextSunset = times.isValid ? Timestamp.toISOLocal(times.nextSunset) : '';
|
|
403
|
+
self.data.prevSunrise = times.isValid ? Timestamp.toISOLocal(times.prevSunrise) : '';
|
|
404
|
+
self.data.prevSunset = times.isValid ? Timestamp.toISOLocal(times.prevSunset) : '';
|
|
387
405
|
versionCheck.checkGitRemote();
|
|
388
406
|
});
|
|
389
407
|
this.status = 0; // Initializing
|
|
@@ -507,6 +525,7 @@ export interface ICircuitState {
|
|
|
507
525
|
isOn: boolean;
|
|
508
526
|
startTime?: Timestamp;
|
|
509
527
|
endTime: Timestamp;
|
|
528
|
+
priority?: string,
|
|
510
529
|
lightingTheme?: number;
|
|
511
530
|
action?: number;
|
|
512
531
|
emitEquipmentChange();
|
|
@@ -681,7 +700,7 @@ class EqStateCollection<T> {
|
|
|
681
700
|
return arr;
|
|
682
701
|
}
|
|
683
702
|
// Finds an item and returns undefined if it doesn't exist.
|
|
684
|
-
public find(f: (value:
|
|
703
|
+
public find(f: (value: T, index?: number, obj?: any) => boolean): T {
|
|
685
704
|
let itm = this.data.find(f);
|
|
686
705
|
if (typeof itm !== 'undefined') return this.createItem(itm);
|
|
687
706
|
}
|
|
@@ -1064,6 +1083,224 @@ export class PumpState extends EqState {
|
|
|
1064
1083
|
}
|
|
1065
1084
|
export class ScheduleStateCollection extends EqStateCollection<ScheduleState> {
|
|
1066
1085
|
public createItem(data: any): ScheduleState { return new ScheduleState(data); }
|
|
1086
|
+
public getActiveSchedules(): ScheduleState[] {
|
|
1087
|
+
let activeScheds: ScheduleState[] = [];
|
|
1088
|
+
for (let i = 0; i < this.length; i++) {
|
|
1089
|
+
let ssched = this.getItemByIndex(i);
|
|
1090
|
+
let st = ssched.scheduleTime;
|
|
1091
|
+
let sched = sys.schedules.getItemById(ssched.id);
|
|
1092
|
+
if (!sched.isActive || ssched.disabled) {
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
st.calcSchedule(state.time, sys.schedules.getItemById(ssched.id));
|
|
1096
|
+
if (typeof st.startTime === 'undefined') continue;
|
|
1097
|
+
if (ssched.isOn || st.shouldBeOn || st.startTime.getTime() > new Date().getTime()) activeScheds.push(ssched);
|
|
1098
|
+
}
|
|
1099
|
+
return activeScheds;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
export class ScheduleTime extends ChildEqState {
|
|
1103
|
+
public initData() { if (typeof this.data.times !== 'undefined') delete this.data.times; }
|
|
1104
|
+
public get calculatedDate(): Date { return typeof this.data.calculatedDate !== 'undefined' && this.data.calculatedDate !== '' ? new Date(this.data.calculatedDate) : new Date(1970, 0, 1); }
|
|
1105
|
+
public set calculatedDate(val: Date) { this._saveTimestamp(val, 'calculatedDate', false); }
|
|
1106
|
+
public get startTime(): Date { return typeof this.data.startTime !== 'undefined' && this.data.startTime !== '' ? new Date(this.data.startTime) : null; }
|
|
1107
|
+
public set startTime(val: Date) { this._saveTimestamp(val, 'startTime'); }
|
|
1108
|
+
public get endTime(): Date { return typeof this.data.endTime !== 'undefined' && this.data.endTime !== '' ? new Date(this.data.endTime) : null; }
|
|
1109
|
+
public set endTime(val: Date) { this._saveTimestamp(val, 'endTime'); }
|
|
1110
|
+
private _saveTimestamp(dt, prop, persist:boolean = true) {
|
|
1111
|
+
if (typeof dt === 'undefined' || !dt) this.setDataVal(prop, '');
|
|
1112
|
+
else this.setDataVal(prop, Timestamp.toISOLocal(dt));
|
|
1113
|
+
}
|
|
1114
|
+
private _calcShouldBeOn(time: number) : boolean {
|
|
1115
|
+
let tmStart = this.startTime ? this.startTime.getTime() : NaN;
|
|
1116
|
+
let tmEnd = this.endTime ? this.endTime.getTime() : NaN;
|
|
1117
|
+
if (isNaN(tmStart) || isNaN(tmEnd) || time < tmStart || time > tmEnd) return false;
|
|
1118
|
+
return true;
|
|
1119
|
+
}
|
|
1120
|
+
public get shouldBeOn(): boolean {
|
|
1121
|
+
let shouldBeOn = this._calcShouldBeOn(state.time.getTime());
|
|
1122
|
+
if (this.data.shouldBeOn !== shouldBeOn) this.setDataVal('shouldBeOn', shouldBeOn);
|
|
1123
|
+
return this.data.shouldBeOn || false;
|
|
1124
|
+
}
|
|
1125
|
+
protected set shouldBeOn(val: boolean) { this.setDataVal('shouldBeOn', val); }
|
|
1126
|
+
public get calculated(): boolean { return this.data.calculated; }
|
|
1127
|
+
public set calculated(val: boolean) { this.setDataVal('calculated', val); }
|
|
1128
|
+
public calcScheduleDate(ts: Timestamp, sched: Schedule): { startTime: Date, endTime: Date } {
|
|
1129
|
+
let times: { startTime: Date, endTime: Date } = { startTime: null, endTime: null };
|
|
1130
|
+
try {
|
|
1131
|
+
let sod = ts.clone().startOfDay();
|
|
1132
|
+
let ysod = ts.clone().addHours(-24).startOfDay();
|
|
1133
|
+
let nsod = ts.clone().addHours(-24).startOfDay();
|
|
1134
|
+
let ytimes: { startTime: Date, endTime: Date } = { startTime: null, endTime: null }; // Yesterday
|
|
1135
|
+
let ttimes: { startTime: Date, endTime: Date } = { startTime: null, endTime: null }; // Today
|
|
1136
|
+
let ntimes: { startTime: Date, endTime: Date } = { startTime: null, endTime: null }; // Tomorrow
|
|
1137
|
+
let tt = sys.board.valueMaps.scheduleTimeTypes.transform(sched.startTimeType);
|
|
1138
|
+
// Add the range for today and yesterday.
|
|
1139
|
+
switch (tt.name) {
|
|
1140
|
+
case 'sunrise':
|
|
1141
|
+
let sr = state.heliotrope.calcAdjustedTimes(sod.toDate(), 0, sched.startTimeOffset);
|
|
1142
|
+
ytimes.startTime = sr.prevSunrise;
|
|
1143
|
+
ttimes.startTime = sr.sunrise;
|
|
1144
|
+
ntimes.startTime = sr.nextSunrise;
|
|
1145
|
+
break;
|
|
1146
|
+
case 'sunset':
|
|
1147
|
+
let ss = state.heliotrope.calcAdjustedTimes(sod.toDate(), 0, sched.startTimeOffset);
|
|
1148
|
+
ytimes.startTime = ss.prevSunset;
|
|
1149
|
+
ttimes.startTime = ss.sunset;
|
|
1150
|
+
ntimes.startTime = ss.nextSunset;
|
|
1151
|
+
break;
|
|
1152
|
+
default:
|
|
1153
|
+
ytimes.startTime = ysod.clone().addMinutes(sched.startTime).toDate();
|
|
1154
|
+
ttimes.startTime = sod.clone().addMinutes(sched.startTime).toDate();
|
|
1155
|
+
ntimes.startTime = nsod.clone().addMinutes(sched.startTime).toDate();
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
tt = sys.board.valueMaps.scheduleTimeTypes.transform(sched.endTimeType);
|
|
1159
|
+
switch (tt.name) {
|
|
1160
|
+
case 'sunrise':
|
|
1161
|
+
let sr = state.heliotrope.calcAdjustedTimes(sod.toDate(), 0, sched.endTimeOffset);
|
|
1162
|
+
// If the start time of the previous window is greater than the previous sunrise then we use the sunrise for today.
|
|
1163
|
+
ytimes.endTime = ytimes.startTime >= sr.prevSunrise ? sr.sunrise : sr.prevSunrise;
|
|
1164
|
+
// If ths start time of the current window is greater than the current sunrise then we use the sunrise for tomorrow.
|
|
1165
|
+
ttimes.endTime = ttimes.startTime >= sr.sunrise ? sr.nextSunrise : sr.sunrise;
|
|
1166
|
+
ntimes.endTime = ntimes.startTime >= sr.nextSunrise ? new Timestamp(sr.nextSunrise).addHours(24).toDate() : sr.nextSunrise;
|
|
1167
|
+
break;
|
|
1168
|
+
case 'sunset':
|
|
1169
|
+
let ss = state.heliotrope.calcAdjustedTimes(sod.toDate(), 0, sched.endTimeOffset);
|
|
1170
|
+
// If the start time of the previous window is greater than the previous sunset then we use the sunset for today.
|
|
1171
|
+
ytimes.endTime = ytimes.startTime >= ss.prevSunset ? ss.sunset : ss.prevSunset;
|
|
1172
|
+
ttimes.endTime = ttimes.startTime >= ss.sunset ? ss.nextSunset : ss.nextSunset;
|
|
1173
|
+
ntimes.endTime = ntimes.startTime >= ss.nextSunset ? new Timestamp(ss.nextSunset).addHours(24).toDate() : ss.nextSunset;
|
|
1174
|
+
break;
|
|
1175
|
+
default:
|
|
1176
|
+
ytimes.endTime = ysod.clone().addMinutes(sched.endTime).toDate();
|
|
1177
|
+
if (ytimes.endTime <= ytimes.startTime) ytimes.endTime = ysod.clone().addHours(24).addMinutes(sched.endTime).toDate();
|
|
1178
|
+
ttimes.endTime = sod.clone().addMinutes(sched.endTime).toDate();
|
|
1179
|
+
if (ttimes.endTime <= ttimes.startTime) ttimes.endTime = sod.clone().addHours(24).addMinutes(sched.endTime).toDate();
|
|
1180
|
+
ntimes.endTime = nsod.clone().addMinutes(sched.endTime).toDate();
|
|
1181
|
+
if (ntimes.endTime <= ntimes.startTime) ntimes.endTime = nsod.clone().addHours(24).addMinutes(sched.endTime).toDate();
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
ttimes.startTime.setSeconds(0, 0); // Set the start time to the beginning of the minute.
|
|
1185
|
+
ttimes.endTime.setSeconds(59, 999); // Set the end time to the end of the minute.
|
|
1186
|
+
ytimes.startTime.setSeconds(0, 0);
|
|
1187
|
+
ytimes.endTime.setSeconds(59, 999);
|
|
1188
|
+
ntimes.startTime.setSeconds(0, 0);
|
|
1189
|
+
ntimes.endTime.setSeconds(59, 999);
|
|
1190
|
+
// Now check the dow for each range. If the start time for the dow matches then include it. If not then do not.
|
|
1191
|
+
let schedDays = sys.board.valueMaps.scheduleDays.toArray();
|
|
1192
|
+
let fnInRange = (time, times) => {
|
|
1193
|
+
let tmStart = times.startTime ? times.startTime.getTime() : NaN;
|
|
1194
|
+
let tmEnd = times.endTime ? times.endTime.getTime() : NaN;
|
|
1195
|
+
if (isNaN(tmStart) || isNaN(tmEnd) || time < tmStart || time > tmEnd) return false;
|
|
1196
|
+
return true;
|
|
1197
|
+
}
|
|
1198
|
+
let tm = ts.getTime();
|
|
1199
|
+
if (fnInRange(tm, ttimes)) {
|
|
1200
|
+
// Check the dow.
|
|
1201
|
+
let sd = schedDays.find(elem => elem.dow === ttimes.startTime.getDay());
|
|
1202
|
+
if (typeof sd !== 'undefined' && (sched.scheduleDays & sd.bitval) !== 0) {
|
|
1203
|
+
times.startTime = ttimes.startTime;
|
|
1204
|
+
times.endTime = ttimes.endTime;
|
|
1205
|
+
return times;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
// First check if we are still running yesterday. This will ensure we have
|
|
1209
|
+
// the first runtime.
|
|
1210
|
+
if (fnInRange(tm, ytimes)) {
|
|
1211
|
+
// Check the dow.
|
|
1212
|
+
let sd = schedDays.find(elem => elem.dow === ytimes.startTime.getDay());
|
|
1213
|
+
if (typeof sd !== 'undefined' && (sched.scheduleDays & sd.bitval) !== 0) {
|
|
1214
|
+
times.startTime = ytimes.startTime;
|
|
1215
|
+
times.endTime = ytimes.startTime;
|
|
1216
|
+
return times;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
// Then check if we are running today. If we have already run then get net next run
|
|
1220
|
+
// time.
|
|
1221
|
+
if (tm <= ttimes.startTime.getTime()) {
|
|
1222
|
+
let sd = schedDays.find(elem => elem.dow === ttimes.startTime.getDay());
|
|
1223
|
+
if (typeof sd !== 'undefined' && (sched.scheduleDays & sd.bitval) !== 0) {
|
|
1224
|
+
times.startTime = ttimes.startTime;
|
|
1225
|
+
times.endTime = ttimes.endTime;
|
|
1226
|
+
return times;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
// Then look for tomorrow.
|
|
1230
|
+
if (tm <= ntimes.startTime.getTime()) {
|
|
1231
|
+
let sd = schedDays.find(elem => elem.dow === ntimes.startTime.getDay());
|
|
1232
|
+
if (typeof sd !== 'undefined' && (sched.scheduleDays & sd.bitval) !== 0) {
|
|
1233
|
+
times.startTime = ntimes.startTime;
|
|
1234
|
+
times.endTime = ntimes.endTime;
|
|
1235
|
+
return times;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return times;
|
|
1239
|
+
} catch (err) {
|
|
1240
|
+
logger.error(`Error calculating date for schedule ${sched.id}: ${err.message}`);
|
|
1241
|
+
}
|
|
1242
|
+
finally { return times; }
|
|
1243
|
+
}
|
|
1244
|
+
public calcSchedule(currentTime: Timestamp, sched: Schedule): boolean {
|
|
1245
|
+
try {
|
|
1246
|
+
let sod = currentTime.clone().startOfDay();
|
|
1247
|
+
|
|
1248
|
+
// There are 3 conditions where the schdedule will be recalculated. The first
|
|
1249
|
+
// 1. The calculated flag is false
|
|
1250
|
+
// 2. The calculated flag is true and the calculated date < the current start of day
|
|
1251
|
+
// 3. Regardless of the calculated date the current end time has passed and the start time is
|
|
1252
|
+
// from a prior date. This will happen when the schedule is complete and we need to calculate the
|
|
1253
|
+
// next run time.
|
|
1254
|
+
let dtCalc = typeof this.calculatedDate !== 'undefined' && typeof this.calculatedDate.getTime === 'function' ? new Date(this.calculatedDate.getTime()).setHours(0, 0, 0, 0) : new Date(1970, 0, 1, 0, 0).getTime();
|
|
1255
|
+
let recalc = !this.calculated;
|
|
1256
|
+
if (!recalc && sod.getTime() !== dtCalc) recalc = true;
|
|
1257
|
+
if (!recalc && (this.endTime.getTime() < new Date().getTime() && this.startTime.getTime() < dtCalc)) {
|
|
1258
|
+
recalc = true;
|
|
1259
|
+
logger.info(`Recalculating expired schedule ${sched.id}`);
|
|
1260
|
+
}
|
|
1261
|
+
if (!recalc) return this.shouldBeOn;
|
|
1262
|
+
//if (this.calculated && sod.getTime() === dtCalc) return this.shouldBeOn;
|
|
1263
|
+
this.calculatedDate = new Date(new Date().setHours(0, 0, 0, 0));
|
|
1264
|
+
if (sched.isActive === false || sched.disabled) return false;
|
|
1265
|
+
let tt = sys.board.valueMaps.scheduleTimeTypes.transform(sched.startTimeType);
|
|
1266
|
+
// If this is a runonce schedule we need to check for the rundate
|
|
1267
|
+
let type = sys.board.valueMaps.scheduleTypes.transform(sched.scheduleType);
|
|
1268
|
+
let times = type.name === 'runonce' ? this.calcScheduleDate(new Timestamp(sched.startDate), sched) : this.calcScheduleDate(state.time.clone(), sched);
|
|
1269
|
+
if (times.startTime && times.endTime.getTime() > currentTime.getTime()) {
|
|
1270
|
+
// Check to see if it should be on.
|
|
1271
|
+
this.startTime = times.startTime;
|
|
1272
|
+
this.endTime = times.endTime;
|
|
1273
|
+
this.calculated = true;
|
|
1274
|
+
return this.shouldBeOn;
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
// Chances are that the current dow is not valid. Fast forward until we get a day that works. That will
|
|
1278
|
+
// be the next scheduled run date.
|
|
1279
|
+
if (type.name !== 'runonce' && sched.scheduleDays > 0) {
|
|
1280
|
+
let schedDays = sys.board.valueMaps.scheduleDays.toArray();
|
|
1281
|
+
let day = sod.clone().addHours(24);
|
|
1282
|
+
let dow = day.getDay();
|
|
1283
|
+
while (dow !== sod.getDay()) {
|
|
1284
|
+
let sd = schedDays.find(elem => elem.dow === day.getDay());
|
|
1285
|
+
if (typeof sd !== 'undefined' && (sched.scheduleDays & sd.bitval) !== 0) {
|
|
1286
|
+
times = this.calcScheduleDate(day, sched);
|
|
1287
|
+
break;
|
|
1288
|
+
}
|
|
1289
|
+
else day.addHours(24);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
this.startTime = times.startTime;
|
|
1293
|
+
this.endTime = times.endTime;
|
|
1294
|
+
this.calculated = true;
|
|
1295
|
+
}
|
|
1296
|
+
return this.shouldBeOn;
|
|
1297
|
+
} catch (err) {
|
|
1298
|
+
this.calculated = true;
|
|
1299
|
+
this.calculatedDate = new Date(new Date().setHours(0, 0, 0, 0));
|
|
1300
|
+
this.startTime = null;
|
|
1301
|
+
this.endTime = null;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1067
1304
|
}
|
|
1068
1305
|
export class ScheduleState extends EqState {
|
|
1069
1306
|
constructor(data: any, dataName?: string) { super(data, dataName); }
|
|
@@ -1092,10 +1329,17 @@ export class ScheduleState extends EqState {
|
|
|
1092
1329
|
public set startTime(val: number) { this.setDataVal('startTime', val); }
|
|
1093
1330
|
public get endTime(): number { return this.data.endTime; }
|
|
1094
1331
|
public set endTime(val: number) { this.setDataVal('endTime', val); }
|
|
1332
|
+
public get startTimeOffset(): number { return this.data.startTimeOffset || 0; }
|
|
1333
|
+
public set startTimeOffset(val: number) { this.setDataVal('startTimeOffset', val); }
|
|
1334
|
+
public get endTimeOffset(): number { return this.data.endTimeOffset || 0; }
|
|
1335
|
+
public set endTimeOffset(val: number) { this.setDataVal('endTimeOffset', val); }
|
|
1336
|
+
|
|
1095
1337
|
public get circuit(): number { return this.data.circuit; }
|
|
1096
1338
|
public set circuit(val: number) { this.setDataVal('circuit', val); }
|
|
1097
1339
|
public get disabled(): boolean { return this.data.disabled; }
|
|
1098
1340
|
public set disabled(val: boolean) { this.setDataVal('disabled', val); }
|
|
1341
|
+
public get triggered(): boolean { return this.data.triggered || false; }
|
|
1342
|
+
public set triggered(val: boolean) { this.setDataVal('triggered', val); }
|
|
1099
1343
|
public get scheduleType(): number { return typeof (this.data.scheduleType) !== 'undefined' ? this.data.scheduleType.val : undefined; }
|
|
1100
1344
|
public set scheduleType(val: number) {
|
|
1101
1345
|
if (this.scheduleType !== val) {
|
|
@@ -1149,11 +1393,18 @@ export class ScheduleState extends EqState {
|
|
|
1149
1393
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1150
1394
|
public get manualPriorityActive(): boolean { return this.data.manualPriorityActive; }
|
|
1151
1395
|
public set manualPriorityActive(val: boolean) { this.setDataVal('manualPriorityActive', val); }
|
|
1396
|
+
public get scheduleTime(): ScheduleTime { return new ScheduleTime(this.data, 'scheduleTime', this); }
|
|
1397
|
+
public recalculate(force?: boolean) {
|
|
1398
|
+
if (force === true) this.scheduleTime.calculated = false;
|
|
1399
|
+
this.scheduleTime.calcSchedule(state.time, sys.schedules.getItemById(this.id));
|
|
1400
|
+
}
|
|
1152
1401
|
public getExtended() {
|
|
1153
1402
|
let sched = this.get(true); // Always operate on a copy.
|
|
1154
1403
|
//if (typeof this.circuit !== 'undefined')
|
|
1155
1404
|
sched.circuit = state.circuits.getInterfaceById(this.circuit).get(true);
|
|
1156
|
-
|
|
1405
|
+
sched.clockMode = sys.board.valueMaps.clockModes.transform(sys.general.options.clockMode) || {};
|
|
1406
|
+
//let times = this.calcScheduleTimes(sched);
|
|
1407
|
+
//sched.times = { shouldBeOn: times.shouldBeOn, startTime: times.shouldBeOn ? Timestamp.toISOLocal(times.startTime) : '', endTime: times.shouldBeOn ? Timestamp.toISOLocal(times.endTime) : '' };
|
|
1157
1408
|
return sched;
|
|
1158
1409
|
}
|
|
1159
1410
|
public emitEquipmentChange() {
|
|
@@ -1224,6 +1475,8 @@ export class CircuitGroupState extends EqState implements ICircuitGroupState, IC
|
|
|
1224
1475
|
}
|
|
1225
1476
|
public get isOn(): boolean { return this.data.isOn; }
|
|
1226
1477
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1478
|
+
public get priority(): string { return this.data.priority || 'manual' }
|
|
1479
|
+
public set priority(val: string) { this.setDataVal('priority', val); }
|
|
1227
1480
|
public get endTime(): Timestamp {
|
|
1228
1481
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1229
1482
|
return new Timestamp(this.data.endTime);
|
|
@@ -1313,6 +1566,8 @@ export class LightGroupState extends EqState implements ICircuitGroupState, ICir
|
|
|
1313
1566
|
this.hasChanged = true;
|
|
1314
1567
|
}
|
|
1315
1568
|
}
|
|
1569
|
+
public get priority(): string { return this.data.priority || 'manual' }
|
|
1570
|
+
public set priority(val: string) { this.setDataVal('priority', val); }
|
|
1316
1571
|
public get endTime(): Timestamp {
|
|
1317
1572
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1318
1573
|
return new Timestamp(this.data.endTime);
|
|
@@ -1578,6 +1833,12 @@ export class HeaterState extends EqState {
|
|
|
1578
1833
|
this.hasChanged = true;
|
|
1579
1834
|
}
|
|
1580
1835
|
}
|
|
1836
|
+
public get prevHeaterOffTemp(): number { return this.data.prevHeaterOffTemp; }
|
|
1837
|
+
public set prevHeaterOffTemp(val: number) {
|
|
1838
|
+
if (this.prevHeaterOffTemp !== val) {
|
|
1839
|
+
this.data.prevHeaterOffTemp = val;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1581
1842
|
public get startupDelay(): boolean { return this.data.startupDelay; }
|
|
1582
1843
|
public set startupDelay(val: boolean) { this.setDataVal('startupDelay', val); }
|
|
1583
1844
|
public get shutdownDelay(): boolean { return this.data.shutdownDelay; }
|
|
@@ -1630,6 +1891,8 @@ export class FeatureState extends EqState implements ICircuitState {
|
|
|
1630
1891
|
}
|
|
1631
1892
|
public get showInFeatures(): boolean { return this.data.showInFeatures; }
|
|
1632
1893
|
public set showInFeatures(val: boolean) { this.setDataVal('showInFeatures', val); }
|
|
1894
|
+
public get priority(): string { return this.data.priority || 'manual' }
|
|
1895
|
+
public set priority(val: string) { this.setDataVal('priority', val); }
|
|
1633
1896
|
public get endTime(): Timestamp {
|
|
1634
1897
|
if (typeof this.data.endTime === 'undefined') return undefined;
|
|
1635
1898
|
return new Timestamp(this.data.endTime);
|
|
@@ -1654,6 +1917,8 @@ export class VirtualCircuitState extends EqState implements ICircuitState {
|
|
|
1654
1917
|
public set nameId(val: number) { this.setDataVal('nameId', val); }
|
|
1655
1918
|
public get isOn(): boolean { return this.data.isOn; }
|
|
1656
1919
|
public set isOn(val: boolean) { this.setDataVal('isOn', val); }
|
|
1920
|
+
public get priority(): string { return 'manual' } // These are always manual priority
|
|
1921
|
+
public set priority(val: string) { ; }
|
|
1657
1922
|
public get type() { return typeof this.data.type !== 'undefined' ? this.data.type.val : -1; }
|
|
1658
1923
|
public set type(val: number) {
|
|
1659
1924
|
if (this.type !== val) {
|
|
@@ -1729,6 +1994,8 @@ export class CircuitState extends EqState implements ICircuitState {
|
|
|
1729
1994
|
this.hasChanged = true;
|
|
1730
1995
|
}
|
|
1731
1996
|
}
|
|
1997
|
+
public get priority(): string { return this.data.priority; }
|
|
1998
|
+
public set priority(val: string) { this.setDataVal('priority', val); }
|
|
1732
1999
|
public get showInFeatures(): boolean { return this.data.showInFeatures; }
|
|
1733
2000
|
public set showInFeatures(val: boolean) { this.setDataVal('showInFeatures', val); }
|
|
1734
2001
|
public get isOn(): boolean { return this.data.isOn; }
|
|
@@ -1762,6 +2029,8 @@ export class CircuitState extends EqState implements ICircuitState {
|
|
|
1762
2029
|
this.hasChanged = true;
|
|
1763
2030
|
}
|
|
1764
2031
|
}
|
|
2032
|
+
public get scheduled(): boolean { return this.data.scheduled || false }
|
|
2033
|
+
public set scheduled(val: boolean) { this.setDataVal('scheduled', val); }
|
|
1765
2034
|
public get startTime(): Timestamp {
|
|
1766
2035
|
if (typeof this.data.startTime === 'undefined') return undefined;
|
|
1767
2036
|
return new Timestamp(this.data.startTime);
|
|
@@ -2364,6 +2633,7 @@ export class ChemDoserState extends EqState implements IChemicalState, IChemCont
|
|
|
2364
2633
|
|
|
2365
2634
|
export class ChemControllerState extends EqState implements IChemControllerState {
|
|
2366
2635
|
public initData() {
|
|
2636
|
+
if (typeof this.activeBodyId === 'undefined') this.data.activeBodyId = 0;
|
|
2367
2637
|
if (typeof this.data.saturationIndex === 'undefined') this.data.saturationIndex = 0;
|
|
2368
2638
|
if (typeof this.data.flowDetected === 'undefined') this.data.flowDetected = false;
|
|
2369
2639
|
if (typeof this.data.orp === 'undefined') this.data.orp = {};
|
|
@@ -2467,6 +2737,8 @@ export class ChemControllerState extends EqState implements IChemControllerState
|
|
|
2467
2737
|
public set address(val: number) { this.setDataVal('address', val); }
|
|
2468
2738
|
public get isBodyOn(): boolean { return this.data.isBodyOn; }
|
|
2469
2739
|
public set isBodyOn(val: boolean) { this.data.isBodyOn = val; }
|
|
2740
|
+
public get activeBodyId(): number { return this.data.activeBodyId || 0; }
|
|
2741
|
+
public set activeBodyId(val: number) { this.data.activeBodyId = val; }
|
|
2470
2742
|
public get flowDetected(): boolean { return this.data.flowDetected; }
|
|
2471
2743
|
public set flowDetected(val: boolean) { this.data.flowDetected = val; }
|
|
2472
2744
|
public get status(): number {
|
|
@@ -3449,10 +3721,11 @@ export class FilterState extends EqState {
|
|
|
3449
3721
|
this.hasChanged = true;
|
|
3450
3722
|
}
|
|
3451
3723
|
}
|
|
3452
|
-
public get pressureUnits(): number { return this.data.pressureUnits; }
|
|
3724
|
+
public get pressureUnits(): number { return typeof this.data.pressureUnits === 'undefined' ? 0 : this.data.pressureUnits.val; }
|
|
3453
3725
|
public set pressureUnits(val: number) {
|
|
3454
|
-
if (this.pressureUnits !== val) {
|
|
3726
|
+
if (this.pressureUnits !== val || typeof this.data.pressureUnits === 'undefined') {
|
|
3455
3727
|
this.setDataVal('pressureUnits', sys.board.valueMaps.pressureUnits.transform(val));
|
|
3728
|
+
this.hasChanged = true;
|
|
3456
3729
|
}
|
|
3457
3730
|
}
|
|
3458
3731
|
public get pressure(): number { return this.data.pressure; }
|