node-red-contrib-energymeterplus 0.3.2 → 0.3.3

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 (2) hide show
  1. package/energyMeterPlus.js +75 -76
  2. package/package.json +1 -1
@@ -11,88 +11,29 @@ module.exports = function(RED) {
11
11
  let inputUnit = config.inputUnit || "W";
12
12
  let currencyCode = config.currency || "USD";
13
13
 
14
- // Baseline offsets
15
- let baseline = node.context().get("baseline") || {
16
- daily: Number(config.baselineDaily) || 0,
17
- weekly: Number(config.baselineWeekly) || 0,
18
- monthly: Number(config.baselineMonthly) || 0,
19
- yearly: Number(config.baselineYearly) || 0
20
- };
21
-
22
- // Live increments
23
- let accumulated = node.context().get("accumulated") || {
24
- daily:0, weekly:0, monthly:0, yearly:0
25
- };
26
-
27
- // Rehydrate lastCheck from context
14
+ // Baseline initialization + merge editor config
15
+ let baseline = node.context().get("baseline") || { daily:0, weekly:0, monthly:0, yearly:0 };
16
+ baseline.daily += Number(config.baselineDaily) || 0;
17
+ baseline.weekly += Number(config.baselineWeekly) || 0;
18
+ baseline.monthly += Number(config.baselineMonthly) || 0;
19
+ baseline.yearly += Number(config.baselineYearly) || 0;
20
+ node.context().set("baseline", baseline);
21
+
22
+ // Accumulated values
23
+ let accumulated = node.context().get("accumulated") || { daily:0, weekly:0, monthly:0, yearly:0 };
24
+
25
+ // Last check timestamp
28
26
  let lastCheck = node.context().get("lastCheck");
29
27
  if (!(lastCheck instanceof Date)) {
30
28
  lastCheck = new Date(lastCheck || Date.now());
31
29
  }
32
30
 
33
- function currencySymbol(code) {
34
- switch (code) {
35
- case "USD": return "$";
36
- case "EUR": return "€";
37
- case "NGN": return "₦";
38
- default: return code;
39
- }
40
- }
41
- function round2(val) {
42
- if (val === null || val === undefined || isNaN(val)) return 0;
43
- return Number(val.toFixed(2));
44
- }
45
-
46
- function archiveYearly(total, year) {
47
- try {
48
- const archivePath = path.join(path.dirname(filePath), "yearly_archive.json");
49
- let archive = [];
50
- if (fs.existsSync(archivePath)) {
51
- archive = JSON.parse(fs.readFileSync(archivePath));
52
- }
53
- archive.push({ year, total, timestamp: new Date().toISOString() });
54
- fs.writeFileSync(archivePath, JSON.stringify(archive, null, 2));
55
- } catch (err) {
56
- node.error("Failed to archive yearly total: " + err);
57
- }
58
- }
59
-
60
- // Rollover logic
61
- function checkRollover() {
62
- let now = new Date();
63
-
64
- // Ensure lastCheck is a Date
65
- if (!(lastCheck instanceof Date)) {
66
- lastCheck = new Date(lastCheck || Date.now());
67
- }
68
-
69
- if (now.getDate() !== lastCheck.getDate()) {
70
- accumulated.daily = 0; baseline.daily = 0;
71
- }
72
- if (now.getDay() === 1 && lastCheck.getDay() !== 1) {
73
- accumulated.weekly = 0; baseline.weekly = 0;
74
- }
75
- if (now.getMonth() !== lastCheck.getMonth()) {
76
- accumulated.monthly = 0; baseline.monthly = 0;
77
- }
78
- if (now.getFullYear() !== lastCheck.getFullYear()) {
79
- archiveYearly(baseline.yearly + accumulated.yearly, lastCheck.getFullYear());
80
- accumulated.yearly = 0; baseline.yearly = 0;
81
- }
82
-
83
- lastCheck = now;
84
- node.context().set("lastCheck", lastCheck);
85
- node.context().set("accumulated", accumulated);
86
- node.context().set("baseline", baseline);
87
- }
88
- setInterval(checkRollover, 60000);
89
-
90
- // Input handler
91
- node.on('input', function(msg) {
31
+ // 1. Input handler: baseline updates + power increments
32
+ node.on('input', function(msg) {
92
33
  let now = new Date();
93
34
  let durationHours = (now - lastCheck) / (1000 * 3600);
94
35
 
95
- // Handle baseline updates
36
+ // Handle baseline updates via message
96
37
  if (msg.topic === "applyBaseline" && msg.payload) {
97
38
  baseline.daily += Number(msg.payload.daily) || 0;
98
39
  baseline.weekly += Number(msg.payload.weekly) || 0;
@@ -101,7 +42,7 @@ module.exports = function(RED) {
101
42
  node.context().set("baseline", baseline);
102
43
  }
103
44
 
104
- // Handle power input
45
+ // 2. Handle power input
105
46
  let power = Number(msg.payload);
106
47
  if (!isNaN(power) && durationHours > 0) {
107
48
  let power_kW = (inputUnit === "W") ? power / 1000 : power;
@@ -117,6 +58,7 @@ module.exports = function(RED) {
117
58
  node.context().set("lastCheck", lastCheck);
118
59
  node.context().set("accumulated", accumulated);
119
60
 
61
+ // 3. Output payload
120
62
  msg.payload = {
121
63
  energyDaily: round2(baseline.daily + accumulated.daily),
122
64
  energyWeekly: round2(baseline.weekly + accumulated.weekly),
@@ -129,6 +71,7 @@ module.exports = function(RED) {
129
71
  currency: currencySymbol(currencyCode)
130
72
  };
131
73
 
74
+ // 4. Persist to file
132
75
  const dir = path.dirname(filePath);
133
76
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
134
77
  try { fs.writeFileSync(filePath, JSON.stringify(msg.payload, null, 2)); }
@@ -136,9 +79,65 @@ module.exports = function(RED) {
136
79
 
137
80
  node.send(msg);
138
81
  });
82
+
83
+ // 5. Rollover logic
84
+ function checkRollover() {
85
+ let now = new Date();
86
+
87
+ if (!(lastCheck instanceof Date)) {
88
+ lastCheck = new Date(lastCheck || Date.now());
89
+ }
90
+
91
+ if (now.getDate() !== lastCheck.getDate()) {
92
+ accumulated.daily = 0; baseline.daily = 0;
93
+ }
94
+ if (now.getDay() === 1 && lastCheck.getDay() !== 1) {
95
+ accumulated.weekly = 0; baseline.weekly = 0;
96
+ }
97
+ if (now.getMonth() !== lastCheck.getMonth()) {
98
+ accumulated.monthly = 0; baseline.monthly = 0;
99
+ }
100
+ if (now.getFullYear() !== lastCheck.getFullYear()) {
101
+ archiveYearly(baseline.yearly + accumulated.yearly, lastCheck.getFullYear());
102
+ accumulated.yearly = 0; baseline.yearly = 0;
103
+ }
104
+
105
+ lastCheck = now;
106
+ node.context().set("lastCheck", lastCheck);
107
+ node.context().set("accumulated", accumulated);
108
+ node.context().set("baseline", baseline);
109
+ }
110
+ setInterval(checkRollover, 60000);
111
+
112
+ // 6. Helpers
113
+ function currencySymbol(code) {
114
+ switch (code) {
115
+ case "USD": return "$";
116
+ case "EUR": return "€";
117
+ case "NGN": return "₦";
118
+ default: return code;
119
+ }
120
+ }
121
+ function round2(val) {
122
+ if (val === null || val === undefined || isNaN(val)) return 0;
123
+ return Number(val.toFixed(2));
124
+ }
125
+ function archiveYearly(total, year) {
126
+ try {
127
+ const archivePath = path.join(path.dirname(filePath), "yearly_archive.json");
128
+ let archive = [];
129
+ if (fs.existsSync(archivePath)) {
130
+ archive = JSON.parse(fs.readFileSync(archivePath));
131
+ }
132
+ archive.push({ year, total, timestamp: new Date().toISOString() });
133
+ fs.writeFileSync(archivePath, JSON.stringify(archive, null, 2));
134
+ } catch (err) {
135
+ node.error("Failed to archive yearly total: " + err);
136
+ }
137
+ }
139
138
  }
140
139
 
141
140
  RED.nodes.registerType("energyMeterPlus", EnergyMeterPlus, {
142
141
  color: "#f3a108fe"
143
142
  });
144
- }
143
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-energymeterplus",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
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",