node-red-contrib-power-saver 3.5.7 → 3.6.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 (35) hide show
  1. package/docs/.vuepress/config.js +3 -10
  2. package/docs/changelog/README.md +13 -0
  3. package/docs/examples/README.md +2 -0
  4. package/docs/examples/example-grid-tariff-capacity-flow.json +1126 -0
  5. package/docs/examples/example-grid-tariff-capacity-part.md +353 -107
  6. package/docs/images/example-capacity-flow.png +0 -0
  7. package/docs/images/lowest-price-config.png +0 -0
  8. package/docs/nodes/ps-strategy-best-save.md +4 -2
  9. package/docs/nodes/ps-strategy-lowest-price.md +27 -13
  10. package/package.json +2 -1
  11. package/src/elvia/elvia-add-tariff.js +1 -2
  12. package/src/elvia/elvia-api.js +3 -0
  13. package/src/strategy-best-save-functions.js +5 -1
  14. package/src/strategy-lowest-price.html +12 -0
  15. package/src/strategy-lowest-price.js +11 -2
  16. package/src/utils.js +5 -2
  17. package/test/data/best-save-overlap-prices.json +197 -0
  18. package/test/data/best-save-overlap-result.json +357 -0
  19. package/test/data/lowest-price-result-cont-max-fail.json +14 -0
  20. package/test/data/lowest-price-result-cont-max.json +20 -0
  21. package/test/data/lowest-price-result-cont.json +6 -5
  22. package/test/data/lowest-price-result-missing-end.json +7 -3
  23. package/test/data/lowest-price-result-split-allday.json +9 -8
  24. package/test/data/lowest-price-result-split-allday10.json +8 -7
  25. package/test/data/lowest-price-result-split-max.json +22 -0
  26. package/test/data/lowest-price-result-split.json +8 -7
  27. package/test/data/nordpool-3-days-result.json +10 -5
  28. package/test/data/result.js +9 -0
  29. package/test/data/tibber-result-end-0-24h.json +9 -4
  30. package/test/data/tibber-result-end-0.json +5 -2
  31. package/test/data/tibber-result.json +28 -14
  32. package/test/power-saver.test.js +6 -0
  33. package/test/strategy-best-save-overlap.test.js +52 -0
  34. package/test/strategy-lowest-price.test.js +88 -31
  35. package/test/utils.test.js +9 -8
@@ -91,6 +91,55 @@ describe("ps-strategy-lowest-price node", function () {
91
91
  n1.receive({ payload: makePayload(prices, time) });
92
92
  });
93
93
  });
94
+ it("should plan correct continuous schedule with max price ok", function (done) {
95
+ const resultContinuousMax = require("./data/lowest-price-result-cont-max.json");
96
+ const flow = makeFlow(4, 1.0);
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", resultContinuousMax.schedule);
102
+ expect(msg.payload).toHaveProperty("config", resultContinuousMax.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(prices, time) });
108
+ });
109
+ });
110
+ it("should plan correct continuous schedule with max price too high", function (done) {
111
+ const resultContinuousMax = require("./data/lowest-price-result-cont-max-fail.json");
112
+ const flow = makeFlow(4, 0.23);
113
+ helper.load(lowestPrice, flow, function () {
114
+ const n1 = helper.getNode("n1");
115
+ const n2 = helper.getNode("n2");
116
+ n2.on("input", function (msg) {
117
+ expect(msg.payload).toHaveProperty("schedule", resultContinuousMax.schedule);
118
+ expect(msg.payload).toHaveProperty("config", resultContinuousMax.config);
119
+ n1.warn.should.not.be.called;
120
+ done();
121
+ });
122
+ const time = DateTime.fromISO(prices.priceData[10].start);
123
+ n1.receive({ payload: makePayload(prices, time) });
124
+ });
125
+ });
126
+ it("should plan correct splitted schedule with max price", function (done) {
127
+ const resultSplittedMax = require("./data/lowest-price-result-split-max.json");
128
+ const flow = makeFlow(6, 0.51);
129
+ flow[0].doNotSplit = false;
130
+ helper.load(lowestPrice, flow, function () {
131
+ const n1 = helper.getNode("n1");
132
+ const n2 = helper.getNode("n2");
133
+ n2.on("input", function (msg) {
134
+ expect(msg.payload).toHaveProperty("schedule", resultSplittedMax.schedule);
135
+ expect(msg.payload).toHaveProperty("config", resultSplittedMax.config);
136
+ n1.warn.should.not.be.called;
137
+ done();
138
+ });
139
+ const time = DateTime.fromISO(prices.priceData[10].start);
140
+ n1.receive({ payload: makePayload(prices, time) });
141
+ });
142
+ });
94
143
  it("should plan correct for all day period - 00-00", function (done) {
95
144
  const resultAllDay = require("./data/lowest-price-result-split-allday.json");
96
145
  const flow = makeFlow(8);
@@ -186,12 +235,14 @@ describe("ps-strategy-lowest-price node", function () {
186
235
  const schedule = cloneDeep(resultSplitted.schedule);
187
236
  const config = cloneDeep(resultSplitted.config);
188
237
  schedule[0].value = true;
189
- schedule.splice(1, 0, { time: "2021-10-11T10:00:00.000+02:00", value: false });
190
- schedule.splice(4, 0, { time: "2021-10-11T20:00:00.000+02:00", value: true });
191
- schedule.splice(5, 0, { time: "2021-10-12T10:00:00.000+02:00", value: false });
238
+ schedule.splice(1, 0, { time: "2021-10-11T10:00:00.000+02:00", value: false, countHours: 10 });
239
+ schedule.splice(4, 0, { time: "2021-10-11T20:00:00.000+02:00", value: true, countHours: 14 });
240
+ schedule.splice(5, 0, { time: "2021-10-12T10:00:00.000+02:00", value: false, countHours: 14 });
192
241
  schedule.splice(schedule.length - 1, 1);
193
242
  config.outputOutsidePeriod = true;
194
- expect(msg.payload).toHaveProperty("schedule", schedule);
243
+ const res = msg.payload.schedule.map((s) => ({ time: s.time, value: s.value }));
244
+ const exp = schedule.map((s) => ({ time: s.time, value: s.value }));
245
+ expect(res).toEqual(exp);
195
246
  expect(msg.payload).toHaveProperty("config", config);
196
247
  n1.warn.should.not.be.called;
197
248
  done();
@@ -218,7 +269,9 @@ describe("ps-strategy-lowest-price node", function () {
218
269
  schedule.splice(schedule.length, 0, { time: "2021-10-12T20:00:00.000+02:00", value: true });
219
270
  // schedule.splice(schedule.length - 1, 1);
220
271
  config.outputOutsidePeriod = true;
221
- expect(msg.payload).toHaveProperty("schedule", schedule);
272
+ const res = msg.payload.schedule.map((s) => ({ time: s.time, value: s.value }));
273
+ const exp = schedule.map((s) => ({ time: s.time, value: s.value }));
274
+ expect(res).toEqual(exp);
222
275
  expect(msg.payload).toHaveProperty("config", config);
223
276
  n1.warn.should.not.be.called;
224
277
  done();
@@ -228,7 +281,7 @@ describe("ps-strategy-lowest-price node", function () {
228
281
  });
229
282
  });
230
283
  it("should work with 0 hours on", function (done) {
231
- const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: false }];
284
+ const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 48 }];
232
285
  const flow = makeFlow(0);
233
286
  helper.load(lowestPrice, flow, function () {
234
287
  const n1 = helper.getNode("n1");
@@ -244,11 +297,11 @@ describe("ps-strategy-lowest-price node", function () {
244
297
  });
245
298
  it("should work with 0 hours on outside on", function (done) {
246
299
  const result = [
247
- { time: "2021-10-11T00:00:00.000+02:00", value: true },
248
- { time: "2021-10-11T10:00:00.000+02:00", value: false },
249
- { time: "2021-10-11T20:00:00.000+02:00", value: true },
250
- { time: "2021-10-12T10:00:00.000+02:00", value: false },
251
- { time: "2021-10-12T20:00:00.000+02:00", value: true },
300
+ { time: "2021-10-11T00:00:00.000+02:00", value: true, countHours: 10 },
301
+ { time: "2021-10-11T10:00:00.000+02:00", value: false, countHours: 10 },
302
+ { time: "2021-10-11T20:00:00.000+02:00", value: true, countHours: 14 },
303
+ { time: "2021-10-12T10:00:00.000+02:00", value: false, countHours: 10 },
304
+ { time: "2021-10-12T20:00:00.000+02:00", value: true, countHours: 4 },
252
305
  ];
253
306
  const flow = makeFlow(0);
254
307
  flow[0].outputOutsidePeriod = true;
@@ -266,11 +319,11 @@ describe("ps-strategy-lowest-price node", function () {
266
319
  });
267
320
  it("should work with 1 hours on", function (done) {
268
321
  const result = [
269
- { time: "2021-10-11T00:00:00.000+02:00", value: false },
270
- { time: "2021-10-11T12:00:00.000+02:00", value: true },
271
- { time: "2021-10-11T13:00:00.000+02:00", value: false },
272
- { time: "2021-10-12T14:00:00.000+02:00", value: true },
273
- { time: "2021-10-12T15:00:00.000+02:00", value: false },
322
+ { time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 12 },
323
+ { time: "2021-10-11T12:00:00.000+02:00", value: true, countHours: 1 },
324
+ { time: "2021-10-11T13:00:00.000+02:00", value: false, countHours: 25 },
325
+ { time: "2021-10-12T14:00:00.000+02:00", value: true, countHours: 1 },
326
+ { time: "2021-10-12T15:00:00.000+02:00", value: false, countHours: 9 },
274
327
  ];
275
328
  const flow = makeFlow(1);
276
329
  helper.load(lowestPrice, flow, function () {
@@ -287,11 +340,11 @@ describe("ps-strategy-lowest-price node", function () {
287
340
  });
288
341
  it("should work with 24 hours on", function (done) {
289
342
  const result = [
290
- { time: "2021-10-11T00:00:00.000+02:00", value: false },
291
- { time: "2021-10-11T10:00:00.000+02:00", value: true },
292
- { time: "2021-10-11T20:00:00.000+02:00", value: false },
293
- { time: "2021-10-12T10:00:00.000+02:00", value: true },
294
- { time: "2021-10-12T20:00:00.000+02:00", value: false },
343
+ { time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 10 },
344
+ { time: "2021-10-11T10:00:00.000+02:00", value: true, countHours: 10 },
345
+ { time: "2021-10-11T20:00:00.000+02:00", value: false, countHours: 14 },
346
+ { time: "2021-10-12T10:00:00.000+02:00", value: true, countHours: 10 },
347
+ { time: "2021-10-12T20:00:00.000+02:00", value: false, countHours: 4 },
295
348
  ];
296
349
  const flow = makeFlow(24);
297
350
  helper.load(lowestPrice, flow, function () {
@@ -311,9 +364,9 @@ describe("ps-strategy-lowest-price node", function () {
311
364
  const oneDayPrices = {};
312
365
  oneDayPrices.priceData = prices.priceData.filter((d) => d.start.startsWith("2021-10-11"));
313
366
  const result = [
314
- { time: "2021-10-11T00:00:00.000+02:00", value: false },
315
- { time: "2021-10-11T12:00:00.000+02:00", value: true },
316
- { time: "2021-10-11T13:00:00.000+02:00", value: false },
367
+ { time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 12 },
368
+ { time: "2021-10-11T12:00:00.000+02:00", value: true, countHours: 1 },
369
+ { time: "2021-10-11T13:00:00.000+02:00", value: false, countHours: 11 },
317
370
  ];
318
371
  const flow = makeFlow(1);
319
372
  helper.load(lowestPrice, flow, function () {
@@ -340,7 +393,7 @@ describe("ps-strategy-lowest-price node", function () {
340
393
  });
341
394
 
342
395
  it("should handle hours on > period", function (done) {
343
- const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: true }];
396
+ const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: true, countHours: 48 }];
344
397
  const flow = [
345
398
  {
346
399
  id: "n1",
@@ -388,11 +441,11 @@ describe("ps-strategy-lowest-price node", function () {
388
441
  });
389
442
  it("should handle hours on > period, false outside", function (done) {
390
443
  const result = [
391
- { time: "2021-10-11T00:00:00.000+02:00", value: false },
392
- { time: "2021-10-11T17:00:00.000+02:00", value: true },
393
- { time: "2021-10-11T22:00:00.000+02:00", value: false },
394
- { time: "2021-10-12T17:00:00.000+02:00", value: true },
395
- { time: "2021-10-12T22:00:00.000+02:00", value: false },
444
+ { time: "2021-10-11T00:00:00.000+02:00", value: false, countHours: 17 },
445
+ { time: "2021-10-11T17:00:00.000+02:00", value: true, countHours: 5 },
446
+ { time: "2021-10-11T22:00:00.000+02:00", value: false, countHours: 19 },
447
+ { time: "2021-10-12T17:00:00.000+02:00", value: true, countHours: 5 },
448
+ { time: "2021-10-12T22:00:00.000+02:00", value: false, countHours: 2 },
396
449
  ];
397
450
  const flow = [
398
451
  {
@@ -451,6 +504,7 @@ describe("ps-strategy-lowest-price node", function () {
451
504
  fromTime: "16",
452
505
  toTime: "00",
453
506
  hoursOn: 3,
507
+ maxPrice: null,
454
508
  doNotSplit: false,
455
509
  sendCurrentValueWhenRescheduling: true,
456
510
  outputIfNoSchedule: false,
@@ -485,6 +539,7 @@ describe("ps-strategy-lowest-price node", function () {
485
539
  fromTime: "16",
486
540
  toTime: "00",
487
541
  hoursOn: 3,
542
+ maxPrice: null,
488
543
  doNotSplit: false,
489
544
  sendCurrentValueWhenRescheduling: true,
490
545
  outputIfNoSchedule: false,
@@ -519,6 +574,7 @@ describe("ps-strategy-lowest-price node", function () {
519
574
  fromTime: "22",
520
575
  toTime: "08",
521
576
  hoursOn: 3,
577
+ maxPrice: null,
522
578
  doNotSplit: true,
523
579
  sendCurrentValueWhenRescheduling: true,
524
580
  outputIfNoSchedule: false,
@@ -544,7 +600,7 @@ describe("ps-strategy-lowest-price node", function () {
544
600
  });
545
601
  });
546
602
 
547
- function makeFlow(hoursOn) {
603
+ function makeFlow(hoursOn, maxPrice = null) {
548
604
  return [
549
605
  {
550
606
  id: "n1",
@@ -553,6 +609,7 @@ function makeFlow(hoursOn) {
553
609
  fromTime: "10",
554
610
  toTime: "20",
555
611
  hoursOn: hoursOn,
612
+ maxPrice: maxPrice,
556
613
  doNotSplit: true,
557
614
  sendCurrentValueWhenRescheduling: true,
558
615
  outputIfNoSchedule: true,
@@ -70,18 +70,19 @@ describe("utils", () => {
70
70
  "2021-06-20T09:00:00+02:00",
71
71
  ];
72
72
  expect(makeSchedule(onOff, startTimes)).toEqual([
73
- { time: "2021-06-20T05:00:00+02:00", value: false },
74
- { time: "2021-06-20T07:00:00+02:00", value: true },
75
- { time: "2021-06-20T09:00:00+02:00", value: false },
73
+ { time: "2021-06-20T05:00:00+02:00", value: false, countHours: 2 },
74
+ { time: "2021-06-20T07:00:00+02:00", value: true, countHours: 2 },
75
+ { time: "2021-06-20T09:00:00+02:00", value: false, countHours: 1 },
76
76
  ]);
77
77
  expect(makeSchedule(onOff, startTimes, true)).toEqual([
78
- { time: "2021-06-20T05:00:00+02:00", value: false },
79
- { time: "2021-06-20T07:00:00+02:00", value: true },
80
- { time: "2021-06-20T09:00:00+02:00", value: false },
78
+ { time: "2021-06-20T05:00:00+02:00", value: false, countHours: 2 },
79
+ { time: "2021-06-20T07:00:00+02:00", value: true, countHours: 2 },
80
+ { time: "2021-06-20T09:00:00+02:00", value: false, countHours: 1 },
81
81
  ]);
82
82
  expect(makeSchedule(onOff, startTimes, false)).toEqual([
83
- { time: "2021-06-20T07:00:00+02:00", value: true },
84
- { time: "2021-06-20T09:00:00+02:00", value: false },
83
+ { time: "2021-06-20T05:00:00+02:00", value: false, countHours: 2 }, // Right???
84
+ { time: "2021-06-20T07:00:00+02:00", value: true, countHours: 2 },
85
+ { time: "2021-06-20T09:00:00+02:00", value: false, countHours: 1 },
85
86
  ]);
86
87
  });
87
88