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,125 @@
1
+ const { DateTime } = require("luxon");
2
+ const { booleanConfig, makeSchedule, loadDayData } = require("./utils");
3
+ const { handleStrategyInput } = require("./handle-input");
4
+ const { getBestContinuous, getBestX } = require("./strategy-lowest-price-functions");
5
+
6
+ module.exports = function (RED) {
7
+ function StrategyLowestPriceNode(config) {
8
+ RED.nodes.createNode(this, config);
9
+ const node = this;
10
+
11
+ const originalConfig = {
12
+ fromTime: config.fromTime,
13
+ toTime: config.toTime,
14
+ hoursOn: config.hoursOn,
15
+ doNotSplit: booleanConfig(config.doNotSplit),
16
+ sendCurrentValueWhenRescheduling: booleanConfig(config.sendCurrentValueWhenRescheduling),
17
+ outputIfNoSchedule: booleanConfig(config.outputIfNoSchedule),
18
+ outputOutsidePeriod: booleanConfig(config.outputOutsidePeriod),
19
+ };
20
+ node.context().set("config", originalConfig);
21
+
22
+ node.on("close", function () {
23
+ clearTimeout(node.schedulingTimeout);
24
+ });
25
+
26
+ node.on("input", function (msg) {
27
+ handleStrategyInput(node, msg, doPlanning);
28
+ });
29
+ }
30
+
31
+ RED.nodes.registerType("ps-strategy-lowest-price", StrategyLowestPriceNode);
32
+ };
33
+
34
+ function doPlanning(node, _, priceData, _, dateDayBefore, _) {
35
+ const dataDayBefore = loadDayData(node, dateDayBefore);
36
+ const values = [...dataDayBefore.hours.map((h) => h.price), ...priceData.map((pd) => pd.value)];
37
+ const startTimes = [...dataDayBefore.hours.map((h) => h.start), ...priceData.map((pd) => pd.start)];
38
+
39
+ const from = parseInt(node.fromTime);
40
+ const to = parseInt(node.toTime);
41
+ const periodStatus = [];
42
+ const startIndexes = [];
43
+ const endIndexes = [];
44
+ let currentStatus = from < to ? "Outside" : "StartMissing";
45
+ let hour;
46
+ priceData.forEach((pd, i) => {
47
+ hour = DateTime.fromISO(pd.start).hour;
48
+ if (hour === to && to === from && currentStatus === "Inside") {
49
+ endIndexes.push(i - 1);
50
+ }
51
+ if (hour === to && to !== from) {
52
+ currentStatus = "Outside";
53
+ endIndexes.push(i - 1);
54
+ }
55
+ if (hour === from) {
56
+ currentStatus = "Inside";
57
+ startIndexes.push(i);
58
+ }
59
+ periodStatus[i] = currentStatus;
60
+ });
61
+ if (currentStatus === "Inside" && hour !== (to === 0 ? 23 : to - 1)) {
62
+ // Last period incomplete
63
+ let i = periodStatus.length - 1;
64
+ do {
65
+ periodStatus[i] = "EndMissing";
66
+ hour = DateTime.fromISO(priceData[i].start).hour;
67
+ i--;
68
+ } while (periodStatus[i] === "Inside" && hour !== from);
69
+ }
70
+ if (hour === (to === 0 ? 23 : to - 1)) {
71
+ endIndexes.push(priceData.length - 1);
72
+ }
73
+
74
+ const onOff = [];
75
+
76
+ // Fill in data from previous plan for StartMissing
77
+ const lastStartMissing = periodStatus.lastIndexOf((s) => s === "StartMissing");
78
+ if (lastStartMissing >= 0 && dataDayBefore?.hours?.length > 0) {
79
+ const lastBefore = DateTime.fromISO(dataDayBefore.hours[dataDayBefore.hours.length - 1].start);
80
+ if (lastBefore >= DateTime.fromISO(priceData[lastStartMissing].start)) {
81
+ for (let i = 0; i <= lastStartMissing; i++) {
82
+ onOff[i] = dataDayBefore.hours.find((h) => h.start === priceData[i].start);
83
+ periodStatus[i] = "Backfilled";
84
+ }
85
+ }
86
+ }
87
+
88
+ // Set onOff for hours that will not be planned
89
+ periodStatus.forEach((s, i) => {
90
+ onOff[i] =
91
+ s === "Outside"
92
+ ? node.outputOutsidePeriod
93
+ : s === "StartMissing" || s === "EndMissing"
94
+ ? node.outputIfNoSchedule
95
+ : null;
96
+ });
97
+
98
+ startIndexes.forEach((s, i) => {
99
+ makePlan(node, values, onOff, s, endIndexes[i]);
100
+ });
101
+
102
+ const schedule = makeSchedule(onOff, startTimes, !onOff[0]);
103
+
104
+ const hours = values.map((v, i) => ({
105
+ price: v,
106
+ onOff: onOff[i],
107
+ start: startTimes[i],
108
+ saving: null,
109
+ }));
110
+ return {
111
+ hours,
112
+ schedule,
113
+ };
114
+ }
115
+
116
+ function makePlan(node, values, onOff, fromIndex, toIndex) {
117
+ const valuesInPeriod = values.slice(fromIndex, toIndex + 1);
118
+ const res = node.doNotSplit
119
+ ? getBestContinuous(valuesInPeriod, node.hoursOn)
120
+ : getBestX(valuesInPeriod, node.hoursOn);
121
+ res.forEach((v, i) => {
122
+ onOff[fromIndex + i] = v;
123
+ });
124
+ return onOff;
125
+ }
@@ -1,58 +1,7 @@
1
1
  const { DateTime } = require("luxon");
2
2
 
3
- /**
4
- * Get today and tomorrow data out of the input message.
5
- * Can accept 3 types of messages: Tibber, Nordpool or plain payload with data already converted.
6
- * @param {*} msg
7
- */
8
- function convertMsg(msg) {
9
- let today = [];
10
- let tomorrow = [];
11
- let source = "Unknown";
12
-
13
- if (msg.payload?.viewer?.homes[0]?.currentSubscription?.priceInfo?.today) {
14
- source = "Tibber";
15
- today = msg.payload.viewer.homes[0].currentSubscription.priceInfo.today.map((v) => ({
16
- value: v.total,
17
- start: v.startsAt,
18
- }));
19
- } else if (msg.data?.new_state?.attributes?.raw_today) {
20
- source = "Nordpool";
21
- today = msg.data.new_state.attributes.raw_today.map((v) => ({
22
- value: v.value,
23
- start: v.start,
24
- }));
25
- } else if (msg.data?.attributes?.raw_today) {
26
- source = "Nordpool";
27
- today = msg.data.attributes.raw_today.map((v) => ({
28
- value: v.value,
29
- start: v.start,
30
- }));
31
- } else {
32
- source = "Other";
33
- today = msg.payload?.today || [];
34
- }
35
-
36
- if (msg.payload?.viewer?.homes[0]?.currentSubscription?.priceInfo?.tomorrow) {
37
- tomorrow = msg.payload.viewer.homes[0].currentSubscription.priceInfo.tomorrow.map((v) => ({
38
- value: v.total,
39
- start: v.startsAt,
40
- }));
41
- } else if (msg.data?.new_state?.attributes?.raw_tomorrow) {
42
- tomorrow = msg.data.new_state.attributes.raw_tomorrow.map((v) => ({
43
- value: v.value,
44
- start: v.start,
45
- }));
46
- } else if (msg.data?.attributes?.raw_tomorrow) {
47
- tomorrow = msg.data.attributes.raw_tomorrow.map((v) => ({
48
- value: v.value,
49
- start: v.start,
50
- }));
51
- } else {
52
- tomorrow = msg.payload?.tomorrow || [];
53
- }
54
-
55
- return { today, tomorrow, source };
3
+ function booleanConfig(value) {
4
+ return value === "true" || value === true;
56
5
  }
57
6
 
58
7
  /**
@@ -104,7 +53,36 @@ function getDiffToNextOn(values, onOff, nextOn = null) {
104
53
  }
105
54
 
106
55
  function getDiff(large, small) {
107
- return Math.round((large - small) * 10000) / 10000;
56
+ return roundPrice(large - small);
57
+ }
58
+
59
+ function getEffectiveConfig(node, msg) {
60
+ const res = node.context().get("config");
61
+ const isConfigMsg = !!msg?.payload?.config;
62
+ if (isConfigMsg) {
63
+ const inputConfig = msg.payload.config;
64
+ Object.keys(inputConfig).forEach((key) => {
65
+ res[key] = inputConfig[key];
66
+ });
67
+ node.context().set("config", res);
68
+ }
69
+ return res;
70
+ }
71
+
72
+ function loadDayData(node, date) {
73
+ // Load saved schedule for the date (YYYY-MM-DD)
74
+ // Return null if not found
75
+ const key = date.toISODate();
76
+ const saved = node.context().get(key);
77
+ const res = saved ?? {
78
+ schedule: [],
79
+ hours: [],
80
+ };
81
+ return res;
82
+ }
83
+
84
+ function roundPrice(value) {
85
+ return Math.round(value * 10000) / 10000;
108
86
  }
109
87
 
110
88
  /**
@@ -129,42 +107,6 @@ function firstOn(values, onOff, defaultValue = 0) {
129
107
  return [...values, defaultValue][[...onOff, true].findIndex((e) => e)];
130
108
  }
131
109
 
132
- /**
133
- * Takes an array of true/false values where true means on and false means off.
134
- * Evaluates of the on/off sequences are valid according to other arguments.
135
- *
136
- * @param {*} onOff Array of on/off values
137
- * @param {*} maxOff Max number of values that can be off in a sequence
138
- * @param {*} minOnAfterOff Min number of values that must be on after maxOff is reached
139
- * @returns
140
- */
141
- function isOnOffSequencesOk(onOff, maxOff, minOnAfterOff) {
142
- let offCount = 0;
143
- let onCount = 0;
144
- let reachedMaxOff = false;
145
- for (let i = 0; i < onOff.length; i++) {
146
- if (!onOff[i]) {
147
- if (maxOff === 0 || reachedMaxOff) {
148
- return false;
149
- }
150
- offCount++;
151
- onCount = 0;
152
- if (offCount >= maxOff) {
153
- reachedMaxOff = true;
154
- }
155
- } else {
156
- if (reachedMaxOff) {
157
- onCount++;
158
- if (onCount >= minOnAfterOff) {
159
- reachedMaxOff = false;
160
- }
161
- }
162
- offCount = 0;
163
- }
164
- }
165
- return true;
166
- }
167
-
168
110
  /**
169
111
  * Count number of the given value at the end of the given array
170
112
  * @param {*} arr
@@ -234,17 +176,19 @@ function getStartAtIndex(effectiveConfig, priceData, time) {
234
176
  }
235
177
 
236
178
  module.exports = {
237
- sortedIndex,
238
- getDiffToNextOn,
239
- firstOn,
240
- isOnOffSequencesOk,
241
- getSavings,
179
+ booleanConfig,
242
180
  countAtEnd,
243
- makeSchedule,
244
- fillArray,
245
- convertMsg,
246
181
  extractPlanForDate,
247
- isSameDate,
248
- getStartAtIndex,
182
+ fillArray,
183
+ firstOn,
249
184
  getDiff,
185
+ getDiffToNextOn,
186
+ getEffectiveConfig,
187
+ getSavings,
188
+ getStartAtIndex,
189
+ isSameDate,
190
+ loadDayData,
191
+ makeSchedule,
192
+ roundPrice,
193
+ sortedIndex,
250
194
  };
@@ -1,153 +1,301 @@
1
1
  module.exports = {
2
- schedule: [
3
- {
4
- time: "2021-06-20T01:50:00.080+02:00",
5
- value: true,
6
- },
7
- {
8
- time: "2021-06-20T01:50:00.100+02:00",
9
- value: false,
10
- },
11
- {
12
- time: "2021-06-20T01:50:00.120+02:00",
13
- value: true,
14
- },
15
- {
16
- time: "2021-06-20T01:50:00.130+02:00",
17
- value: false,
18
- },
19
- {
20
- time: "2021-06-20T01:50:00.180+02:00",
21
- value: true,
22
- },
23
- ],
2
+ schedule: [],
24
3
  hours: [
25
4
  {
26
- price: 0.3,
27
- onOff: true,
5
+ price: 0.2494,
6
+ onOff: false,
28
7
  start: "2021-06-20T01:50:00.000+02:00",
29
- saving: null,
8
+ saving: 0.0395,
30
9
  },
31
10
  {
32
- price: 0.4,
33
- onOff: true,
11
+ price: 0.2209,
12
+ onOff: false,
34
13
  start: "2021-06-20T01:50:00.010+02:00",
35
- saving: null,
14
+ saving: 0.011,
36
15
  },
37
16
  {
38
- price: 0.8,
17
+ price: 0.2099,
39
18
  onOff: true,
40
19
  start: "2021-06-20T01:50:00.020+02:00",
41
20
  saving: null,
42
21
  },
43
22
  {
44
- price: 0.9,
45
- onOff: false,
23
+ price: 0.219,
24
+ onOff: true,
46
25
  start: "2021-06-20T01:50:00.030+02:00",
47
- saving: 0.7,
26
+ saving: null,
48
27
  },
49
28
  {
50
- price: 0.7,
51
- onOff: false,
29
+ price: 0.2614,
30
+ onOff: true,
52
31
  start: "2021-06-20T01:50:00.040+02:00",
53
- saving: 0.5,
32
+ saving: null,
54
33
  },
55
34
  {
56
- price: 0.6,
57
- onOff: false,
35
+ price: 0.4426,
36
+ onOff: true,
58
37
  start: "2021-06-20T01:50:00.050+02:00",
59
- saving: 0.4,
38
+ saving: null,
60
39
  },
61
40
  {
62
- price: 0.5,
63
- onOff: false,
41
+ price: 0.5433,
42
+ onOff: true,
64
43
  start: "2021-06-20T01:50:00.060+02:00",
65
- saving: 0.3,
44
+ saving: null,
66
45
  },
67
46
  {
68
- price: 0.75,
69
- onOff: false,
47
+ price: 0.5667,
48
+ onOff: true,
70
49
  start: "2021-06-20T01:50:00.070+02:00",
71
- saving: 0.55,
50
+ saving: null,
72
51
  },
73
52
  {
74
- price: 0.2,
53
+ price: 0.5847,
75
54
  onOff: true,
76
55
  start: "2021-06-20T01:50:00.080+02:00",
77
56
  saving: null,
78
57
  },
79
58
  {
80
- price: 0.85,
59
+ price: 0.5762,
81
60
  onOff: true,
82
61
  start: "2021-06-20T01:50:00.090+02:00",
83
62
  saving: null,
84
63
  },
85
64
  {
86
- price: 1.5,
87
- onOff: false,
65
+ price: 0.5391,
66
+ onOff: true,
88
67
  start: "2021-06-20T01:50:00.100+02:00",
89
- saving: 0.7,
68
+ saving: null,
90
69
  },
91
70
  {
92
- price: 1.4,
93
- onOff: false,
71
+ price: 0.5124,
72
+ onOff: true,
94
73
  start: "2021-06-20T01:50:00.110+02:00",
95
- saving: 0.6,
74
+ saving: null,
96
75
  },
97
76
  {
98
- price: 0.8,
77
+ price: 0.504,
99
78
  onOff: true,
100
79
  start: "2021-06-20T01:50:00.120+02:00",
101
80
  saving: null,
102
81
  },
103
82
  {
104
- price: 0.9,
105
- onOff: false,
83
+ price: 0.5057,
84
+ onOff: true,
106
85
  start: "2021-06-20T01:50:00.130+02:00",
107
- saving: 0.7,
86
+ saving: null,
108
87
  },
109
88
  {
110
- price: 0.7,
111
- onOff: false,
89
+ price: 0.5066,
90
+ onOff: true,
112
91
  start: "2021-06-20T01:50:00.140+02:00",
113
- saving: 0.5,
92
+ saving: null,
114
93
  },
115
94
  {
116
- price: 0.6,
117
- onOff: false,
95
+ price: 0.5136,
96
+ onOff: true,
118
97
  start: "2021-06-20T01:50:00.150+02:00",
119
- saving: 0.4,
98
+ saving: null,
120
99
  },
121
100
  {
122
- price: 0.5,
123
- onOff: false,
101
+ price: 0.5143,
102
+ onOff: true,
124
103
  start: "2021-06-20T01:50:00.160+02:00",
125
- saving: 0.3,
104
+ saving: null,
126
105
  },
127
106
  {
128
- price: 0.75,
129
- onOff: false,
107
+ price: 0.5306,
108
+ onOff: true,
130
109
  start: "2021-06-20T01:50:00.170+02:00",
131
- saving: 0.55,
110
+ saving: null,
132
111
  },
133
112
  {
134
- price: 0.2,
113
+ price: 0.5534,
135
114
  onOff: true,
136
115
  start: "2021-06-20T01:50:00.180+02:00",
137
116
  saving: null,
138
117
  },
139
118
  {
140
- price: 0.85,
119
+ price: 0.5511,
141
120
  onOff: true,
142
121
  start: "2021-06-20T01:50:00.190+02:00",
143
122
  saving: null,
144
123
  },
124
+ {
125
+ price: 0.5491,
126
+ onOff: true,
127
+ start: "2021-06-20T01:50:00.200+02:00",
128
+ saving: null,
129
+ },
130
+ {
131
+ price: 0.5485,
132
+ onOff: true,
133
+ start: "2021-06-20T01:50:00.210+02:00",
134
+ saving: null,
135
+ },
136
+ {
137
+ price: 0.5481,
138
+ onOff: true,
139
+ start: "2021-06-20T01:50:00.220+02:00",
140
+ saving: null,
141
+ },
142
+ {
143
+ price: 0.5218,
144
+ onOff: true,
145
+ start: "2021-06-20T01:50:00.230+02:00",
146
+ saving: null,
147
+ },
148
+ {
149
+ price: 0.4533,
150
+ onOff: true,
151
+ start: "2021-06-20T01:50:00.240+02:00",
152
+ saving: null,
153
+ },
154
+ {
155
+ price: 0.4196,
156
+ onOff: true,
157
+ start: "2021-06-20T01:50:00.250+02:00",
158
+ saving: null,
159
+ },
160
+ {
161
+ price: 0.4037,
162
+ onOff: true,
163
+ start: "2021-06-20T01:50:00.260+02:00",
164
+ saving: null,
165
+ },
166
+ {
167
+ price: 0.3829,
168
+ onOff: true,
169
+ start: "2021-06-20T01:50:00.270+02:00",
170
+ saving: null,
171
+ },
172
+ {
173
+ price: 0.4555,
174
+ onOff: true,
175
+ start: "2021-06-20T01:50:00.280+02:00",
176
+ saving: null,
177
+ },
178
+ {
179
+ price: 0.4721,
180
+ onOff: true,
181
+ start: "2021-06-20T01:50:00.290+02:00",
182
+ saving: null,
183
+ },
184
+ {
185
+ price: 0.4903,
186
+ onOff: true,
187
+ start: "2021-06-20T01:50:00.300+02:00",
188
+ saving: null,
189
+ },
190
+ {
191
+ price: 0.5028,
192
+ onOff: true,
193
+ start: "2021-06-20T01:50:00.310+02:00",
194
+ saving: null,
195
+ },
196
+ {
197
+ price: 0.5163,
198
+ onOff: true,
199
+ start: "2021-06-20T01:50:00.320+02:00",
200
+ saving: null,
201
+ },
202
+ {
203
+ price: 0.5264,
204
+ onOff: true,
205
+ start: "2021-06-20T01:50:00.330+02:00",
206
+ saving: null,
207
+ },
208
+ {
209
+ price: 0.5346,
210
+ onOff: true,
211
+ start: "2021-06-20T01:50:00.340+02:00",
212
+ saving: null,
213
+ },
214
+ {
215
+ price: 0.5224,
216
+ onOff: true,
217
+ start: "2021-06-20T01:50:00.350+02:00",
218
+ saving: null,
219
+ },
220
+ {
221
+ price: 0.5071,
222
+ onOff: true,
223
+ start: "2021-06-20T01:50:00.360+02:00",
224
+ saving: null,
225
+ },
226
+ {
227
+ price: 0.5045,
228
+ onOff: true,
229
+ start: "2021-06-20T01:50:00.370+02:00",
230
+ saving: null,
231
+ },
232
+ {
233
+ price: 0.5024,
234
+ onOff: true,
235
+ start: "2021-06-20T01:50:00.380+02:00",
236
+ saving: null,
237
+ },
238
+ {
239
+ price: 0.5038,
240
+ onOff: true,
241
+ start: "2021-06-20T01:50:00.390+02:00",
242
+ saving: null,
243
+ },
244
+ {
245
+ price: 0.5118,
246
+ onOff: true,
247
+ start: "2021-06-20T01:50:00.400+02:00",
248
+ saving: null,
249
+ },
250
+ {
251
+ price: 0.5111,
252
+ onOff: true,
253
+ start: "2021-06-20T01:50:00.410+02:00",
254
+ saving: null,
255
+ },
256
+ {
257
+ price: 0.5108,
258
+ onOff: true,
259
+ start: "2021-06-20T01:50:00.420+02:00",
260
+ saving: null,
261
+ },
262
+ {
263
+ price: 0.5055,
264
+ onOff: true,
265
+ start: "2021-06-20T01:50:00.430+02:00",
266
+ saving: null,
267
+ },
268
+ {
269
+ price: 0.5042,
270
+ onOff: true,
271
+ start: "2021-06-20T01:50:00.440+02:00",
272
+ saving: null,
273
+ },
274
+ {
275
+ price: 0.5259,
276
+ onOff: true,
277
+ start: "2021-06-20T01:50:00.450+02:00",
278
+ saving: null,
279
+ },
280
+ {
281
+ price: 0.5282,
282
+ onOff: true,
283
+ start: "2021-06-20T01:50:00.460+02:00",
284
+ saving: null,
285
+ },
286
+ {
287
+ price: 0.5252,
288
+ onOff: true,
289
+ start: "2021-06-20T01:50:00.470+02:00",
290
+ saving: null,
291
+ },
145
292
  ],
146
- source: "Other",
293
+ source: undefined,
147
294
  config: {
148
295
  maxHoursToSaveInSequence: 5,
149
296
  minHoursOnAfterMaxSequenceSaved: 2,
150
297
  minSaving: 0.25,
298
+ sendCurrentValueWhenRescheduling: undefined,
151
299
  outputIfNoSchedule: false,
152
300
  scheduleOnlyFromCurrentTime: true,
153
301
  },