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.
- package/CHANGELOG.md +1 -43
- package/README.md +3 -231
- package/docs/.vuepress/config.js +67 -0
- package/docs/.vuepress/dist/404.html +15 -0
- package/docs/.vuepress/dist/assets/css/styles.e835bef6.css +8 -0
- package/docs/.vuepress/dist/assets/img/back-to-top.8b37f773.svg +1 -0
- package/docs/.vuepress/dist/assets/img/elvia-config-no-config.b4bb972c.png +0 -0
- package/docs/.vuepress/dist/assets/img/elvia-config-no-tariff.3f89aba8.png +0 -0
- package/docs/.vuepress/dist/assets/img/elvia-config-select-tariff.0f73fd56.png +0 -0
- package/docs/.vuepress/dist/assets/img/elvia-config-subscription-key.8be8ab8a.png +0 -0
- package/docs/.vuepress/dist/assets/img/elvia-flow.bae2a4d5.png +0 -0
- package/docs/.vuepress/dist/assets/img/example-flow-1.3ff3e23f.png +0 -0
- package/docs/.vuepress/dist/assets/img/example-flow-2.b653b58d.png +0 -0
- package/docs/.vuepress/dist/assets/img/migrate-best-save.f73420f6.png +0 -0
- package/docs/.vuepress/dist/assets/img/migrate-power-saver.aae13f9d.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-power-saver.51ff2e5d.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-ps-elvia-add-tariff.94ea2b09.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-ps-receive-price.76eaa418.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-ps-strategy-best-save.392292d5.png +0 -0
- package/docs/.vuepress/dist/assets/img/node-ps-strategy-lowest-price.3a4ad347.png +0 -0
- package/docs/.vuepress/dist/assets/img/power-saver-nordpool-current-state.bf14afde.png +0 -0
- package/docs/.vuepress/dist/assets/img/power-saver-nordpool-events-state.8c392507.png +0 -0
- package/docs/.vuepress/dist/assets/img/power-saver-tibber-mqtt.16891dd2.png +0 -0
- package/docs/.vuepress/dist/assets/js/293.5e967839.js +1 -0
- package/docs/.vuepress/dist/assets/js/491.c183eba3.js +1 -0
- package/docs/.vuepress/dist/assets/js/812.79dad458.js +2 -0
- package/docs/.vuepress/dist/assets/js/812.79dad458.js.LICENSE.txt +8 -0
- package/docs/.vuepress/dist/assets/js/app.80d4373d.js +1 -0
- package/docs/.vuepress/dist/assets/js/runtime~app.665b411c.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-08683c60.9edcaa60.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-0aca7ba6.5eca5160.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-0b5e3c8c.c02472fb.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-1ad821fa.a628e907.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-30acb564.80b4190d.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-3706649a.d7f73384.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-4637f9e4.df94c6ea.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-510ed0d4.a78d8542.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-5954bcb2.4f4712f3.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-5db8da3a.2e49b81a.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-61f728ca.7b545524.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-677dfaed.756e0fb5.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-7c87f26e.57507077.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-8daa1a0e.1ea39527.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-b4a42144.a812c440.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-e8c55052.30f30acd.js +1 -0
- package/docs/.vuepress/dist/assets/js/v-fffb8e28.850019c1.js +1 -0
- package/docs/.vuepress/dist/changelog/index.html +15 -0
- package/docs/.vuepress/dist/contribute/index.html +15 -0
- package/docs/.vuepress/dist/euro.png +0 -0
- package/docs/.vuepress/dist/examples/example-nordpool-current-state.html +169 -0
- package/docs/.vuepress/dist/examples/example-nordpool-events-state.html +173 -0
- package/docs/.vuepress/dist/examples/example-tibber-mqtt.html +182 -0
- package/docs/.vuepress/dist/examples/index.html +15 -0
- package/docs/.vuepress/dist/guide/index.html +52 -0
- package/docs/.vuepress/dist/index.html +15 -0
- package/docs/.vuepress/dist/logo.png +0 -0
- package/docs/.vuepress/dist/nodes/index.html +15 -0
- package/docs/.vuepress/dist/nodes/old-power-saver-doc.html +97 -0
- package/docs/.vuepress/dist/nodes/power-saver.html +15 -0
- package/docs/.vuepress/dist/nodes/ps-elvia-add-tariff.html +15 -0
- package/docs/.vuepress/dist/nodes/ps-receive-price.html +80 -0
- package/docs/.vuepress/dist/nodes/ps-strategy-best-save.html +65 -0
- package/docs/.vuepress/dist/nodes/ps-strategy-lowest-price.html +89 -0
- package/docs/.vuepress/dist/nodes/strategy-input.html +40 -0
- package/docs/.vuepress/public/euro.png +0 -0
- package/docs/.vuepress/public/logo.png +0 -0
- package/docs/README.md +32 -0
- package/docs/changelog/README.md +55 -0
- package/docs/contribute/README.md +39 -0
- package/docs/examples/README.md +5 -0
- package/docs/examples/example-nordpool-current-state.md +166 -0
- package/docs/examples/example-nordpool-events-state.md +170 -0
- package/docs/examples/example-tibber-mqtt.md +179 -0
- package/docs/guide/README.md +193 -0
- package/docs/images/all-nodes.png +0 -0
- package/docs/images/best-save-config.png +0 -0
- package/docs/images/elvia-add-tariff-node-used.png +0 -0
- package/docs/images/elvia-config-no-config.png +0 -0
- package/docs/images/elvia-config-no-tariff.png +0 -0
- package/docs/images/elvia-config-select-tariff.png +0 -0
- package/docs/images/elvia-config-subscription-key.png +0 -0
- package/docs/images/elvia-flow.png +0 -0
- package/docs/images/elvia-tariff-config.png +0 -0
- package/docs/images/euro.png +0 -0
- package/docs/images/example-flow-1.png +0 -0
- package/docs/images/example-flow-2.png +0 -0
- package/docs/images/logo.png +0 -0
- package/docs/images/lowest-price-config.png +0 -0
- package/docs/images/migrate-best-save.png +0 -0
- package/docs/images/migrate-power-saver.png +0 -0
- package/docs/images/node-power-saver.png +0 -0
- package/docs/images/node-ps-elvia-add-tariff.png +0 -0
- package/docs/images/node-ps-elvia-tariff-types.png +0 -0
- package/docs/images/node-ps-elvia-tariff.png +0 -0
- package/docs/images/node-ps-receive-price.png +0 -0
- package/docs/images/node-ps-strategy-best-save.png +0 -0
- package/docs/images/node-ps-strategy-lowest-price.png +0 -0
- package/{doc → docs/images}/node-red-contrib-power-saver-flow.png +0 -0
- package/docs/images/power-saver-nordpool-current-state.png +0 -0
- package/docs/images/power-saver-nordpool-events-state.png +0 -0
- package/docs/images/power-saver-tibber-mqtt.png +0 -0
- package/docs/nodes/README.md +53 -0
- package/docs/nodes/old-power-saver-doc.md +231 -0
- package/docs/nodes/power-saver.md +23 -0
- package/docs/nodes/ps-elvia-add-tariff.md +52 -0
- package/docs/nodes/ps-receive-price.md +153 -0
- package/docs/nodes/ps-strategy-best-save.md +142 -0
- package/docs/nodes/ps-strategy-lowest-price.md +165 -0
- package/docs/nodes/strategy-input.md +39 -0
- package/package.json +16 -4
- package/src/elvia/elvia-add-tariff.html +70 -0
- package/src/elvia/elvia-add-tariff.js +47 -0
- package/src/elvia/elvia-api.js +61 -0
- package/src/elvia/elvia-config.html +44 -0
- package/src/elvia/elvia-config.js +25 -0
- package/src/elvia/elvia-tariff-types.html +34 -0
- package/src/elvia/elvia-tariff-types.js +23 -0
- package/src/elvia/elvia-tariff.html +89 -0
- package/src/elvia/elvia-tariff.js +26 -0
- package/src/elvia/icons/elvia_hvite.svg +4 -0
- package/src/elvia/icons/elvia_positive_4 copy.svg +4 -0
- package/src/handle-input.js +159 -0
- package/src/power-saver.html +116 -0
- package/{power-saver.js → src/power-saver.js} +9 -32
- package/src/receive-price-functions.js +99 -0
- package/src/receive-price.html +30 -0
- package/src/receive-price.js +21 -0
- package/src/strategy-best-save-functions.js +110 -0
- package/src/strategy-best-save.html +116 -0
- package/src/strategy-best-save.js +95 -0
- package/src/strategy-lowest-price-functions.js +35 -0
- package/src/strategy-lowest-price.html +168 -0
- package/src/strategy-lowest-price.js +125 -0
- package/{utils.js → src/utils.js} +44 -100
- package/test/data/adjustedResult.js +219 -71
- package/test/data/adjustedResult_old.js +154 -0
- package/test/data/best-save-result.json +357 -0
- package/test/data/converted-prices.json +196 -0
- package/test/data/elvia-input-grid-tariff.json +760 -0
- package/test/data/elvia-input-power-prices.json +194 -0
- package/test/data/elvia-output-add-tariff.json +290 -0
- package/test/data/lowest-price-result-cont.json +18 -0
- package/test/data/lowest-price-result-split-allday.json +21 -0
- package/test/data/lowest-price-result-split-allday10.json +20 -0
- package/test/data/lowest-price-result-split.json +20 -0
- package/test/data/nordpool-current-state-prices.json +283 -0
- package/test/data/nordpool-event-prices.json +574 -0
- package/test/data/reconfigResult.js +220 -67
- package/test/data/reconfigResult_old.js +141 -0
- package/test/data/tibber-prices-single-home.json +64 -0
- package/test/data/tibber-prices.json +124 -0
- package/test/data/{tibber_result.json → tibber-result.json} +0 -0
- package/test/elvia.test.js +26 -0
- package/test/mostSavedStrategy.test.js +22 -55
- package/test/power-saver.test.js +21 -5
- package/test/receive-price-functions.test.js +153 -0
- package/test/receive-price.test.js +122 -0
- package/test/send-config-input.test.js +8 -10
- package/test/strategy-best-save-test-utils.js +32 -0
- package/test/strategy-best-save.test.js +103 -0
- package/test/strategy-lowest-price-functions.test.js +40 -0
- package/test/strategy-lowest-price.test.js +338 -0
- package/test/test-utils.js +0 -18
- package/test/utils.test.js +22 -180
- package/doc/example-nordpool-current-state.md +0 -166
- package/doc/example-nordpool-events-state.md +0 -153
- package/doc/example-tibber-mqtt.md +0 -189
- package/doc/power-saver-nordpool-current-state.png +0 -0
- package/doc/power-saver-nordpool-events-state.png +0 -0
- package/doc/power-saver-tibber-mqtt.png +0 -0
- package/mostSavedStrategy.js +0 -84
- package/power-saver.html +0 -308
- package/test/data/tibber_data.json +0 -412
- 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
|
-

|
|
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
|
|
Binary file
|
|
Binary file
|
package/mostSavedStrategy.js
DELETED
|
@@ -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>
|