node-red-contrib-power-saver 2.1.0 → 3.0.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 (174) hide show
  1. package/CHANGELOG.md +1 -43
  2. package/README.md +3 -231
  3. package/docs/.vuepress/config.js +67 -0
  4. package/docs/.vuepress/dist/404.html +15 -0
  5. package/docs/.vuepress/dist/assets/css/styles.e835bef6.css +8 -0
  6. package/docs/.vuepress/dist/assets/img/back-to-top.8b37f773.svg +1 -0
  7. package/docs/.vuepress/dist/assets/img/elvia-config-no-config.b4bb972c.png +0 -0
  8. package/docs/.vuepress/dist/assets/img/elvia-config-no-tariff.3f89aba8.png +0 -0
  9. package/docs/.vuepress/dist/assets/img/elvia-config-select-tariff.0f73fd56.png +0 -0
  10. package/docs/.vuepress/dist/assets/img/elvia-config-subscription-key.8be8ab8a.png +0 -0
  11. package/docs/.vuepress/dist/assets/img/elvia-flow.bae2a4d5.png +0 -0
  12. package/docs/.vuepress/dist/assets/img/example-flow-1.3ff3e23f.png +0 -0
  13. package/docs/.vuepress/dist/assets/img/example-flow-2.b653b58d.png +0 -0
  14. package/docs/.vuepress/dist/assets/img/migrate-best-save.f73420f6.png +0 -0
  15. package/docs/.vuepress/dist/assets/img/migrate-power-saver.aae13f9d.png +0 -0
  16. package/docs/.vuepress/dist/assets/img/node-power-saver.51ff2e5d.png +0 -0
  17. package/docs/.vuepress/dist/assets/img/node-ps-elvia-add-tariff.94ea2b09.png +0 -0
  18. package/docs/.vuepress/dist/assets/img/node-ps-receive-price.76eaa418.png +0 -0
  19. package/docs/.vuepress/dist/assets/img/node-ps-strategy-best-save.392292d5.png +0 -0
  20. package/docs/.vuepress/dist/assets/img/node-ps-strategy-lowest-price.3a4ad347.png +0 -0
  21. package/docs/.vuepress/dist/assets/img/power-saver-nordpool-current-state.bf14afde.png +0 -0
  22. package/docs/.vuepress/dist/assets/img/power-saver-nordpool-events-state.8c392507.png +0 -0
  23. package/docs/.vuepress/dist/assets/img/power-saver-tibber-mqtt.16891dd2.png +0 -0
  24. package/docs/.vuepress/dist/assets/js/293.5e967839.js +1 -0
  25. package/docs/.vuepress/dist/assets/js/491.c183eba3.js +1 -0
  26. package/docs/.vuepress/dist/assets/js/812.79dad458.js +2 -0
  27. package/docs/.vuepress/dist/assets/js/812.79dad458.js.LICENSE.txt +8 -0
  28. package/docs/.vuepress/dist/assets/js/app.80d4373d.js +1 -0
  29. package/docs/.vuepress/dist/assets/js/runtime~app.665b411c.js +1 -0
  30. package/docs/.vuepress/dist/assets/js/v-08683c60.9edcaa60.js +1 -0
  31. package/docs/.vuepress/dist/assets/js/v-0aca7ba6.5eca5160.js +1 -0
  32. package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.c02472fb.js +1 -0
  33. package/docs/.vuepress/dist/assets/js/v-1ad821fa.a628e907.js +1 -0
  34. package/docs/.vuepress/dist/assets/js/v-30acb564.80b4190d.js +1 -0
  35. package/docs/.vuepress/dist/assets/js/v-3706649a.d7f73384.js +1 -0
  36. package/docs/.vuepress/dist/assets/js/v-4637f9e4.df94c6ea.js +1 -0
  37. package/docs/.vuepress/dist/assets/js/v-510ed0d4.a78d8542.js +1 -0
  38. package/docs/.vuepress/dist/assets/js/v-5954bcb2.4f4712f3.js +1 -0
  39. package/docs/.vuepress/dist/assets/js/v-5db8da3a.2e49b81a.js +1 -0
  40. package/docs/.vuepress/dist/assets/js/v-61f728ca.7b545524.js +1 -0
  41. package/docs/.vuepress/dist/assets/js/v-677dfaed.756e0fb5.js +1 -0
  42. package/docs/.vuepress/dist/assets/js/v-7c87f26e.57507077.js +1 -0
  43. package/docs/.vuepress/dist/assets/js/v-8daa1a0e.1ea39527.js +1 -0
  44. package/docs/.vuepress/dist/assets/js/v-b4a42144.a812c440.js +1 -0
  45. package/docs/.vuepress/dist/assets/js/v-e8c55052.30f30acd.js +1 -0
  46. package/docs/.vuepress/dist/assets/js/v-fffb8e28.850019c1.js +1 -0
  47. package/docs/.vuepress/dist/changelog/index.html +15 -0
  48. package/docs/.vuepress/dist/contribute/index.html +15 -0
  49. package/docs/.vuepress/dist/euro.png +0 -0
  50. package/docs/.vuepress/dist/examples/example-nordpool-current-state.html +169 -0
  51. package/docs/.vuepress/dist/examples/example-nordpool-events-state.html +173 -0
  52. package/docs/.vuepress/dist/examples/example-tibber-mqtt.html +182 -0
  53. package/docs/.vuepress/dist/examples/index.html +15 -0
  54. package/docs/.vuepress/dist/guide/index.html +52 -0
  55. package/docs/.vuepress/dist/index.html +15 -0
  56. package/docs/.vuepress/dist/logo.png +0 -0
  57. package/docs/.vuepress/dist/nodes/index.html +15 -0
  58. package/docs/.vuepress/dist/nodes/old-power-saver-doc.html +97 -0
  59. package/docs/.vuepress/dist/nodes/power-saver.html +15 -0
  60. package/docs/.vuepress/dist/nodes/ps-elvia-add-tariff.html +15 -0
  61. package/docs/.vuepress/dist/nodes/ps-receive-price.html +80 -0
  62. package/docs/.vuepress/dist/nodes/ps-strategy-best-save.html +65 -0
  63. package/docs/.vuepress/dist/nodes/ps-strategy-lowest-price.html +89 -0
  64. package/docs/.vuepress/dist/nodes/strategy-input.html +40 -0
  65. package/docs/.vuepress/public/euro.png +0 -0
  66. package/docs/.vuepress/public/logo.png +0 -0
  67. package/docs/README.md +32 -0
  68. package/docs/changelog/README.md +55 -0
  69. package/docs/contribute/README.md +39 -0
  70. package/docs/examples/README.md +5 -0
  71. package/docs/examples/example-nordpool-current-state.md +166 -0
  72. package/docs/examples/example-nordpool-events-state.md +170 -0
  73. package/docs/examples/example-tibber-mqtt.md +179 -0
  74. package/docs/guide/README.md +193 -0
  75. package/docs/images/all-nodes.png +0 -0
  76. package/docs/images/best-save-config.png +0 -0
  77. package/docs/images/elvia-add-tariff-node-used.png +0 -0
  78. package/docs/images/elvia-config-no-config.png +0 -0
  79. package/docs/images/elvia-config-no-tariff.png +0 -0
  80. package/docs/images/elvia-config-select-tariff.png +0 -0
  81. package/docs/images/elvia-config-subscription-key.png +0 -0
  82. package/docs/images/elvia-flow.png +0 -0
  83. package/docs/images/elvia-tariff-config.png +0 -0
  84. package/docs/images/euro.png +0 -0
  85. package/docs/images/example-flow-1.png +0 -0
  86. package/docs/images/example-flow-2.png +0 -0
  87. package/docs/images/logo.png +0 -0
  88. package/docs/images/lowest-price-config.png +0 -0
  89. package/docs/images/migrate-best-save.png +0 -0
  90. package/docs/images/migrate-power-saver.png +0 -0
  91. package/docs/images/node-power-saver.png +0 -0
  92. package/docs/images/node-ps-elvia-add-tariff.png +0 -0
  93. package/docs/images/node-ps-elvia-tariff-types.png +0 -0
  94. package/docs/images/node-ps-elvia-tariff.png +0 -0
  95. package/docs/images/node-ps-receive-price.png +0 -0
  96. package/docs/images/node-ps-strategy-best-save.png +0 -0
  97. package/docs/images/node-ps-strategy-lowest-price.png +0 -0
  98. package/{doc → docs/images}/node-red-contrib-power-saver-flow.png +0 -0
  99. package/docs/images/power-saver-nordpool-current-state.png +0 -0
  100. package/docs/images/power-saver-nordpool-events-state.png +0 -0
  101. package/docs/images/power-saver-tibber-mqtt.png +0 -0
  102. package/docs/nodes/README.md +53 -0
  103. package/docs/nodes/old-power-saver-doc.md +231 -0
  104. package/docs/nodes/power-saver.md +23 -0
  105. package/docs/nodes/ps-elvia-add-tariff.md +52 -0
  106. package/docs/nodes/ps-receive-price.md +153 -0
  107. package/docs/nodes/ps-strategy-best-save.md +142 -0
  108. package/docs/nodes/ps-strategy-lowest-price.md +165 -0
  109. package/docs/nodes/strategy-input.md +39 -0
  110. package/package.json +16 -4
  111. package/src/elvia/elvia-add-tariff.html +70 -0
  112. package/src/elvia/elvia-add-tariff.js +47 -0
  113. package/src/elvia/elvia-api.js +61 -0
  114. package/src/elvia/elvia-config.html +44 -0
  115. package/src/elvia/elvia-config.js +25 -0
  116. package/src/elvia/elvia-tariff-types.html +34 -0
  117. package/src/elvia/elvia-tariff-types.js +23 -0
  118. package/src/elvia/elvia-tariff.html +89 -0
  119. package/src/elvia/elvia-tariff.js +26 -0
  120. package/src/elvia/icons/elvia_hvite.svg +4 -0
  121. package/src/elvia/icons/elvia_positive_4 copy.svg +4 -0
  122. package/src/handle-input.js +159 -0
  123. package/src/power-saver.html +116 -0
  124. package/{power-saver.js → src/power-saver.js} +9 -32
  125. package/src/receive-price-functions.js +99 -0
  126. package/src/receive-price.html +30 -0
  127. package/src/receive-price.js +21 -0
  128. package/src/strategy-best-save-functions.js +110 -0
  129. package/src/strategy-best-save.html +116 -0
  130. package/src/strategy-best-save.js +95 -0
  131. package/src/strategy-lowest-price-functions.js +35 -0
  132. package/src/strategy-lowest-price.html +168 -0
  133. package/src/strategy-lowest-price.js +125 -0
  134. package/{utils.js → src/utils.js} +44 -100
  135. package/test/data/adjustedResult.js +219 -71
  136. package/test/data/adjustedResult_old.js +154 -0
  137. package/test/data/best-save-result.json +357 -0
  138. package/test/data/converted-prices.json +196 -0
  139. package/test/data/elvia-input-grid-tariff.json +760 -0
  140. package/test/data/elvia-input-power-prices.json +194 -0
  141. package/test/data/elvia-output-add-tariff.json +290 -0
  142. package/test/data/lowest-price-result-cont.json +18 -0
  143. package/test/data/lowest-price-result-split-allday.json +21 -0
  144. package/test/data/lowest-price-result-split-allday10.json +20 -0
  145. package/test/data/lowest-price-result-split.json +20 -0
  146. package/test/data/nordpool-current-state-prices.json +283 -0
  147. package/test/data/nordpool-event-prices.json +574 -0
  148. package/test/data/reconfigResult.js +220 -67
  149. package/test/data/reconfigResult_old.js +141 -0
  150. package/test/data/tibber-prices-single-home.json +64 -0
  151. package/test/data/tibber-prices.json +124 -0
  152. package/test/data/{tibber_result.json → tibber-result.json} +0 -0
  153. package/test/elvia.test.js +26 -0
  154. package/test/mostSavedStrategy.test.js +22 -55
  155. package/test/power-saver.test.js +21 -5
  156. package/test/receive-price-functions.test.js +153 -0
  157. package/test/receive-price.test.js +122 -0
  158. package/test/send-config-input.test.js +8 -10
  159. package/test/strategy-best-save-test-utils.js +32 -0
  160. package/test/strategy-best-save.test.js +103 -0
  161. package/test/strategy-lowest-price-functions.test.js +40 -0
  162. package/test/strategy-lowest-price.test.js +338 -0
  163. package/test/test-utils.js +0 -18
  164. package/test/utils.test.js +22 -180
  165. package/doc/example-nordpool-current-state.md +0 -166
  166. package/doc/example-nordpool-events-state.md +0 -153
  167. package/doc/example-tibber-mqtt.md +0 -189
  168. package/doc/power-saver-nordpool-current-state.png +0 -0
  169. package/doc/power-saver-nordpool-events-state.png +0 -0
  170. package/doc/power-saver-tibber-mqtt.png +0 -0
  171. package/mostSavedStrategy.js +0 -84
  172. package/power-saver.html +0 -308
  173. package/test/data/tibber_data.json +0 -412
  174. package/test/data/tibber_prices.json +0 -412
@@ -0,0 +1,103 @@
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 bestSave = require("../src/strategy-best-save.js");
6
+ const prices = require("./data/converted-prices.json");
7
+ const result = require("./data/best-save-result.json");
8
+ const { testPlan: plan } = require("./test-utils");
9
+ const { makeFlow } = require("./strategy-best-save-test-utils");
10
+
11
+ helper.init(require.resolve("node-red"));
12
+
13
+ describe("ps-strategy-best-save node", function () {
14
+ beforeEach(function (done) {
15
+ helper.startServer(done);
16
+ });
17
+
18
+ afterEach(function (done) {
19
+ helper.unload().then(function () {
20
+ helper.stopServer(done);
21
+ });
22
+ });
23
+
24
+ it("should be loaded", function (done) {
25
+ const flow = [{ id: "n1", type: "ps-strategy-best-save", name: "test name" }];
26
+ helper.load(bestSave, flow, function () {
27
+ const n1 = helper.getNode("n1");
28
+ expect(n1).toHaveProperty("name", "test name");
29
+ done();
30
+ });
31
+ });
32
+
33
+ it("should log error when illegal data is received", function (done) {
34
+ const flow = [{ id: "n1", type: "ps-strategy-best-save", name: "test name" }];
35
+ helper.load(bestSave, flow, function () {
36
+ const n1 = helper.getNode("n1");
37
+ n1.receive({});
38
+ n1.warn.should.be.calledWithExactly("No payload");
39
+ n1.receive({ payload: "Error" });
40
+ n1.warn.should.be.calledWithExactly("Payload is not an object");
41
+ n1.receive({ payload: [] });
42
+ n1.warn.should.be.calledWithExactly("Payload is missing priceData");
43
+ n1.receive({ payload: { priceData: [] } });
44
+ n1.warn.should.be.calledWithExactly("priceData is empty");
45
+ n1.receive({ payload: { priceData: { today: [], tomorrow: [] } } });
46
+ n1.warn.should.be.calledWithExactly("Illegal priceData in payload. Did you use the receive-price node?");
47
+
48
+ ["start", "value"].forEach((attr) => {
49
+ const testData1 = cloneDeep(prices);
50
+ delete testData1.priceData[3][attr];
51
+ n1.receive({ payload: testData1 });
52
+ n1.warn.should.be.calledWithExactly(
53
+ "Malformed entries in priceData. All entries must contain start and value."
54
+ );
55
+ });
56
+
57
+ n1.receive({ payload: cloneDeep(prices) });
58
+ n1.warn.should.not.be.called;
59
+ done();
60
+ });
61
+ });
62
+ it("should send new schedule on output 3", function (done) {
63
+ const flow = makeFlow(3, 2);
64
+ helper.load(bestSave, flow, function () {
65
+ const n1 = helper.getNode("n1");
66
+ const n2 = helper.getNode("n2");
67
+ const n3 = helper.getNode("n3");
68
+ const n4 = helper.getNode("n4");
69
+ let countOn = 0;
70
+ let countOff = 0;
71
+ n2.on("input", function (msg) {
72
+ expect(msg).toHaveProperty("payload", result);
73
+ n1.warn.should.not.be.called;
74
+ });
75
+ n3.on("input", function (msg) {
76
+ countOn++;
77
+ expect(msg).toHaveProperty("payload", true);
78
+ if (countOn === 7 && countOff === 7) {
79
+ done();
80
+ }
81
+ });
82
+ n4.on("input", function (msg) {
83
+ countOff++;
84
+ expect(msg).toHaveProperty("payload", false);
85
+ if (countOn === 7 && countOff === 7) {
86
+ done();
87
+ }
88
+ });
89
+ n1.receive({ payload: makePayload(prices, plan.time) });
90
+ });
91
+ });
92
+ });
93
+
94
+ function makePayload(prices, time) {
95
+ const payload = cloneDeep(prices);
96
+ payload.time = time;
97
+ let entryTime = DateTime.fromISO(payload.time);
98
+ payload.priceData.forEach((e) => {
99
+ e.start = entryTime.toISO();
100
+ entryTime = entryTime.plus({ milliseconds: 10 });
101
+ });
102
+ return payload;
103
+ }
@@ -0,0 +1,40 @@
1
+ const { DateTime } = require("luxon");
2
+ const expect = require("expect");
3
+ const { getBestContinuous, getBestX } = require("../src/strategy-lowest-price-functions");
4
+ const convertedPrices = require("./data/converted-prices.json");
5
+ const { cloneDeep } = require("lodash");
6
+
7
+ describe("strategy-utils", () => {
8
+ it("can find best x", () => {
9
+ const values = convertedPrices.priceData.slice(0, 48).map((p) => p.value);
10
+ const result = cloneDeep(values).fill(false);
11
+ result.fill(true, 0, 6);
12
+ result.fill(true, 24, 31);
13
+ expect(getBestX(values, 13)).toEqual(result);
14
+ const values1 = convertedPrices.priceData.slice(0, 12).map((p) => p.value);
15
+ const result1 = cloneDeep(values1).fill(false);
16
+ expect(getBestX(values1, 1)).toEqual(cloneDeep(result1).fill(true, 2, 3));
17
+ expect(getBestX(values1, 3)).toEqual(cloneDeep(result1).fill(true, 1, 4));
18
+ const values2 = convertedPrices.priceData.slice(24, 28).map((p) => p.value);
19
+ const result2 = cloneDeep(values2).fill(false);
20
+ expect(getBestX(values2, 1)).toEqual(cloneDeep(result2).fill(true, 3, 4));
21
+ expect(getBestX(values2, 3)).toEqual(cloneDeep(result2).fill(true, 1, 4));
22
+ });
23
+
24
+ it("can find best x continuous", () => {
25
+ const values = convertedPrices.priceData.slice(0, 12).map((p) => p.value);
26
+ const result = cloneDeep(values).fill(false);
27
+ expect(getBestContinuous(values, 1)).toEqual(cloneDeep(result).fill(true, 2, 3));
28
+ expect(getBestContinuous(values, 3)).toEqual(cloneDeep(result).fill(true, 1, 4));
29
+ const values2 = convertedPrices.priceData.slice(24, 28).map((p) => p.value);
30
+ const result2 = cloneDeep(values2).fill(false);
31
+ expect(getBestContinuous(values2, 1)).toEqual(cloneDeep(result2).fill(true, 3, 4));
32
+ expect(getBestContinuous(values2, 3)).toEqual(cloneDeep(result2).fill(true, 1, 4));
33
+ });
34
+ });
35
+
36
+ function validatePeriod(period, start, end, count) {
37
+ expect(period.length).toEqual(count);
38
+ expect(period[0]).toEqual(start);
39
+ expect(period[count - 1]).toEqual(end);
40
+ }
@@ -0,0 +1,338 @@
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 lowestPrice = require("../src/strategy-lowest-price.js");
6
+ const prices = require("./data/converted-prices.json");
7
+
8
+ helper.init(require.resolve("node-red"));
9
+
10
+ describe("ps-strategy-lowest-price node", function () {
11
+ beforeEach(function (done) {
12
+ helper.startServer(done);
13
+ });
14
+
15
+ afterEach(function (done) {
16
+ helper.unload().then(function () {
17
+ helper.stopServer(done);
18
+ });
19
+ });
20
+
21
+ it("should be loaded", function (done) {
22
+ const flow = [{ id: "n1", type: "ps-strategy-lowest-price", name: "test name" }];
23
+ helper.load(lowestPrice, flow, function () {
24
+ const n1 = helper.getNode("n1");
25
+ expect(n1).toHaveProperty("name", "test name");
26
+ done();
27
+ });
28
+ });
29
+
30
+ it("should log error when illegal data is received", function (done) {
31
+ const flow = [{ id: "n1", type: "ps-strategy-lowest-price", name: "test name" }];
32
+ helper.load(lowestPrice, flow, function () {
33
+ const n1 = helper.getNode("n1");
34
+ n1.receive({});
35
+ n1.warn.should.be.calledWithExactly("No payload");
36
+ n1.receive({ payload: "Error" });
37
+ n1.warn.should.be.calledWithExactly("Payload is not an object");
38
+ n1.receive({ payload: [] });
39
+ n1.warn.should.be.calledWithExactly("Payload is missing priceData");
40
+ n1.receive({ payload: { priceData: [] } });
41
+ n1.warn.should.be.calledWithExactly("priceData is empty");
42
+ n1.receive({ payload: { priceData: { today: [], tomorrow: [] } } });
43
+ n1.warn.should.be.calledWithExactly("Illegal priceData in payload. Did you use the receive-price node?");
44
+
45
+ ["start", "value"].forEach((attr) => {
46
+ const testData1 = cloneDeep(prices);
47
+ delete testData1.priceData[3][attr];
48
+ n1.receive({ payload: testData1 });
49
+ n1.warn.should.be.calledWithExactly(
50
+ "Malformed entries in priceData. All entries must contain start and value."
51
+ );
52
+ });
53
+
54
+ n1.receive({ payload: cloneDeep(prices) });
55
+ n1.warn.should.not.be.called;
56
+ done();
57
+ });
58
+ });
59
+ it("should plan correct continuous schedule", function (done) {
60
+ const resultContinuous = require("./data/lowest-price-result-cont.json");
61
+ const flow = makeFlow(4);
62
+ helper.load(lowestPrice, flow, function () {
63
+ const n1 = helper.getNode("n1");
64
+ const n2 = helper.getNode("n2");
65
+ n2.on("input", function (msg) {
66
+ expect(msg.payload).toHaveProperty("schedule", resultContinuous.schedule);
67
+ expect(msg.payload).toHaveProperty("config", resultContinuous.config);
68
+ n1.warn.should.not.be.called;
69
+ done();
70
+ });
71
+ const time = DateTime.fromISO(prices.priceData[10].start);
72
+ n1.receive({ payload: makePayload(prices, time) });
73
+ });
74
+ });
75
+ it("should plan correct splitted schedule", function (done) {
76
+ const resultSplitted = require("./data/lowest-price-result-split.json");
77
+ const flow = makeFlow(6);
78
+ flow[0].doNotSplit = false;
79
+ helper.load(lowestPrice, flow, function () {
80
+ const n1 = helper.getNode("n1");
81
+ const n2 = helper.getNode("n2");
82
+ n2.on("input", function (msg) {
83
+ expect(msg.payload).toHaveProperty("schedule", resultSplitted.schedule);
84
+ expect(msg.payload).toHaveProperty("config", resultSplitted.config);
85
+ n1.warn.should.not.be.called;
86
+ done();
87
+ });
88
+ const time = DateTime.fromISO(prices.priceData[10].start);
89
+ n1.receive({ payload: makePayload(prices, time) });
90
+ });
91
+ });
92
+ it("should plan correct for all day period - 00-00", function (done) {
93
+ const resultAllDay = require("./data/lowest-price-result-split-allday.json");
94
+ const flow = makeFlow(8);
95
+ flow[0].doNotSplit = false;
96
+ flow[0].fromTime = "00";
97
+ flow[0].toTime = "00";
98
+ helper.load(lowestPrice, flow, function () {
99
+ const n1 = helper.getNode("n1");
100
+ const n2 = helper.getNode("n2");
101
+ n2.on("input", function (msg) {
102
+ expect(msg.payload).toHaveProperty("schedule", resultAllDay.schedule);
103
+ expect(msg.payload).toHaveProperty("config", resultAllDay.config);
104
+ n1.warn.should.not.be.called;
105
+ done();
106
+ });
107
+ const time = DateTime.fromISO(prices.priceData[10].start);
108
+ n1.receive({ payload: makePayload(prices, time) });
109
+ });
110
+ });
111
+ it("should plan correct for all day period - 10-10", function (done) {
112
+ const resultAllDay10 = require("./data/lowest-price-result-split-allday10.json");
113
+ const flow = makeFlow(10);
114
+ flow[0].doNotSplit = false;
115
+ flow[0].fromTime = "10";
116
+ flow[0].toTime = "10";
117
+ helper.load(lowestPrice, flow, function () {
118
+ const n1 = helper.getNode("n1");
119
+ const n2 = helper.getNode("n2");
120
+ n2.on("input", function (msg) {
121
+ const config = cloneDeep(resultAllDay10.config);
122
+ expect(msg.payload).toHaveProperty("schedule", resultAllDay10.schedule);
123
+ expect(msg.payload).toHaveProperty("config", config);
124
+ n1.warn.should.not.be.called;
125
+ done();
126
+ });
127
+ const time = DateTime.fromISO(prices.priceData[10].start);
128
+ n1.receive({ payload: makePayload(prices, time) });
129
+ });
130
+ });
131
+ it("should plan correct for all day period - 10-10 outside on", function (done) {
132
+ const resultAllDay10 = require("./data/lowest-price-result-split-allday10.json");
133
+ const flow = makeFlow(10);
134
+ flow[0].doNotSplit = false;
135
+ flow[0].fromTime = "10";
136
+ flow[0].toTime = "10";
137
+ flow[0].outputOutsidePeriod = true;
138
+ helper.load(lowestPrice, flow, function () {
139
+ const n1 = helper.getNode("n1");
140
+ const n2 = helper.getNode("n2");
141
+ n2.on("input", function (msg) {
142
+ const config = cloneDeep(resultAllDay10.config);
143
+ config.outputOutsidePeriod = true;
144
+ expect(msg.payload).toHaveProperty("schedule", resultAllDay10.schedule);
145
+ expect(msg.payload).toHaveProperty("config", config);
146
+ n1.warn.should.not.be.called;
147
+ done();
148
+ });
149
+ const time = DateTime.fromISO(prices.priceData[10].start);
150
+ n1.receive({ payload: makePayload(prices, time) });
151
+ });
152
+ });
153
+ it("should plan correct for all day period - 10-10 nosched on", function (done) {
154
+ const resultAllDay10 = require("./data/lowest-price-result-split-allday10.json");
155
+ const flow = makeFlow(10);
156
+ flow[0].doNotSplit = false;
157
+ flow[0].fromTime = "10";
158
+ flow[0].toTime = "10";
159
+ flow[0].outputIfNoSchedule = "true";
160
+ helper.load(lowestPrice, flow, function () {
161
+ const n1 = helper.getNode("n1");
162
+ const n2 = helper.getNode("n2");
163
+ n2.on("input", function (msg) {
164
+ const config = cloneDeep(resultAllDay10.config);
165
+ config.outputIfNoSchedule = true;
166
+ expect(msg.payload).toHaveProperty("schedule", resultAllDay10.schedule);
167
+ expect(msg.payload).toHaveProperty("config", config);
168
+ n1.warn.should.not.be.called;
169
+ done();
170
+ });
171
+ const time = DateTime.fromISO(prices.priceData[10].start);
172
+ n1.receive({ payload: makePayload(prices, time) });
173
+ });
174
+ });
175
+ it("should plan correct outside selected period - split", function (done) {
176
+ const resultSplitted = require("./data/lowest-price-result-split.json");
177
+ const flow = makeFlow(6);
178
+ flow[0].doNotSplit = false;
179
+ flow[0].outputOutsidePeriod = true;
180
+ helper.load(lowestPrice, flow, function () {
181
+ const n1 = helper.getNode("n1");
182
+ const n2 = helper.getNode("n2");
183
+ n2.on("input", function (msg) {
184
+ const schedule = cloneDeep(resultSplitted.schedule);
185
+ const config = cloneDeep(resultSplitted.config);
186
+ schedule[0].value = true;
187
+ schedule.splice(1, 0, { time: "2021-10-11T10:00:00.000+02:00", value: false });
188
+ schedule.splice(4, 0, { time: "2021-10-11T20:00:00.000+02:00", value: true });
189
+ schedule.splice(5, 0, { time: "2021-10-12T10:00:00.000+02:00", value: false });
190
+ schedule.splice(schedule.length - 1, 1);
191
+ config.outputOutsidePeriod = true;
192
+ expect(msg.payload).toHaveProperty("schedule", schedule);
193
+ expect(msg.payload).toHaveProperty("config", config);
194
+ n1.warn.should.not.be.called;
195
+ done();
196
+ });
197
+ const time = DateTime.fromISO(prices.priceData[10].start);
198
+ n1.receive({ payload: makePayload(prices, time) });
199
+ });
200
+ });
201
+ it("should plan correct outside selected period - cont", function (done) {
202
+ const resultContinuous = require("./data/lowest-price-result-cont.json");
203
+ const flow = makeFlow(4);
204
+ flow[0].doNotSplit = true;
205
+ flow[0].outputOutsidePeriod = true;
206
+ helper.load(lowestPrice, flow, function () {
207
+ const n1 = helper.getNode("n1");
208
+ const n2 = helper.getNode("n2");
209
+ n2.on("input", function (msg) {
210
+ const schedule = cloneDeep(resultContinuous.schedule);
211
+ const config = cloneDeep(resultContinuous.config);
212
+ schedule[0].value = true;
213
+ schedule.splice(1, 0, { time: "2021-10-11T10:00:00.000+02:00", value: false });
214
+ schedule.splice(4, 0, { time: "2021-10-11T20:00:00.000+02:00", value: true });
215
+ schedule.splice(5, 0, { time: "2021-10-12T10:00:00.000+02:00", value: false });
216
+ schedule.splice(schedule.length, 0, { time: "2021-10-12T20:00:00.000+02:00", value: true });
217
+ // schedule.splice(schedule.length - 1, 1);
218
+ config.outputOutsidePeriod = true;
219
+ expect(msg.payload).toHaveProperty("schedule", schedule);
220
+ expect(msg.payload).toHaveProperty("config", config);
221
+ n1.warn.should.not.be.called;
222
+ done();
223
+ });
224
+ const time = DateTime.fromISO(prices.priceData[10].start);
225
+ n1.receive({ payload: makePayload(prices, time) });
226
+ });
227
+ });
228
+ it("should work with 0 hours on", function (done) {
229
+ const result = [{ time: "2021-10-11T00:00:00.000+02:00", value: false }];
230
+ const flow = makeFlow(0);
231
+ helper.load(lowestPrice, flow, function () {
232
+ const n1 = helper.getNode("n1");
233
+ const n2 = helper.getNode("n2");
234
+ n2.on("input", function (msg) {
235
+ expect(msg.payload).toHaveProperty("schedule", result);
236
+ n1.warn.should.not.be.called;
237
+ done();
238
+ });
239
+ const time = DateTime.fromISO(prices.priceData[10].start);
240
+ n1.receive({ payload: makePayload(prices, time) });
241
+ });
242
+ });
243
+ it("should work with 0 hours on outside on", function (done) {
244
+ const result = [
245
+ { time: "2021-10-11T00:00:00.000+02:00", value: true },
246
+ { time: "2021-10-11T10:00:00.000+02:00", value: false },
247
+ { time: "2021-10-11T20:00:00.000+02:00", value: true },
248
+ { time: "2021-10-12T10:00:00.000+02:00", value: false },
249
+ { time: "2021-10-12T20:00:00.000+02:00", value: true },
250
+ ];
251
+ const flow = makeFlow(0);
252
+ flow[0].outputOutsidePeriod = true;
253
+ helper.load(lowestPrice, flow, function () {
254
+ const n1 = helper.getNode("n1");
255
+ const n2 = helper.getNode("n2");
256
+ n2.on("input", function (msg) {
257
+ expect(msg.payload).toHaveProperty("schedule", result);
258
+ n1.warn.should.not.be.called;
259
+ done();
260
+ });
261
+ const time = DateTime.fromISO(prices.priceData[10].start);
262
+ n1.receive({ payload: makePayload(prices, time) });
263
+ });
264
+ });
265
+ it("should work with 1 hours on", function (done) {
266
+ const result = [
267
+ { time: "2021-10-11T00:00:00.000+02:00", value: false },
268
+ { time: "2021-10-11T12:00:00.000+02:00", value: true },
269
+ { time: "2021-10-11T13:00:00.000+02:00", value: false },
270
+ { time: "2021-10-12T14:00:00.000+02:00", value: true },
271
+ { time: "2021-10-12T15:00:00.000+02:00", value: false },
272
+ ];
273
+ const flow = makeFlow(1);
274
+ helper.load(lowestPrice, flow, function () {
275
+ const n1 = helper.getNode("n1");
276
+ const n2 = helper.getNode("n2");
277
+ n2.on("input", function (msg) {
278
+ expect(msg.payload).toHaveProperty("schedule", result);
279
+ n1.warn.should.not.be.called;
280
+ done();
281
+ });
282
+ const time = DateTime.fromISO(prices.priceData[10].start);
283
+ n1.receive({ payload: makePayload(prices, time) });
284
+ });
285
+ });
286
+ it("should work with 24 hours on", function (done) {
287
+ const result = [
288
+ { time: "2021-10-11T00:00:00.000+02:00", value: false },
289
+ { time: "2021-10-11T10:00:00.000+02:00", value: true },
290
+ { time: "2021-10-11T20:00:00.000+02:00", value: false },
291
+ { time: "2021-10-12T10:00:00.000+02:00", value: true },
292
+ { time: "2021-10-12T20:00:00.000+02:00", value: false },
293
+ ];
294
+ const flow = makeFlow(24);
295
+ helper.load(lowestPrice, flow, function () {
296
+ const n1 = helper.getNode("n1");
297
+ const n2 = helper.getNode("n2");
298
+ n2.on("input", function (msg) {
299
+ expect(msg.payload).toHaveProperty("schedule", result);
300
+ n1.warn.should.not.be.called;
301
+ done();
302
+ });
303
+ const time = DateTime.fromISO(prices.priceData[10].start);
304
+ n1.receive({ payload: makePayload(prices, time) });
305
+ });
306
+ });
307
+ });
308
+
309
+ function makeFlow(hoursOn) {
310
+ return [
311
+ {
312
+ id: "n1",
313
+ type: "ps-strategy-lowest-price",
314
+ name: "test name",
315
+ fromTime: "10",
316
+ toTime: "20",
317
+ hoursOn: hoursOn,
318
+ doNotSplit: true,
319
+ sendCurrentValueWhenRescheduling: true,
320
+ outputIfNoSchedule: true,
321
+ wires: [["n3"], ["n4"], ["n2"]],
322
+ },
323
+ { id: "n2", type: "helper" },
324
+ { id: "n3", type: "helper" },
325
+ { id: "n4", type: "helper" },
326
+ ];
327
+ }
328
+
329
+ function makePayload(prices, time) {
330
+ const payload = cloneDeep(prices);
331
+ payload.time = time;
332
+ // let entryTime = DateTime.fromISO(payload.time);
333
+ // payload.priceData.forEach((e) => {
334
+ // e.start = entryTime.toISO();
335
+ // entryTime = entryTime.plus({ milliseconds: 10 });
336
+ // });
337
+ return payload;
338
+ }
@@ -18,23 +18,6 @@ const testPlan = {
18
18
  time: "2021-06-20T01:50:00+02:00",
19
19
  };
20
20
 
21
- function makeFlow(maxHoursToSaveInSequence, minHoursOnAfterMaxSequenceSaved) {
22
- return [
23
- {
24
- id: "n1",
25
- type: "power-saver",
26
- name: "test name",
27
- maxHoursToSaveInSequence,
28
- minHoursOnAfterMaxSequenceSaved,
29
- minSaving: 0.001,
30
- wires: [["n3"], ["n4"], ["n2"]],
31
- },
32
- { id: "n2", type: "helper" },
33
- { id: "n3", type: "helper" },
34
- { id: "n4", type: "helper" },
35
- ];
36
- }
37
-
38
21
  function makePayload(prices, time) {
39
22
  const payload = cloneDeep(prices);
40
23
  payload.time = time;
@@ -118,7 +101,6 @@ function equalPlan(expected, actual) {
118
101
 
119
102
  module.exports = {
120
103
  testPlan,
121
- makeFlow,
122
104
  makePayload,
123
105
  equalPlan,
124
106
  };