nodejs-poolcontroller 7.2.0 → 7.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/Changelog +13 -0
  3. package/Dockerfile +1 -0
  4. package/README.md +5 -5
  5. package/app.ts +11 -0
  6. package/config/Config.ts +3 -0
  7. package/config/VersionCheck.ts +8 -4
  8. package/controller/Constants.ts +165 -9
  9. package/controller/Equipment.ts +186 -65
  10. package/controller/Errors.ts +22 -1
  11. package/controller/State.ts +273 -57
  12. package/controller/boards/EasyTouchBoard.ts +194 -95
  13. package/controller/boards/IntelliCenterBoard.ts +115 -42
  14. package/controller/boards/IntelliTouchBoard.ts +104 -30
  15. package/controller/boards/NixieBoard.ts +155 -53
  16. package/controller/boards/SystemBoard.ts +1529 -514
  17. package/controller/comms/Comms.ts +219 -42
  18. package/controller/comms/messages/Messages.ts +16 -4
  19. package/controller/comms/messages/config/ChlorinatorMessage.ts +13 -3
  20. package/controller/comms/messages/config/CircuitGroupMessage.ts +6 -0
  21. package/controller/comms/messages/config/CircuitMessage.ts +1 -1
  22. package/controller/comms/messages/config/CoverMessage.ts +1 -0
  23. package/controller/comms/messages/config/EquipmentMessage.ts +4 -0
  24. package/controller/comms/messages/config/ExternalMessage.ts +43 -25
  25. package/controller/comms/messages/config/FeatureMessage.ts +8 -1
  26. package/controller/comms/messages/config/GeneralMessage.ts +8 -0
  27. package/controller/comms/messages/config/HeaterMessage.ts +15 -9
  28. package/controller/comms/messages/config/IntellichemMessage.ts +4 -1
  29. package/controller/comms/messages/config/OptionsMessage.ts +13 -1
  30. package/controller/comms/messages/config/PumpMessage.ts +4 -20
  31. package/controller/comms/messages/config/RemoteMessage.ts +4 -0
  32. package/controller/comms/messages/config/ScheduleMessage.ts +11 -0
  33. package/controller/comms/messages/config/SecurityMessage.ts +1 -0
  34. package/controller/comms/messages/config/ValveMessage.ts +12 -2
  35. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +14 -6
  36. package/controller/comms/messages/status/EquipmentStateMessage.ts +78 -24
  37. package/controller/comms/messages/status/HeaterStateMessage.ts +25 -5
  38. package/controller/comms/messages/status/IntelliChemStateMessage.ts +55 -26
  39. package/controller/nixie/Nixie.ts +18 -16
  40. package/controller/nixie/NixieEquipment.ts +6 -6
  41. package/controller/nixie/bodies/Body.ts +7 -4
  42. package/controller/nixie/bodies/Filter.ts +7 -4
  43. package/controller/nixie/chemistry/ChemController.ts +800 -283
  44. package/controller/nixie/chemistry/Chlorinator.ts +22 -14
  45. package/controller/nixie/circuits/Circuit.ts +42 -7
  46. package/controller/nixie/heaters/Heater.ts +303 -30
  47. package/controller/nixie/pumps/Pump.ts +57 -30
  48. package/controller/nixie/schedules/Schedule.ts +10 -7
  49. package/controller/nixie/valves/Valve.ts +7 -5
  50. package/defaultConfig.json +32 -1
  51. package/issue_template.md +1 -1
  52. package/logger/DataLogger.ts +37 -22
  53. package/package.json +20 -18
  54. package/web/Server.ts +529 -31
  55. package/web/bindings/influxDB.json +157 -5
  56. package/web/bindings/mqtt.json +112 -13
  57. package/web/bindings/mqttAlt.json +109 -11
  58. package/web/interfaces/baseInterface.ts +2 -1
  59. package/web/interfaces/httpInterface.ts +2 -0
  60. package/web/interfaces/influxInterface.ts +103 -54
  61. package/web/interfaces/mqttInterface.ts +16 -5
  62. package/web/services/config/Config.ts +179 -43
  63. package/web/services/state/State.ts +51 -5
  64. package/web/services/state/StateSocket.ts +19 -2
@@ -20,7 +20,7 @@ import * as extend from "extend";
20
20
  import * as util from "util";
21
21
  import { setTimeout } from "timers";
22
22
  import { logger } from "../logger/Logger";
23
- import { state, CommsState } from "./State";
23
+ import { state, CommsState, ChemicalChlorState } from "./State";
24
24
  import { Timestamp, ControllerType, utils } from "./Constants";
25
25
  export { ControllerType };
26
26
  import { webApp } from "../web/Server";
@@ -31,6 +31,7 @@ import { conn } from './comms/Comms';
31
31
  import { versionCheck } from "../config/VersionCheck";
32
32
  import { NixieControlPanel } from "./nixie/Nixie";
33
33
  import { NixieBoard } from 'controller/boards/NixieBoard';
34
+ import { child } from "winston";
34
35
 
35
36
  interface IPoolSystem {
36
37
  cfgPath: string;
@@ -68,7 +69,7 @@ interface IPoolSystem {
68
69
  }
69
70
 
70
71
  export class PoolSystem implements IPoolSystem {
71
- public _hasChanged: boolean=false;
72
+ public _hasChanged: boolean = false;
72
73
  constructor() {
73
74
  this.cfgPath = path.posix.join(process.cwd(), '/data/poolConfig.json');
74
75
  }
@@ -99,7 +100,12 @@ export class PoolSystem implements IPoolSystem {
99
100
  { val: 3, name: 'IT5S', part: 'i5+3S', desc: 'IntelliTouch i5+3S', bodies: 1, circuits: 6, shared: false },
100
101
  { val: 4, name: 'IT9S', part: 'i9+3S', desc: 'IntelliTouch i9+3S', bodies: 1, circuits: 9, shared: false },
101
102
  { val: 5, name: 'IT10D', part: 'i10D', desc: 'IntelliTouch i10D', bodies: 1, circuits: 10, shared: false, dual: true }
103
+ ],
104
+ expansionModules: [
105
+ { val: 32, name: 'IT5X', part: 'i5X', desc: 'IntelliTouch i5X', circuits: 5, shared: false },
106
+ { val: 33, name: 'IT10X', part: 'i10X', desc: 'IntelliTouch i10X', circuits: 10, shared: false }
102
107
  ]
108
+
103
109
  });
104
110
  arr.push({
105
111
  type: 'intellicenter', name: 'IntelliCenter',
@@ -109,7 +115,8 @@ export class PoolSystem implements IPoolSystem {
109
115
  { val: 2, name: 'i8P', part: '521977Z', desc: 'IntelliCenter i8P', bodies: 1, valves: 2, circuits: 8, shared: false, dual: false, chlorinators: 1, chemControllers: 1 },
110
116
  { val: 3, name: 'i8PS', part: '521968Z', desc: 'IntelliCenter i8PS', bodies: 2, valves: 4, circuits: 9, shared: true, dual: false, chlorinators: 1, chemControllers: 1 },
111
117
  { val: 4, name: 'i10P', part: '521993Z', desc: 'IntelliCenter i10P', bodies: 1, valves: 2, circuits: 10, shared: false, dual: false, chlorinators: 1, chemControllers: 1 }, // This is a guess
112
- { val: 5, name: 'i10PS', part: '521873Z', desc: 'IntelliCenter i10PS', bodies: 2, valves: 4, circuits: 11, shared: true, dual: false, chlorinators: 1, chemControllers: 1 }
118
+ { val: 5, name: 'i10PS', part: '521873Z', desc: 'IntelliCenter i10PS', bodies: 2, valves: 4, circuits: 11, shared: true, dual: false, chlorinators: 1, chemControllers: 1 },
119
+ { val: 7, name: 'i10D', part: '523029Z', desc: 'IntelliCenter i10D', bodies: 2, valves: 2, circuits: 11, shared: false, dual: true, chlorinators: 2, chemControllers: 2 },
113
120
  ]
114
121
  });
115
122
  arr.push({
@@ -124,13 +131,14 @@ export class PoolSystem implements IPoolSystem {
124
131
  return arr;
125
132
  }
126
133
  public async start() {
134
+ let self = this;
127
135
  this.data.appVersion = state.appVersion.installed = this.appVersion = JSON.parse(fs.readFileSync(path.posix.join(process.cwd(), '/package.json'), 'utf8')).version;
128
136
  versionCheck.compare(); // if we installed a new version, reset the flag so we don't show an outdated message for up to 2 days
129
137
  logger.info(`Starting Pool System ${this.controllerType}`);
130
138
  if (this.controllerType === 'unknown' || typeof this.controllerType === 'undefined') {
131
139
  // Delay for 7.5 seconds to give any OCPs a chance to start emitting messages.
132
140
  logger.info(`Listening for any installed OCPs`);
133
- setTimeout(() => { this.initNixieController(); }, 7500);
141
+ setTimeout(() => { self.initNixieController(); }, 7500);
134
142
  }
135
143
  else
136
144
  this.initNixieController();
@@ -139,7 +147,22 @@ export class PoolSystem implements IPoolSystem {
139
147
  let cfg = this.loadConfigFile(this.cfgPath, {});
140
148
  let cfgDefault = this.loadConfigFile(path.posix.join(process.cwd(), '/defaultPool.json'), {});
141
149
  cfg = extend(true, {}, cfgDefault, cfg);
142
- this.data = this.onchange(cfg, function() { sys.dirty = true; });
150
+ // First lets remove all the null ids.
151
+ EqItemCollection.removeNullIds(cfg.bodies);
152
+ EqItemCollection.removeNullIds(cfg.schedules);
153
+ EqItemCollection.removeNullIds(cfg.features);
154
+ EqItemCollection.removeNullIds(cfg.circuits);
155
+ EqItemCollection.removeNullIds(cfg.pumps);
156
+ EqItemCollection.removeNullIds(cfg.chlorinators);
157
+ EqItemCollection.removeNullIds(cfg.valves);
158
+ EqItemCollection.removeNullIds(cfg.heaters);
159
+ EqItemCollection.removeNullIds(cfg.covers);
160
+ EqItemCollection.removeNullIds(cfg.circuitGroups);
161
+ EqItemCollection.removeNullIds(cfg.lightGroups);
162
+ EqItemCollection.removeNullIds(cfg.remotes);
163
+ EqItemCollection.removeNullIds(cfg.chemControllers);
164
+ EqItemCollection.removeNullIds(cfg.filters);
165
+ this.data = this.onchange(cfg, function () { sys.dirty = true; });
143
166
  this.general = new General(this.data, 'pool');
144
167
  this.equipment = new Equipment(this.data, 'equipment');
145
168
  this.configVersion = new ConfigVersion(this.data, 'configVersion');
@@ -191,6 +214,7 @@ export class PoolSystem implements IPoolSystem {
191
214
  }
192
215
  public get controllerType(): ControllerType { return this.data.controllerType as ControllerType; }
193
216
  public set controllerType(val: ControllerType) {
217
+ let self = this;
194
218
  if (this.controllerType !== val || this.controllerType === ControllerType.Virtual) {
195
219
  console.log('RESETTING DATA -- Data files backed up to ./logs directory.');
196
220
  // Only go in here if there is a change to the controller type.
@@ -200,7 +224,7 @@ export class PoolSystem implements IPoolSystem {
200
224
  EquipmentStateMessage.initDefaults();
201
225
  // We are actually changing the config so lets clear out all the data.
202
226
  this.board = BoardFactory.fromControllerType(val, this);
203
- if (this.data.controllerType === ControllerType.Unknown || this.controllerType === ControllerType.Virtual) setTimeout(() => { this.initNixieController(); }, 7500);
227
+ if (this.data.controllerType === ControllerType.Unknown || this.controllerType === ControllerType.Virtual) setTimeout(() => { self.initNixieController(); }, 7500);
204
228
  }
205
229
  }
206
230
  public resetData() {
@@ -248,43 +272,6 @@ export class PoolSystem implements IPoolSystem {
248
272
  break;
249
273
  }
250
274
  }
251
- /* public searchForAdditionalDevices() {
252
- if (this.controllerType === ControllerType.Unknown || typeof this.controllerType === 'undefined' && !conn.mockPort) {
253
- //logger.info("Searching chlorinators, pumps and chem controllers");
254
- //EquipmentStateMessage.initVirtual();
255
- //sys.board.virtualChlorinatorController.search();
256
- //sys.board.virtualPumpControllers.search();
257
- //sys.board.virtualChemControllers.search();
258
- state.equipment.controllerType = sys.controllerType = ControllerType.Nixie;
259
- let board = sys.board as NixieBoard;
260
- (async () => { await board.initNixieBoard(); })();
261
- }
262
- else {
263
- if (this.controllerType === ControllerType.Virtual) {
264
- state.mode = 0;
265
- state.status = 1;
266
- sys.equipment.setEquipmentIds();
267
- state.emitControllerChange();
268
- sys.board.processStatusAsync();
269
- }
270
- else {
271
- let board = sys.board as NixieBoard;
272
- (async () => { await board.initNixieBoard(); })();
273
- }
274
-
275
- // if the app crashes while the pumps are running we need to reset the 'virtualControllerStatus' to stopped so it can start again
276
- sys.board.virtualPumpControllers.softStop();
277
- //sys.board.virtualChlorinatorController.stop();
278
- sys.board.virtualChemControllers.stop();
279
- // try to start any virtual controllers that are present irregardless of overall controller virtual status
280
- sys.board.virtualPumpControllers.start();
281
- //sys.board.virtualChlorinatorController.start();
282
- sys.board.virtualChemControllers.start();
283
- sys.board.heaters.initTempSensors();
284
- sys.board.heaters.updateHeaterServices();
285
- state.cleanupState();
286
- }
287
- } */
288
275
  public board: SystemBoard = new SystemBoard(this);
289
276
  public ncp: NixieControlPanel = new NixieControlPanel();
290
277
  public processVersionChanges(ver: ConfigVersion) { this.board.requestConfiguration(ver); }
@@ -320,6 +307,7 @@ export class PoolSystem implements IPoolSystem {
320
307
  public appVersion: string;
321
308
  public get dirty(): boolean { return this._isDirty; }
322
309
  public set dirty(val) {
310
+ let self = this;
323
311
  this._isDirty = val;
324
312
  this._lastUpdated = new Date();
325
313
  this.data.lastUpdated = this._lastUpdated.toLocaleString();
@@ -328,7 +316,7 @@ export class PoolSystem implements IPoolSystem {
328
316
  this._timerDirty = null;
329
317
  }
330
318
  if (this._isDirty) {
331
- this._timerDirty = setTimeout(() => this.persist(), 3000);
319
+ this._timerDirty = setTimeout(() => self.persist(), 3000);
332
320
  }
333
321
  }
334
322
  public persist() {
@@ -534,6 +522,14 @@ class EqItemCollection<T> implements IEqItemCollection {
534
522
  this.name = name;
535
523
  }
536
524
  }
525
+ public static removeNullIds(data: any) {
526
+ if (typeof data !== 'undefined' && Array.isArray(data) && typeof data.length === 'number') {
527
+ for (let i = data.length - 1; i >= 0; i--) {
528
+ if (typeof data[i].id !== 'number') data.splice(i, 1);
529
+ else if (typeof data[i].id === 'undefined' || isNaN(data[i].id)) data.splice(i, 1);
530
+ }
531
+ }
532
+ }
537
533
  public getItemByIndex(ndx: number, add?: boolean, data?: any): T {
538
534
  if (this.data.length > ndx) return this.createItem(this.data[ndx]);
539
535
  if (typeof add !== 'undefined' && add)
@@ -617,7 +613,7 @@ class EqItemCollection<T> implements IEqItemCollection {
617
613
  }
618
614
  public sort(fn: (a, b) => number) { this.data.sort(fn); }
619
615
  public count(fn: () => boolean): number { return this.data.filter(fn).length; }
620
- public getNextEquipmentId(range: EquipmentIdRange, exclude?:number[]): number {
616
+ public getNextEquipmentId(range: EquipmentIdRange, exclude?: number[]): number {
621
617
  for (let i = range.start; i <= range.end; i++) {
622
618
  let eq = this.data.find(elem => elem.id === i);
623
619
  if (typeof eq === 'undefined') {
@@ -658,6 +654,7 @@ export class General extends EqItem {
658
654
  if (master === -1)
659
655
  super.clear();
660
656
  }
657
+
661
658
  }
662
659
  // Custom Names are IntelliTouch Only
663
660
  export class CustomNameCollection extends EqItemCollection<CustomName> {
@@ -726,25 +723,28 @@ export class Options extends EqItem {
726
723
  if (typeof this.data.units === 'undefined') this.data.units = 0;
727
724
  if (typeof this.data.clockMode === 'undefined') this.data.clockMode = 12;
728
725
  if (typeof this.data.adjustDST === 'undefined') this.data.adjustDST = true;
726
+ if (typeof this.data.freezeThreshold === 'undefined') this.data.freezeThreshold = 35;
729
727
  }
730
728
  public get clockMode(): number | any { return this.data.clockMode; }
731
729
  public set clockMode(val: number | any) { this.setDataVal('clockMode', sys.board.valueMaps.clockModes.encode(val)); }
732
730
  public get units(): number | any { return this.data.units; }
733
- public set units(val: number | any) { this.setDataVal('units', sys.board.valueMaps.tempUnits.encode(val)); }
731
+ public set units(val: number | any) { this.setDataVal('units', sys.board.valueMaps.systemUnits.encode(val)); }
734
732
  public get clockSource(): string { return this.data.clockSource; }
735
733
  public set clockSource(val: string) { this.setDataVal('clockSource', val); }
736
734
  public get adjustDST(): boolean { return this.data.adjustDST; }
737
735
  public set adjustDST(val: boolean) { this.setDataVal('adjustDST', val); }
738
736
  public get manualPriority(): boolean { return this.data.manualPriority; }
739
737
  public set manualPriority(val: boolean) { this.setDataVal('manualPriority', val); }
740
- public get vacationMode(): boolean { return this.data.vacationMode; }
741
- public set vacationMode(val: boolean) { this.setDataVal('vacationMode', val); }
738
+ public get vacation(): VacationOptions { return new VacationOptions(this.data, 'vacation', this); }
742
739
  public get manualHeat(): boolean { return this.data.manualHeat; }
743
740
  public set manualHeat(val: boolean) { this.setDataVal('manualHeat', val); }
744
741
  public get pumpDelay(): boolean { return this.data.pumpDelay; }
745
742
  public set pumpDelay(val: boolean) { this.setDataVal('pumpDelay', val); }
746
743
  public get cooldownDelay(): boolean { return this.data.cooldownDelay; }
747
744
  public set cooldownDelay(val: boolean) { this.setDataVal('cooldownDelay', val); }
745
+ public get freezeThreshold(): number { return this.data.freezeThreshold; }
746
+ public set freezeThreshold(val: number) { this.setDataVal('freezeThreshold', val); }
747
+
748
748
  //public get airTempAdj(): number { return typeof this.data.airTempAdj === 'undefined' ? 0 : this.data.airTempAdj; }
749
749
  //public set airTempAdj(val: number) { this.setDataVal('airTempAdj', val); }
750
750
  //public get waterTempAdj1(): number { return typeof this.data.waterTempAdj1 === 'undefined' ? 0 : this.data.waterTempAdj1; }
@@ -756,6 +756,42 @@ export class Options extends EqItem {
756
756
  //public get solarTempAdj2(): number { return typeof this.data.solarTempAdj2 === 'undefined' ? 0 : this.data.solarTempAdj2; }
757
757
  //public set solarTempAdj2(val: number) { this.setDataVal('solarTempAd2', val); }
758
758
  }
759
+ export class VacationOptions extends ChildEqItem {
760
+ public initData() {
761
+ if (typeof this.data.enabled === 'undefined') this.data.enabled = false;
762
+ if (typeof this.data.useTimeframe === 'undefined') this.data.useTimeframe = false;
763
+ }
764
+ private _startDate: Date;
765
+ private _endDate: Date;
766
+ public get enabled(): boolean { return this.data.enabled; }
767
+ public set enabled(val: boolean) { this.setDataVal('enabled', val); }
768
+ public get useTimeframe(): boolean { return this.data.useTimeframe; }
769
+ public set useTimeframe(val: boolean) { this.setDataVal('useTimeframe', val); }
770
+ public get startDate() {
771
+ if (typeof this._startDate === 'undefined') this._startDate = typeof this.data.startDate === 'string' ? new Date(this.data.startDate) : undefined;
772
+ return this._startDate;
773
+ }
774
+ public set startDate(val: Date | string | number) {
775
+ this._startDate = new Date(val);
776
+ this._saveDate('startDate', this._startDate);
777
+ }
778
+ public get endDate() {
779
+ if (typeof this._endDate === 'undefined') this._endDate = typeof this.data.endDate === 'string' ? new Date(this.data.endDate) : undefined;
780
+ return this._endDate;
781
+ }
782
+ public set endDate(val: Date | string | number) {
783
+ this._endDate = new Date(val);
784
+ this._saveDate('endDate', this._endDate);
785
+ }
786
+
787
+ private _saveDate(prop: string, dt: Date) {
788
+ if (typeof dt !== 'undefined' && !isNaN(dt.getTime())) {
789
+ dt.setHours(0, 0, 0, 0);
790
+ this.setDataVal(prop, Timestamp.toISOLocal(dt));
791
+ }
792
+ else this.setDataVal(prop, undefined);
793
+ }
794
+ }
759
795
  export class Location extends EqItem {
760
796
  public dataName = 'locationConfig';
761
797
  public get address(): string { return this.data.address; }
@@ -820,6 +856,8 @@ export class Equipment extends EqItem {
820
856
  public set shared(val: boolean) { this.setDataVal('shared', val); }
821
857
  public get dual(): boolean { return this.data.dual; }
822
858
  public set dual(val: boolean) { this.setDataVal('dual', val); }
859
+ public get intakeReturnValves(): boolean { return this.data.intakeReturnValves; }
860
+ public set intakeReturnValves(val: boolean) { this.setDataVal('intakeReturnValves', val); }
823
861
  public get maxBodies(): number { return this.data.maxBodies || 4; }
824
862
  public set maxBodies(val: number) { this.setDataVal('maxBodies', val); }
825
863
  public get maxValves(): number { return this.data.maxValves || 26; }
@@ -941,7 +979,6 @@ export class ConfigVersion extends EqItem {
941
979
  if (prop === 'lastUpdated') continue;
942
980
  this.data[prop] = 0;
943
981
  }
944
-
945
982
  }
946
983
  }
947
984
 
@@ -965,6 +1002,9 @@ export class BodyCollection extends EqItemCollection<Body> {
965
1002
  }
966
1003
  export class Body extends EqItem {
967
1004
  public dataName = 'bodyConfig';
1005
+ public initData() {
1006
+ if (typeof this.data.capacityUnits === 'undefined') this.data.capacityUnits = 1;
1007
+ }
968
1008
  public get id(): number { return this.data.id; }
969
1009
  public set id(val: number) { this.data.id = val; }
970
1010
  public get name(): string { return this.data.name; }
@@ -989,7 +1029,14 @@ export class Body extends EqItem {
989
1029
  public set heatSetpoint(val: number) { this.setDataVal('setPoint', val); }
990
1030
  public get coolSetpoint(): number { return this.data.coolSetpoint; }
991
1031
  public set coolSetpoint(val: number) { this.setDataVal('coolSetpoint', val); }
1032
+ public get capacityUnits(): number | any { return this.data.capacityUnits; }
1033
+ public set capacityUnits(val: number | any) { this.setDataVal('capacityUnits', sys.board.valueMaps.volumeUnits.encode(val)); }
992
1034
  public getHeatModes() { return sys.board.bodies.getHeatModes(this.id); }
1035
+ public getExtended() {
1036
+ let body = this.get(true);
1037
+ body.capacityUnits = sys.board.valueMaps.volumeUnits.transform(this.capacityUnits || 1);
1038
+ return body;
1039
+ }
993
1040
  }
994
1041
  export class ScheduleCollection extends EqItemCollection<Schedule> {
995
1042
  constructor(data: any, name?: string) { super(data, name || "schedules"); }
@@ -1037,12 +1084,12 @@ export class Schedule extends EqItem {
1037
1084
  public get isActive(): boolean { return this.data.isActive; }
1038
1085
  public set isActive(val: boolean) { this.setDataVal('isActive', val); }
1039
1086
  public get startMonth(): number { return this._startDate.getMonth() + 1; }
1040
- public set startMonth(val: number) { this._startDate.setMonth(val - 1); this._saveStartDate(); }
1041
- public get startDay(): number { return this._startDate.getDate(); }
1042
- public set startDay(val: number) { this._startDate.setDate(val); this._saveStartDate(); }
1043
- public get startYear(): number { return this._startDate.getFullYear(); }
1044
- public set startYear(val: number) { this._startDate.setFullYear(val < 100 ? val + 2000 : val); this._saveStartDate(); }
1045
- public get startDate(): Date { return this._startDate; }
1087
+ public set startMonth(val: number) { if (typeof this._startDate === 'undefined') this._startDate = new Date(); this._startDate.setMonth(val - 1); this._saveStartDate(); }
1088
+ public get startDay(): number { if (typeof this._startDate === 'undefined') this._startDate = new Date(); return this._startDate.getDate(); }
1089
+ public set startDay(val: number) { if (typeof this._startDate === 'undefined') this._startDate = new Date(); this._startDate.setDate(val); this._saveStartDate(); }
1090
+ public get startYear(): number { if (typeof this._startDate === 'undefined') this._startDate = new Date(); return this._startDate.getFullYear(); }
1091
+ public set startYear(val: number) { if (typeof this._startDate === 'undefined') this._startDate = new Date(); this._startDate.setFullYear(val < 100 ? val + 2000 : val); this._saveStartDate(); }
1092
+ public get startDate(): Date { return typeof this._startDate === 'undefined' ? this._startDate = new Date() : this._startDate; }
1046
1093
  public set startDate(val: Date) { this._startDate = val; }
1047
1094
  public get scheduleType(): number | any { return this.data.scheduleType; }
1048
1095
  public set scheduleType(val: number | any) { this.setDataVal('scheduleType', sys.board.valueMaps.scheduleTypes.encode(val)); }
@@ -1100,6 +1147,13 @@ export class CircuitCollection extends EqItemCollection<Circuit> {
1100
1147
  }
1101
1148
  export class Circuit extends EqItem implements ICircuit {
1102
1149
  public dataName = 'circuitConfig';
1150
+ public initData() {
1151
+ if (typeof this.data.freeze === 'undefined') this.data.freeze = false;
1152
+ if (typeof this.data.type === 'undefined') this.data.type = 0;
1153
+ if (typeof this.data.isActive === 'undefined') this.data.isActive = false;
1154
+ if (typeof this.data.eggTimer === 'undefined') this.data.eggTimer = 720;
1155
+ if (typeof this.data.showInFeatures === 'undefined') this.data.showInFeatures = true;
1156
+ }
1103
1157
  public get id(): number { return this.data.id; }
1104
1158
  public set id(val: number) { this.setDataVal('id', val); }
1105
1159
  public get name(): string { return this.data.name; }
@@ -1191,7 +1245,7 @@ export interface ICircuit {
1191
1245
  //showInCircuits?: boolean;
1192
1246
  showInFeatures?: boolean;
1193
1247
  macro?: boolean;
1194
- getLightThemes?: () => {};
1248
+ getLightThemes?: (type?: number) => {};
1195
1249
  get(copy?: boolean);
1196
1250
  master: number;
1197
1251
  }
@@ -1210,6 +1264,9 @@ export class PumpCollection extends EqItemCollection<Pump> {
1210
1264
  }
1211
1265
  export class Pump extends EqItem {
1212
1266
  public dataName = 'pumpConfig';
1267
+ public initData() {
1268
+ if (typeof this.data.isVirtual !== 'undefined') delete this.data.isVirtual;
1269
+ }
1213
1270
  public get id(): number { return this.data.id; }
1214
1271
  public set id(val: number) { this.setDataVal('id', val); }
1215
1272
  public get address(): number { return this.data.address || this.data.id + 95; }
@@ -1256,8 +1313,10 @@ export class Pump extends EqItem {
1256
1313
  public set vacuumTime(val: number) { this.setDataVal('vacuumTime', val); }
1257
1314
  public get backgroundCircuit() { return this.data.backgroundCircuit; }
1258
1315
  public set backgroundCircuit(val: number) { this.setDataVal('backgroundCircuit', val); }
1259
- public get isVirtual() { return this.data.isVirtual; }
1260
- public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1316
+ public get filterSize() { return this.data.filterSize; }
1317
+ public set filterSize(val: number) { this.setDataVal('filterSize', val); }
1318
+ // public get isVirtual() { return this.data.isVirtual; }
1319
+ // public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1261
1320
  public get connectionId(): string { return this.data.connectionId; }
1262
1321
  public set connectionId(val: string) { this.setDataVal('connectionId', val); }
1263
1322
  public get deviceBinding(): string { return this.data.deviceBinding; }
@@ -1385,8 +1444,12 @@ export class Chlorinator extends EqItem {
1385
1444
  //public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1386
1445
  public get disabled(): boolean { return this.data.disabled; }
1387
1446
  public set disabled(val: boolean) { this.setDataVal('disabled', val); }
1447
+ public get isDosing(): boolean { return this.data.isDosing; }
1448
+ public set isDosing(val: boolean) { this.setDataVal('isDosing', val); }
1388
1449
  public get ignoreSaltReading() { return this.data.ignoreSaltReading; }
1389
1450
  public set ignoreSaltReading(val: boolean) { this.setDataVal('ignoreSaltReading', val); }
1451
+ public get model() { return this.data.model; }
1452
+ public set model(val: number | any) { this.setDataVal('model', sys.board.valueMaps.chlorinatorModel.encode(val)); }
1390
1453
  }
1391
1454
  export class ValveCollection extends EqItemCollection<Valve> {
1392
1455
  constructor(data: any, name?: string) { super(data, name || "valves"); }
@@ -1409,8 +1472,8 @@ export class Valve extends EqItem {
1409
1472
  public set isIntake(val: boolean) { this.setDataVal('isIntake', val); }
1410
1473
  public get isReturn(): boolean { return utils.makeBool(this.data.isReturn); }
1411
1474
  public set isReturn(val: boolean) { this.setDataVal('isReturn', val); }
1412
- public get isVirtual(): boolean { return utils.makeBool(this.data.isVirtual); }
1413
- public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1475
+ // public get isVirtual(): boolean { return utils.makeBool(this.data.isVirtual); }
1476
+ // public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1414
1477
  public get pinId(): number { return this.data.pinId || 0; }
1415
1478
  public set pinId(val: number) { this.setDataVal('pinId', val); }
1416
1479
  public get isActive(): boolean { return this.data.isActive; }
@@ -1457,8 +1520,6 @@ export class Heater extends EqItem {
1457
1520
  public set efficiencyMode(val: number) { this.setDataVal('efficiencyMode', val); }
1458
1521
  public get isActive(): boolean { return this.data.isActive; }
1459
1522
  public set isActive(val: boolean) { this.setDataVal('isActive', val); }
1460
- public get isVirtual(): boolean { return this.data.isVirtual; }
1461
- public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1462
1523
  public get coolingEnabled(): boolean { return this.data.coolingEnabled; }
1463
1524
  public set coolingEnabled(val: boolean) { this.setDataVal('coolingEnabled', val); }
1464
1525
  public get heatingEnabled(): boolean { return this.data.heatingEnabled; }
@@ -1495,6 +1556,10 @@ export class Cover extends EqItem {
1495
1556
  public set normallyOn(val: boolean) { this.setDataVal('normallyOn', val); }
1496
1557
  public get circuits(): number[] { return this.data.circuits; }
1497
1558
  public set circuits(val: number[]) { this.setDataVal('circuits', val); }
1559
+ public get chlorActive(): boolean { return this.data.chlorActive; }
1560
+ public set chlorActive(val: boolean) { this.setDataVal('chlorActive', val); }
1561
+ public get chlorOutput(): boolean { return this.data.chlorOutput; }
1562
+ public set chlorOutput(val: boolean) { this.setDataVal('chlorOutput', val); }
1498
1563
  }
1499
1564
  export interface ICircuitGroup {
1500
1565
  id: number;
@@ -1816,7 +1881,7 @@ export class ChemController extends EqItem {
1816
1881
  //var chemController = {
1817
1882
  // id: 'number', // Id of the controller
1818
1883
  // name: 'string', // Name assigned to the controller
1819
- // type: 'valueMap', // intellichem, homegrown, rem -- There is an unknown but that should probably go away.
1884
+ // type: 'valueMap', // intellichem, rem -- There is an unknown but that should probably go away.
1820
1885
  // body: 'valueMap', // Body assigned to the chem controller.
1821
1886
  // address: 'number', // Address for IntelliChem controller only.
1822
1887
  // isActive: 'booean',
@@ -1905,8 +1970,8 @@ export class ChemController extends EqItem {
1905
1970
  public set address(val: number) { this.setDataVal('address', val); }
1906
1971
  public get isActive(): boolean { return this.data.isActive; }
1907
1972
  public set isActive(val: boolean) { this.setDataVal('isActive', val); }
1908
- public get isVirtual(): boolean { return this.data.isVirtual; }
1909
- public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1973
+ // public get isVirtual(): boolean { return this.data.isVirtual; }
1974
+ // public set isVirtual(val: boolean) { this.setDataVal('isVirtual', val); }
1910
1975
  public get calciumHardness(): number { return this.data.calciumHardness; }
1911
1976
  public set calciumHardness(val: number) { this.setDataVal('calciumHardness', val); }
1912
1977
  public get cyanuricAcid(): number { return this.data.cyanuricAcid; }
@@ -1995,6 +2060,7 @@ export class Chemical extends ChildEqItem {
1995
2060
  public set startDelay(val: number) { this.setDataVal('startDelay', val); }
1996
2061
  public get pump(): ChemicalPump { return new ChemicalPump(this.data, 'pump', this); }
1997
2062
  public get tank(): ChemicalTank { return new ChemicalTank(this.data, 'tank', this); }
2063
+ public get chlor(): ChemicalChlor { return new ChemicalChlor(this.data, 'chlor', this); }
1998
2064
  public get setpoint(): number { return this.data.setpoint; }
1999
2065
  public set setpoint(val: number) { this.setDataVal('setpoint', val); }
2000
2066
  public get tolerance(): AlarmSetting { return new AlarmSetting(this.data, 'tolerance', this); }
@@ -2038,6 +2104,7 @@ export class ChemicalORP extends Chemical {
2038
2104
  this.data.chemType = 'orp';
2039
2105
  if (typeof this.data.setpoint === 'undefined') this.data.setpoint = 600;
2040
2106
  if (typeof this.data.useChlorinator === 'undefined') this.data.useChlorinator = false;
2107
+ if (typeof this.data.chlorDosingMethod === 'undefined') this.data.chlorDosingMethod = 0;
2041
2108
  if (typeof this.data.probe === 'undefined') this.data.probe = {};
2042
2109
  if (typeof this.data.tolerance === 'undefined') this.data.tolerance = { low: 650, high: 800, enabled: true };
2043
2110
  if (typeof this.data.phLockout === 'undefined') this.data.phLockout = 7.8;
@@ -2048,6 +2115,8 @@ export class ChemicalORP extends Chemical {
2048
2115
  public get phLockout(): number { return this.data.phLockout; }
2049
2116
  public set phLockout(val: number) { this.setDataVal('phLockout', val); }
2050
2117
  public get probe(): ChemicalORPProbe { return new ChemicalORPProbe(this.data, 'probe', this); }
2118
+ public get chlorDosingMethod(): number | any { return this.data.chlorDosingMethod; }
2119
+ public set chlorDosingMethod(val: number | any) { this.setDataVal('chlorDosingMethod', sys.board.valueMaps.chemChlorDosingMethods.encode(val)); }
2051
2120
  public getExtended() {
2052
2121
  let chem = super.getExtended();
2053
2122
  chem.probe = this.probe.getExtended();
@@ -2064,6 +2133,11 @@ export class Filter extends EqItem {
2064
2133
  if (typeof this.data.filterType === 'undefined') this.data.filterType = 3;
2065
2134
  if (typeof this.data.capacity === 'undefined') this.data.capacity = 0;
2066
2135
  if (typeof this.data.capacityUnits === 'undefined') this.data.capacityUnits = 0;
2136
+ if (typeof this.data.cleanPressure === 'undefined') this.data.cleanPressure = 0;
2137
+ if (typeof this.data.dirtyPressure === 'undefined') this.data.dirtyPressure = 0;
2138
+ if (typeof this.data.pressureUnits === 'undefined') this.data.pressureUnits = 0;
2139
+ // Start this out at pool and let the user switch it around if necessary.
2140
+ if (typeof this.data.pressureCircuitId === 'undefined') this.data.pressureCircuitId = 6;
2067
2141
  }
2068
2142
  public get id(): number { return this.data.id; }
2069
2143
  public set id(val: number) { this.setDataVal('id', val); }
@@ -2079,10 +2153,22 @@ export class Filter extends EqItem {
2079
2153
  public set isActive(val: boolean) { this.setDataVal('isActive', val); }
2080
2154
  public get name(): string { return this.data.name; }
2081
2155
  public set name(val: string) { this.setDataVal('name', val); }
2156
+
2157
+ public get showPressure(): boolean { return this.data.showPressure; }
2158
+ public set showPressure(val: boolean) { this.setDataVal('showPressure', val); }
2082
2159
  public get lastCleanDate(): Timestamp { return this.data.lastCleanDate; }
2083
2160
  public set lastCleanDate(val: Timestamp) { this.setDataVal('lastCleanDate', val); }
2084
2161
  public get needsCleaning(): number { return this.data.needsCleaning; }
2085
2162
  public set needsCleaning(val: number) { this.setDataVal('needsCleaning', val); }
2163
+ public get pressureUnits(): number { return this.data.pressureUnits; }
2164
+ public set pressureUnits(val: number) { this.setDataVal('pressureUnits', val); }
2165
+ public get pressureCircuitId(): number { return this.data.pressureCircuitId; }
2166
+ public set pressureCircuitId(val: number) { this.setDataVal('pressureCircuitId', val); }
2167
+ public get cleanPressure(): number { return this.data.cleanPressure; }
2168
+ public set cleanPressure(val: number) { this.setDataVal('cleanPressure', val); }
2169
+ public get dirtyPressure(): number { return this.data.dirtyPressure; }
2170
+ public set dirtyPressure(val: number) { this.setDataVal('dirtyPressure', val); }
2171
+
2086
2172
  public get connectionId(): string { return this.data.connectionId; }
2087
2173
  public set connectionId(val: string) { this.setDataVal('connectionId', val); }
2088
2174
  public get deviceBinding(): string { return this.data.deviceBinding; }
@@ -2164,6 +2250,41 @@ export class ChemicalTank extends ChildEqItem {
2164
2250
  return tank;
2165
2251
  }
2166
2252
  }
2253
+ export class ChemicalChlor extends ChildEqItem {
2254
+ // This whole class is a reference to the first chlorinator.
2255
+ // This may not follow a best practice
2256
+ // and certainly won't work for multiple chlors
2257
+ public dataName = 'chemicalChlorConfig';
2258
+ public initData() {
2259
+ super.initData();
2260
+ }
2261
+ public get enabled(): boolean {
2262
+ let chlor = sys.chlorinators.getItemById(1);
2263
+ return chlor.isActive;
2264
+ }
2265
+ public get type(): number | any {
2266
+ let chlor = sys.chlorinators.getItemById(1);
2267
+ return chlor.type;
2268
+ }
2269
+ public get model(): number {
2270
+ let chlor = sys.chlorinators.getItemById(1);
2271
+ return typeof chlor.model !== 'undefined' ? chlor.model : 0;
2272
+ }
2273
+ public get ratedLbs(): number {
2274
+ let model = sys.board.valueMaps.chlorinatorModel.get(this.model);
2275
+ return typeof model.chlorinePerSec !== 'undefined' ? model.chlorinePerSec : 0;
2276
+ }
2277
+ public set ratedLbs(val: number) { this.setDataVal('ratedLbs', val); }
2278
+ public get superChlor() {
2279
+ let chlor = sys.chlorinators.getItemById(1);
2280
+ return typeof chlor.superChlor !== 'undefined' ? chlor.superChlor : false;
2281
+ }
2282
+ public getExtended() {
2283
+ let chlor = this.get(true);
2284
+ chlor.model = sys.board.valueMaps.chlorinatorModel.transform(this.model);
2285
+ return chlor;
2286
+ }
2287
+ }
2167
2288
  export class AlarmSetting extends ChildEqItem {
2168
2289
  public dataName = 'AlarmSettingConfig';
2169
2290
  public initData() {
@@ -96,15 +96,28 @@ export class InvalidEquipmentDataError extends EquipmentError {
96
96
  }
97
97
  public eqData;
98
98
  }
99
+ export class ServiceProcessError extends ApiError {
100
+ constructor(message: string, serviceName: string, process?: string) {
101
+ super(message, 290, 400);
102
+ this.name = 'ServiceProcessError';
103
+ this.service = serviceName;
104
+ this.process = process;
105
+ }
106
+ public process: string;
107
+ public service: string;
108
+ }
99
109
  export class ServiceParameterError extends ApiError {
100
110
  constructor(message: string, serviceName: string, paramName: string, value) {
101
111
  super(message, 280, 400);
102
112
  this.name = 'InvalidServiceParameter';
113
+
103
114
  this.value = value;
104
- this.parameter = value;
115
+ this.parameter = paramName;
116
+ this.service = serviceName;
105
117
  }
106
118
  public value;
107
119
  public parameter: string;
120
+ public service: string;
108
121
  }
109
122
  export class InvalidOperationError extends ApiError {
110
123
  constructor(message: string, operation: string) {
@@ -114,6 +127,14 @@ export class InvalidOperationError extends ApiError {
114
127
  }
115
128
  public operation: string;
116
129
  }
130
+ export class EquipmentTimeoutError extends ApiError {
131
+ constructor(message: string, operation: string) {
132
+ super(message, 100, 400);
133
+ this.name = 'TimeoutError';
134
+ this.operation = operation;
135
+ }
136
+ public operation: string;
137
+ }
117
138
  export class ParameterOutOfRangeError extends InvalidOperationError {
118
139
  constructor(message: string, operation: string, parameter: string, value) {
119
140
  super(message, operation);