node-red-contrib-power-saver 4.0.0 → 4.1.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/docs/.vuepress/components/AdsenseAdd.vue +40 -0
- package/docs/README.md +2 -0
- package/docs/changelog/README.md +6 -0
- package/docs/contribute/README.md +4 -0
- package/docs/examples/README.md +6 -0
- package/docs/faq/README.md +4 -0
- package/docs/faq/best-save-viewer.md +8 -0
- package/docs/guide/README.md +16 -0
- package/docs/nodes/README.md +14 -0
- package/docs/nodes/dynamic-commands.md +12 -0
- package/docs/nodes/dynamic-config.md +8 -0
- package/docs/nodes/old-power-saver-doc.md +4 -0
- package/docs/nodes/ps-elvia-add-tariff.md +12 -0
- package/docs/nodes/ps-general-add-tariff.md +12 -0
- package/docs/nodes/ps-receive-price.md +12 -0
- package/docs/nodes/ps-schedule-merger.md +4 -0
- package/docs/nodes/ps-strategy-best-save.md +20 -0
- package/docs/nodes/ps-strategy-fixed-schedule.md +12 -0
- package/docs/nodes/ps-strategy-heat-capacitor.md +8 -0
- package/docs/nodes/ps-strategy-lowest-price.md +12 -0
- package/docs/nodes/strategy-input.md +4 -0
- package/package.json +1 -1
- package/src/handle-input.js +3 -0
- package/src/handle-output.js +9 -1
- package/src/schedule-merger.js +5 -1
- package/src/strategy-functions.js +1 -1
- package/test/send-config-input.test.js +5 -4
- package/test/strategy-best-save.test.js +46 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<ins
|
|
4
|
+
v-if="øverst"
|
|
5
|
+
class="adsbygoogle"
|
|
6
|
+
style="display: inline-block; width: 740px; height: 90px"
|
|
7
|
+
data-ad-client="ca-pub-9857859182772006"
|
|
8
|
+
data-ad-slot="7524453641"
|
|
9
|
+
></ins>
|
|
10
|
+
<ins
|
|
11
|
+
v-if="type === 'nederst'"
|
|
12
|
+
class="adsbygoogle"
|
|
13
|
+
style="display: block"
|
|
14
|
+
data-ad-client="ca-pub-9857859182772006"
|
|
15
|
+
data-ad-slot="3584713798"
|
|
16
|
+
data-ad-format="auto"
|
|
17
|
+
data-full-width-responsive="true"
|
|
18
|
+
></ins>
|
|
19
|
+
<ins
|
|
20
|
+
v-if="type === 'artikkel'"
|
|
21
|
+
class="adsbygoogle"
|
|
22
|
+
style="display: block; text-align: center"
|
|
23
|
+
data-ad-layout="in-article"
|
|
24
|
+
data-ad-format="fluid"
|
|
25
|
+
data-ad-client="ca-pub-9857859182772006"
|
|
26
|
+
data-ad-slot="3152349396"
|
|
27
|
+
></ins>
|
|
28
|
+
</div>
|
|
29
|
+
</template>
|
|
30
|
+
|
|
31
|
+
<script setup>
|
|
32
|
+
import { onMounted } from "vue";
|
|
33
|
+
|
|
34
|
+
const props = defineProps({
|
|
35
|
+
type: { type: String, required: false, default: "artikkel" },
|
|
36
|
+
});
|
|
37
|
+
onMounted(() => {
|
|
38
|
+
(adsbygoogle = window.adsbygoogle || []).push({});
|
|
39
|
+
});
|
|
40
|
+
</script>
|
package/docs/README.md
CHANGED
|
@@ -32,6 +32,8 @@ footer: Created by Otto Paulsen and contributors
|
|
|
32
32
|
footerHtml: true
|
|
33
33
|
---
|
|
34
34
|
|
|
35
|
+
<AdsenseAdd type="artikkel"/>
|
|
36
|
+
|
|
35
37
|
This is a collection of nodes for the popular [Node-RED](https://nodered.org/) that you can use to save money on variable electricity prices. Node-RED is a widely used low-code programming tool that can be used together with many smart home solutions to create automations.
|
|
36
38
|
|
|
37
39
|
<DonateButtons/>
|
package/docs/changelog/README.md
CHANGED
|
@@ -7,6 +7,12 @@ sidebarDepth: 1
|
|
|
7
7
|
|
|
8
8
|
List the most significant changes.
|
|
9
9
|
|
|
10
|
+
## 4.1.0
|
|
11
|
+
|
|
12
|
+
- Fix bug with override function. It did not override longer than until next scheduled change. Now it overrides until set to auto again.
|
|
13
|
+
- Show override on node status.
|
|
14
|
+
- Fix name filter on schedule merger node.
|
|
15
|
+
|
|
10
16
|
## 4.0.0
|
|
11
17
|
|
|
12
18
|
This is a major rewrite of some of the central code, in order to lay ground for further development and maintenance.
|
|
@@ -50,3 +50,7 @@ Main developer: [Otto Paulsen](https://github.com/ottopaulsen)
|
|
|
50
50
|
Heat Capacitor developer: [Arne Klaveness](https://github.com/TomTorger)
|
|
51
51
|
|
|
52
52
|
Example contributors: [Stefan](https://github.com/oakhill87/node-red-contrib-power-saver)
|
|
53
|
+
|
|
54
|
+
###
|
|
55
|
+
|
|
56
|
+
<AdsenseAdd type="nederst"/>
|
package/docs/examples/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<AdsenseAdd type="øverst"/>
|
|
2
|
+
|
|
1
3
|
# Examples
|
|
2
4
|
|
|
3
5
|
[Nord Pool and `current state` node in HA](./example-nordpool-current-state)
|
|
@@ -17,3 +19,7 @@
|
|
|
17
19
|
## User provided examples
|
|
18
20
|
|
|
19
21
|
[Output schedule to a sensor entity](./example-next-schedule-entity.md) (by Stefan)
|
|
22
|
+
|
|
23
|
+
###
|
|
24
|
+
|
|
25
|
+
<AdsenseAdd type="nederst"/>
|
package/docs/faq/README.md
CHANGED
|
@@ -48,3 +48,7 @@ Another alternative is to reduce the minimum saving from 0.05 to 0.001. Then the
|
|
|
48
48
|
## Can we get Legionella bacteria when turning off the water heater?
|
|
49
49
|
|
|
50
50
|
Be aware that the norwegian [FHI](https://www.fhi.no/nettpub/legionellaveilederen/) recommends that the temperature in the water heater is at least 70°C to avoid growth of legionella. At this temperature, legionalla bacteria die quickly.
|
|
51
|
+
|
|
52
|
+
###
|
|
53
|
+
|
|
54
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -15,6 +15,10 @@ Then paste it here and see the result below:
|
|
|
15
15
|
|
|
16
16
|
<BestSaveVerificator/>
|
|
17
17
|
|
|
18
|
+
###
|
|
19
|
+
|
|
20
|
+
<AdsenseAdd type="artikkel"/>
|
|
21
|
+
|
|
18
22
|
## Explanation
|
|
19
23
|
|
|
20
24
|
### Config
|
|
@@ -53,6 +57,10 @@ If the average per hour is less than what you have configured as `Minimum saving
|
|
|
53
57
|
|
|
54
58
|
If the number is black, it could be used, but only if all other criteria are satisfied, and only if it saves more than any other combination.
|
|
55
59
|
|
|
60
|
+
###
|
|
61
|
+
|
|
62
|
+
<AdsenseAdd type="artikkel"/>
|
|
63
|
+
|
|
56
64
|
## Something seems wrong
|
|
57
65
|
|
|
58
66
|
The tool is not using the same code as the node, so in case there is a bug in the node (or in the tool) the numbers may not match.
|
package/docs/guide/README.md
CHANGED
|
@@ -31,6 +31,10 @@ Here prices are received from Tibber, converted in the `ps-receive-price` node,
|
|
|
31
31
|
The node collection fits very well with Home Assistant (HA), as Node-RED is frequently used for automations, and there also is an integration with Nord Pool, but there is no direct dependency to HA, so all nodes can be used also without HA.
|
|
32
32
|
:::
|
|
33
33
|
|
|
34
|
+
###
|
|
35
|
+
|
|
36
|
+
<AdsenseAdd type="artikkel"/>
|
|
37
|
+
|
|
34
38
|
## Getting started
|
|
35
39
|
|
|
36
40
|
### Installation
|
|
@@ -129,6 +133,10 @@ Data can be sent from both the `current state` node or the `events: state` node.
|
|
|
129
133
|
|
|
130
134
|
[See example with Nord Pool and `events: state` node](../examples/example-nordpool-events-state.md)
|
|
131
135
|
|
|
136
|
+
###
|
|
137
|
+
|
|
138
|
+
<AdsenseAdd type="artikkel"/>
|
|
139
|
+
|
|
132
140
|
### Add grid tariff
|
|
133
141
|
|
|
134
142
|
When also the grid tariff changes per hour, it must be added to the electricity price in order to get the calculations right.
|
|
@@ -192,6 +200,10 @@ There are many ways you can use the output:
|
|
|
192
200
|
- Send a notification
|
|
193
201
|
:::
|
|
194
202
|
|
|
203
|
+
###
|
|
204
|
+
|
|
205
|
+
<AdsenseAdd type="artikkel"/>
|
|
206
|
+
|
|
195
207
|
### Display schedule
|
|
196
208
|
|
|
197
209
|
**Output 3** can be used to print or display the calculated schedule. If you just want to see it, send it to a debug node. You can also use it to display the result as graphs in HA.
|
|
@@ -214,6 +226,10 @@ Se the [Schedule Merger](../nodes/ps-schedule-merger.md) node for more details.
|
|
|
214
226
|
|
|
215
227
|
There are more details and more information in the documentation for each [node](/nodes/) and in the [examples](/examples/).
|
|
216
228
|
|
|
229
|
+
###
|
|
230
|
+
|
|
231
|
+
<AdsenseAdd type="artikkel"/>
|
|
232
|
+
|
|
217
233
|
## Migration from v2
|
|
218
234
|
|
|
219
235
|
The `Power Saver` node from v2 has been removed and must be replaced.
|
package/docs/nodes/README.md
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
<AdsenseAdd type="øverst"/>
|
|
2
|
+
|
|
1
3
|
# Nodes
|
|
2
4
|
|
|
3
5
|
Here is an overview of the nodes, and links to detailed descriptions for eah of them.
|
|
4
6
|
|
|
7
|
+
###
|
|
8
|
+
|
|
9
|
+
<AdsenseAdd type="artikkel"/>
|
|
10
|
+
|
|
5
11
|
## Strategy nodes
|
|
6
12
|
|
|
7
13
|
These are the nodes used to calculate and control saving.
|
|
@@ -30,6 +36,10 @@ A strategy for moving consumption from expensive to cheap periods utilizing clim
|
|
|
30
36
|
|
|
31
37
|
A strategy for setting a fixed daily or weekly schedule.
|
|
32
38
|
|
|
39
|
+
###
|
|
40
|
+
|
|
41
|
+
<AdsenseAdd type="artikkel"/>
|
|
42
|
+
|
|
33
43
|
## Utility nodes
|
|
34
44
|
|
|
35
45
|
### [ps-receive-price](./ps-receive-price)
|
|
@@ -69,3 +79,7 @@ Use this to get a list of the tariff types available in the Elvia API.
|
|
|
69
79
|
### ps-elvia-tariff
|
|
70
80
|
|
|
71
81
|
Use this to get the Elvia grid tariff for a selected tariff type.
|
|
82
|
+
|
|
83
|
+
###
|
|
84
|
+
|
|
85
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
###
|
|
2
|
+
|
|
3
|
+
<AdsenseAdd type="øverst"/>
|
|
4
|
+
|
|
1
5
|
# Dynamic commands
|
|
2
6
|
|
|
3
7
|
You can dynamically send some commands to the node via its input, by using a `commands` object in the payload as described below.
|
|
@@ -39,6 +43,10 @@ You can get the node to send the current output to output 1 or 2 any time by sen
|
|
|
39
43
|
|
|
40
44
|
When you do this, the current schedule is actually recalculated based on the last received data. The current output is sent to output 1 or 2.
|
|
41
45
|
|
|
46
|
+
###
|
|
47
|
+
|
|
48
|
+
<AdsenseAdd type="artikkel"/>
|
|
49
|
+
|
|
42
50
|
### reset
|
|
43
51
|
|
|
44
52
|
You can reset data the node has saved in context by sending this message:
|
|
@@ -77,3 +85,7 @@ and make a plan based on those prices:
|
|
|
77
85
|
|
|
78
86
|
If the context storage is `file` you can use this to create a new schedule after a restart,
|
|
79
87
|
instead of fetching prices again.
|
|
88
|
+
|
|
89
|
+
###
|
|
90
|
+
|
|
91
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -31,6 +31,10 @@ to be able to override a specific strategy node.
|
|
|
31
31
|
The `name` is the exact same value as you set as name in the nodes config.
|
|
32
32
|
:::
|
|
33
33
|
|
|
34
|
+
###
|
|
35
|
+
|
|
36
|
+
<AdsenseAdd type="artikkel"/>
|
|
37
|
+
|
|
34
38
|
## Output values
|
|
35
39
|
|
|
36
40
|
Valid values for `outputValueForOntype` and `outputValueForOfftype` are `bool`, `num` and `str` and must correspond
|
|
@@ -74,3 +78,7 @@ The nodes config is saved in the nodes context.
|
|
|
74
78
|
If dynamic config is sent as input, this replaces the saved config.
|
|
75
79
|
It is the config that is saved in context that is used when calculating.
|
|
76
80
|
When Node-RED starts or the flow is redeployed, the config defined in the node replaces the saved config and will be used when planning.
|
|
81
|
+
|
|
82
|
+
###
|
|
83
|
+
|
|
84
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -12,6 +12,10 @@ Node to add grid tariff from Elvia.
|
|
|
12
12
|
You need an Elvia API subscription key to use this node. See [configuration](#elvia-configuration).
|
|
13
13
|
:::
|
|
14
14
|
|
|
15
|
+
###
|
|
16
|
+
|
|
17
|
+
<AdsenseAdd type="artikkel"/>
|
|
18
|
+
|
|
15
19
|
## Description
|
|
16
20
|
|
|
17
21
|
When grid tariff changes from hour to hour, this should normally also be considered when finding the most favorable hours to use power. This node retrieves prices from Elvia, so if you are an elvia customer, you can put this node between the `ps-receive-price` node and the strategy nodes. When configured, it will add Elvia tariff to the power prices before doing the calculation:
|
|
@@ -47,6 +51,10 @@ Now you should be able to select the right tariff:
|
|
|
47
51
|
|
|
48
52
|
The next time you use this node, you can select the same config as you created the first time, and then you can also select tariff immediately.
|
|
49
53
|
|
|
54
|
+
###
|
|
55
|
+
|
|
56
|
+
<AdsenseAdd type="artikkel"/>
|
|
57
|
+
|
|
50
58
|
## Input
|
|
51
59
|
|
|
52
60
|
The input is the [common strategy input format](./strategy-input.md)
|
|
@@ -54,3 +62,7 @@ The input is the [common strategy input format](./strategy-input.md)
|
|
|
54
62
|
## Output
|
|
55
63
|
|
|
56
64
|
The output is the [common strategy input format](./strategy-input.md)
|
|
65
|
+
|
|
66
|
+
###
|
|
67
|
+
|
|
68
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -9,6 +9,10 @@ next: ./ps-elvia-add-tariff.md
|
|
|
9
9
|
|
|
10
10
|
Node to add a value, for example a variable grid tariff, to the price before it is used to calculate savings in the strategy nodes.
|
|
11
11
|
|
|
12
|
+
###
|
|
13
|
+
|
|
14
|
+
<AdsenseAdd type="artikkel"/>
|
|
15
|
+
|
|
12
16
|
## Description
|
|
13
17
|
|
|
14
18
|
This node is useful if there is an addition to the electricity price that varies over the day or the week, as it might be for the grid tariff.
|
|
@@ -64,6 +68,10 @@ Fill in the last date the config is valid.
|
|
|
64
68
|
|
|
65
69
|
If this is empty, the config is valid until forever.
|
|
66
70
|
|
|
71
|
+
###
|
|
72
|
+
|
|
73
|
+
<AdsenseAdd type="artikkel"/>
|
|
74
|
+
|
|
67
75
|
## Input
|
|
68
76
|
|
|
69
77
|
The input is the [common strategy input format](./strategy-input.md)
|
|
@@ -73,3 +81,7 @@ The input is the [common strategy input format](./strategy-input.md)
|
|
|
73
81
|
The output is the [common strategy input format](./strategy-input.md)
|
|
74
82
|
|
|
75
83
|
If there is a config property in the input payload, it is passed on to the output payload.
|
|
84
|
+
|
|
85
|
+
###
|
|
86
|
+
|
|
87
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -27,6 +27,10 @@ That is why this is now a separate node.
|
|
|
27
27
|
|
|
28
28
|
There is no configuration except from node name.
|
|
29
29
|
|
|
30
|
+
###
|
|
31
|
+
|
|
32
|
+
<AdsenseAdd type="artikkel"/>
|
|
33
|
+
|
|
30
34
|
## Input
|
|
31
35
|
|
|
32
36
|
### Tibber input
|
|
@@ -120,6 +124,10 @@ Then use the id in the following query, replacing the id with the one you found
|
|
|
120
124
|
|
|
121
125
|
This is the query you shall put in the `tibber-query` node.
|
|
122
126
|
|
|
127
|
+
###
|
|
128
|
+
|
|
129
|
+
<AdsenseAdd type="artikkel"/>
|
|
130
|
+
|
|
123
131
|
### Nord Pool input
|
|
124
132
|
|
|
125
133
|
This is especially designed to work for Home Assistant (HA), and the [Nord Pool custom component](https://github.com/custom-components/nordpool). The Nord Pool component provides a _sensor_ that gives price per hour for today and tomorrow (after 13:00). Send the output from this sensor directly to the `ps-receive-price` node. Make sure this is done whenever the node is updated, as well as when the system starts up.
|
|
@@ -156,3 +164,7 @@ If you cannot use any of the two above (Tibber or Nord Pool), create the input t
|
|
|
156
164
|
## Output
|
|
157
165
|
|
|
158
166
|
The output is the [common strategy input format](./strategy-input.md), so it can be sent directly to the strategy nodes, or via any `ps-xxx-add-tariff` node.
|
|
167
|
+
|
|
168
|
+
###
|
|
169
|
+
|
|
170
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -55,6 +55,10 @@ the schedule from for example the Lowest Price node, using the Schedule Merger n
|
|
|
55
55
|
| Send when rescheduling | Check this to make sure on or off output is sent immediately after rescheduling. If unchecked, the output is sent only if it has not been sent before, or is different from the current value. |
|
|
56
56
|
| If no schedule, send | What to do if there is no valid schedule any more (turn on or off). This value will be sent also before there is any valid schedule, or after the last hour there is price data for. |
|
|
57
57
|
|
|
58
|
+
###
|
|
59
|
+
|
|
60
|
+
<AdsenseAdd type="artikkel"/>
|
|
61
|
+
|
|
58
62
|
### Dynamic config
|
|
59
63
|
|
|
60
64
|
The following config values can be changed dynamically:
|
|
@@ -37,6 +37,10 @@ The picture at the bottom of the page, under [Integration with MagicMirror](#int
|
|
|
37
37
|
NB! The `Min recover` only has effect if the previous save-period is of length `Max per sequence`. If the save-period is shorter, the following on-period may be as short as one hour.
|
|
38
38
|
:::
|
|
39
39
|
|
|
40
|
+
###
|
|
41
|
+
|
|
42
|
+
<AdsenseAdd type="artikkel"/>
|
|
43
|
+
|
|
40
44
|
### Dynamic config
|
|
41
45
|
|
|
42
46
|
The following config values can be changed dynamically:
|
|
@@ -91,6 +95,10 @@ A payload with the value set in config, default `false` is sent to output 2 when
|
|
|
91
95
|
|
|
92
96
|
When a valid input is received, and the schedule is recalculated, the resulting schedule, as well as some other information, is sent to output 3. You can use this to see the plan and verify that it meets your expectations. You can also use it to display the schedule in any way you like.
|
|
93
97
|
|
|
98
|
+
###
|
|
99
|
+
|
|
100
|
+
<AdsenseAdd type="artikkel"/>
|
|
101
|
+
|
|
94
102
|
Example of output:
|
|
95
103
|
|
|
96
104
|
```json
|
|
@@ -155,6 +163,10 @@ The `schedule` and the `hours` arrays from Output 3 are both saved to the nodes
|
|
|
155
163
|
|
|
156
164
|
You can see the saved data if you select the node in Node-RED, and view "Context data", and refresh the Node context.
|
|
157
165
|
|
|
166
|
+
###
|
|
167
|
+
|
|
168
|
+
<AdsenseAdd type="artikkel"/>
|
|
169
|
+
|
|
158
170
|
## Algorithm
|
|
159
171
|
|
|
160
172
|
The calculation that decides what hours to turn off works as follows:
|
|
@@ -216,6 +228,10 @@ and set `msg.payload` to the following JSON value:
|
|
|
216
228
|
|
|
217
229
|
This is an alternative to fetching new prices and send as input.
|
|
218
230
|
|
|
231
|
+
###
|
|
232
|
+
|
|
233
|
+
<AdsenseAdd type="artikkel"/>
|
|
234
|
+
|
|
219
235
|
## Integration with MagicMirror
|
|
220
236
|
|
|
221
237
|
Are you using [MagicMirror](https://magicmirror.builders/)? Are you also using [Tibber](https://tibber.com/)? If so, there is a module for MM called [MMM-Tibber](https://github.com/ottopaulsen/MMM-Tibber), that easily can be used to show savings from this node.
|
|
@@ -229,3 +245,7 @@ Read more about this in the [MMM-Tibber documentation](https://github.com/ottopa
|
|
|
229
245
|
## Viewer
|
|
230
246
|
|
|
231
247
|
If you like to analyze the data output by the node, take a look at the [Best Save Viewer](../faq/best-save-viewer.md).
|
|
248
|
+
|
|
249
|
+
###
|
|
250
|
+
|
|
251
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -39,6 +39,10 @@ Here is an example of how to combine it with the Lowest Price node:
|
|
|
39
39
|
| If no schedule, send | What to do if there is no valid schedule any more (turn on or off). This value will be sent also before there is any valid schedule, or after the last hour there is price data for. |
|
|
40
40
|
| Context storage | Select context storage to save data to, if more than one is configured in the Node-RED `settings.js` file. |
|
|
41
41
|
|
|
42
|
+
###
|
|
43
|
+
|
|
44
|
+
<AdsenseAdd type="artikkel"/>
|
|
45
|
+
|
|
42
46
|
### Dynamic config
|
|
43
47
|
|
|
44
48
|
The following config values can be changed dynamically:
|
|
@@ -91,6 +95,10 @@ When a valid input is received, and the schedule is recalculated, the resulting
|
|
|
91
95
|
|
|
92
96
|
The aoutput is similar to the output from the other strategy nodes.
|
|
93
97
|
|
|
98
|
+
###
|
|
99
|
+
|
|
100
|
+
<AdsenseAdd type="artikkel"/>
|
|
101
|
+
|
|
94
102
|
## Usage ideas
|
|
95
103
|
|
|
96
104
|
### Turn on every morning
|
|
@@ -99,3 +107,7 @@ If you want to make sure that a switch is turned on at least 2 hours every morni
|
|
|
99
107
|
even if you are using the Lowest Price node to turn it on only the 4 cheapest hours during
|
|
100
108
|
the whole day, you can use this node to make sure it is on this period, and then merge it with the
|
|
101
109
|
Lowest Price schedule using the Schedule Merger node with the `OR` function.
|
|
110
|
+
|
|
111
|
+
###
|
|
112
|
+
|
|
113
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -37,6 +37,10 @@ It is a good application for cabins/heated storage spaces, as the entity never a
|
|
|
37
37
|
|
|
38
38
|
The node consumes price information and outputs $\Delta T$ on its first output and the planned schedule and benefit calculations on the second output. The $\Delta T$ is used to adjust the set-point of a climate entity.
|
|
39
39
|
|
|
40
|
+
###
|
|
41
|
+
|
|
42
|
+
<AdsenseAdd type="artikkel"/>
|
|
43
|
+
|
|
40
44
|
### The impact of **Time +1C**
|
|
41
45
|
|
|
42
46
|
This time is used to optimize the timing of when to turn up the heat. An increase in temperature from 22 to 23C will cause an increased electricity consumption for quite some time after the change has occurred. For heat-pumps, the air is heated first, then walls, furniture etc., creating a high electricity demand in the first hour or so before the demand slowly reduces down to normal levels. The algorithm uses this time to estimate a period of increased consumption and places this at an optimum point in time.
|
|
@@ -349,3 +353,7 @@ Full example:
|
|
|
349
353
|
]
|
|
350
354
|
}
|
|
351
355
|
```
|
|
356
|
+
|
|
357
|
+
###
|
|
358
|
+
|
|
359
|
+
<AdsenseAdd type="nederst"/>
|
|
@@ -64,6 +64,10 @@ If `Consecutive on period` is on (checked), hours will be turned on only if the
|
|
|
64
64
|
If you leave `Max price` blank, it has no effect.
|
|
65
65
|
:::
|
|
66
66
|
|
|
67
|
+
###
|
|
68
|
+
|
|
69
|
+
<AdsenseAdd type="artikkel"/>
|
|
70
|
+
|
|
67
71
|
### Dynamic config
|
|
68
72
|
|
|
69
73
|
The following config values can be changed dynamically:
|
|
@@ -113,6 +117,10 @@ A payload with the value set in config, default `false` is sent to output 2 when
|
|
|
113
117
|
|
|
114
118
|
When a valid input is received, and the schedule is recalculated, the resulting schedule, as well as some other information, is sent to output 3. You can use this to see the plan and verify that it meets your expectations. You can also use it to display the schedule in any way you like.
|
|
115
119
|
|
|
120
|
+
###
|
|
121
|
+
|
|
122
|
+
<AdsenseAdd type="artikkel"/>
|
|
123
|
+
|
|
116
124
|
Example of output:
|
|
117
125
|
|
|
118
126
|
```json
|
|
@@ -246,3 +254,7 @@ If you want to find the `x` hours with the highest prices, do as follows:
|
|
|
246
254
|
1. Calculate `y` as the total number of hours in the period. For example, if the period is from `08:00` to `20:00`, then `y = 12`.
|
|
247
255
|
2. Configure `Hours On = y - x`, so if `x = 4`, then `Hours On = 12 - 4 = 8`.
|
|
248
256
|
3. Use **Output 2** to get a signal when you have the hours with the highest prices. Just remember that the value sent to output 2 is `false`, not `true` as it is on output 1.
|
|
257
|
+
|
|
258
|
+
###
|
|
259
|
+
|
|
260
|
+
<AdsenseAdd type="nederst"/>
|
package/package.json
CHANGED
package/src/handle-input.js
CHANGED
|
@@ -86,6 +86,9 @@ function makePlanFromPriceData(node, msg, config, doPlanning, calcSavings) {
|
|
|
86
86
|
function getCommands(msg) {
|
|
87
87
|
const legalCommands = ["reset", "replan", "sendOutput", "sendSchedule"];
|
|
88
88
|
const commands = { legal: true };
|
|
89
|
+
if (msg.payload?.config?.override === "auto") {
|
|
90
|
+
commands.runSchedule = true;
|
|
91
|
+
}
|
|
89
92
|
if (!msg?.payload?.commands) {
|
|
90
93
|
return commands;
|
|
91
94
|
}
|
package/src/handle-output.js
CHANGED
|
@@ -13,6 +13,9 @@ function handleOutput(node, config, plan, outputCommands, planFromTime) {
|
|
|
13
13
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
// Clear status
|
|
17
|
+
node.status({});
|
|
18
|
+
|
|
16
19
|
// Prepare output
|
|
17
20
|
let output3 = {
|
|
18
21
|
payload: {
|
|
@@ -44,10 +47,15 @@ function handleOutput(node, config, plan, outputCommands, planFromTime) {
|
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
// Run schedule
|
|
50
|
+
clearTimeout(node.schedulingTimeout);
|
|
47
51
|
if (outputCommands.runSchedule) {
|
|
48
|
-
clearTimeout(node.schedulingTimeout);
|
|
49
52
|
node.schedulingTimeout = runSchedule(node, plan.schedule, planFromTime, true);
|
|
50
53
|
}
|
|
54
|
+
|
|
55
|
+
// Set status if override
|
|
56
|
+
if (config.override !== "auto") {
|
|
57
|
+
node.status({ fill: "yellow", shape: "dot", text: "Override " + config.override });
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
function sendSwitch(node, onOff) {
|
package/src/schedule-merger.js
CHANGED
|
@@ -45,6 +45,10 @@ module.exports = function (RED) {
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
node.on("input", function (msg) {
|
|
48
|
+
if (msg.payload?.name && msg.payload.name !== node.name) {
|
|
49
|
+
// If payload.name is set, and does not match this nodes name, discard message
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
48
52
|
if (msg.payload.hours) {
|
|
49
53
|
// Delete config from strategy nodes so it does not merge
|
|
50
54
|
// with config for this node.
|
|
@@ -100,7 +104,7 @@ module.exports = function (RED) {
|
|
|
100
104
|
|
|
101
105
|
handleOutput(node, config, plan, outputCommands, planFromTime);
|
|
102
106
|
},
|
|
103
|
-
commands.replan ? 0 : node.schedulingDelay
|
|
107
|
+
commands.replan || msg.payload.config ? 0 : node.schedulingDelay
|
|
104
108
|
);
|
|
105
109
|
});
|
|
106
110
|
}
|
|
@@ -26,7 +26,7 @@ function strategyOnInput(node, msg, doPlanning, calcSavings) {
|
|
|
26
26
|
node.sendCurrentValueWhenRescheduling
|
|
27
27
|
),
|
|
28
28
|
sendSchedule: strategyShallSendSchedule(msg, commands),
|
|
29
|
-
runSchedule: commands.replan !== false,
|
|
29
|
+
runSchedule: commands.replan !== false && config.override === "auto",
|
|
30
30
|
};
|
|
31
31
|
handleOutput(node, config, plan, outputCommands, planFromTime);
|
|
32
32
|
}
|
|
@@ -115,6 +115,7 @@ describe("send config as input", () => {
|
|
|
115
115
|
});
|
|
116
116
|
it("can override", function (done) {
|
|
117
117
|
const flow = makeFlow(3, 2, false);
|
|
118
|
+
const time = prices.priceData[0].start;
|
|
118
119
|
helper.load(bestSave, flow, function () {
|
|
119
120
|
const n1 = helper.getNode("n1");
|
|
120
121
|
const n2 = helper.getNode("n2");
|
|
@@ -131,6 +132,7 @@ describe("send config as input", () => {
|
|
|
131
132
|
console.log("countOn = " + countOn + ", countOff = " + countOff);
|
|
132
133
|
expect(countOn).toEqual(2);
|
|
133
134
|
expect(countOff).toEqual(2);
|
|
135
|
+
n1.status.should.be.calledWithExactly({ fill: "yellow", shape: "dot", text: "Override on" });
|
|
134
136
|
done();
|
|
135
137
|
}, 900);
|
|
136
138
|
}
|
|
@@ -139,20 +141,19 @@ describe("send config as input", () => {
|
|
|
139
141
|
countOn++;
|
|
140
142
|
expect(msg).toHaveProperty("payload", true);
|
|
141
143
|
if (countOn === 2) {
|
|
142
|
-
n1.receive({ payload: { config: { override: "on" } } });
|
|
144
|
+
n1.receive({ payload: { config: { override: "on" }, time } });
|
|
143
145
|
}
|
|
144
146
|
});
|
|
145
147
|
n4.on("input", function (msg) {
|
|
146
148
|
countOff++;
|
|
147
149
|
expect(msg).toHaveProperty("payload", false);
|
|
148
150
|
if (countOff === 1) {
|
|
149
|
-
n1.receive({ payload: { config: { override: "on" }, name: "wrong name" } });
|
|
151
|
+
n1.receive({ payload: { config: { override: "on" }, name: "wrong name" }, time });
|
|
150
152
|
}
|
|
151
153
|
if (countOff === 2) {
|
|
152
|
-
n1.receive({ payload: { config: { override: "on" } } });
|
|
154
|
+
n1.receive({ payload: { config: { override: "on" }, time } });
|
|
153
155
|
}
|
|
154
156
|
});
|
|
155
|
-
const time = prices.priceData[0].start;
|
|
156
157
|
n1.receive({ payload: makePayload(prices, time) });
|
|
157
158
|
});
|
|
158
159
|
});
|
|
@@ -96,6 +96,7 @@ describe("ps-strategy-best-save node", function () {
|
|
|
96
96
|
n1.receive({ payload: makePayload(prices, plan.time) });
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
|
+
|
|
99
100
|
it("should not send output when rescheduling", function (done) {
|
|
100
101
|
const flow = makeFlow(3, 2, false);
|
|
101
102
|
helper.load(bestSave, flow, function () {
|
|
@@ -140,6 +141,51 @@ describe("ps-strategy-best-save node", function () {
|
|
|
140
141
|
n1.receive({ payload });
|
|
141
142
|
});
|
|
142
143
|
});
|
|
144
|
+
|
|
145
|
+
it("should handle override", function (done) {
|
|
146
|
+
const flow = makeFlow(3, 2);
|
|
147
|
+
const expected = cloneDeep(result);
|
|
148
|
+
expected.version = version;
|
|
149
|
+
expected.time = plan.time;
|
|
150
|
+
expected.source = "Tibber";
|
|
151
|
+
expected.current = false;
|
|
152
|
+
let timeoutSet = false;
|
|
153
|
+
helper.load(bestSave, flow, function () {
|
|
154
|
+
const n1 = helper.getNode("n1");
|
|
155
|
+
const n2 = helper.getNode("n2");
|
|
156
|
+
const n3 = helper.getNode("n3");
|
|
157
|
+
const n4 = helper.getNode("n4");
|
|
158
|
+
let countOn = 0;
|
|
159
|
+
let countOff = 0;
|
|
160
|
+
n2.on("input", function (msg) {
|
|
161
|
+
expect(equalPlan(expected, msg.payload)).toBeTruthy();
|
|
162
|
+
n1.warn.should.not.be.called;
|
|
163
|
+
if (!timeoutSet) {
|
|
164
|
+
timeoutSet = true;
|
|
165
|
+
setTimeout(() => {
|
|
166
|
+
console.log("countOn = " + countOn + ", countOff = " + countOff);
|
|
167
|
+
expect(countOn).toEqual(2);
|
|
168
|
+
expect(countOff).toEqual(2);
|
|
169
|
+
done();
|
|
170
|
+
}, 900);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
n3.on("input", function (msg) {
|
|
174
|
+
console.log("on");
|
|
175
|
+
countOn++;
|
|
176
|
+
expect(msg).toHaveProperty("payload", true);
|
|
177
|
+
});
|
|
178
|
+
n4.on("input", function (msg) {
|
|
179
|
+
console.log("off");
|
|
180
|
+
countOff++;
|
|
181
|
+
expect(msg).toHaveProperty("payload", false);
|
|
182
|
+
if (countOff === 2) {
|
|
183
|
+
n1.receive({ payload: { config: { override: "on" }, time: plan.time } });
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
n1.receive({ payload: makePayload(prices, plan.time) });
|
|
187
|
+
});
|
|
188
|
+
});
|
|
143
189
|
});
|
|
144
190
|
|
|
145
191
|
function makePayload(prices, time) {
|