node-red-contrib-power-saver 3.6.2 → 4.1.0

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 (109) hide show
  1. package/.eslintrc.js +15 -0
  2. package/docs/.vuepress/components/AdsenseAdd.vue +40 -0
  3. package/docs/.vuepress/components/DonateButtons.vue +26 -3
  4. package/docs/.vuepress/components/VippsPlakat.vue +20 -0
  5. package/docs/.vuepress/config.js +17 -10
  6. package/docs/.vuepress/public/ads.txt +1 -0
  7. package/docs/README.md +6 -4
  8. package/docs/changelog/README.md +61 -1
  9. package/docs/contribute/README.md +12 -3
  10. package/docs/examples/README.md +6 -0
  11. package/docs/examples/example-grid-tariff-capacity-flow.json +23 -7
  12. package/docs/examples/example-grid-tariff-capacity-part.md +657 -22
  13. package/docs/faq/README.md +5 -1
  14. package/docs/faq/best-save-viewer.md +9 -1
  15. package/docs/guide/README.md +36 -5
  16. package/docs/images/best-save-config.png +0 -0
  17. package/docs/images/combine-two-lowest-price.png +0 -0
  18. package/docs/images/fixed-schedule-config.png +0 -0
  19. package/docs/images/global-context-window.png +0 -0
  20. package/docs/images/lowest-price-config.png +0 -0
  21. package/docs/images/node-ps-schedule-merger.png +0 -0
  22. package/docs/images/node-ps-strategy-fixed-schedule.png +0 -0
  23. package/docs/images/ps-strategy-fixed-schedule-example.png +0 -0
  24. package/docs/images/schedule-merger-config.png +0 -0
  25. package/docs/images/schedule-merger-example-1.png +0 -0
  26. package/docs/images/vipps-plakat.png +0 -0
  27. package/docs/images/vipps-qr.png +0 -0
  28. package/docs/images/vipps-smiling-rgb-orange-pos.png +0 -0
  29. package/docs/nodes/README.md +24 -4
  30. package/docs/nodes/dynamic-commands.md +91 -0
  31. package/docs/nodes/dynamic-config.md +84 -0
  32. package/docs/nodes/old-power-saver-doc.md +4 -0
  33. package/docs/nodes/ps-elvia-add-tariff.md +16 -0
  34. package/docs/nodes/ps-general-add-tariff.md +22 -0
  35. package/docs/nodes/ps-receive-price.md +14 -1
  36. package/docs/nodes/ps-schedule-merger.md +231 -0
  37. package/docs/nodes/ps-strategy-best-save.md +65 -109
  38. package/docs/nodes/ps-strategy-fixed-schedule.md +113 -0
  39. package/docs/nodes/ps-strategy-heat-capacitor.md +14 -1
  40. package/docs/nodes/ps-strategy-lowest-price.md +61 -110
  41. package/docs/nodes/strategy-input.md +4 -0
  42. package/package.json +5 -2
  43. package/src/elvia/elvia-add-tariff.html +1 -2
  44. package/src/elvia/elvia-add-tariff.js +0 -1
  45. package/src/elvia/elvia-api.js +6 -0
  46. package/src/elvia/elvia-tariff.html +1 -1
  47. package/src/general-add-tariff.html +14 -8
  48. package/src/general-add-tariff.js +0 -1
  49. package/src/handle-input.js +97 -106
  50. package/src/handle-output.js +117 -0
  51. package/src/receive-price-functions.js +3 -3
  52. package/src/schedule-merger-functions.js +98 -0
  53. package/src/schedule-merger.html +135 -0
  54. package/src/schedule-merger.js +112 -0
  55. package/src/strategy-best-save.html +38 -1
  56. package/src/strategy-best-save.js +17 -63
  57. package/src/strategy-fixed-schedule.html +339 -0
  58. package/src/strategy-fixed-schedule.js +84 -0
  59. package/src/strategy-functions.js +35 -0
  60. package/src/strategy-lowest-price.html +76 -38
  61. package/src/strategy-lowest-price.js +16 -35
  62. package/src/utils.js +75 -2
  63. package/test/commands-input-best-save.test.js +142 -0
  64. package/test/commands-input-lowest-price.test.js +149 -0
  65. package/test/commands-input-schedule-merger.test.js +128 -0
  66. package/test/data/best-save-overlap-result.json +5 -1
  67. package/test/data/best-save-result.json +4 -0
  68. package/test/data/commands-result-best-save.json +383 -0
  69. package/test/data/commands-result-lowest-price.json +340 -0
  70. package/test/data/fixed-schedule-result.json +353 -0
  71. package/test/data/lowest-price-result-cont-max-fail.json +5 -1
  72. package/test/data/lowest-price-result-cont-max.json +3 -1
  73. package/test/data/lowest-price-result-cont.json +8 -1
  74. package/test/data/lowest-price-result-missing-end.json +8 -3
  75. package/test/data/lowest-price-result-neg-cont.json +27 -0
  76. package/test/data/lowest-price-result-neg-split.json +23 -0
  77. package/test/data/lowest-price-result-split-allday.json +3 -1
  78. package/test/data/lowest-price-result-split-allday10.json +1 -0
  79. package/test/data/lowest-price-result-split-max.json +3 -1
  80. package/test/data/lowest-price-result-split.json +3 -1
  81. package/test/data/merge-schedule-data.js +238 -0
  82. package/test/data/negative-prices.json +197 -0
  83. package/test/data/nordpool-event-prices.json +96 -480
  84. package/test/data/nordpool-zero-prices.json +90 -0
  85. package/test/data/reconfigResult.js +1 -0
  86. package/test/data/result.js +1 -0
  87. package/test/data/tibber-result-end-0-24h.json +12 -2
  88. package/test/data/tibber-result-end-0.json +12 -2
  89. package/test/data/tibber-result.json +1 -0
  90. package/test/receive-price.test.js +22 -0
  91. package/test/schedule-merger-functions.test.js +101 -0
  92. package/test/schedule-merger-test-utils.js +27 -0
  93. package/test/schedule-merger.test.js +130 -0
  94. package/test/send-config-input.test.js +46 -2
  95. package/test/strategy-best-save-test-utils.js +1 -1
  96. package/test/strategy-best-save.test.js +91 -0
  97. package/test/strategy-fixed-schedule.test.js +117 -0
  98. package/test/strategy-heat-capacitor.test.js +1 -1
  99. package/test/strategy-lowest-price-functions.test.js +1 -1
  100. package/test/strategy-lowest-price-test-utils.js +31 -0
  101. package/test/strategy-lowest-price.test.js +55 -45
  102. package/test/test-utils.js +43 -36
  103. package/test/utils.test.js +13 -0
  104. package/docs/images/node-power-saver.png +0 -0
  105. package/docs/nodes/power-saver.md +0 -23
  106. package/src/power-saver.html +0 -116
  107. package/src/power-saver.js +0 -260
  108. package/test/commands-input.test.js +0 -47
  109. package/test/power-saver.test.js +0 -189
@@ -0,0 +1,117 @@
1
+ const cloneDeep = require("lodash.clonedeep");
2
+ const { DateTime } = require("luxon");
3
+ const expect = require("expect");
4
+ const helper = require("node-red-node-test-helper");
5
+ const fixedSchedule = require("../src/strategy-fixed-schedule.js");
6
+ const prices = require("./data/converted-prices.json");
7
+ const result = require("./data/fixed-schedule-result.json");
8
+ const { testPlan: plan, equalPlan, equalSchedule } = require("./test-utils");
9
+
10
+ helper.init(require.resolve("node-red"));
11
+
12
+ describe("ps-strategy-fixed-schedule node", function () {
13
+ beforeEach(function (done) {
14
+ helper.startServer(done);
15
+ });
16
+
17
+ afterEach(function (done) {
18
+ helper.unload().then(function () {
19
+ helper.stopServer(done);
20
+ });
21
+ });
22
+
23
+ it("should be loaded", function (done) {
24
+ const flow = [{ id: "n1", type: "ps-strategy-fixed-schedule", name: "test name" }];
25
+ helper.load(fixedSchedule, flow, function () {
26
+ const n1 = helper.getNode("n1");
27
+ expect(n1).toHaveProperty("name", "test name");
28
+ done();
29
+ });
30
+ });
31
+
32
+ it("should turn on 2 to 6", function (done) {
33
+ const flow = makeFlow();
34
+ const expected = cloneDeep(result);
35
+ helper.load(fixedSchedule, flow, function () {
36
+ const n1 = helper.getNode("n1");
37
+ const n2 = helper.getNode("n2");
38
+ n2.on("input", function (msg) {
39
+ expect(equalPlan(expected, msg.payload)).toBeTruthy();
40
+ n1.warn.should.not.be.called;
41
+ done();
42
+ });
43
+ n1.receive({ payload: prices, time: prices.priceData[0].start });
44
+ });
45
+ });
46
+ it("should send correct if no schedule", function (done) {
47
+ const flow = makeFlow(true);
48
+ const expected = cloneDeep(result);
49
+ expected.schedule.push({
50
+ time: "2021-10-13T00:00:00.000+02:00",
51
+ value: true,
52
+ countHours: null,
53
+ });
54
+ expected.config.outputIfNoSchedule = true;
55
+
56
+ helper.load(fixedSchedule, flow, function () {
57
+ const n1 = helper.getNode("n1");
58
+ const n2 = helper.getNode("n2");
59
+ n2.on("input", function (msg) {
60
+ expect(equalPlan(expected, msg.payload)).toBeTruthy();
61
+ n1.warn.should.not.be.called;
62
+ done();
63
+ });
64
+ n1.receive({ payload: prices, time: prices.priceData[0].start });
65
+ });
66
+ });
67
+ it("should work only on days specified", function (done) {
68
+ const flow = makeFlow(false);
69
+ flow[0].periods.splice(1, 1);
70
+ flow[0].days["Tue"] = false;
71
+ const expected = [
72
+ {
73
+ time: "2021-10-11T00:00:00.000+02:00",
74
+ value: true,
75
+ countHours: 24,
76
+ },
77
+ {
78
+ time: "2021-10-12T00:00:00.000+02:00",
79
+ value: false,
80
+ countHours: 24,
81
+ },
82
+ ];
83
+
84
+ helper.load(fixedSchedule, flow, function () {
85
+ const n1 = helper.getNode("n1");
86
+ const n2 = helper.getNode("n2");
87
+ n2.on("input", function (msg) {
88
+ expect(equalSchedule(expected, msg.payload.schedule)).toBeTruthy();
89
+ done();
90
+ });
91
+ n1.receive({ payload: prices, time: prices.priceData[0].start });
92
+ });
93
+ });
94
+ });
95
+
96
+ function makeFlow(outputIfNoSchedule = false) {
97
+ return [
98
+ {
99
+ id: "n1",
100
+ type: "ps-strategy-fixed-schedule",
101
+ name: "test name",
102
+ days: { Mon: true, Tue: true, Wed: true, Thu: true, Fri: true, Sat: true, Sun: true },
103
+ periods: [
104
+ { start: "02", value: true },
105
+ { start: "06", value: false },
106
+ ],
107
+ sendCurrentValueWhenRescheduling: true,
108
+ outputIfNoSchedule,
109
+ // validFrom: null,
110
+ // validTo: null,
111
+ wires: [["n3"], ["n4"], ["n2"]],
112
+ },
113
+ { id: "n2", type: "helper" },
114
+ { id: "n3", type: "helper" },
115
+ { id: "n4", type: "helper" },
116
+ ];
117
+ }
@@ -11,7 +11,7 @@ const {
11
11
  const converted_prices = require("./data/converted-prices.json");
12
12
  const decreasing_end_prices = require("./data/tibber-decreasing2-24h.json");
13
13
 
14
- describe("Test heat capacitor strategy functions", () => {
14
+ describe("ps-strategy-heat-capacitor-functions", () => {
15
15
  let prices, decreasing_24h_prices, start_date, buy_pattern, sell_pattern;
16
16
 
17
17
  //User input
@@ -4,7 +4,7 @@ const { getBestContinuous, getBestX } = require("../src/strategy-lowest-price-fu
4
4
  const convertedPrices = require("./data/converted-prices.json");
5
5
  const cloneDeep = require("lodash.clonedeep");
6
6
 
7
- describe("strategy-utils", () => {
7
+ describe("strategy-lowest-price-functions", () => {
8
8
  it("can find best x", () => {
9
9
  const values = convertedPrices.priceData.slice(0, 48).map((p) => p.value);
10
10
  const result = cloneDeep(values).fill(false);
@@ -0,0 +1,31 @@
1
+ const cloneDeep = require("lodash.clonedeep");
2
+ const { DateTime } = require("luxon");
3
+
4
+ function makeFlow(hoursOn, maxPrice = null, doNotSplit = true, fromTime = "10", toTime = "20") {
5
+ return [
6
+ {
7
+ id: "n1",
8
+ type: "ps-strategy-lowest-price",
9
+ name: "test name",
10
+ fromTime,
11
+ toTime,
12
+ hoursOn,
13
+ maxPrice,
14
+ doNotSplit,
15
+ sendCurrentValueWhenRescheduling: true,
16
+ outputIfNoSchedule: true,
17
+ wires: [["n3"], ["n4"], ["n2"]],
18
+ },
19
+ { id: "n2", type: "helper" },
20
+ { id: "n3", type: "helper" },
21
+ { id: "n4", type: "helper" },
22
+ ];
23
+ }
24
+
25
+ function makePayload(prices, time) {
26
+ const payload = cloneDeep(prices);
27
+ payload.time = time;
28
+ return payload;
29
+ }
30
+
31
+ module.exports = { makeFlow, makePayload };
@@ -4,8 +4,9 @@ const expect = require("expect");
4
4
  const helper = require("node-red-node-test-helper");
5
5
  const lowestPrice = require("../src/strategy-lowest-price.js");
6
6
  const prices = require("./data/converted-prices.json");
7
- const { testPlan: plan } = require("./test-utils");
7
+ const negativePrices = require("./data/negative-prices.json");
8
8
  const { version } = require("../package.json");
9
+ const { makeFlow, makePayload } = require("./strategy-lowest-price-test-utils");
9
10
 
10
11
  helper.init(require.resolve("node-red"));
11
12
 
@@ -83,7 +84,6 @@ describe("ps-strategy-lowest-price node", function () {
83
84
  const n2 = helper.getNode("n2");
84
85
  n2.on("input", function (msg) {
85
86
  expect(msg.payload).toHaveProperty("schedule", resultSplitted.schedule);
86
- expect(msg.payload).toHaveProperty("config", resultSplitted.config);
87
87
  n1.warn.should.not.be.called;
88
88
  done();
89
89
  });
@@ -91,6 +91,38 @@ describe("ps-strategy-lowest-price node", function () {
91
91
  n1.receive({ payload: makePayload(prices, time) });
92
92
  });
93
93
  });
94
+ it("should plan correct negative prices continuous schedule", function (done) {
95
+ const resultContinuous = require("./data/lowest-price-result-neg-cont.json");
96
+ const flow = makeFlow(5, null, true, "00", "00");
97
+ helper.load(lowestPrice, flow, function () {
98
+ const n1 = helper.getNode("n1");
99
+ const n2 = helper.getNode("n2");
100
+ n2.on("input", function (msg) {
101
+ expect(msg.payload).toHaveProperty("schedule", resultContinuous.schedule);
102
+ expect(msg.payload).toHaveProperty("config", resultContinuous.config);
103
+ n1.warn.should.not.be.called;
104
+ done();
105
+ });
106
+ const time = DateTime.fromISO(prices.priceData[10].start);
107
+ n1.receive({ payload: makePayload(negativePrices, time) });
108
+ });
109
+ });
110
+ it("should plan correct negative prices splitted schedule", function (done) {
111
+ const resultSplitted = require("./data/lowest-price-result-neg-split.json");
112
+ const flow = makeFlow(5, null, true, "00", "00");
113
+ flow[0].doNotSplit = false;
114
+ helper.load(lowestPrice, flow, function () {
115
+ const n1 = helper.getNode("n1");
116
+ const n2 = helper.getNode("n2");
117
+ n2.on("input", function (msg) {
118
+ expect(msg.payload).toHaveProperty("schedule", resultSplitted.schedule);
119
+ n1.warn.should.not.be.called;
120
+ done();
121
+ });
122
+ const time = DateTime.fromISO(prices.priceData[10].start);
123
+ n1.receive({ payload: makePayload(negativePrices, time) });
124
+ });
125
+ });
94
126
  it("should plan correct continuous schedule with max price ok", function (done) {
95
127
  const resultContinuousMax = require("./data/lowest-price-result-cont-max.json");
96
128
  const flow = makeFlow(4, 1.0);
@@ -99,7 +131,6 @@ describe("ps-strategy-lowest-price node", function () {
99
131
  const n2 = helper.getNode("n2");
100
132
  n2.on("input", function (msg) {
101
133
  expect(msg.payload).toHaveProperty("schedule", resultContinuousMax.schedule);
102
- expect(msg.payload).toHaveProperty("config", resultContinuousMax.config);
103
134
  n1.warn.should.not.be.called;
104
135
  done();
105
136
  });
@@ -115,7 +146,6 @@ describe("ps-strategy-lowest-price node", function () {
115
146
  const n2 = helper.getNode("n2");
116
147
  n2.on("input", function (msg) {
117
148
  expect(msg.payload).toHaveProperty("schedule", resultContinuousMax.schedule);
118
- expect(msg.payload).toHaveProperty("config", resultContinuousMax.config);
119
149
  n1.warn.should.not.be.called;
120
150
  done();
121
151
  });
@@ -132,7 +162,6 @@ describe("ps-strategy-lowest-price node", function () {
132
162
  const n2 = helper.getNode("n2");
133
163
  n2.on("input", function (msg) {
134
164
  expect(msg.payload).toHaveProperty("schedule", resultSplittedMax.schedule);
135
- expect(msg.payload).toHaveProperty("config", resultSplittedMax.config);
136
165
  n1.warn.should.not.be.called;
137
166
  done();
138
167
  });
@@ -151,7 +180,6 @@ describe("ps-strategy-lowest-price node", function () {
151
180
  const n2 = helper.getNode("n2");
152
181
  n2.on("input", function (msg) {
153
182
  expect(msg.payload).toHaveProperty("schedule", resultAllDay.schedule);
154
- expect(msg.payload).toHaveProperty("config", resultAllDay.config);
155
183
  n1.warn.should.not.be.called;
156
184
  done();
157
185
  });
@@ -171,7 +199,6 @@ describe("ps-strategy-lowest-price node", function () {
171
199
  n2.on("input", function (msg) {
172
200
  const config = cloneDeep(resultAllDay10.config);
173
201
  expect(msg.payload).toHaveProperty("schedule", resultAllDay10.schedule);
174
- expect(msg.payload).toHaveProperty("config", config);
175
202
  n1.warn.should.not.be.called;
176
203
  done();
177
204
  });
@@ -193,7 +220,6 @@ describe("ps-strategy-lowest-price node", function () {
193
220
  const config = cloneDeep(resultAllDay10.config);
194
221
  config.outputOutsidePeriod = true;
195
222
  expect(msg.payload).toHaveProperty("schedule", resultAllDay10.schedule);
196
- expect(msg.payload).toHaveProperty("config", config);
197
223
  n1.warn.should.not.be.called;
198
224
  done();
199
225
  });
@@ -215,7 +241,6 @@ describe("ps-strategy-lowest-price node", function () {
215
241
  const config = cloneDeep(resultAllDay10.config);
216
242
  config.outputIfNoSchedule = true;
217
243
  expect(msg.payload).toHaveProperty("schedule", resultAllDay10.schedule);
218
- expect(msg.payload).toHaveProperty("config", config);
219
244
  n1.warn.should.not.be.called;
220
245
  done();
221
246
  });
@@ -242,8 +267,8 @@ describe("ps-strategy-lowest-price node", function () {
242
267
  config.outputOutsidePeriod = true;
243
268
  const res = msg.payload.schedule.map((s) => ({ time: s.time, value: s.value }));
244
269
  const exp = schedule.map((s) => ({ time: s.time, value: s.value }));
270
+ exp.pop();
245
271
  expect(res).toEqual(exp);
246
- expect(msg.payload).toHaveProperty("config", config);
247
272
  n1.warn.should.not.be.called;
248
273
  done();
249
274
  });
@@ -267,10 +292,10 @@ describe("ps-strategy-lowest-price node", function () {
267
292
  schedule.splice(4, 0, { time: "2021-10-11T20:00:00.000+02:00", value: true });
268
293
  schedule.splice(5, 0, { time: "2021-10-12T10:00:00.000+02:00", value: false });
269
294
  schedule.splice(schedule.length, 0, { time: "2021-10-12T20:00:00.000+02:00", value: true });
270
- // schedule.splice(schedule.length - 1, 1);
271
295
  config.outputOutsidePeriod = true;
272
296
  const res = msg.payload.schedule.map((s) => ({ time: s.time, value: s.value }));
273
297
  const exp = schedule.map((s) => ({ time: s.time, value: s.value }));
298
+ exp.splice(8, 1);
274
299
  expect(res).toEqual(exp);
275
300
  expect(msg.payload).toHaveProperty("config", config);
276
301
  n1.warn.should.not.be.called;
@@ -281,7 +306,10 @@ describe("ps-strategy-lowest-price node", function () {
281
306
  });
282
307
  });
283
308
  it("should work with 0 hours on", function (done) {
284
- const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 48 }];
309
+ const result = [
310
+ { time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 48 },
311
+ { time: "2021-10-13T00:00:00.000+02:00", value: true, countHours: null },
312
+ ];
285
313
  const flow = makeFlow(0);
286
314
  helper.load(lowestPrice, flow, function () {
287
315
  const n1 = helper.getNode("n1");
@@ -324,6 +352,7 @@ describe("ps-strategy-lowest-price node", function () {
324
352
  { time: "2021-10-11T13:00:00.000+02:00", value: false, countHours: 25 },
325
353
  { time: "2021-10-12T14:00:00.000+02:00", value: true, countHours: 1 },
326
354
  { time: "2021-10-12T15:00:00.000+02:00", value: false, countHours: 9 },
355
+ { time: "2021-10-13T00:00:00.000+02:00", value: true, countHours: null },
327
356
  ];
328
357
  const flow = makeFlow(1);
329
358
  helper.load(lowestPrice, flow, function () {
@@ -345,6 +374,7 @@ describe("ps-strategy-lowest-price node", function () {
345
374
  { time: "2021-10-11T20:00:00.000+02:00", value: false, countHours: 14 },
346
375
  { time: "2021-10-12T10:00:00.000+02:00", value: true, countHours: 10 },
347
376
  { time: "2021-10-12T20:00:00.000+02:00", value: false, countHours: 4 },
377
+ { time: "2021-10-13T00:00:00.000+02:00", value: true, countHours: null },
348
378
  ];
349
379
  const flow = makeFlow(24);
350
380
  helper.load(lowestPrice, flow, function () {
@@ -367,6 +397,7 @@ describe("ps-strategy-lowest-price node", function () {
367
397
  { time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 12 },
368
398
  { time: "2021-10-11T12:00:00.000+02:00", value: true, countHours: 1 },
369
399
  { time: "2021-10-11T13:00:00.000+02:00", value: false, countHours: 11 },
400
+ { time: "2021-10-12T00:00:00.000+02:00", value: true, countHours: null },
370
401
  ];
371
402
  const flow = makeFlow(1);
372
403
  helper.load(lowestPrice, flow, function () {
@@ -393,7 +424,10 @@ describe("ps-strategy-lowest-price node", function () {
393
424
  });
394
425
 
395
426
  it("should handle hours on > period", function (done) {
396
- const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: true, countHours: 48 }];
427
+ const result = [
428
+ { time: "2021-10-11T00:00:00.000+02:00", value: true, countHours: 48 },
429
+ { time: "2021-10-13T00:00:00.000+02:00", value: false, countHours: null },
430
+ ];
397
431
  const flow = [
398
432
  {
399
433
  id: "n1",
@@ -446,6 +480,7 @@ describe("ps-strategy-lowest-price node", function () {
446
480
  { time: "2021-10-11T22:00:00.000+02:00", value: false, countHours: 19 },
447
481
  { time: "2021-10-12T17:00:00.000+02:00", value: true, countHours: 5 },
448
482
  { time: "2021-10-12T22:00:00.000+02:00", value: false, countHours: 2 },
483
+ { time: "2021-10-13T00:00:00.000+02:00", value: true, countHours: null },
449
484
  ];
450
485
  const flow = [
451
486
  {
@@ -496,6 +531,8 @@ describe("ps-strategy-lowest-price node", function () {
496
531
  const input = require("./data/tibber-data-end-0.json");
497
532
  const result = require("./data/tibber-result-end-0.json");
498
533
  result.version = version;
534
+ result.strategyNodeId = "n1";
535
+ result.current = false;
499
536
  const flow = [
500
537
  {
501
538
  id: "n1",
@@ -509,6 +546,7 @@ describe("ps-strategy-lowest-price node", function () {
509
546
  sendCurrentValueWhenRescheduling: true,
510
547
  outputIfNoSchedule: false,
511
548
  outputOutsidePeriod: false,
549
+ override: "auto",
512
550
  wires: [["n3"], ["n4"], ["n2"]],
513
551
  },
514
552
  { id: "n2", type: "helper" },
@@ -531,6 +569,8 @@ describe("ps-strategy-lowest-price node", function () {
531
569
  const input = require("./data/tibber-data-end-0-24h.json");
532
570
  const result = require("./data/tibber-result-end-0-24h.json");
533
571
  result.version = version;
572
+ result.strategyNodeId = "n1";
573
+ result.current = false;
534
574
  const flow = [
535
575
  {
536
576
  id: "n1",
@@ -566,6 +606,8 @@ describe("ps-strategy-lowest-price node", function () {
566
606
  const input = require("./data/lowest-price-input-missing-end.json");
567
607
  const result = require("./data/lowest-price-result-missing-end.json");
568
608
  result.payload.version = version;
609
+ result.payload.strategyNodeId = "n1";
610
+ result.payload.current = false;
569
611
  const flow = [
570
612
  {
571
613
  id: "n1",
@@ -599,35 +641,3 @@ describe("ps-strategy-lowest-price node", function () {
599
641
  });
600
642
  });
601
643
  });
602
-
603
- function makeFlow(hoursOn, maxPrice = null) {
604
- return [
605
- {
606
- id: "n1",
607
- type: "ps-strategy-lowest-price",
608
- name: "test name",
609
- fromTime: "10",
610
- toTime: "20",
611
- hoursOn: hoursOn,
612
- maxPrice: maxPrice,
613
- doNotSplit: true,
614
- sendCurrentValueWhenRescheduling: true,
615
- outputIfNoSchedule: true,
616
- wires: [["n3"], ["n4"], ["n2"]],
617
- },
618
- { id: "n2", type: "helper" },
619
- { id: "n3", type: "helper" },
620
- { id: "n4", type: "helper" },
621
- ];
622
- }
623
-
624
- function makePayload(prices, time) {
625
- const payload = cloneDeep(prices);
626
- payload.time = time;
627
- // let entryTime = DateTime.fromISO(payload.time);
628
- // payload.priceData.forEach((e) => {
629
- // e.start = entryTime.toISO();
630
- // entryTime = entryTime.plus({ milliseconds: 10 });
631
- // });
632
- return payload;
633
- }
@@ -37,59 +37,64 @@ function makePayload(prices, time) {
37
37
 
38
38
  function equalPlan(expected, actual) {
39
39
  let res = true;
40
- if (expected.schedule.length !== actual.schedule.length) {
41
- console.log(
42
- "Schedules have different lengths: Expected " + expected.schedule.length + ", got " + actual.schedule.length
43
- );
40
+
41
+ if (!equalSchedule(expected.schedule, actual.schedule)) {
44
42
  res = false;
45
43
  }
46
- if (expected.hours.length !== actual.hours.length) {
47
- console.log("Hours have different lengths: Expected " + expected.hours.length + ", got " + actual.hours.length);
44
+
45
+ if (!equalHours(expected.hours, actual.hours)) {
46
+ res = false;
48
47
  }
49
- expected.schedule.forEach((s, i) => {
48
+
49
+ ["maxHoursToSaveInSequence", "minHoursOnAfterMaxSequenceSaved", "minSaving", "outputIfNoSchedule"].forEach((key) => {
50
+ if (expected.config[key] != actual.config[key]) {
51
+ console.log(
52
+ "Different config values for " + key + ": Expected " + expected.config[key] + ", got " + actual.config[key]
53
+ );
54
+ res = false;
55
+ }
56
+ });
57
+
58
+ return res;
59
+ }
60
+
61
+ function equalSchedule(expected, actual) {
62
+ let res = true;
63
+ if (expected.length !== actual.length) {
64
+ console.log("Schedules have different lengths: Expected " + expected.length + ", got " + actual.length);
65
+ res = false;
66
+ }
67
+
68
+ expected.forEach((s, i) => {
50
69
  ["time", "value"].forEach((key) => {
51
- if (s[key] != actual.schedule[i][key]) {
70
+ if (s[key] != actual[i][key]) {
52
71
  console.log(
53
- "Different schedule values for " +
54
- key +
55
- " at index " +
56
- i +
57
- ": Expected " +
58
- s[key] +
59
- ", got " +
60
- actual.schedule[i][key]
72
+ "Different schedule values for " + key + " at index " + i + ": Expected " + s[key] + ", got " + actual[i][key]
61
73
  );
62
74
  res = false;
63
75
  }
64
76
  });
65
77
  });
66
- expected.hours.forEach((s, i) => {
67
- ["price", "onOff", "start", "saving"].forEach((key) => {
68
- if (s[key] != actual.hours[i][key]) {
78
+ return res;
79
+ }
80
+
81
+ function equalHours(expected, actual, properties = ["price", "onOff", "start", "saving"]) {
82
+ let res = true;
83
+ if (expected.length !== actual.length) {
84
+ console.log("Hours have different lengths: Expected " + expected.hours.length + ", got " + actual.hours.length);
85
+ }
86
+
87
+ expected.forEach((s, i) => {
88
+ properties.forEach((key) => {
89
+ if (s[key] != actual[i][key]) {
69
90
  console.log(
70
- "Different hour values for " +
71
- key +
72
- " at index " +
73
- i +
74
- ": Expected " +
75
- s[key] +
76
- ", got " +
77
- actual.hours[i][key]
91
+ "Different hour values for " + key + " at index " + i + ": Expected " + s[key] + ", got " + actual[i][key]
78
92
  );
79
93
  res = false;
80
94
  }
81
95
  });
82
96
  });
83
97
 
84
- ["maxHoursToSaveInSequence", "minHoursOnAfterMaxSequenceSaved", "minSaving", "outputIfNoSchedule"].forEach((key) => {
85
- if (expected.config[key] != actual.config[key]) {
86
- console.log(
87
- "Different config values for " + key + ": Expected " + expected.config[key] + ", got " + actual.config[key]
88
- );
89
- res = false;
90
- }
91
- });
92
-
93
98
  return res;
94
99
  }
95
100
 
@@ -97,4 +102,6 @@ module.exports = {
97
102
  testPlan,
98
103
  makePayload,
99
104
  equalPlan,
105
+ equalHours,
106
+ equalSchedule,
100
107
  };
@@ -1,3 +1,4 @@
1
+ const cloneDeep = require("lodash.clonedeep");
1
2
  const { DateTime } = require("luxon");
2
3
  const expect = require("expect");
3
4
  const {
@@ -8,10 +9,12 @@ const {
8
9
  getSavings,
9
10
  countAtEnd,
10
11
  makeSchedule,
12
+ makeScheduleFromHours,
11
13
  fillArray,
12
14
  extractPlanForDate,
13
15
  isSameDate,
14
16
  } = require("../src/utils");
17
+ const testResult = require("./data/best-save-result.json");
15
18
 
16
19
  describe("utils", () => {
17
20
  it("can test boolean config", () => {
@@ -195,4 +198,14 @@ describe("utils", () => {
195
198
  };
196
199
  expect(extractPlanForDate(plan, "2021-06-20T01:50:00.000+02:00")).toEqual(part1);
197
200
  });
201
+ it("Can make schedule from hours", () => {
202
+ const hours = cloneDeep(testResult.hours);
203
+ const schedule = makeScheduleFromHours(hours, null);
204
+ const resultToValidate = schedule.map((s) => ({ time: s.time, value: s.value }));
205
+ resultToValidate.push({
206
+ time: "2021-06-20T02:50:00.470+02:00",
207
+ value: false,
208
+ });
209
+ expect(resultToValidate).toEqual(testResult.schedule);
210
+ });
198
211
  });
Binary file
@@ -1,23 +0,0 @@
1
- ---
2
- next: ./ps-strategy-best-save.md
3
- ---
4
-
5
- # power-saver <Badge type="warning" text="deprecated" vertical="middle" />
6
-
7
- ![Power Saver node](../images/node-power-saver.png)
8
-
9
- This is the node from version 2. It is still working, but should be replaced.
10
-
11
- To migrate, just replace the Power Saver node by a combination of the ps-receive-price and the ps-best-save nodes:
12
-
13
- Replace the `Power Saver` node from version 2:
14
-
15
- ![Power Saver node](../images/migrate-power-saver.png)
16
-
17
- with this combination of `ps-receive-price` and `ps-strategy-best-save` from version 3:
18
-
19
- ![Migrate Power Saver](../images/migrate-best-save.png)
20
-
21
- The configuration is done in the `ps-strategy-best-save` node, and is the same as in the old `Power Saver` node.
22
-
23
- Should you need it, here is the [old documentation](./old-power-saver-doc) for the PowerSaver node from version 2.