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.
Files changed (47) hide show
  1. package/.docker/Dockerfile.armv6 +29 -0
  2. package/.docker/Dockerfile.armv7 +29 -0
  3. package/.docker/Dockerfile.linux +62 -0
  4. package/.docker/Dockerfile.windows +43 -0
  5. package/.docker/docker-compose.yml +47 -0
  6. package/.docker/ecosystem.config.js +35 -0
  7. package/.github/workflows/docker-publish-njsPC-linux.yml +81 -0
  8. package/.github/workflows/docker-publish-njsPC-windows.yml +41 -0
  9. package/Dockerfile +4 -3
  10. package/README.md +4 -1
  11. package/config/Config.ts +1 -1
  12. package/controller/Constants.ts +164 -67
  13. package/controller/Equipment.ts +78 -18
  14. package/controller/Lockouts.ts +15 -0
  15. package/controller/State.ts +280 -7
  16. package/controller/boards/EasyTouchBoard.ts +225 -101
  17. package/controller/boards/IntelliCenterBoard.ts +67 -18
  18. package/controller/boards/IntelliTouchBoard.ts +2 -4
  19. package/controller/boards/NixieBoard.ts +84 -27
  20. package/controller/boards/SunTouchBoard.ts +8 -2
  21. package/controller/boards/SystemBoard.ts +3259 -3242
  22. package/controller/comms/ScreenLogic.ts +47 -44
  23. package/controller/comms/messages/Messages.ts +4 -4
  24. package/controller/comms/messages/config/ChlorinatorMessage.ts +10 -3
  25. package/controller/comms/messages/config/ExternalMessage.ts +4 -1
  26. package/controller/comms/messages/config/PumpMessage.ts +8 -7
  27. package/controller/comms/messages/config/RemoteMessage.ts +48 -43
  28. package/controller/comms/messages/status/ChlorinatorStateMessage.ts +8 -2
  29. package/controller/comms/messages/status/EquipmentStateMessage.ts +9 -4
  30. package/controller/nixie/NixieEquipment.ts +1 -1
  31. package/controller/nixie/bodies/Body.ts +1 -1
  32. package/controller/nixie/chemistry/ChemController.ts +37 -28
  33. package/controller/nixie/circuits/Circuit.ts +36 -0
  34. package/controller/nixie/heaters/Heater.ts +24 -5
  35. package/controller/nixie/pumps/Pump.ts +155 -97
  36. package/controller/nixie/schedules/Schedule.ts +207 -126
  37. package/defaultConfig.json +3 -3
  38. package/logger/DataLogger.ts +7 -7
  39. package/package.json +2 -2
  40. package/sendSocket.js +32 -0
  41. package/web/Server.ts +17 -11
  42. package/web/bindings/homeassistant.json +2 -2
  43. package/web/interfaces/mqttInterface.ts +18 -18
  44. package/web/services/config/Config.ts +34 -1
  45. package/web/services/state/State.ts +10 -3
  46. package/web/services/state/StateSocket.ts +7 -3
  47. package/web/services/utilities/Utilities.ts +3 -3
@@ -4,11 +4,12 @@ import { logger } from '../../../logger/Logger';
4
4
 
5
5
  import { NixieEquipment, NixieChildEquipment, NixieEquipmentCollection, INixieControlPanel } from "../NixieEquipment";
6
6
  import { CircuitGroup, CircuitGroupCircuit, ICircuitGroup, ICircuitGroupCircuit, LightGroup, LightGroupCircuit, Schedule, ScheduleCollection, sys } from "../../../controller/Equipment";
7
- import { CircuitGroupState, ICircuitGroupState, ScheduleState, state, } from "../../State";
7
+ import { ICircuitState, CircuitGroupState, ICircuitGroupState, ScheduleState, ScheduleTime, state, } from "../../State";
8
8
  import { setTimeout, clearTimeout } from 'timers';
9
9
  import { NixieControlPanel } from '../Nixie';
10
10
  import { webApp, InterfaceServerResponse } from "../../../web/Server";
11
11
  import { delayMgr } from '../../../controller/Lockouts';
12
+ import { time } from 'console';
12
13
 
13
14
 
14
15
  export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSchedule> {
@@ -23,9 +24,8 @@ export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSched
23
24
  await c.setScheduleAsync(data);
24
25
  logger.info(`A Schedule was not found for id #${schedule.id} creating Schedule`);
25
26
  }
26
- else {
27
+ else
27
28
  await c.setScheduleAsync(data);
28
- }
29
29
  }
30
30
  catch (err) { logger.error(`setScheduleAsync: ${err.message}`); return Promise.reject(err); }
31
31
  }
@@ -46,24 +46,168 @@ export class NixieScheduleCollection extends NixieEquipmentCollection<NixieSched
46
46
  }
47
47
  public async triggerSchedules() {
48
48
  try {
49
- let ctx = new NixieScheduleContext();
50
- for (let i = 0; i < this.length; i++) {
51
- (this[i] as NixieSchedule).triggerScheduleAsync(ctx);
52
- }
53
- // Set the heat modes for the bodies.
54
- for (let i = 0; i < ctx.heatModes.length; i++) {
55
- let mode = ctx.heatModes[i];
56
- let body = sys.bodies.getItemById(mode.id);
57
- await sys.board.bodies.setHeatModeAsync(sys.bodies.getItemById(mode.id), mode.heatMode);
58
- if (typeof mode.heatSetpoint !== 'undefined') await sys.board.bodies.setHeatSetpointAsync(body, mode.heatSetpoint);
59
- if (typeof mode.coolSetpoint !== 'undefined') await sys.board.bodies.setCoolSetpointAsync(body, mode.coolSetpoint);
49
+ // This is a listing of all the active schedules that are either currently on or should be on.
50
+ let sscheds: ScheduleState[] = state.schedules.getActiveSchedules();
51
+ // Go through all the schedules and hash them by circuit id.
52
+ let circuits: { circuitId: number, cstate: ICircuitState, hasNixie: boolean, sscheds: ScheduleState[] }[] = []
53
+ for (let i = 0; i < sscheds.length; i++) {
54
+ // We only care about schedules that are currently running or should be running.
55
+ if (!sscheds[i].isOn && !sscheds[i].scheduleTime.shouldBeOn) continue;
56
+ let circ = circuits.find(elem => elem.circuitId === sscheds[i].circuit);
57
+ let sched = sys.schedules.getItemById(sscheds[i].id)
58
+ if (typeof circ === 'undefined') circuits.push({
59
+ circuitId: sscheds[i].circuit,
60
+ cstate: state.circuits.getInterfaceById(sscheds[i].circuit), hasNixie: sched.master !== 0, sscheds: [sscheds[i]]
61
+ });
62
+ else {
63
+ if (sched.master !== 0) circ.hasNixie = true;
64
+ circ.sscheds.push(sscheds[i]);
65
+ }
60
66
  }
61
- // Alright now that we are done with that we need to set all the circuit states that need changing.
62
- for (let i = 0; i < ctx.circuits.length; i++) {
63
- let circuit = ctx.circuits[i];
64
- await sys.board.circuits.setCircuitStateAsync(circuit.id, circuit.isOn);
67
+ // Sort this so that body circuits are evaluated first. This is required when there are schedules for things like cleaner
68
+ // or delay circuits. If we do not do this then a schedule that requires the pool to be on for instance will never
69
+ // get triggered.
70
+ circuits.sort((x, y) => y.circuitId === 6 || y.circuitId === 1 ? 1 : y.circuitId - x.circuitId);
71
+
72
+
73
+ /*
74
+ RSG 5-8-22
75
+ Manual OP needs to play a role here.From the IC manual:
76
+ # Manual OP General
77
+
78
+ From the manual:
79
+ Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program,
80
+ the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled
81
+ program will resume. This feature will turn off any scheduled program to allow manual pump override.The Default setting is OFF.
82
+
83
+ ## When on
84
+ 1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has
85
+ expired.When that occurs the schedule should be reset to run at the designated time.If the user resets the schedule by turning the
86
+ circuit back on again ~~then the schedule will resume and turn off at the specified time~~then the schedule will be ignored and
87
+ the circuit will run until the egg timer expires or the circuit / feature is manually turned off.This setting WILL affect
88
+ other schedules that may impact this circuit.
89
+
90
+ ## When off
91
+ 1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time
92
+ off has expired.When that occurs the schedule should be reset to run at the designated time.If the user resets the schedule by
93
+ turning the circuit back on again then the schedule will resume and turn off at the specified time.
94
+ */
95
+
96
+ let mOP = sys.general.options.manualPriority;
97
+
98
+ // Now lets evaluate the schedules by virtue of their state related to the circuits.
99
+ for (let i = 0; i < circuits.length; i++) {
100
+ let c = circuits[i];
101
+ if (!c.hasNixie) continue; // If this has nothing to do with Nixie move on.
102
+ let shouldBeOn = typeof c.sscheds.find(elem => elem.scheduleTime.shouldBeOn === true) !== 'undefined';
103
+ // 1. If the feature is currently running and the schedule is not on then it will set the priority for the circuit to [scheduled].
104
+ // 2. If the feature is currently running but there are overlapping schedules then this will catch any schedules that need to be turned off.
105
+ if (c.cstate.isOn && shouldBeOn) {
106
+ c.cstate.priority = shouldBeOn ? 'scheduled' : 'manual';
107
+ for (let j = 0; j < c.sscheds.length; j++) {
108
+ let ssched = c.sscheds[j];
109
+ ssched.triggered = ssched.scheduleTime.shouldBeOn;
110
+ if (mOP && ssched.manualPriorityActive) {
111
+ ssched.isOn = false;
112
+ // Not sure what setting a delay for this does but ok.
113
+ if (!c.cstate.manualPriorityActive) delayMgr.setManualPriorityDelay(c.cstate);
114
+ }
115
+ else ssched.isOn = ssched.scheduleTime.shouldBeOn && !ssched.manualPriorityActive;
116
+ }
117
+ }
118
+ // 3. If the schedule should be on and it isn't and the schedule has not been triggered then we need to
119
+ // turn the schedule and circuit on.
120
+ else if (!c.cstate.isOn && shouldBeOn) {
121
+ // The circuit is not on but it should be. Check to ensure all schedules have been triggered.
122
+ let untriggered = false;
123
+ // If this schedule has been triggered then mOP comes into play if manualPriority has been set in the config.
124
+ for (let j = 0; j < c.sscheds.length; j++) {
125
+ let ssched = c.sscheds[j];
126
+ // If this schedule is turned back on then the egg timer will come into play. This is all that is required
127
+ // for the mOP function. The setEndDate for the circuit makes the determination as to when off will occur.
128
+ if (mOP && ssched.scheduleTime.shouldBeOn && ssched.triggered) {
129
+ ssched.manualPriorityActive = true;
130
+ }
131
+ // The reason we check to see if anything has not been triggered is so we do not have to perform the circuit changes
132
+ // if the schedule has already been triggered.
133
+ else if (!ssched.triggered) untriggered = true;
134
+ }
135
+ let heatSource = { heatMode: 'nochange', heatSetpoint: undefined, coolSetpoint: undefined };
136
+ // Check to see if any of the schedules have not been triggered. If they haven't then trigger them and turn the circuit on.
137
+ if (untriggered) {
138
+ // Get the heat modes and temps for all the schedules that have not been triggered.
139
+ let body = sys.bodies.find(elem => elem.circuit === c.circuitId);
140
+ if (typeof body !== 'undefined') {
141
+ // If this is a body circuit then we need to set the heat mode and the temperature but only do this once. If
142
+ // the user changes it later then that is on them.
143
+ for (let j = 0; j < c.sscheds.length; j++) {
144
+ if (sscheds[j].triggered) continue;
145
+ let ssched = sscheds[j];
146
+ let hs = sys.board.valueMaps.heatSources.transform(c.sscheds[i].heatSource);
147
+ switch (hs.name) {
148
+ case 'nochange':
149
+ case 'dontchange':
150
+ break;
151
+ case 'off':
152
+ // If the heatsource setting is off only change it if it is currently don't change.
153
+ if (heatSource.heatMode === 'nochange') heatSource.heatMode = hs.name;
154
+ break;
155
+ default:
156
+ switch (heatSource.heatMode) {
157
+ case 'off':
158
+ case 'nochange':
159
+ case 'dontchange':
160
+ heatSource.heatMode = hs.name;
161
+ heatSource.heatSetpoint = ssched.heatSetpoint;
162
+ heatSource.coolSetpoint = hs.hasCoolSetpoint ? ssched.coolSetpoint : undefined;
163
+ break;
164
+ }
165
+ break;
166
+ }
167
+ // Ok if we need to change the setpoint or the heatmode then lets do it.
168
+ if (heatSource.heatMode !== 'nochange') {
169
+ await sys.board.bodies.setHeatModeAsync(body, sys.board.valueMaps.heatSources.getValue(heatSource.heatMode));
170
+ if (typeof heatSource.heatSetpoint !== 'undefined') await sys.board.bodies.setHeatSetpointAsync(body, heatSource.heatSetpoint);
171
+ if (typeof heatSource.coolSetpoint !== 'undefined') await sys.board.bodies.setCoolSetpointAsync(body, heatSource.coolSetpoint);
172
+ }
173
+ }
174
+ }
175
+ // By now we have everything we need to turn on the circuit.
176
+ for (let j = 0; j < c.sscheds.length; j++) {
177
+ let ssched = c.sscheds[j];
178
+ if (!ssched.triggered && ssched.scheduleTime.shouldBeOn) {
179
+ if (!c.cstate.isOn) {
180
+ await sys.board.circuits.setCircuitStateAsync(c.circuitId, true);
181
+ }
182
+ let ssched = c.sscheds[j];
183
+ c.cstate.priority = 'scheduled';
184
+ ssched.triggered = ssched.isOn = ssched.scheduleTime.shouldBeOn;
185
+ ssched.manualPriorityActive = false;
186
+ }
187
+ }
188
+ }
189
+ }
190
+ else if (c.cstate.isOn && !shouldBeOn) {
191
+ // Time to turn off the schedule.
192
+ for (let j = 0; j < c.sscheds.length; j++) {
193
+ let ssched = c.sscheds[j];
194
+ // Only turn off the schedule if it is not actively mOP.
195
+ if (c.cstate.isOn && !ssched.manualPriorityActive) await sys.board.circuits.setCircuitStateAsync(c.circuitId, false);
196
+ c.cstate.priority = 'manual';
197
+ // The schedule has expired we need to clear all the info for it.
198
+ ssched.manualPriorityActive = ssched.triggered = ssched.isOn = c.sscheds[j].scheduleTime.shouldBeOn;
199
+ }
200
+ }
201
+ else if (!c.cstate.isOn && !shouldBeOn) {
202
+ // Everything is off so lets clear it all.
203
+ for (let j = 0; j < c.sscheds.length; j++) {
204
+ let ssched = c.sscheds[j];
205
+ ssched.isOn = ssched.manualPriorityActive = ssched.triggered = false;
206
+ }
207
+ }
208
+ state.emitEquipmentChanges();
65
209
  }
66
- } catch (err) { logger.error(`Error triggering schedules: ${err}`); }
210
+ } catch (err) { logger.error(`Error triggering nixie schedules: ${err.message}`); }
67
211
  }
68
212
  }
69
213
  export class NixieSchedule extends NixieEquipment {
@@ -77,29 +221,44 @@ export class NixieSchedule extends NixieEquipment {
77
221
  super(ncp);
78
222
  this.schedule = schedule;
79
223
  this.pollEquipmentAsync();
224
+ let ssched = state.schedules.getItemById(schedule.id, true);
225
+ ssched.circuit = schedule.circuit;
226
+ ssched.scheduleDays = schedule.scheduleDays;
227
+ ssched.scheduleType = schedule.scheduleType;
228
+ ssched.changeHeatSetpoint = schedule.changeHeatSetpoint;
229
+ ssched.heatSetpoint = schedule.heatSetpoint;
230
+ ssched.coolSetpoint = schedule.coolSetpoint;
231
+ ssched.heatSource = schedule.heatSource;
232
+ ssched.startTime = schedule.startTime;
233
+ ssched.endTime = schedule.endTime;
234
+ ssched.startTimeType = schedule.startTimeType;
235
+ ssched.endTimeType = schedule.endTimeType;
236
+ ssched.startDate = schedule.startDate;
80
237
  }
81
238
  public get id(): number { return typeof this.schedule !== 'undefined' ? this.schedule.id : -1; }
82
239
  public async setScheduleAsync(data: any) {
83
240
  try {
84
241
  let schedule = this.schedule;
242
+ let sschedule = state.schedules.getItemById(schedule.id);
243
+ sschedule.scheduleTime.calculated = false;
85
244
  }
86
245
  catch (err) { logger.error(`Nixie setScheduleAsync: ${err.message}`); return Promise.reject(err); }
87
246
  }
88
- public async pollEquipmentAsync() {
89
- let self = this;
90
- try {
91
- if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
92
- this._pollTimer = null;
93
- let success = false;
94
- }
95
- catch (err) { logger.error(`Nixie Error polling Schedule - ${err}`); }
96
- finally { this._pollTimer = setTimeout(async () => await self.pollEquipmentAsync(), this.pollingInterval || 10000); }
97
- }
247
+ public async pollEquipmentAsync() {}
98
248
  public async validateSetupAsync(Schedule: Schedule, temp: ScheduleState) {
99
249
  try {
100
250
  // The validation will be different if the Schedule is on or not. So lets get that information.
101
251
  } catch (err) { logger.error(`Nixie Error checking Schedule Hardware ${this.schedule.id}: ${err.message}`); return Promise.reject(err); }
102
252
  }
253
+ public async closeAsync() {
254
+ try {
255
+ if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
256
+ this._pollTimer = null;
257
+ }
258
+ catch (err) { logger.error(`Nixie Schedule closeAsync: ${err.message}`); return Promise.reject(err); }
259
+ }
260
+ public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
261
+ /*
103
262
  public async triggerScheduleAsync(ctx: NixieScheduleContext) {
104
263
  try {
105
264
  if (this.schedule.isActive === false) return;
@@ -109,22 +268,20 @@ export class NixieSchedule extends NixieEquipment {
109
268
  // Schedules can be overridden so it is important that when the
110
269
  // state is changed for the schedule if it is currently active that
111
270
  // Nixie does not override the state of the scheduled circuit or feature.
112
- /*
113
- RSG 5-8-22
114
- Manual OP needs to play a role here. From the IC manual:
115
- # Manual OP General
271
+ //RSG 5-8-22
272
+ //Manual OP needs to play a role here. From the IC manual:
273
+ //# Manual OP General
116
274
 
117
- From the manual:
118
- Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program, the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled program will resume. This feature will turn off any scheduled program to allow manual pump override. The Default setting is OFF.
275
+ //From the manual:
276
+ //Manual OP Priority: ON: This feature allows for a circuit to be manually switched OFF and switched ON within a scheduled program, the circuit will continue to run for a maximum of 12 hours or whatever that circuit Egg Timer is set to, after which the scheduled program will resume. This feature will turn off any scheduled program to allow manual pump override. The Default setting is OFF.
119
277
 
120
- ## When on
121
- 1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again ~~then the schedule will resume and turn off at the specified time~~ then the schedule will be ignored and the circuit will run until the egg timer expires or the circuit/feature is manually turned off. This setting WILL affect other schedules that may impact this circuit.
278
+ //## When on
279
+ //1. If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again ~~then the schedule will resume and turn off at the specified time~~ then the schedule will be ignored and the circuit will run until the egg timer expires or the circuit/feature is manually turned off. This setting WILL affect other schedules that may impact this circuit.
122
280
 
123
- ## When off
124
- 1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again then the schedule will resume and turn off at the specified time.
281
+ //## When off
282
+ //1. "Normal" = If a schedule should be on and the user turns the schedule off then the schedule expires until such time as the time off has expired. When that occurs the schedule should be reset to run at the designated time. If the user resets the schedule by turning the circuit back on again then the schedule will resume and turn off at the specified time.
125
283
 
126
- Interestingly, there also seems to be a schedule level setting for this. We will ignore that for now as the logic could get much more complicated.
127
- */
284
+ //Interestingly, there also seems to be a schedule level setting for this. We will ignore that for now as the logic could get much more complicated.
128
285
  // 1. If the feature happens to be running and the schedule is not yet turned on then
129
286
  // it should not override what the user says.
130
287
  // 2. If a schedule is running and the state of the circuit changes to off then the new state should suspend the schedule
@@ -154,8 +311,7 @@ export class NixieSchedule extends NixieEquipment {
154
311
  ssched.isOn = false;
155
312
  return;
156
313
  }
157
- let shouldBeOn = this.shouldBeOn(); // This should also set the validity for the schedule if there are errors.
158
-
314
+ let shouldBeOn = ssched.shouldBeOn(); // This should also set the validity for the schedule if there are errors.
159
315
  let manualPriorityActive: boolean = shouldBeOn ? sys.board.schedules.manualPriorityActive(ssched) : false;
160
316
  //console.log(`Processing schedule ${this.schedule.id} - ${circuit.name} : ShouldBeOn: ${shouldBeOn} ManualPriorityActive: ${manualPriorityActive} Running: ${this.running} Suspended: ${this.suspended} Resumed: ${this.resumed}`);
161
317
 
@@ -199,6 +355,11 @@ export class NixieSchedule extends NixieEquipment {
199
355
  this.running = true;
200
356
  }
201
357
  else if (shouldBeOn && this.running) {
358
+ // Check to see if circuit is on, if not turn it on.
359
+ // RKS: 07-09-23 - This was in PR#819 buut this needs further review since the circuit states are not to be set here. This would
360
+ // trash delays and manualPriority.
361
+ // if(!cstate.isOn) ctx.setCircuit(circuit.id, true);
362
+
202
363
  // With mOP, we need to see if the schedule will come back into play and also set the circut
203
364
  if (this.suspended && cstate.isOn) {
204
365
  if (sys.general.options.manualPriority) {
@@ -208,13 +369,13 @@ export class NixieSchedule extends NixieEquipment {
208
369
  this.resumed = true;
209
370
  }
210
371
  this.suspended = !cstate.isOn;
211
- if (manualPriorityActive){
372
+ if (manualPriorityActive) {
212
373
  ssched.isOn = false;
213
374
  ssched.manualPriorityActive = true;
214
375
  }
215
376
  else {
216
377
  ssched.isOn = cstate.isOn;
217
- ssched.manualPriorityActive = false;
378
+ ssched.manualPriorityActive = false;
218
379
  }
219
380
  }
220
381
  // Our schedule has expired it is time to turn it off, but only if !manualPriorityActive.
@@ -235,86 +396,6 @@ export class NixieSchedule extends NixieEquipment {
235
396
  }
236
397
  ssched.emitEquipmentChange();
237
398
  } catch (err) { logger.error(`Error processing schedule: ${err.message}`); }
238
-
239
399
  }
240
- protected calcTime(dt: Timestamp, type: number, offset: number): Timestamp {
241
- let tt = sys.board.valueMaps.scheduleTimeTypes.transform(type);
242
- switch (tt.name) {
243
- case 'sunrise':
244
- return new Timestamp(state.heliotrope.sunrise);
245
- case 'sunset':
246
- return new Timestamp(state.heliotrope.sunset);
247
- default:
248
- return dt.startOfDay().addMinutes(offset);
249
- }
250
- }
251
- protected shouldBeOn(): boolean {
252
- if (this.schedule.isActive === false) return false;
253
- if (this.schedule.disabled) return false;
254
- // Be careful with toDate since this returns a mutable date object from the state timestamp. startOfDay makes it immutable.
255
- let sod = state.time.startOfDay()
256
- let dow = sod.toDate().getDay();
257
- let type = sys.board.valueMaps.scheduleTypes.transform(this.schedule.scheduleType);
258
- if (type.name === 'runonce') {
259
- // If we are not matching up with the day then we shouldn't be running.
260
- if (sod.fullYear !== this.schedule.startYear || sod.month + 1 !== this.schedule.startMonth || sod.date !== this.schedule.startDay) return false;
261
- }
262
- else {
263
- // Convert the dow to the bit value.
264
- let sd = sys.board.valueMaps.scheduleDays.toArray().find(elem => elem.dow === dow);
265
- let dayVal = sd.bitVal || sd.val; // The bitval allows mask overrides.
266
- // First check to see if today is one of our days.
267
- if ((this.schedule.scheduleDays & dayVal) === 0) return false;
268
- }
269
- // Next normalize our start and end times. Fortunately, the start and end times are normalized here so that
270
- // [0, {name: 'manual', desc: 'Manual }]
271
- // [1, { name: 'sunrise', desc: 'Sunrise' }],
272
- // [2, { name: 'sunset', desc: 'Sunset' }]
273
- let tmStart = this.calcTime(sod, this.schedule.startTimeType, this.schedule.startTime).getTime();
274
- let tmEnd = this.calcTime(sod, this.schedule.endTimeType, this.schedule.endTime).getTime();
275
-
276
- if (isNaN(tmStart)) return false;
277
- if (isNaN(tmEnd)) return false;
278
- // If we are past our window we should be off.
279
- let tm = state.time.getTime();
280
- if (tm >= tmEnd) return false;
281
- if (tm <= tmStart) return false;
282
-
283
- // Let's now check to see
284
-
285
- // If we make it here we should be on.
286
- return true;
287
- }
288
-
289
-
290
- public async closeAsync() {
291
- try {
292
- if (typeof this._pollTimer !== 'undefined' || this._pollTimer) clearTimeout(this._pollTimer);
293
- this._pollTimer = null;
294
- }
295
- catch (err) { logger.error(`Nixie Schedule closeAsync: ${err.message}`); return Promise.reject(err); }
296
- }
297
- public logData(filename: string, data: any) { this.controlPanel.logData(filename, data); }
400
+ */
298
401
  }
299
- class NixieScheduleContext {
300
- constructor() {
301
-
302
- }
303
- public circuits: { id: number, isOn: boolean }[] = [];
304
- public heatModes: { id: number, heatMode: number, heatSetpoint?: number, coolSetpoint?: number }[] = [];
305
- public setCircuit(id: number, isOn: boolean) {
306
- let c = this.circuits.find(elem => elem.id === id);
307
- if (typeof c === 'undefined') this.circuits.push({ id: id, isOn: isOn });
308
- else c.isOn = isOn;
309
- }
310
- public setHeatMode(id: number, heatMode: string, heatSetpoint?: number, coolSetpoint?: number) {
311
- let mode = sys.board.valueMaps.heatModes.transformByName(heatMode);
312
- let hm = this.heatModes.find(elem => elem.id == id);
313
- if (typeof hm === 'undefined') this.heatModes.push({ id: id, heatMode: mode.val, heatSetpoint: heatSetpoint, coolSetpoint: coolSetpoint });
314
- else {
315
- hm.heatMode = mode.val;
316
- if (typeof heatSetpoint !== 'undefined') hm.heatSetpoint = heatSetpoint;
317
- if (typeof coolSetpoint !== 'undefined') hm.coolSetpoint = coolSetpoint;
318
- }
319
- }
320
- }
@@ -164,7 +164,7 @@
164
164
  "username": "",
165
165
  "password": "",
166
166
  "selfSignedCertificate": false,
167
- "rootTopic": "@bind=(state.equipment.model).replace(' ','-').replace('/','').toLowerCase();",
167
+ "rootTopic": "@bind=(state.equipment.model).replace(/ /g,'-').replace('/','').toLowerCase();",
168
168
  "retain": true,
169
169
  "qos": 0,
170
170
  "changesOnly": true
@@ -204,7 +204,7 @@
204
204
  "port": 1883,
205
205
  "username": "",
206
206
  "password": "",
207
- "rootTopic": "@bind=(state.equipment.model).replace(' ','-').replace('/','').toLowerCase();Alt",
207
+ "rootTopic": "@bind=(state.equipment.model).replace(/ /,'-').replace('/','').toLowerCase();Alt",
208
208
  "retain": true,
209
209
  "qos": 0,
210
210
  "changesOnly": true
@@ -230,7 +230,7 @@
230
230
  "vars": {
231
231
  "_note": "hassTopic is the topic that HASS reads for configuration and should not be changed. mqttTopic should match the topic in the MQTT binding (do not use MQTTAlt for HASS).",
232
232
  "hassTopic": "homeassistant",
233
- "mqttTopic": "@bind=(state.equipment.model).replace(' ','-').replace('/','').toLowerCase();"
233
+ "mqttTopic": "@bind=(state.equipment.model).replace(/ /g,'-').replace('/','').toLowerCase();"
234
234
  }
235
235
  },
236
236
  "rem": {
@@ -35,7 +35,7 @@ export class DataLogger {
35
35
  } catch (err) { logger.error(`Skipping invalid dose history entry: ${err.message}`); }
36
36
  }
37
37
  return arr;
38
- } catch (err) { logger.error(err); }
38
+ } catch (err) { logger.error(`Skipping dose history ${err.message}`); }
39
39
  }
40
40
  // This method uses a callback to end the file read from the end of the file. If the callback returns false then the iteration through the
41
41
  // file will end and the log entries will be returned. If the callback returns true then the entry will be added to the
@@ -103,11 +103,11 @@ export class DataLogger {
103
103
  }
104
104
  catch (err) { return Promise.reject(err); }
105
105
  finally { if (typeof file !== 'undefined') await new Promise<boolean>((resolve, reject) => fs.close(file, (err) => { if (err) reject(err); else resolve(true); })); }
106
- } catch (err) { logger.error(err); }
106
+ } catch (err) { logger.error(`readFromEndAsync: ${err.message}`); }
107
107
  }
108
108
  return arr;
109
109
  }
110
- catch (err) { logger.error(err); }
110
+ catch (err) { logger.error(`readFromEndAsync: ${logFile} ${err.message}`); }
111
111
 
112
112
  }
113
113
  // This method uses a callback to end the file read from the end of the file. If the callback returns false then the iteration through the
@@ -235,11 +235,11 @@ export class DataLogger {
235
235
  }
236
236
  catch (err) { return Promise.reject(err); }
237
237
  finally { if (typeof file !== 'undefined') await new Promise<boolean>((resolve, reject) => fs.close(file, (err) => { if (err) reject(err); else resolve(true); })); }
238
- } catch (err) { logger.error(err); }
238
+ } catch (err) { logger.error(`readFromStart: ${err.message}`); }
239
239
  }
240
240
  return arr;
241
241
  }
242
- catch (err) { logger.error(err); }
242
+ catch (err) { logger.error(`readFromStart ${logFile}: ${err.message}`); }
243
243
 
244
244
  }
245
245
 
@@ -316,7 +316,7 @@ export class DataLogger {
316
316
  else
317
317
  lines.unshift(data.toString());
318
318
  fs.writeFileSync(logPath, lines.join('\n'));
319
- } catch (err) { logger.error(err); }
319
+ } catch (err) { logger.error(`writeStart ${logFile}: ${err.message}`); }
320
320
  }
321
321
  public static writeEnd(logFile: string, entry: DataLoggerEntry) {
322
322
  try {
@@ -384,7 +384,7 @@ export class DataLogger {
384
384
  finally { if (typeof file !== 'undefined') await new Promise<boolean>((resolve, reject) => fs.close(file, (err) => { if (err) reject(err); else resolve(true); })); }
385
385
  }
386
386
  return lines;
387
- } catch (err) { logger.error(err); }
387
+ } catch (err) { logger.error(`readEnd ${logFile}: ${err.message}`); }
388
388
  }
389
389
  private static makeLogFilePath(logFile: string) { return `${DataLogger.ensureLogPath()}/${logFile}`; }
390
390
  private static ensureLogPath(): string {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodejs-poolcontroller",
3
- "version": "8.0.1",
3
+ "version": "8.0.2",
4
4
  "description": "nodejs-poolController",
5
5
  "main": "app.js",
6
6
  "author": {
@@ -33,7 +33,7 @@
33
33
  "multicast-dns": "^7.2.4",
34
34
  "node-screenlogic": "^2.0.0",
35
35
  "node-ssdp": "^4.0.1",
36
- "serialport": "^10.4.0",
36
+ "serialport": "^11.0.0",
37
37
  "socket.io": "^4.5.0",
38
38
  "socket.io-client": "^4.5.0",
39
39
  "source-map-support": "^0.5.21",
package/sendSocket.js ADDED
@@ -0,0 +1,32 @@
1
+ // Import Socket.IO client
2
+ const io = require('socket.io-client');
3
+
4
+ // Connect to the server
5
+ const socket = io('http://localhost:4200');
6
+
7
+ // Event handler for successful connection
8
+ socket.on('connect', () => {
9
+ console.log('Connected to server.');
10
+
11
+ // Emit data to the server
12
+ socket.emit('message', 'Hello, server!');
13
+ socket.emit('echo', `testing 123`);
14
+ socket.on('echo', (string)=>{
15
+ console.log(string);
16
+ })
17
+ //const hexData = "02 10 01 01 14 00 03 10 02 10 01 01 14 00 03 10 02 10 01 01 14 00 03 10 02 10 02 01 80 20 00 00 00 00 00 00 b5 00 03 10 02 10 03 01 20 20 6f 50 6c 6f 54 20 6d 65 20 70 37 20 5f 34 20 46 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 07 00 10 d6 10 03 01 02 00 01 10 14 10 03 01 02 00 01 10 14 10 03 01 02 00 01 10 14";
18
+ const hexData = "10 02 01 80 20 00 00 00 00 00 00 b5 00 03 10 02 10 03";
19
+ const formattedHexData = hexData.split(' ').map(hex => parseInt(hex, 16));
20
+ //socket.emit('rawbytes', Buffer.from([1, 2, 3]));
21
+ socket.emit('rawbytes', formattedHexData);
22
+ });
23
+
24
+ // Event handler for receiving data from the server
25
+ socket.on('message', (data) => {
26
+ console.log('Received from server:', data);
27
+ });
28
+
29
+ // Event handler for disconnection
30
+ socket.on('disconnect', () => {
31
+ console.log('Disconnected from server.');
32
+ });
package/web/Server.ts CHANGED
@@ -681,6 +681,10 @@ export class HttpServer extends ProtoServer {
681
681
  logger.error(`Error replaying packet: ${err.message}`);
682
682
  }
683
683
  });
684
+ sock.on('rawbytes', (data:any)=>{
685
+ let port = conn.findPortById(0);
686
+ port.pushIn(Buffer.from(data));
687
+ })
684
688
  sock.on('sendLogMessages', function (sendMessages: boolean) {
685
689
  console.log(`sendLogMessages set to ${sendMessages}`);
686
690
  if (!sendMessages) sock.leave('msgLogger');
@@ -1636,7 +1640,7 @@ export class REMInterfaceServer extends ProtoServer {
1636
1640
  }
1637
1641
  return (response.status.code === 200) ? JSON.parse(response.data) : [];
1638
1642
  }
1639
- catch (err) { logger.error(err); }
1643
+ catch (err) { logger.error(`getDevices: ${err.message}`); }
1640
1644
  }
1641
1645
  }
1642
1646
  export class BackupFile {
@@ -1719,16 +1723,18 @@ export class RestoreFile {
1719
1723
  this.njsPC.poolConfig = await this.extractFile(zip, 'njsPC/data/poolConfig.json');
1720
1724
  this.njsPC.poolState = await this.extractFile(zip, 'njsPC/data/poolState.json');
1721
1725
  }
1722
- for (let i = 0; i < this.options.servers.length; i++) {
1723
- // Extract each server from the file.
1724
- let srv = this.options.servers[i];
1725
- if (srv.backup && srv.success) {
1726
- this.servers.push({
1727
- name: srv.name,
1728
- uuid: srv.uuid,
1729
- serverConfig: await this.extractFile(zip, `${srv.name}/serverConfig.json`),
1730
- controllerConfig: await this.extractFile(zip, `${srv.name}/data/controllerConfig.json`)
1731
- });
1726
+ if (typeof this.options.servers !== 'undefined') {
1727
+ for (let i = 0; i < this.options.servers.length; i++) {
1728
+ // Extract each server from the file.
1729
+ let srv = this.options.servers[i];
1730
+ if (srv.backup && srv.success) {
1731
+ this.servers.push({
1732
+ name: srv.name,
1733
+ uuid: srv.uuid,
1734
+ serverConfig: await this.extractFile(zip, `${srv.name}/serverConfig.json`),
1735
+ controllerConfig: await this.extractFile(zip, `${srv.name}/data/controllerConfig.json`)
1736
+ });
1737
+ }
1732
1738
  }
1733
1739
  }
1734
1740
  } catch(err) { this.errors.push(err); logger.error(`Error extracting restore options from ${file}: ${err.message}`); }
@@ -34,7 +34,7 @@
34
34
  }
35
35
  ],
36
36
  "rootTopic-DIRECTIONS": "rootTopic in config.json is ingored. Instead set the two topic variables in the vars section",
37
- "_rootTopic": "@bind=(state.equipment.model).replace(' ','-').replace(' / ','').toLowerCase();",
37
+ "_rootTopic": "@bind=(state.equipment.model).replace(/ /g,'-').replace(' / ','').toLowerCase();",
38
38
  "clientId": "@bind=`hass_njsPC_${webApp.mac().replace(/:/g, '_'}-${webApp.httpPort()}`;"
39
39
  }
40
40
  },
@@ -434,4 +434,4 @@
434
434
  ]
435
435
  }
436
436
  ]
437
- }
437
+ }