node-red-contrib-power-saver 2.1.0 → 3.0.3

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 (179) hide show
  1. package/.github/FUNDING.yml +12 -0
  2. package/CHANGELOG.md +1 -43
  3. package/README.md +3 -231
  4. package/docs/.vuepress/config.js +67 -0
  5. package/docs/.vuepress/dist/404.html +15 -0
  6. package/docs/.vuepress/dist/assets/css/styles.e835bef6.css +8 -0
  7. package/docs/.vuepress/dist/assets/img/back-to-top.8b37f773.svg +1 -0
  8. package/docs/.vuepress/dist/assets/img/elvia-config-no-config.b4bb972c.png +0 -0
  9. package/docs/.vuepress/dist/assets/img/elvia-config-no-tariff.3f89aba8.png +0 -0
  10. package/docs/.vuepress/dist/assets/img/elvia-config-select-tariff.0f73fd56.png +0 -0
  11. package/docs/.vuepress/dist/assets/img/elvia-config-subscription-key.8be8ab8a.png +0 -0
  12. package/docs/.vuepress/dist/assets/img/elvia-flow.bae2a4d5.png +0 -0
  13. package/docs/.vuepress/dist/assets/img/example-flow-1.3ff3e23f.png +0 -0
  14. package/docs/.vuepress/dist/assets/img/example-flow-2.b653b58d.png +0 -0
  15. package/docs/.vuepress/dist/assets/img/migrate-best-save.f73420f6.png +0 -0
  16. package/docs/.vuepress/dist/assets/img/migrate-power-saver.aae13f9d.png +0 -0
  17. package/docs/.vuepress/dist/assets/img/node-power-saver.51ff2e5d.png +0 -0
  18. package/docs/.vuepress/dist/assets/img/node-ps-elvia-add-tariff.94ea2b09.png +0 -0
  19. package/docs/.vuepress/dist/assets/img/node-ps-receive-price.76eaa418.png +0 -0
  20. package/docs/.vuepress/dist/assets/img/node-ps-strategy-best-save.392292d5.png +0 -0
  21. package/docs/.vuepress/dist/assets/img/node-ps-strategy-lowest-price.3a4ad347.png +0 -0
  22. package/docs/.vuepress/dist/assets/img/power-saver-nordpool-current-state.bf14afde.png +0 -0
  23. package/docs/.vuepress/dist/assets/img/power-saver-nordpool-events-state.8c392507.png +0 -0
  24. package/docs/.vuepress/dist/assets/img/power-saver-tibber-mqtt.16891dd2.png +0 -0
  25. package/docs/.vuepress/dist/assets/js/293.5e967839.js +1 -0
  26. package/docs/.vuepress/dist/assets/js/491.c183eba3.js +1 -0
  27. package/docs/.vuepress/dist/assets/js/812.79dad458.js +2 -0
  28. package/docs/.vuepress/dist/assets/js/812.79dad458.js.LICENSE.txt +8 -0
  29. package/docs/.vuepress/dist/assets/js/app.4ee3384b.js +1 -0
  30. package/docs/.vuepress/dist/assets/js/runtime~app.cafd6537.js +1 -0
  31. package/docs/.vuepress/dist/assets/js/v-08683c60.07fe8291.js +1 -0
  32. package/docs/.vuepress/dist/assets/js/v-0aca7ba6.aec5ba75.js +1 -0
  33. package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.d008d8bc.js +1 -0
  34. package/docs/.vuepress/dist/assets/js/v-1ad821fa.85407071.js +1 -0
  35. package/docs/.vuepress/dist/assets/js/v-30acb564.73b8e29f.js +1 -0
  36. package/docs/.vuepress/dist/assets/js/v-3706649a.d7f73384.js +1 -0
  37. package/docs/.vuepress/dist/assets/js/v-4637f9e4.22ab9413.js +1 -0
  38. package/docs/.vuepress/dist/assets/js/v-510ed0d4.204a09ec.js +1 -0
  39. package/docs/.vuepress/dist/assets/js/v-5954bcb2.be07962c.js +1 -0
  40. package/docs/.vuepress/dist/assets/js/v-5db8da3a.ac192f35.js +1 -0
  41. package/docs/.vuepress/dist/assets/js/v-61f728ca.802ab15e.js +1 -0
  42. package/docs/.vuepress/dist/assets/js/v-677dfaed.9bbbd037.js +1 -0
  43. package/docs/.vuepress/dist/assets/js/v-7c87f26e.457a1a60.js +1 -0
  44. package/docs/.vuepress/dist/assets/js/v-8daa1a0e.db8b59c6.js +1 -0
  45. package/docs/.vuepress/dist/assets/js/v-b4a42144.6e0c5aa0.js +1 -0
  46. package/docs/.vuepress/dist/assets/js/v-e8c55052.5f85b6cd.js +1 -0
  47. package/docs/.vuepress/dist/assets/js/v-fffb8e28.e815e852.js +1 -0
  48. package/docs/.vuepress/dist/changelog/index.html +15 -0
  49. package/docs/.vuepress/dist/contribute/index.html +15 -0
  50. package/docs/.vuepress/dist/euro.png +0 -0
  51. package/docs/.vuepress/dist/examples/example-nordpool-current-state.html +169 -0
  52. package/docs/.vuepress/dist/examples/example-nordpool-events-state.html +173 -0
  53. package/docs/.vuepress/dist/examples/example-tibber-mqtt.html +182 -0
  54. package/docs/.vuepress/dist/examples/index.html +15 -0
  55. package/docs/.vuepress/dist/guide/index.html +52 -0
  56. package/docs/.vuepress/dist/index.html +15 -0
  57. package/docs/.vuepress/dist/logo.png +0 -0
  58. package/docs/.vuepress/dist/nodes/index.html +15 -0
  59. package/docs/.vuepress/dist/nodes/old-power-saver-doc.html +97 -0
  60. package/docs/.vuepress/dist/nodes/power-saver.html +15 -0
  61. package/docs/.vuepress/dist/nodes/ps-elvia-add-tariff.html +15 -0
  62. package/docs/.vuepress/dist/nodes/ps-receive-price.html +80 -0
  63. package/docs/.vuepress/dist/nodes/ps-strategy-best-save.html +65 -0
  64. package/docs/.vuepress/dist/nodes/ps-strategy-lowest-price.html +89 -0
  65. package/docs/.vuepress/dist/nodes/strategy-input.html +40 -0
  66. package/docs/.vuepress/public/euro.png +0 -0
  67. package/docs/.vuepress/public/logo.png +0 -0
  68. package/docs/README.md +32 -0
  69. package/docs/changelog/README.md +71 -0
  70. package/docs/contribute/README.md +39 -0
  71. package/docs/examples/README.md +5 -0
  72. package/docs/examples/example-nordpool-current-state.md +166 -0
  73. package/docs/examples/example-nordpool-events-state.md +170 -0
  74. package/docs/examples/example-tibber-mqtt.md +179 -0
  75. package/docs/guide/README.md +202 -0
  76. package/docs/images/all-nodes.png +0 -0
  77. package/docs/images/best-save-config.png +0 -0
  78. package/docs/images/elvia-add-tariff-node-used.png +0 -0
  79. package/docs/images/elvia-config-no-config.png +0 -0
  80. package/docs/images/elvia-config-no-tariff.png +0 -0
  81. package/docs/images/elvia-config-select-tariff.png +0 -0
  82. package/docs/images/elvia-config-subscription-key.png +0 -0
  83. package/docs/images/elvia-flow.png +0 -0
  84. package/docs/images/elvia-tariff-config.png +0 -0
  85. package/docs/images/euro.png +0 -0
  86. package/docs/images/example-flow-1.png +0 -0
  87. package/docs/images/example-flow-2.png +0 -0
  88. package/docs/images/logo.png +0 -0
  89. package/docs/images/lowest-price-config.png +0 -0
  90. package/docs/images/migrate-best-save.png +0 -0
  91. package/docs/images/migrate-power-saver.png +0 -0
  92. package/docs/images/node-power-saver.png +0 -0
  93. package/docs/images/node-ps-elvia-add-tariff.png +0 -0
  94. package/docs/images/node-ps-elvia-tariff-types.png +0 -0
  95. package/docs/images/node-ps-elvia-tariff.png +0 -0
  96. package/docs/images/node-ps-receive-price.png +0 -0
  97. package/docs/images/node-ps-strategy-best-save.png +0 -0
  98. package/docs/images/node-ps-strategy-lowest-price.png +0 -0
  99. package/{doc → docs/images}/node-red-contrib-power-saver-flow.png +0 -0
  100. package/docs/images/power-saver-nordpool-current-state.png +0 -0
  101. package/docs/images/power-saver-nordpool-events-state.png +0 -0
  102. package/docs/images/power-saver-tibber-mqtt.png +0 -0
  103. package/docs/nodes/README.md +53 -0
  104. package/docs/nodes/old-power-saver-doc.md +231 -0
  105. package/docs/nodes/power-saver.md +23 -0
  106. package/docs/nodes/ps-elvia-add-tariff.md +52 -0
  107. package/docs/nodes/ps-receive-price.md +153 -0
  108. package/docs/nodes/ps-strategy-best-save.md +142 -0
  109. package/docs/nodes/ps-strategy-lowest-price.md +165 -0
  110. package/docs/nodes/strategy-input.md +39 -0
  111. package/package.json +16 -4
  112. package/src/elvia/elvia-add-tariff.html +70 -0
  113. package/src/elvia/elvia-add-tariff.js +47 -0
  114. package/src/elvia/elvia-api.js +61 -0
  115. package/src/elvia/elvia-config.html +46 -0
  116. package/src/elvia/elvia-config.js +19 -0
  117. package/src/elvia/elvia-tariff-types.html +34 -0
  118. package/src/elvia/elvia-tariff-types.js +25 -0
  119. package/src/elvia/elvia-tariff.html +89 -0
  120. package/src/elvia/elvia-tariff.js +22 -0
  121. package/src/elvia/icons/elvia_hvite.svg +4 -0
  122. package/src/elvia/icons/elvia_positive_4 copy.svg +4 -0
  123. package/src/handle-input.js +162 -0
  124. package/src/power-saver.html +116 -0
  125. package/{power-saver.js → src/power-saver.js} +9 -32
  126. package/src/receive-price-functions.js +99 -0
  127. package/src/receive-price.html +30 -0
  128. package/src/receive-price.js +21 -0
  129. package/src/strategy-best-save-functions.js +110 -0
  130. package/src/strategy-best-save.html +116 -0
  131. package/src/strategy-best-save.js +95 -0
  132. package/src/strategy-lowest-price-functions.js +35 -0
  133. package/src/strategy-lowest-price.html +168 -0
  134. package/src/strategy-lowest-price.js +125 -0
  135. package/{utils.js → src/utils.js} +44 -100
  136. package/test/data/adjustedResult.js +219 -71
  137. package/test/data/adjustedResult_old.js +154 -0
  138. package/test/data/best-save-result.json +357 -0
  139. package/test/data/converted-prices.json +197 -0
  140. package/test/data/elvia-input-grid-tariff.json +760 -0
  141. package/test/data/elvia-input-power-prices.json +194 -0
  142. package/test/data/elvia-output-add-tariff.json +290 -0
  143. package/test/data/lowest-price-result-cont.json +18 -0
  144. package/test/data/lowest-price-result-split-allday.json +21 -0
  145. package/test/data/lowest-price-result-split-allday10.json +20 -0
  146. package/test/data/lowest-price-result-split.json +20 -0
  147. package/test/data/nordpool-current-state-prices.json +283 -0
  148. package/test/data/nordpool-event-prices.json +574 -0
  149. package/test/data/reconfigResult.js +220 -67
  150. package/test/data/reconfigResult_old.js +141 -0
  151. package/test/data/tibber-data-end-0-24h.json +197 -0
  152. package/test/data/tibber-data-end-0.json +101 -0
  153. package/test/data/tibber-prices-single-home.json +64 -0
  154. package/test/data/tibber-prices.json +124 -0
  155. package/test/data/tibber-result-end-0-24h.json +320 -0
  156. package/test/data/tibber-result-end-0.json +168 -0
  157. package/test/data/{tibber_result.json → tibber-result.json} +0 -0
  158. package/test/elvia.test.js +26 -0
  159. package/test/mostSavedStrategy.test.js +22 -55
  160. package/test/power-saver.test.js +21 -5
  161. package/test/receive-price-functions.test.js +153 -0
  162. package/test/receive-price.test.js +122 -0
  163. package/test/send-config-input.test.js +8 -10
  164. package/test/strategy-best-save-test-utils.js +32 -0
  165. package/test/strategy-best-save.test.js +103 -0
  166. package/test/strategy-lowest-price-functions.test.js +40 -0
  167. package/test/strategy-lowest-price.test.js +538 -0
  168. package/test/test-utils.js +0 -18
  169. package/test/utils.test.js +22 -180
  170. package/doc/example-nordpool-current-state.md +0 -166
  171. package/doc/example-nordpool-events-state.md +0 -153
  172. package/doc/example-tibber-mqtt.md +0 -189
  173. package/doc/power-saver-nordpool-current-state.png +0 -0
  174. package/doc/power-saver-nordpool-events-state.png +0 -0
  175. package/doc/power-saver-tibber-mqtt.png +0 -0
  176. package/mostSavedStrategy.js +0 -84
  177. package/power-saver.html +0 -308
  178. package/test/data/tibber_data.json +0 -412
  179. package/test/data/tibber_prices.json +0 -412
@@ -1,189 +0,0 @@
1
- ## Example with Tibber, a switch and MQTT
2
-
3
- In this example, data is read from Tibber and used to turn on/off a switch. Data is also sent to MQTT for being displayed on Magic Mirror. This is optional, of course.
4
-
5
- ![Example with Tibber and MQTT](./power-saver-tibber-mqtt.png)
6
-
7
- Flow:
8
-
9
- ```json
10
- [
11
- {
12
- "id": "467a5fe.d0bbba",
13
- "type": "mqtt out",
14
- "z": "d938c47f.3398f8",
15
- "name": "Send switch to MM",
16
- "topic": "powersaver/switch",
17
- "qos": "0",
18
- "retain": "false",
19
- "broker": "24fbcfb5.569ea",
20
- "x": 730,
21
- "y": 120,
22
- "wires": []
23
- },
24
- {
25
- "id": "8c533305.eacaa",
26
- "type": "power-saver",
27
- "z": "d938c47f.3398f8",
28
- "name": "Power Saver",
29
- "maxHoursToSaveInSequence": "3",
30
- "minHoursOnAfterMaxSequenceSaved": "1",
31
- "minSaving": "0.001",
32
- "sendCurrentValueWhenRescheduling": true,
33
- "outputIfNoSchedule": "true",
34
- "x": 490,
35
- "y": 120,
36
- "wires": [
37
- [
38
- "5e485ff7.db156",
39
- "467a5fe.d0bbba"
40
- ],
41
- [
42
- "9c978d1c.ee76",
43
- "467a5fe.d0bbba"
44
- ],
45
- [
46
- "42d8b632.402e38"
47
- ]
48
- ]
49
- },
50
- {
51
- "id": "ab2d599a.077738",
52
- "type": "tibber-query",
53
- "z": "d938c47f.3398f8",
54
- "name": "Get Tibber prices",
55
- "active": true,
56
- "apiEndpointRef": "b70ec5d0.6f8f08",
57
- "x": 290,
58
- "y": 120,
59
- "wires": [
60
- [
61
- "8c533305.eacaa"
62
- ]
63
- ]
64
- },
65
- {
66
- "id": "4f11b5ae.4cc22c",
67
- "type": "inject",
68
- "z": "d938c47f.3398f8",
69
- "name": "Refresh",
70
- "props": [
71
- {
72
- "p": "payload"
73
- }
74
- ],
75
- "repeat": "3600",
76
- "crontab": "",
77
- "once": false,
78
- "onceDelay": "1",
79
- "topic": "",
80
- "payload": "{ viewer { homes { currentSubscription{ priceInfo{ today { total startsAt } tomorrow { total startsAt } } } } } }",
81
- "payloadType": "str",
82
- "x": 100,
83
- "y": 120,
84
- "wires": [
85
- [
86
- "ab2d599a.077738"
87
- ]
88
- ]
89
- },
90
- {
91
- "id": "42d8b632.402e38",
92
- "type": "mqtt out",
93
- "z": "d938c47f.3398f8",
94
- "name": "Send schedule to MM",
95
- "topic": "powersaver/plan",
96
- "qos": "0",
97
- "retain": "true",
98
- "broker": "24fbcfb5.569ea",
99
- "x": 740,
100
- "y": 220,
101
- "wires": []
102
- },
103
- {
104
- "id": "5e485ff7.db156",
105
- "type": "api-call-service",
106
- "z": "d938c47f.3398f8",
107
- "name": "Turn on VVB",
108
- "server": "ec4a12a1.b2be9",
109
- "version": 1,
110
- "debugenabled": false,
111
- "service_domain": "switch",
112
- "service": "turn_on",
113
- "entityId": "switch.varmtvannsbereder_switch",
114
- "data": "",
115
- "dataType": "jsonata",
116
- "mergecontext": "",
117
- "output_location": "",
118
- "output_location_type": "none",
119
- "mustacheAltTags": false,
120
- "x": 710,
121
- "y": 60,
122
- "wires": [
123
- []
124
- ]
125
- },
126
- {
127
- "id": "9c978d1c.ee76",
128
- "type": "api-call-service",
129
- "z": "d938c47f.3398f8",
130
- "name": "Turn off VVB",
131
- "server": "ec4a12a1.b2be9",
132
- "version": 1,
133
- "debugenabled": true,
134
- "service_domain": "switch",
135
- "service": "turn_off",
136
- "entityId": "switch.varmtvannsbereder_switch",
137
- "data": "",
138
- "dataType": "json",
139
- "mergecontext": "",
140
- "output_location": "",
141
- "output_location_type": "none",
142
- "mustacheAltTags": false,
143
- "x": 710,
144
- "y": 180,
145
- "wires": [
146
- []
147
- ]
148
- },
149
- {
150
- "id": "24fbcfb5.569ea",
151
- "type": "mqtt-broker",
152
- "name": "MQTT",
153
- "broker": "10.0.0.15",
154
- "port": "1883",
155
- "clientid": "",
156
- "usetls": false,
157
- "compatmode": false,
158
- "keepalive": "60",
159
- "cleansession": true,
160
- "birthTopic": "",
161
- "birthQos": "0",
162
- "birthPayload": "",
163
- "closeTopic": "",
164
- "closeQos": "0",
165
- "closePayload": "",
166
- "willTopic": "",
167
- "willQos": "0",
168
- "willPayload": ""
169
- },
170
- {
171
- "id": "b70ec5d0.6f8f08",
172
- "type": "tibber-api-endpoint",
173
- "feedUrl": "wss://api.tibber.com/v1-beta/gql/subscriptions",
174
- "queryUrl": "https://api.tibber.com/v1-beta/gql",
175
- "name": "Tibber API"
176
- },
177
- {
178
- "id": "ec4a12a1.b2be9",
179
- "type": "server",
180
- "name": "Home Assistant",
181
- "legacy": false,
182
- "addon": true,
183
- "rejectUnauthorizedCerts": true,
184
- "ha_boolean": "y|yes|true|on|home|open",
185
- "connectionDelay": true,
186
- "cacheJson": true
187
- }
188
- ]
189
- ```
Binary file
@@ -1,84 +0,0 @@
1
- "use strict";
2
-
3
- const { isOnOffSequencesOk, fillArray } = require("./utils");
4
- /**
5
- * Turn off the hours where you save most compared to the next hour on.
6
- *
7
- * @param {*} values Array of prices
8
- * @param {*} maxOffInARow Max number of hours that can be saved in a row
9
- * @param {*} minOnAfterMaxOffInARow Min number of hours that must be on after maxOffInARow is saved
10
- * @param {*} minSaving Minimum amount that must be saved in order to turn off
11
- * @param {*} lastValueDayBefore Value of the last hour the day before
12
- * @param {*} lastCountDayBefore Number of lastValueDayBefore in a row
13
- * @returns Array with same number of values as in values array, where true is on, false is off
14
- */
15
- module.exports = {
16
- calculate: function (
17
- values,
18
- maxOffInARow,
19
- minOnAfterMaxOffInARow,
20
- minSaving,
21
- lastValueDayBefore = undefined,
22
- lastCountDayBefore = 0
23
- ) {
24
- const dayBefore = fillArray(lastValueDayBefore, lastCountDayBefore);
25
- const last = values.length - 1;
26
-
27
- // Create matrix with saving per hour
28
- const savingPerHour = [];
29
- for (let hour = 0; hour < last; hour++) {
30
- const row = [];
31
- for (let count = 1; count <= maxOffInARow; count++) {
32
- const on = hour + count;
33
- const saving = values[hour] - values[on >= last ? last : on];
34
- row.push(saving);
35
- }
36
- savingPerHour.push(row);
37
- }
38
-
39
- // Create list with summary saving per sequence
40
- let savingsList = [];
41
- for (let hour = 0; hour < last; hour++) {
42
- for (let count = 1; count <= maxOffInARow; count++) {
43
- let saving = 0;
44
- for (let offset = 0; offset < count && hour + offset < last; offset++) {
45
- saving += savingPerHour[hour + offset][count - offset - 1];
46
- }
47
- if (
48
- saving > minSaving * count &&
49
- hour + count <= last &&
50
- values[hour] > values[hour + count] + minSaving
51
- ) {
52
- savingsList.push({ hour, count, saving });
53
- }
54
- }
55
- }
56
-
57
- savingsList.sort((a, b) => b.saving - a.saving);
58
- let onOff = values.map((v) => true); // Start with all on
59
-
60
- // Find the best possible sequences
61
- while (savingsList.length > 0) {
62
- const { hour, count } = savingsList[0];
63
- const onOffCopy = [...onOff];
64
- for (let c = 0; c < count; c++) {
65
- onOff[hour + c] = false;
66
- }
67
- if (
68
- isOnOffSequencesOk(
69
- [...dayBefore, ...onOff],
70
- maxOffInARow,
71
- minOnAfterMaxOffInARow
72
- )
73
- ) {
74
- savingsList = savingsList.filter(
75
- (s) => s.hour < hour || s.hour >= hour + count
76
- );
77
- } else {
78
- onOff = [...onOffCopy];
79
- savingsList.splice(0, 1);
80
- }
81
- }
82
- return onOff;
83
- },
84
- };
package/power-saver.html DELETED
@@ -1,308 +0,0 @@
1
- <script type="text/javascript">
2
- RED.nodes.registerType("power-saver", {
3
- category: "function",
4
- color: "#a6bbcf",
5
- defaults: {
6
- name: { value: "Power Saver" },
7
- maxHoursToSaveInSequence: {
8
- value: 3,
9
- required: true,
10
- validate: RED.validators.number(),
11
- },
12
- minHoursOnAfterMaxSequenceSaved: {
13
- value: 2,
14
- required: true,
15
- validate: RED.validators.number(),
16
- },
17
- minSaving: {
18
- value: 0.01,
19
- required: true,
20
- validate: RED.validators.number(),
21
- },
22
- sendCurrentValueWhenRescheduling: {
23
- value: true,
24
- required: true,
25
- // validate: RED.validators.number(),
26
- align: "left",
27
- },
28
- outputIfNoSchedule: { value: true, required: true, align: "left" },
29
- scheduleOnlyFromCurrentTime: {
30
- value: true,
31
- required: true,
32
- align: "left",
33
- },
34
- },
35
- inputs: 1,
36
- outputs: 3,
37
- icon: "font-awesome/fa-eur",
38
- color: "#FFCC66",
39
- label: function () {
40
- return this.name || "power-saver";
41
- },
42
- outputLabels: ["on", "off", "schedule"],
43
- oneditprepare: function () {
44
- $("#node-input-outputIfNoSchedule").typedInput({
45
- types: [
46
- {
47
- value: "onoff",
48
- options: [
49
- { value: true, label: "On" },
50
- { value: false, label: "Off" },
51
- ],
52
- },
53
- ],
54
- });
55
- $("#node-input-scheduleOnlyFromCurrentTime").typedInput({
56
- types: [
57
- {
58
- value: "nowOrStart",
59
- options: [
60
- { value: false, label: "Whole data set" },
61
- { value: true, label: "From current hour" },
62
- ],
63
- },
64
- ],
65
- });
66
- },
67
- });
68
- </script>
69
-
70
- <script type="text/html" data-template-name="power-saver">
71
- <div class="form-row">
72
- <label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
73
- <input type="text" id="node-input-name" placeholder="Name" style="width: 240px">
74
- </div>
75
- <h3>Rules</h3>
76
- <div class="form-row">
77
- <label for="node-input-maxHoursToSaveInSequence"><i class="fa fa-arrows-h"></i> Max per sequence</label>
78
- <input type="text" id="node-input-maxHoursToSaveInSequence" style="width: 80px" placeholder="Max hours to save in sequence">
79
- </div>
80
- <div class="form-row">
81
- <label for="node-input-minHoursOnAfterMaxSequenceSaved"><i class="fa fa-ellipsis-h"></i> Min recover</label>
82
- <input type="text"
83
- id="node-input-minHoursOnAfterMaxSequenceSaved"
84
- style="width: 80px"
85
- placeholder="Min hours on after a max sequence">
86
- </div>
87
- <div class="form-row">
88
- <label for="node-input-minSaving"><i class="fa fa-eur"></i> Min saving</label>
89
- <input type="text" id="node-input-minSaving" placeholder="Minimum to save for turning off" style="width: 80px">
90
- </div>
91
- <div class="form-row">
92
- <label for="node-input-scheduleOnlyFromCurrentTime">Schedule for</label>
93
- <input type="boolean" id="node-input-scheduleOnlyFromCurrentTime" style="width: 160px">
94
- </label>
95
- </div>
96
- <h3>Output</h3>
97
- <div class="form-row">
98
- <label for="node-input-sendCurrentValueWhenRescheduling" style="width:240px">
99
- <input type="checkbox"
100
- id="node-input-sendCurrentValueWhenRescheduling"
101
- style="display:inline-block; width:22px; vertical-align:top;"
102
- autocomplete="off"><span>Send when rescheduling</span>
103
- </label>
104
- </div>
105
- <div class="form-row">
106
- <label for="node-input-outputIfNoSchedule">If no schedule, send</label>
107
- <input type="boolean" id="node-input-outputIfNoSchedule" style="width: 80px">
108
- </label>
109
- </div>
110
- </script>
111
-
112
- <script type="text/html" data-help-name="power-saver">
113
- <p>A node you can use to save money by turning off and on a switch based on power prices.</p>
114
- <p>You can use it to control for example a heater, a water heater or any other power consumer that is
115
- acceptable to turn off now and then.</p>
116
- <p>It is designed to be used with Home Assistant, but can also be used without.
117
- Prices can be supplied by the
118
- <a href="https://github.com/custom-components/nordpool">nordpool</a> custom component,
119
- by a Tibber query or by any other means that can produce data in a valid format.</p>
120
- <h3>Configuration</h3>
121
- <p>
122
- <strong>Max per sequence</strong> is the maximum number of hours that can be saved (turned off) in a sequence.
123
- </p>
124
- <p>
125
- <strong>Min recover</strong> If power is saved the maximum number of hours that is allowed (Max per sequence),
126
- it must be followed by a period of not saving power that is minimum <em>Min recover</em> hours.
127
- </p>
128
- <p>
129
- <strong>Min saving</strong> The minimum price that must be saved per kWh per hour, in order to turn off an hour.
130
- For a sequence of multiple hours, the average per hour is evaluated against this value.
131
- </p>
132
- <p>
133
- You can select to <strong>Schedule for</strong> either the whole data set
134
- or just from the current time when the schedule is calculated.
135
- </p>
136
- <p>
137
- Check <strong>Send when rescheduling</strong> to make sure the correct value for the current
138
- time is sent on output 1 or output 2,
139
- when a new schedule is calculated. The new schedule is sent to output 3 anyway.
140
- </p>
141
- <p>
142
- <strong>If no schedule, send</strong> Select if the output shall be set to on or off when there is no valid schedule.
143
- If you control a water heater or control heat in a room, selecting "on" is recommended.
144
- </p>
145
- <h3>Input</h3>
146
- <dl class="message-properties">
147
- <dt>payload
148
- <span class="property-type">object</span>
149
- </dt>
150
- <dd>The payload should be an object with two arrays: <code>raw_today</code> and <code>raw_tomorrow</code>.
151
- Each array shall contain one object per hour, with attributes start, end and value.
152
- <br>
153
- The <code>raw_today</code> and <code>raw_tomorrow</code> may alternatively be in the
154
- <code>data.new_state.attributes</code> part of the message.
155
- This is exactly what is delivered by the sensor given by the nordpool custom component,
156
- so just put the output from that sensor to the input of this node.
157
- </dd>
158
- </dl>
159
-
160
- <h3>Outputs</h3>
161
- <ol class="node-ports">
162
- <li>Output 1
163
- <dl class="message-properties">
164
- <dt>payload <span class="property-type">string</span></dt>
165
- <dd>A payload containing <code>true</code> is sent to this output when the power shall be turned on.</dd>
166
- </dl>
167
- </li>
168
- <li>Output 2
169
- <dl class="message-properties">
170
- <dt>payload <span class="property-type">string</span></dt>
171
- <dd>A payload containing <code>false</code> is sent to this output when the power shall be turned off.</dd>
172
- </dl>
173
- </li>
174
- <li>Output 3
175
- <dl class="message-properties">
176
- <dt>payload <span class="property-type">string</span></dt>
177
- <dd>When the schedule is created or changed, a payload containing information about the schedule is sent to this output.</dd>
178
- </dl>
179
- </li>
180
- </ol>
181
-
182
- <h3>Details</h3>
183
- <p>
184
- Set up the Home Assistants <em>events: state</em> node to receive state changes on the nordpool sensor.
185
- Example of entity id: <code>sensor.nordpool_kwh_trheim_nok_3_095_025</code>, but you must check what your sensor id is.
186
- </p>
187
- <p>
188
- Connect the nordpool sensor output directly to the input on this node. Connect output 1 to a node that can turn the power switch on,
189
- and output 2 to a node that can turn the power switch off. The values are true and false,
190
- so they may be connected to the same node, and use the value to turn on or off.
191
- </p>
192
- <p>You may use output 3 to display the schedule.</p>
193
- <p><strong>Input payload example</strong><br/>
194
- <pre>
195
- {
196
- raw_today:[
197
- {
198
- start: "2021-06-20T00:00:00+02:00",
199
- end: "2021-06-20T01:00:00+02:00",
200
- value: 0.37,
201
- },
202
- {
203
- start: "2021-06-20T01:00:00+02:00",
204
- end: "2021-06-20T02:00:00+02:00",
205
- value: 0.4,
206
- },
207
- {
208
- start: "2021-06-20T02:00:00+02:00",
209
- end: "2021-06-20T03:00:00+02:00",
210
- value: 0.35,
211
- },
212
- {
213
- start: "2021-06-20T03:00:00+02:00",
214
- end: "2021-06-20T04:00:00+02:00",
215
- value: 0.65,
216
- },
217
- // One entry per hour, 24 hours
218
- ],
219
- raw_tomorrow: [
220
- {
221
- start: "2021-06-20T00:00:00+02:00",
222
- end: "2021-06-21T01:00:00+02:00",
223
- value: 1.5,
224
- },
225
- {
226
- start: "2021-06-21T01:00:00+02:00",
227
- end: "2021-06-21T02:00:00+02:00",
228
- value: 1.4,
229
- },
230
- {
231
- start: "2021-06-21T02:00:00+02:00",
232
- end: "2021-06-21T03:00:00+02:00",
233
- value: 0.8,
234
- },
235
- // One entry per hour, 24 hours
236
- ],
237
- };
238
- </pre>
239
- </p>
240
- <p><strong>Output 3 payload example</strong><br/>
241
- <pre>
242
- {
243
- "schedule": [
244
- {
245
- "time": "2021-09-30T00:00:00.000+02:00",
246
- "value": false
247
- },
248
- {
249
- "time": "2021-09-30T01:00:00.000+02:00",
250
- "value": true
251
- },
252
- {
253
- "time": "2021-09-30T02:00:00.000+02:00",
254
- "value": true
255
- },
256
- ...
257
- ],
258
- "hours": [
259
- {
260
- "price": 1.2584,
261
- "onOff": false,
262
- "start": "2021-09-30T00:00:00.000+02:00",
263
- "saving": 0.2034
264
- },
265
- {
266
- "price": 1.0550,
267
- "onOff": true,
268
- "start": "2021-09-30T01:00:00.000+02:00",
269
- "saving": null
270
- },
271
- {
272
- "price": 1.2054,
273
- "onOff": true,
274
- "start": "2021-09-30T02:00:00.000+02:00",
275
- "saving": null
276
- },
277
- ...
278
- ]
279
- }
280
-
281
- </pre>
282
- </p>
283
- <p>
284
- The <code>value</code> in <code>schedule</code> is <code>true</code> to turn on and <code>false</code> to turn off.
285
- </p>
286
- <p>
287
- The <code>savings</code> array shows the price per kWh saved that hour, only for hours that are turned off.
288
- Hours that are turned on has <code>null</code> as saving.
289
- </p>
290
- <h3>Algorithm</h3>
291
- <p>
292
- Currently there is only one algorithm / strategy for saving power. It is the <strong>most saved</strong> strategy.
293
- In this strategy it is assumed that when the power has been off for some time, the consumer will catch up by consuming
294
- power as soon as the power is turned on again. Based on this assumption, the algorithm will turn off those hours
295
- that has the highest difference between the hour that is off and the next hour that is on, and by this, turn off
296
- the hours where it is most to save. This would be a good fit for turning off a heater controlled by a thermostat.
297
- </p>
298
- <p>
299
- Other strategies are planned, but not yet implemented.
300
- </p>
301
- <h3>References</h3>
302
- <ul>
303
- <li><a href="https://github.com/ottopaulsen/node-red-contrib-power-saver">
304
- node-red-contrib-power-saver</a> on GitHub</li>
305
- <li>The <a href="https://github.com/custom-components/nordpool">
306
- nordpool</a> custom component for Home Assistant</li>
307
- </ul>
308
- </script>