node-red-contrib-power-saver 3.3.0 → 3.4.1

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 (146) hide show
  1. package/.firebase/hosting.ZG9jcy8udnVlcHJlc3MvZGlzdA.cache +221 -0
  2. package/.firebaserc +5 -0
  3. package/.github/workflows/firebase-hosting-merge.yml +20 -0
  4. package/.github/workflows/firebase-hosting-pull-request.yml +17 -0
  5. package/CHANGELOG.md +1 -1
  6. package/README.md +1 -1
  7. package/docs/.vuepress/clientAppEnhance.js +5 -0
  8. package/docs/.vuepress/components/BestSaveVerificator.vue +306 -0
  9. package/docs/.vuepress/components/DonateButtons.vue +87 -0
  10. package/docs/.vuepress/config.js +40 -7
  11. package/docs/.vuepress/dist/404.html +24 -6
  12. package/docs/.vuepress/dist/assets/css/835.styles.c5afb22b.css +1 -0
  13. package/docs/.vuepress/dist/assets/css/896.styles.21a80cb6.css +1 -0
  14. package/docs/.vuepress/dist/assets/css/styles.1c48cbd0.css +10 -0
  15. package/docs/.vuepress/dist/assets/img/best-save-config.79a2f39a.png +0 -0
  16. package/docs/.vuepress/dist/assets/img/copy-payload-best-save.b9192985.png +0 -0
  17. package/docs/.vuepress/dist/assets/img/heat-capacitor-temperatureVsPrice.6e74905b.png +0 -0
  18. package/docs/.vuepress/dist/assets/img/node-ps-strategy-heat-capacitor-cascade-control.2e75ed9e.png +0 -0
  19. package/docs/.vuepress/dist/assets/img/node-ps-strategy-heat-capacitor-simple-flow-example.29d9bf59.png +0 -0
  20. package/docs/.vuepress/dist/assets/img/oven-setpoint-calculation.5bda0eec.png +0 -0
  21. package/docs/.vuepress/dist/assets/img/overshoot-time.b3b5d70e.png +0 -0
  22. package/docs/.vuepress/dist/assets/js/229.5c5378fa.js +1 -0
  23. package/docs/.vuepress/dist/assets/js/331.872104cd.js +1 -0
  24. package/docs/.vuepress/dist/assets/js/405.f4edd94d.js +2 -0
  25. package/docs/.vuepress/dist/assets/js/{812.79dad458.js.LICENSE.txt → 405.f4edd94d.js.LICENSE.txt} +0 -0
  26. package/docs/.vuepress/dist/assets/js/490.1e639e05.js +1 -0
  27. package/docs/.vuepress/dist/assets/js/{491.c183eba3.js → 491.bd938119.js} +1 -1
  28. package/docs/.vuepress/dist/assets/js/555.d8963d84.js +1 -0
  29. package/docs/.vuepress/dist/assets/js/811.5f659592.js +1 -0
  30. package/docs/.vuepress/dist/assets/js/app.999deae8.js +1 -0
  31. package/docs/.vuepress/dist/assets/js/runtime~app.1774b667.js +1 -0
  32. package/docs/.vuepress/dist/assets/js/v-0607240a.1304cbb6.js +1 -0
  33. package/docs/.vuepress/dist/assets/js/v-08683c60.52e94cb6.js +1 -0
  34. package/docs/.vuepress/dist/assets/js/v-0aca7ba6.cac5d4b9.js +1 -0
  35. package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.18561f6e.js +1 -0
  36. package/docs/.vuepress/dist/assets/js/v-1ad821fa.6697a349.js +1 -0
  37. package/docs/.vuepress/dist/assets/js/v-1b3a0ab8.c6c4e19b.js +1 -0
  38. package/docs/.vuepress/dist/assets/js/v-1e2b191e.07b8ab21.js +1 -0
  39. package/docs/.vuepress/dist/assets/js/v-29504124.00be7399.js +1 -0
  40. package/docs/.vuepress/dist/assets/js/v-30acb564.28af12af.js +1 -0
  41. package/docs/.vuepress/dist/assets/js/{v-3706649a.d7f73384.js → v-3706649a.c76d575b.js} +1 -1
  42. package/docs/.vuepress/dist/assets/js/v-4637f9e4.d334c29a.js +1 -0
  43. package/docs/.vuepress/dist/assets/js/v-4c28314d.8cbb0f9d.js +1 -0
  44. package/docs/.vuepress/dist/assets/js/v-510ed0d4.14f08cf3.js +1 -0
  45. package/docs/.vuepress/dist/assets/js/{v-5954bcb2.182daf70.js → v-5954bcb2.dff3fc67.js} +1 -1
  46. package/docs/.vuepress/dist/assets/js/v-5db8da3a.e5e6d7a6.js +1 -0
  47. package/docs/.vuepress/dist/assets/js/v-61f728ca.81968036.js +1 -0
  48. package/docs/.vuepress/dist/assets/js/v-677dfaed.c159b0f4.js +1 -0
  49. package/docs/.vuepress/dist/assets/js/v-7446a652.88afb5e4.js +1 -0
  50. package/docs/.vuepress/dist/assets/js/v-7c87f26e.8ed52391.js +1 -0
  51. package/docs/.vuepress/dist/assets/js/v-84304104.ad4da2d5.js +1 -0
  52. package/docs/.vuepress/dist/assets/js/{v-8daa1a0e.dde202c9.js → v-8daa1a0e.ed84ca09.js} +1 -1
  53. package/docs/.vuepress/dist/assets/js/{v-b4a42144.9e5f9728.js → v-b4a42144.9a2a0c9f.js} +1 -1
  54. package/docs/.vuepress/dist/assets/js/v-e8c55052.b7d52fc6.js +1 -0
  55. package/docs/.vuepress/dist/assets/js/v-fffb8e28.d09ab959.js +1 -0
  56. package/docs/.vuepress/dist/changelog/index.html +24 -6
  57. package/docs/.vuepress/dist/contribute/index.html +24 -6
  58. package/docs/.vuepress/dist/examples/example-cascade-temperature-control.html +304 -0
  59. package/docs/.vuepress/dist/examples/example-heat-capacitor.html +247 -0
  60. package/docs/.vuepress/dist/examples/example-next-schedule-entity.html +28 -10
  61. package/docs/.vuepress/dist/examples/example-nordpool-current-state.html +25 -8
  62. package/docs/.vuepress/dist/examples/example-nordpool-events-state.html +25 -7
  63. package/docs/.vuepress/dist/examples/example-tibber-mqtt.html +25 -8
  64. package/docs/.vuepress/dist/examples/index.html +24 -6
  65. package/docs/.vuepress/dist/faq/best-save-viewer.html +33 -0
  66. package/docs/.vuepress/dist/faq/index.html +24 -6
  67. package/docs/.vuepress/dist/guide/index.html +25 -7
  68. package/docs/.vuepress/dist/index.html +24 -6
  69. package/docs/.vuepress/dist/nodes/index.html +24 -6
  70. package/docs/.vuepress/dist/nodes/old-power-saver-doc.html +26 -8
  71. package/docs/.vuepress/dist/nodes/power-saver.html +24 -6
  72. package/docs/.vuepress/dist/nodes/ps-elvia-add-tariff.html +24 -6
  73. package/docs/.vuepress/dist/nodes/ps-general-add-tariff.html +24 -6
  74. package/docs/.vuepress/dist/nodes/ps-receive-price.html +27 -9
  75. package/docs/.vuepress/dist/nodes/ps-strategy-best-save.html +46 -9
  76. package/docs/.vuepress/dist/nodes/ps-strategy-heat-capacitor.html +260 -0
  77. package/docs/.vuepress/dist/nodes/ps-strategy-lowest-price.html +41 -8
  78. package/docs/.vuepress/dist/nodes/strategy-input.html +25 -7
  79. package/docs/README.md +4 -1
  80. package/docs/changelog/README.md +19 -0
  81. package/docs/contribute/README.md +11 -3
  82. package/docs/examples/README.md +8 -2
  83. package/docs/examples/example-cascade-temperature-control.md +346 -0
  84. package/docs/examples/example-heat-capacitor.md +271 -0
  85. package/docs/faq/README.md +23 -0
  86. package/docs/faq/best-save-viewer.md +61 -0
  87. package/docs/images/copy-payload-best-save.png +0 -0
  88. package/docs/images/heat-capacitor-temperatureVsPrice.png +0 -0
  89. package/docs/images/node-ps-strategy-heat-capacitor-cascade-control.png +0 -0
  90. package/docs/images/node-ps-strategy-heat-capacitor-simple-flow-example.png +0 -0
  91. package/docs/images/node-ps-strategy-heat-capacitor.png +0 -0
  92. package/docs/images/oven-setpoint-calculation.png +0 -0
  93. package/docs/images/overshoot-time.png +0 -0
  94. package/docs/images/temperature-manipulation-config.png +0 -0
  95. package/docs/nodes/README.md +7 -1
  96. package/docs/nodes/ps-strategy-best-save.md +18 -0
  97. package/docs/nodes/ps-strategy-heat-capacitor.md +346 -0
  98. package/docs/nodes/ps-strategy-lowest-price.md +14 -0
  99. package/examples/add-general-tariff.json +103 -0
  100. package/examples/best-save-for-water-heater.json +140 -0
  101. package/examples/elvia-add-tariff.json +99 -0
  102. package/examples/elvia-get-tariff-types.json +58 -0
  103. package/examples/elvia-get-tariff.json +60 -0
  104. package/examples/heat-capacitor-for-room-heating.json +186 -0
  105. package/examples/lowest-price-for-heating-cables.json +159 -0
  106. package/firebase.json +6 -0
  107. package/package.json +20 -8
  108. package/public/404.html +33 -0
  109. package/public/index.html +89 -0
  110. package/src/elvia/elvia-tariff.js +2 -1
  111. package/src/handle-input.js +2 -2
  112. package/src/strategy-heat-capacitor-functions.js +246 -0
  113. package/src/strategy-heat-capacitor.html +85 -0
  114. package/src/strategy-heat-capacitor.js +125 -0
  115. package/test/data/converted-prices.json +1 -1
  116. package/test/data/multiple-trades.json +53 -0
  117. package/test/data/tibber-decreasing-24h.json +101 -0
  118. package/test/data/tibber-decreasing2-24h.json +101 -0
  119. package/test/strategy-best-save.test.js +14 -8
  120. package/test/strategy-heat-capacitor-node.test.js +183 -0
  121. package/test/strategy-heat-capacitor.test.js +103 -0
  122. package/test/strategy-lowest-price-functions.test.js +1 -1
  123. package/test/utils.test.js +0 -2
  124. package/docs/.vuepress/dist/.nojekyll +0 -0
  125. package/docs/.vuepress/dist/assets/css/styles.e835bef6.css +0 -8
  126. package/docs/.vuepress/dist/assets/img/best-save-config.93fa3c21.png +0 -0
  127. package/docs/.vuepress/dist/assets/js/293.5e967839.js +0 -1
  128. package/docs/.vuepress/dist/assets/js/812.79dad458.js +0 -2
  129. package/docs/.vuepress/dist/assets/js/app.49ad6d54.js +0 -1
  130. package/docs/.vuepress/dist/assets/js/runtime~app.547d0f4e.js +0 -1
  131. package/docs/.vuepress/dist/assets/js/v-0607240a.661e1808.js +0 -1
  132. package/docs/.vuepress/dist/assets/js/v-08683c60.a6b9cf5b.js +0 -1
  133. package/docs/.vuepress/dist/assets/js/v-0aca7ba6.b42fad7f.js +0 -1
  134. package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.3b37457f.js +0 -1
  135. package/docs/.vuepress/dist/assets/js/v-1ad821fa.6e2194d0.js +0 -1
  136. package/docs/.vuepress/dist/assets/js/v-1e2b191e.98cc227b.js +0 -1
  137. package/docs/.vuepress/dist/assets/js/v-30acb564.f2fcd69f.js +0 -1
  138. package/docs/.vuepress/dist/assets/js/v-4637f9e4.b320f5e8.js +0 -1
  139. package/docs/.vuepress/dist/assets/js/v-510ed0d4.3c7e0056.js +0 -1
  140. package/docs/.vuepress/dist/assets/js/v-5db8da3a.f2de6cb9.js +0 -1
  141. package/docs/.vuepress/dist/assets/js/v-61f728ca.6fdbbb92.js +0 -1
  142. package/docs/.vuepress/dist/assets/js/v-677dfaed.0013f083.js +0 -1
  143. package/docs/.vuepress/dist/assets/js/v-7446a652.d05e2648.js +0 -1
  144. package/docs/.vuepress/dist/assets/js/v-7c87f26e.1127dcf5.js +0 -1
  145. package/docs/.vuepress/dist/assets/js/v-e8c55052.8384b053.js +0 -1
  146. package/docs/.vuepress/dist/assets/js/v-fffb8e28.3406fd88.js +0 -1
@@ -0,0 +1,53 @@
1
+ {
2
+ "source": "Tibber",
3
+ "priceData": [
4
+ { "value": 1.4157, "start": "2022-02-06T00:00:00.000+01:00" },
5
+ { "value": 1.3705, "start": "2022-02-06T01:00:00.000+01:00" },
6
+ { "value": 1.2487, "start": "2022-02-06T02:00:00.000+01:00" },
7
+ { "value": 1.1812, "start": "2022-02-06T03:00:00.000+01:00" },
8
+ { "value": 0.9632, "start": "2022-02-06T04:00:00.000+01:00" },
9
+ { "value": 1.0862, "start": "2022-02-06T05:00:00.000+01:00" },
10
+ { "value": 1.2638, "start": "2022-02-06T06:00:00.000+01:00" },
11
+ { "value": 1.3266, "start": "2022-02-06T07:00:00.000+01:00" },
12
+ { "value": 1.3734, "start": "2022-02-06T08:00:00.000+01:00" },
13
+ { "value": 1.4653, "start": "2022-02-06T09:00:00.000+01:00" },
14
+ { "value": 1.513, "start": "2022-02-06T10:00:00.000+01:00" },
15
+ { "value": 1.518, "start": "2022-02-06T11:00:00.000+01:00" },
16
+ { "value": 1.505, "start": "2022-02-06T12:00:00.000+01:00" },
17
+ { "value": 1.5038, "start": "2022-02-06T13:00:00.000+01:00" },
18
+ { "value": 1.5239, "start": "2022-02-06T14:00:00.000+01:00" },
19
+ { "value": 1.5269, "start": "2022-02-06T15:00:00.000+01:00" },
20
+ { "value": 1.5464, "start": "2022-02-06T16:00:00.000+01:00" },
21
+ { "value": 1.5553, "start": "2022-02-06T17:00:00.000+01:00" },
22
+ { "value": 1.6066, "start": "2022-02-06T18:00:00.000+01:00" },
23
+ { "value": 1.5541, "start": "2022-02-06T19:00:00.000+01:00" },
24
+ { "value": 1.5437, "start": "2022-02-06T20:00:00.000+01:00" },
25
+ { "value": 1.4912, "start": "2022-02-06T21:00:00.000+01:00" },
26
+ { "value": 1.473, "start": "2022-02-06T22:00:00.000+01:00" },
27
+ { "value": 1.4281, "start": "2022-02-06T23:00:00.000+01:00" },
28
+ { "value": 1.4179, "start": "2022-02-07T00:00:00.000+01:00" },
29
+ { "value": 1.3966, "start": "2022-02-07T01:00:00.000+01:00" },
30
+ { "value": 1.3953, "start": "2022-02-07T02:00:00.000+01:00" },
31
+ { "value": 1.3939, "start": "2022-02-07T03:00:00.000+01:00" },
32
+ { "value": 1.3962, "start": "2022-02-07T04:00:00.000+01:00" },
33
+ { "value": 1.4335, "start": "2022-02-07T05:00:00.000+01:00" },
34
+ { "value": 1.5191, "start": "2022-02-07T06:00:00.000+01:00" },
35
+ { "value": 1.6244, "start": "2022-02-07T07:00:00.000+01:00" },
36
+ { "value": 1.6508, "start": "2022-02-07T08:00:00.000+01:00" },
37
+ { "value": 1.6142, "start": "2022-02-07T09:00:00.000+01:00" },
38
+ { "value": 1.5632, "start": "2022-02-07T10:00:00.000+01:00" },
39
+ { "value": 1.5516, "start": "2022-02-07T11:00:00.000+01:00" },
40
+ { "value": 1.4922, "start": "2022-02-07T12:00:00.000+01:00" },
41
+ { "value": 1.5288, "start": "2022-02-07T13:00:00.000+01:00" },
42
+ { "value": 1.5522, "start": "2022-02-07T14:00:00.000+01:00" },
43
+ { "value": 1.5526, "start": "2022-02-07T15:00:00.000+01:00" },
44
+ { "value": 1.5799, "start": "2022-02-07T16:00:00.000+01:00" },
45
+ { "value": 1.6909, "start": "2022-02-07T17:00:00.000+01:00" },
46
+ { "value": 1.7136, "start": "2022-02-07T18:00:00.000+01:00" },
47
+ { "value": 1.703, "start": "2022-02-07T19:00:00.000+01:00" },
48
+ { "value": 1.675, "start": "2022-02-07T20:00:00.000+01:00" },
49
+ { "value": 1.6173, "start": "2022-02-07T21:00:00.000+01:00" },
50
+ { "value": 1.5638, "start": "2022-02-07T22:00:00.000+01:00" },
51
+ { "value": 1.4645, "start": "2022-02-07T23:00:00.000+01:00" }
52
+ ]
53
+ }
@@ -0,0 +1,101 @@
1
+ {
2
+ "source": "Tibber",
3
+ "priceData": [
4
+ {
5
+ "value": 1.8019,
6
+ "start": "2022-01-12T00:00:00.000+01:00"
7
+ },
8
+ {
9
+ "value": 1.7711,
10
+ "start": "2022-01-12T01:00:00.000+01:00"
11
+ },
12
+ {
13
+ "value": 1.7539,
14
+ "start": "2022-01-12T02:00:00.000+01:00"
15
+ },
16
+ {
17
+ "value": 1.7508,
18
+ "start": "2022-01-12T03:00:00.000+01:00"
19
+ },
20
+ {
21
+ "value": 1.7616,
22
+ "start": "2022-01-12T04:00:00.000+01:00"
23
+ },
24
+ {
25
+ "value": 1.7825,
26
+ "start": "2022-01-12T05:00:00.000+01:00"
27
+ },
28
+ {
29
+ "value": 1.7829,
30
+ "start": "2022-01-12T06:00:00.000+01:00"
31
+ },
32
+ {
33
+ "value": 1.8422,
34
+ "start": "2022-01-12T07:00:00.000+01:00"
35
+ },
36
+ {
37
+ "value": 1.8725,
38
+ "start": "2022-01-12T08:00:00.000+01:00"
39
+ },
40
+ {
41
+ "value": 1.8356,
42
+ "start": "2022-01-12T09:00:00.000+01:00"
43
+ },
44
+ {
45
+ "value": 1.8053,
46
+ "start": "2022-01-12T10:00:00.000+01:00"
47
+ },
48
+ {
49
+ "value": 1.7625,
50
+ "start": "2022-01-12T11:00:00.000+01:00"
51
+ },
52
+ {
53
+ "value": 1.7601,
54
+ "start": "2022-01-12T12:00:00.000+01:00"
55
+ },
56
+ {
57
+ "value": 1.7481,
58
+ "start": "2022-01-12T13:00:00.000+01:00"
59
+ },
60
+ {
61
+ "value": 1.7269,
62
+ "start": "2022-01-12T14:00:00.000+01:00"
63
+ },
64
+ {
65
+ "value": 1.717,
66
+ "start": "2022-01-12T15:00:00.000+01:00"
67
+ },
68
+ {
69
+ "value": 1.7181,
70
+ "start": "2022-01-12T16:00:00.000+01:00"
71
+ },
72
+ {
73
+ "value": 1.7169,
74
+ "start": "2022-01-12T17:00:00.000+01:00"
75
+ },
76
+ {
77
+ "value": 1.7015,
78
+ "start": "2022-01-12T18:00:00.000+01:00"
79
+ },
80
+ {
81
+ "value": 1.6708,
82
+ "start": "2022-01-12T19:00:00.000+01:00"
83
+ },
84
+ {
85
+ "value": 1.6499,
86
+ "start": "2022-01-12T20:00:00.000+01:00"
87
+ },
88
+ {
89
+ "value": 1.644,
90
+ "start": "2022-01-12T21:00:00.000+01:00"
91
+ },
92
+ {
93
+ "value": 1.624,
94
+ "start": "2022-01-12T22:00:00.000+01:00"
95
+ },
96
+ {
97
+ "value": 1.6078,
98
+ "start": "2022-01-12T23:00:00.000+01:00"
99
+ }
100
+ ]
101
+ }
@@ -0,0 +1,101 @@
1
+ {
2
+ "priceData": [
3
+ {
4
+ "value": 1.7261,
5
+ "start": "2022-01-16T00:00:00.000+01:00"
6
+ },
7
+ {
8
+ "value": 1.7153,
9
+ "start": "2022-01-16T01:00:00.000+01:00"
10
+ },
11
+ {
12
+ "value": 1.6872,
13
+ "start": "2022-01-16T02:00:00.000+01:00"
14
+ },
15
+ {
16
+ "value": 1.6842,
17
+ "start": "2022-01-16T03:00:00.000+01:00"
18
+ },
19
+ {
20
+ "value": 1.6831,
21
+ "start": "2022-01-16T04:00:00.000+01:00"
22
+ },
23
+ {
24
+ "value": 1.6811,
25
+ "start": "2022-01-16T05:00:00.000+01:00"
26
+ },
27
+ {
28
+ "value": 1.6117,
29
+ "start": "2022-01-16T06:00:00.000+01:00"
30
+ },
31
+ {
32
+ "value": 1.5729,
33
+ "start": "2022-01-16T07:00:00.000+01:00"
34
+ },
35
+ {
36
+ "value": 1.5969,
37
+ "start": "2022-01-16T08:00:00.000+01:00"
38
+ },
39
+ {
40
+ "value": 1.6049,
41
+ "start": "2022-01-16T09:00:00.000+01:00"
42
+ },
43
+ {
44
+ "value": 1.6117,
45
+ "start": "2022-01-16T10:00:00.000+01:00"
46
+ },
47
+ {
48
+ "value": 1.6103,
49
+ "start": "2022-01-16T11:00:00.000+01:00"
50
+ },
51
+ {
52
+ "value": 1.6004,
53
+ "start": "2022-01-16T12:00:00.000+01:00"
54
+ },
55
+ {
56
+ "value": 1.5937,
57
+ "start": "2022-01-16T13:00:00.000+01:00"
58
+ },
59
+ {
60
+ "value": 1.5476,
61
+ "start": "2022-01-16T14:00:00.000+01:00"
62
+ },
63
+ {
64
+ "value": 1.5866,
65
+ "start": "2022-01-16T15:00:00.000+01:00"
66
+ },
67
+ {
68
+ "value": 1.5959,
69
+ "start": "2022-01-16T16:00:00.000+01:00"
70
+ },
71
+ {
72
+ "value": 1.6316,
73
+ "start": "2022-01-16T17:00:00.000+01:00"
74
+ },
75
+ {
76
+ "value": 1.5908,
77
+ "start": "2022-01-16T18:00:00.000+01:00"
78
+ },
79
+ {
80
+ "value": 1.5805,
81
+ "start": "2022-01-16T19:00:00.000+01:00"
82
+ },
83
+ {
84
+ "value": 1.5808,
85
+ "start": "2022-01-16T20:00:00.000+01:00"
86
+ },
87
+ {
88
+ "value": 1.5681,
89
+ "start": "2022-01-16T21:00:00.000+01:00"
90
+ },
91
+ {
92
+ "value": 1.5218,
93
+ "start": "2022-01-16T22:00:00.000+01:00"
94
+ },
95
+ {
96
+ "value": 1.46,
97
+ "start": "2022-01-16T23:00:00.000+01:00"
98
+ }
99
+ ],
100
+ "source": "Tibber"
101
+ }
@@ -5,8 +5,9 @@ const helper = require("node-red-node-test-helper");
5
5
  const bestSave = require("../src/strategy-best-save.js");
6
6
  const prices = require("./data/converted-prices.json");
7
7
  const result = require("./data/best-save-result.json");
8
- const { testPlan: plan } = require("./test-utils");
8
+ const { testPlan: plan, equalPlan } = require("./test-utils");
9
9
  const { makeFlow } = require("./strategy-best-save-test-utils");
10
+ const { version } = require("../package.json");
10
11
 
11
12
  helper.init(require.resolve("node-red"));
12
13
 
@@ -61,6 +62,11 @@ describe("ps-strategy-best-save node", function () {
61
62
  });
62
63
  it("should send new schedule on output 3", function (done) {
63
64
  const flow = makeFlow(3, 2);
65
+ const expected = cloneDeep(result);
66
+ expected.version = version;
67
+ expected.time = plan.time;
68
+ expected.source = "Tibber";
69
+ expected.current = false;
64
70
  helper.load(bestSave, flow, function () {
65
71
  const n1 = helper.getNode("n1");
66
72
  const n2 = helper.getNode("n2");
@@ -69,22 +75,22 @@ describe("ps-strategy-best-save node", function () {
69
75
  let countOn = 0;
70
76
  let countOff = 0;
71
77
  n2.on("input", function (msg) {
72
- expect(msg).toHaveProperty("payload", result);
78
+ expect(equalPlan(expected, msg.payload)).toBeTruthy();
73
79
  n1.warn.should.not.be.called;
80
+ setTimeout(() => {
81
+ console.log("countOn = " + countOn + ", countOff = " + countOff);
82
+ expect(countOn).toEqual(7);
83
+ expect(countOff).toEqual(7);
84
+ done();
85
+ }, 900);
74
86
  });
75
87
  n3.on("input", function (msg) {
76
88
  countOn++;
77
89
  expect(msg).toHaveProperty("payload", true);
78
- if (countOn === 7 && countOff === 7) {
79
- done();
80
- }
81
90
  });
82
91
  n4.on("input", function (msg) {
83
92
  countOff++;
84
93
  expect(msg).toHaveProperty("payload", false);
85
- if (countOn === 7 && countOff === 7) {
86
- done();
87
- }
88
94
  });
89
95
  n1.receive({ payload: makePayload(prices, plan.time) });
90
96
  });
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ const cloneDeep = require("lodash.clonedeep");
3
+ const { DateTime } = require("luxon");
4
+ const expect = require("expect");
5
+ const helper = require("node-red-node-test-helper");
6
+ const node = require("../src/strategy-heat-capacitor.js");
7
+ const prices = require("./data/converted-prices.json");
8
+ const multiTrade = require("./data/multiple-trades.json");
9
+
10
+ helper.init(require.resolve("node-red"));
11
+
12
+ describe("ps-strategy-heat-capacitor 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-heat-capacitor", name: "Temp. Adj." }];
25
+ helper.load(node, flow, function () {
26
+ const n1 = helper.getNode("n1");
27
+ expect(n1).toHaveProperty("name", "Temp. Adj.");
28
+ done();
29
+ });
30
+ });
31
+
32
+ it("should log error when illegal data is received", function (done) {
33
+ const flow = [{ id: "n1", type: "ps-strategy-heat-capacitor", name: "Temp. Adj." }];
34
+ helper.load(node, flow, function () {
35
+ const n1 = helper.getNode("n1");
36
+ n1.receive({});
37
+ n1.warn.should.be.calledWithExactly("No payload");
38
+ n1.receive({ payload: "Error" });
39
+ n1.warn.should.be.calledWithExactly("Payload is not an object");
40
+ n1.receive({ payload: [] });
41
+ n1.warn.should.be.calledWithExactly("Payload is missing priceData");
42
+ n1.receive({ payload: { priceData: [] } });
43
+ n1.warn.should.be.calledWithExactly("priceData is empty");
44
+ n1.receive({ payload: { priceData: { today: [], tomorrow: [] } } });
45
+ n1.warn.should.be.calledWithExactly("Illegal priceData in payload. Did you use the receive-price node?");
46
+
47
+ ["start", "value"].forEach((attr) => {
48
+ const testData1 = cloneDeep(prices);
49
+ delete testData1.priceData[3][attr];
50
+ n1.receive({ payload: testData1 });
51
+ n1.warn.should.be.calledWithExactly(
52
+ "Malformed entries in priceData. All entries must contain start and value."
53
+ );
54
+ });
55
+
56
+ n1.receive({ payload: cloneDeep(prices) });
57
+ n1.warn.should.not.be.called;
58
+ done();
59
+ });
60
+ });
61
+
62
+ it("should be configurable", function (done) {
63
+ const flow = [{ id: "n1", type: "ps-strategy-heat-capacitor", name: "Heat Capacitor" }];
64
+ helper.load(node, flow, function () {
65
+ const n1 = helper.getNode("n1");
66
+ n1.receive({
67
+ payload: {
68
+ config: {
69
+ timeHeat1C: 1,
70
+ timeCool1C: 2,
71
+ setpoint: 3,
72
+ maxTempAdjustment: 4,
73
+ minSavings: 5,
74
+ boostTempHeat: 6,
75
+ boostTempCool: 7,
76
+ },
77
+ },
78
+ });
79
+ expect(n1).toHaveProperty("timeHeat1C", 1);
80
+ expect(n1).toHaveProperty("timeCool1C", 2);
81
+ expect(n1).toHaveProperty("boostTempHeat", 6);
82
+ expect(n1).toHaveProperty("boostTempCool", 7);
83
+ expect(n1).toHaveProperty("setpoint", 3);
84
+ expect(n1).toHaveProperty("maxTempAdjustment", 4);
85
+ expect(n1).toHaveProperty("minSavings", 5);
86
+ n1.receive({ payload: { config: { setpoint: 24 } } });
87
+ expect(n1).toHaveProperty("setpoint", 24);
88
+ done();
89
+ });
90
+ });
91
+
92
+ it("should plan correctly", function (done) {
93
+ const result = 0.5;
94
+ const flow = makeFlow();
95
+ helper.load(node, flow, function () {
96
+ const n1 = helper.getNode("n1");
97
+ const n2 = helper.getNode("n2");
98
+ const n3 = helper.getNode("n3");
99
+ let bothReceived = false;
100
+ n2.on("input", function (msg) {
101
+ expect(msg).toHaveProperty("payload", 22.5);
102
+ n1.warn.should.not.be.called;
103
+ bothReceived ? done() : (bothReceived = true);
104
+ });
105
+ n3.on("input", function (msg) {
106
+ expect(msg).toHaveProperty("payload", -0.5);
107
+ n1.warn.should.not.be.called;
108
+ bothReceived ? done() : (bothReceived = true);
109
+ });
110
+ const time = DateTime.fromISO(prices.priceData[10].start);
111
+ const p = cloneDeep(prices);
112
+ p.time = time;
113
+ n1.receive({ payload: p });
114
+ });
115
+ });
116
+
117
+ it("should plan correctly, multiTrade", function (done) {
118
+ const result = 0.5;
119
+ const flow = makeFlow();
120
+ helper.load(node, flow, function () {
121
+ const n1 = helper.getNode("n1");
122
+ const n2 = helper.getNode("n2");
123
+ const n3 = helper.getNode("n3");
124
+ let bothReceived = false;
125
+ n2.on("input", function (msg) {
126
+ expect(msg).toHaveProperty("payload", 24.5);
127
+ n1.warn.should.not.be.called;
128
+ bothReceived ? done() : (bothReceived = true);
129
+ });
130
+ n3.on("input", function (msg) {
131
+ expect(msg).toHaveProperty("payload", 1.5);
132
+ n1.warn.should.not.be.called;
133
+ bothReceived ? done() : (bothReceived = true);
134
+ });
135
+ const time = DateTime.fromISO(multiTrade.priceData[4].start).plus({ minutes: 10 });
136
+ multiTrade.time = time;
137
+ n1.receive({ payload: multiTrade });
138
+ });
139
+ });
140
+
141
+ it("should merge and trim priceData", function (done) {
142
+ const flow = makeFlow();
143
+ helper.load(node, flow, function () {
144
+ const n1 = helper.getNode("n1");
145
+ n1.receive({ payload: multiTrade });
146
+ n1.receive({ payload: prices });
147
+ expect(n1.priceData.length).toEqual(72);
148
+ done();
149
+ });
150
+ });
151
+ });
152
+
153
+ function makeFlow() {
154
+ return [
155
+ {
156
+ id: "n1",
157
+ type: "ps-strategy-heat-capacitor",
158
+ name: "Temp. Adj.",
159
+ timeHeat1C: 60,
160
+ timeCool1C: 50,
161
+ boostTempHeat: 1,
162
+ boostTempCool: 1,
163
+ setpoint: 23,
164
+ maxTempAdjustment: 0.5,
165
+ minSavings: 0.08,
166
+ wires: [["n2"], ["n3"], ["n4"]],
167
+ },
168
+ { id: "n2", type: "helper" },
169
+ { id: "n3", type: "helper" },
170
+ { id: "n4", type: "helper" },
171
+ ];
172
+ }
173
+
174
+ function makePayload(prices, time) {
175
+ const payload = cloneDeep(prices);
176
+ payload.time = time;
177
+ let entryTime = DateTime.fromISO(payload.time);
178
+ payload.priceData.forEach((e) => {
179
+ e.start = entryTime.toISO();
180
+ entryTime = entryTime.plus({ milliseconds: 10 });
181
+ });
182
+ return payload;
183
+ }
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+ const { DateTime } = require("luxon");
3
+ const expect = require("expect");
4
+ const {
5
+ calculateSchedule,
6
+ calculateOpportunities,
7
+ findBestBuySellPattern,
8
+ calculateValueDictList,
9
+ removeLowBuySellPairs,
10
+ } = require("../src/strategy-heat-capacitor-functions");
11
+ const converted_prices = require("./data/converted-prices.json");
12
+ const decreasing_end_prices = require("./data/tibber-decreasing2-24h.json");
13
+
14
+ describe("Test heat capacitor strategy functions", () => {
15
+ let prices, decreasing_24h_prices, start_date, buy_pattern, sell_pattern;
16
+
17
+ //User input
18
+ const timeHeat1C = 60;
19
+ const timeCool1C = 40;
20
+ const maxTempAdjustment = 1;
21
+ const boostTempHeat = 1;
22
+ const boostTempCool = 1;
23
+ const minSavings = 0.1;
24
+
25
+ before(function () {
26
+ prices = converted_prices.priceData.slice(0, 1).map((p) => p.value);
27
+ decreasing_24h_prices = decreasing_end_prices.priceData.slice(0, 1).map((p) => p.value);
28
+ start_date = DateTime.fromISO(converted_prices.priceData[0].start);
29
+ buy_pattern = Array(Math.round(timeHeat1C * maxTempAdjustment * 2)).fill(1);
30
+ sell_pattern = Array(Math.round(timeCool1C * maxTempAdjustment * 2)).fill(1);
31
+ });
32
+
33
+ it("Can calculate procurement opportunities", () => {
34
+ const my_prices = prices.slice(0, 1);
35
+ const my_buy_pattern = Array(5).fill(1);
36
+ //Calculate what it will cost to procure/sell 1 kWh as a function of time
37
+ let result = calculateOpportunities(my_prices, my_buy_pattern, 1);
38
+ //Remove float precisions errors by rounding
39
+ result = result.map((x) => Math.round(x * 1000000) / 1000000);
40
+ expect(result).toEqual(Array(56).fill(my_prices[0]));
41
+ });
42
+
43
+ it("Can find procurement pattern", () => {
44
+ //Use a simple price list
45
+ const my_prices = [1, 2, 2, 1, 8, 1];
46
+
47
+ const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
48
+ const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
49
+
50
+ const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
51
+
52
+ expect(my_buy_sell).toEqual([
53
+ [0, 141],
54
+ [100, 240],
55
+ ]);
56
+ });
57
+
58
+ it("DictList test", () => {
59
+ const my_prices = [1, 2, 2, 1, 8, 1];
60
+ const my_buy_sell_indexes = [
61
+ [0, 173],
62
+ [131, 251],
63
+ ];
64
+ const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
65
+ const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
66
+ const result = calculateValueDictList(my_buy_sell_indexes, buy_prices, sell_prices, start_date);
67
+
68
+ expect(result[0].sellDate).toEqual(start_date.plus({ minutes: 131 }));
69
+ });
70
+
71
+ it("DictList test at decreasing end", () => {
72
+ const my_prices = decreasing_end_prices.priceData.map((p) => p.value);
73
+ const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
74
+ const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
75
+
76
+ const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
77
+ const my_schedule = calculateSchedule(
78
+ start_date,
79
+ my_buy_sell,
80
+ buy_prices,
81
+ sell_prices,
82
+ maxTempAdjustment,
83
+ boostTempHeat,
84
+ boostTempCool,
85
+ buy_pattern.length,
86
+ sell_pattern.length
87
+ );
88
+ expect(my_schedule.temperatures[my_schedule.temperatures.length - 1]).toEqual(-maxTempAdjustment);
89
+ });
90
+
91
+ it("Check removal of low benefit buy-sell pairs", () => {
92
+ const my_prices = [1, 2, 1, 1.05, 1, 2];
93
+ const buy_prices = calculateOpportunities(my_prices, buy_pattern, 1);
94
+ const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
95
+ const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
96
+
97
+ const result = removeLowBuySellPairs(my_buy_sell, buy_prices, sell_prices, minSavings, start_date);
98
+ //Should remove the sell at 1.05 and the re-buy at 1 (only 0.05 difference)
99
+ const compare = [my_buy_sell[0].slice(0, 2), [my_buy_sell[1][0], my_buy_sell[1][2]]];
100
+
101
+ expect(result).toEqual(compare);
102
+ });
103
+ });
@@ -2,7 +2,7 @@ const { DateTime } = require("luxon");
2
2
  const expect = require("expect");
3
3
  const { getBestContinuous, getBestX } = require("../src/strategy-lowest-price-functions");
4
4
  const convertedPrices = require("./data/converted-prices.json");
5
- const { cloneDeep } = require("lodash");
5
+ const cloneDeep = require("lodash.clonedeep");
6
6
 
7
7
  describe("strategy-utils", () => {
8
8
  it("can find best x", () => {
@@ -5,14 +5,12 @@ const {
5
5
  sortedIndex,
6
6
  firstOn,
7
7
  getDiffToNextOn,
8
- isOnOffSequencesOk,
9
8
  getSavings,
10
9
  countAtEnd,
11
10
  makeSchedule,
12
11
  fillArray,
13
12
  extractPlanForDate,
14
13
  isSameDate,
15
- getStartAtIndex,
16
14
  } = require("../src/utils");
17
15
 
18
16
  describe("utils", () => {
File without changes