node-red-contrib-energymeterplus 0.2.2 → 0.2.4

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- #### **EnergyMeterPlus Node Version 0.2.1**
1
+ #### **EnergyMeterPlus Node Version 0.2.4**
2
2
 
3
3
 
4
4
 
@@ -50,7 +50,7 @@ EnergyMeterPlus is a custom Node‑RED function node designed to convert instant
50
50
 
51
51
 
52
52
 
53
- ###### **Configuration:**
53
+ ##### **Configuration:**
54
54
 
55
55
 
56
56
 
@@ -164,3 +164,45 @@ After 24 hours → \~108.6 kWh added to daily total.
164
164
 
165
165
  Costs scale automatically with your configured unit cost.
166
166
 
167
+
168
+
169
+
170
+
171
+ ##### **Updates:**
172
+
173
+
174
+
175
+ ###### **V2.3:**
176
+
177
+
178
+
179
+ Cleared bug where baseline values remain visible after applying the values
180
+
181
+
182
+
183
+ Cleared the rollover logic to eliminate double-counting.
184
+
185
+
186
+
187
+ Baselines now applied once and stored in context.
188
+
189
+
190
+
191
+ updated counters to continuous absorption, no transfer at rollover
192
+
193
+
194
+
195
+ ###### v2.4
196
+
197
+
198
+
199
+ Fixed a bug where the internal counters were not updating incrementally.
200
+
201
+
202
+
203
+ Added payload validation
204
+
205
+
206
+
207
+ Fixed Baselines bug: Baselines captured once, stored in context, and survive after UI clears
208
+
@@ -18,6 +18,13 @@
18
18
  icon: "sun.png",
19
19
  label: function() {
20
20
  return this.name || "energyMeterPlus";
21
+ },
22
+ oneditsave: function() {
23
+ // Cosmetic blanking: clear baseline fields in the editor UI after deploy
24
+ $("#node-input-baselineDaily").val("");
25
+ $("#node-input-baselineWeekly").val("");
26
+ $("#node-input-baselineMonthly").val("");
27
+ $("#node-input-baselineYearly").val("");
21
28
  }
22
29
  });
23
30
  </script>
@@ -11,12 +11,6 @@ module.exports = function(RED) {
11
11
  let inputUnit = config.inputUnit || "W";
12
12
  let currencyCode = config.currency || "USD";
13
13
 
14
- // Baseline corrections
15
- let baselineDaily = Number(config.baselineDaily) || 0;
16
- let baselineWeekly = Number(config.baselineWeekly) || 0;
17
- let baselineMonthly = Number(config.baselineMonthly) || 0;
18
- let baselineYearly = Number(config.baselineYearly) || 0;
19
-
20
14
  // Load persisted baseline or initialize
21
15
  let baseline = node.context().get("baseline") || {
22
16
  daily: 0,
@@ -25,21 +19,23 @@ module.exports = function(RED) {
25
19
  yearly: 0
26
20
  };
27
21
 
28
- // Apply baselines if new values entered
29
- let baselineApplied = node.context().get("baselineApplied") || false;
30
- if (!baselineApplied || baselineDaily || baselineWeekly || baselineMonthly || baselineYearly) {
31
- baseline.daily += baselineDaily;
32
- baseline.weekly += baselineWeekly;
33
- baseline.monthly += baselineMonthly;
34
- baseline.yearly += baselineYearly;
35
-
36
- node.context().set("baselineApplied", true);
37
-
38
- // Cosmetic blanking: clear config fields so they show empty in UI
39
- config.baselineDaily = "";
40
- config.baselineWeekly = "";
41
- config.baselineMonthly = "";
42
- config.baselineYearly = "";
22
+ // Load stored numeric baselines (persisted separately)
23
+ let storedBaselines = node.context().get("storedBaselines") || {
24
+ daily: Number(config.baselineDaily) || 0,
25
+ weekly: Number(config.baselineWeekly) || 0,
26
+ monthly: Number(config.baselineMonthly) || 0,
27
+ yearly: Number(config.baselineYearly) || 0
28
+ };
29
+
30
+ // Apply baselines once if new values entered
31
+ if (config.baselineDaily || config.baselineWeekly || config.baselineMonthly || config.baselineYearly) {
32
+ baseline.daily += storedBaselines.daily;
33
+ baseline.weekly += storedBaselines.weekly;
34
+ baseline.monthly += storedBaselines.monthly;
35
+ baseline.yearly += storedBaselines.yearly;
36
+
37
+ node.context().set("baseline", baseline);
38
+ node.context().set("storedBaselines", storedBaselines);
43
39
  }
44
40
 
45
41
  // Load last timestamp or initialize
@@ -55,6 +51,12 @@ module.exports = function(RED) {
55
51
  }
56
52
  }
57
53
 
54
+ // Safe rounding helper
55
+ function round2(val) {
56
+ if (val === null || val === undefined || isNaN(val)) return 0;
57
+ return Number(val.toFixed(2));
58
+ }
59
+
58
60
  // Archive yearly totals
59
61
  function archiveYearly(total, year) {
60
62
  try {
@@ -74,36 +76,22 @@ module.exports = function(RED) {
74
76
  }
75
77
  }
76
78
 
77
- // Safe rounding helper
78
- function round2(val) {
79
- if (val === null || val === undefined || isNaN(val)) return 0;
80
- return Number(val.toFixed(2));
81
- }
82
-
83
- // Rollover logic with baseline reset
79
+ // Rollover logic
84
80
  function checkRollover() {
85
81
  let now = new Date();
86
82
 
87
- // Daily rollover (midnight)
88
83
  if (now.getDate() !== lastCheck.getDate()) {
89
- // Reset daily back to baseline offset instead of 0
90
- baseline.daily = baselineDaily;
84
+ baseline.daily = storedBaselines.daily;
91
85
  }
92
-
93
- // Weekly rollover (Sunday → Monday)
94
86
  if (now.getDay() === 1 && lastCheck.getDay() !== 1) {
95
- baseline.weekly = baselineWeekly; // reset to baseline offset
87
+ baseline.weekly = storedBaselines.weekly;
96
88
  }
97
-
98
- // Monthly rollover (1st of month)
99
89
  if (now.getMonth() !== lastCheck.getMonth()) {
100
- baseline.monthly = baselineMonthly; // reset to baseline offset
90
+ baseline.monthly = storedBaselines.monthly;
101
91
  }
102
-
103
- // Yearly rollover (Jan 1)
104
92
  if (now.getFullYear() !== lastCheck.getFullYear()) {
105
93
  archiveYearly(baseline.yearly, lastCheck.getFullYear());
106
- baseline.yearly = baselineYearly; // reset to baseline offset
94
+ baseline.yearly = storedBaselines.yearly;
107
95
  }
108
96
 
109
97
  lastCheck = now;
@@ -114,24 +102,25 @@ module.exports = function(RED) {
114
102
 
115
103
  node.on('input', function(msg) {
116
104
  let now = new Date();
117
- let durationHours = (now - lastCheck) / (1000 * 3600);
118
- lastCheck = now;
119
- node.context().set("lastCheck", lastCheck);
120
105
 
121
- let power = Number(msg.payload) || 0;
122
- let power_kW = (inputUnit === "W") ? power / 1000 : power;
123
-
124
- let energyIncrement = power_kW * durationHours;
106
+ // Calculate duration BEFORE updating lastCheck
107
+ let durationHours = (now - lastCheck) / (1000 * 3600);
125
108
 
126
- // Add increment to daily
127
- baseline.daily += energyIncrement;
109
+ let power = Number(msg.payload);
110
+ if (!isNaN(power)) {
111
+ let power_kW = (inputUnit === "W") ? power / 1000 : power;
112
+ let energyIncrement = power_kW * durationHours;
128
113
 
129
- // Continuous absorption
130
- baseline.weekly += energyIncrement;
131
- baseline.monthly += energyIncrement;
132
- baseline.yearly += energyIncrement;
114
+ // Apply increment
115
+ baseline.daily += energyIncrement;
116
+ baseline.weekly += energyIncrement;
117
+ baseline.monthly += energyIncrement;
118
+ baseline.yearly += energyIncrement;
119
+ }
133
120
 
134
- function round2(val) { return Number(val.toFixed(2)); }
121
+ // Update lastCheck AFTER increment
122
+ lastCheck = now;
123
+ node.context().set("lastCheck", lastCheck);
135
124
 
136
125
  msg.payload = {
137
126
  daily_kWh: round2(baseline.daily),
@@ -151,7 +140,6 @@ module.exports = function(RED) {
151
140
  fs.mkdirSync(dir, { recursive: true });
152
141
  }
153
142
 
154
- // Write snapshot safely
155
143
  try {
156
144
  fs.writeFileSync(filePath, JSON.stringify(msg.payload, null, 2));
157
145
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-energymeterplus",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "A custom Node-RED node that integrates power readings into energy totals with persistence and cost calculation.",
5
5
  "author": "Arcfrankye",
6
6
  "license": "MIT",