node-red-contrib-power-saver 4.1.3 → 4.1.4
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 -1
- package/docs/.vuepress/client.js +7 -0
- package/docs/.vuepress/clientAppEnhance.js +1 -1
- package/docs/.vuepress/components/AdsenseAdd.vue +1 -1
- package/docs/.vuepress/config.js +31 -30
- package/docs/README.md +6 -0
- package/docs/changelog/README.md +4 -0
- package/docs/examples/example-grid-tariff-capacity-part.md +16 -5
- package/docs/privacy.md +17 -0
- package/examples/example-grid-tariff-capacity-flow.json +2 -2
- package/package.json +19 -18
- package/src/schedule-merger-functions.js +2 -1
- package/src/strategy-heat-capacitor-functions.js +38 -4
- package/src/strategy-heat-capacitor.html +2 -2
- package/src/strategy-heat-capacitor.js +94 -63
- package/test/commands-input-best-save.test.js +15 -15
- package/test/commands-input-lowest-price.test.js +15 -15
- package/test/commands-input-schedule-merger.test.js +11 -11
- package/test/data/heat-capacitor-prices-NaN-test.json +197 -0
- package/test/elvia.test.js +2 -2
- package/test/general-add-tariff-functions.test.js +6 -6
- package/test/general-add-tariff.test.js +7 -7
- package/test/mostSavedStrategy.test.js +17 -17
- package/test/receive-price-functions.test.js +9 -9
- package/test/receive-price.test.js +11 -11
- package/test/schedule-merger-functions.test.js +18 -28
- package/test/schedule-merger.test.js +17 -17
- package/test/send-config-input.test.js +13 -13
- package/test/strategy-best-save-overlap.test.js +2 -2
- package/test/strategy-best-save.test.js +28 -28
- package/test/strategy-fixed-schedule.test.js +5 -5
- package/test/strategy-heat-capacitor-node.test.js +115 -16
- package/test/strategy-heat-capacitor.test.js +8 -6
- package/test/strategy-lowest-price-3days.test.js +4 -4
- package/test/strategy-lowest-price-functions.test.js +13 -13
- package/test/strategy-lowest-price.test.js +34 -34
- package/test/utils.test.js +46 -45
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const cloneDeep = require("lodash.clonedeep");
|
|
2
2
|
const { DateTime } = require("luxon");
|
|
3
|
-
const expect = require("
|
|
3
|
+
const expect = require("chai").expect;
|
|
4
4
|
const helper = require("node-red-node-test-helper");
|
|
5
5
|
const bestSave = require("../src/strategy-best-save.js");
|
|
6
6
|
const prices = require("./data/converted-prices.json");
|
|
@@ -27,7 +27,7 @@ describe("ps-strategy-best-save node", function () {
|
|
|
27
27
|
const flow = [{ id: "n1", type: "ps-strategy-best-save", name: "test name" }];
|
|
28
28
|
helper.load(bestSave, flow, function () {
|
|
29
29
|
const n1 = helper.getNode("n1");
|
|
30
|
-
expect(n1).
|
|
30
|
+
expect(n1).to.have.property("name", "test name");
|
|
31
31
|
done();
|
|
32
32
|
});
|
|
33
33
|
});
|
|
@@ -76,22 +76,22 @@ describe("ps-strategy-best-save node", function () {
|
|
|
76
76
|
let countOn = 0;
|
|
77
77
|
let countOff = 0;
|
|
78
78
|
n2.on("input", function (msg) {
|
|
79
|
-
expect(equalPlan(expected, msg.payload)).
|
|
79
|
+
expect(equalPlan(expected, msg.payload)).to.equal(true);
|
|
80
80
|
n1.warn.should.not.be.called;
|
|
81
81
|
setTimeout(() => {
|
|
82
82
|
console.log("countOn = " + countOn + ", countOff = " + countOff);
|
|
83
|
-
expect(countOn).
|
|
84
|
-
expect(countOff).
|
|
83
|
+
expect(countOn).to.equal(7);
|
|
84
|
+
expect(countOff).to.equal(7);
|
|
85
85
|
done();
|
|
86
86
|
}, 900);
|
|
87
87
|
});
|
|
88
88
|
n3.on("input", function (msg) {
|
|
89
89
|
countOn++;
|
|
90
|
-
expect(msg).
|
|
90
|
+
expect(msg).to.have.deep.property("payload", true);
|
|
91
91
|
});
|
|
92
92
|
n4.on("input", function (msg) {
|
|
93
93
|
countOff++;
|
|
94
|
-
expect(msg).
|
|
94
|
+
expect(msg).to.have.deep.property("payload", false);
|
|
95
95
|
});
|
|
96
96
|
n1.receive({ payload: makePayload(prices, plan.time) });
|
|
97
97
|
});
|
|
@@ -120,19 +120,19 @@ describe("ps-strategy-best-save node", function () {
|
|
|
120
120
|
case 2:
|
|
121
121
|
setTimeout(() => {
|
|
122
122
|
console.log("countOn = " + countOn + ", countOff = " + countOff);
|
|
123
|
-
expect(countOn).
|
|
124
|
-
expect(countOff).
|
|
123
|
+
expect(countOn).to.equal(0);
|
|
124
|
+
expect(countOff).to.equal(1);
|
|
125
125
|
done();
|
|
126
126
|
}, 100);
|
|
127
127
|
}
|
|
128
128
|
});
|
|
129
129
|
n3.on("input", function (msg) {
|
|
130
130
|
countOn++;
|
|
131
|
-
expect(msg).
|
|
131
|
+
expect(msg).to.have.deep.property("payload", true);
|
|
132
132
|
});
|
|
133
133
|
n4.on("input", function (msg) {
|
|
134
134
|
countOff++;
|
|
135
|
-
expect(msg).
|
|
135
|
+
expect(msg).to.have.deep.property("payload", false);
|
|
136
136
|
});
|
|
137
137
|
const payload = {
|
|
138
138
|
...convertedPrices,
|
|
@@ -158,25 +158,25 @@ describe("ps-strategy-best-save node", function () {
|
|
|
158
158
|
let countOn = 0;
|
|
159
159
|
let countOff = 0;
|
|
160
160
|
n2.on("input", function (msg) {
|
|
161
|
-
expect(equalPlan(expected, msg.payload)).
|
|
161
|
+
expect(equalPlan(expected, msg.payload)).to.equal(true);
|
|
162
162
|
n1.warn.should.not.be.called;
|
|
163
163
|
if (!timeoutSet) {
|
|
164
164
|
timeoutSet = true;
|
|
165
165
|
setTimeout(() => {
|
|
166
166
|
console.log("countOn = " + countOn + ", countOff = " + countOff);
|
|
167
|
-
expect(countOn).
|
|
168
|
-
expect(countOff).
|
|
167
|
+
expect(countOn).to.equal(2);
|
|
168
|
+
expect(countOff).to.equal(2);
|
|
169
169
|
done();
|
|
170
170
|
}, 900);
|
|
171
171
|
}
|
|
172
172
|
});
|
|
173
173
|
n3.on("input", function (msg) {
|
|
174
174
|
countOn++;
|
|
175
|
-
expect(msg).
|
|
175
|
+
expect(msg).to.have.deep.property("payload", true);
|
|
176
176
|
});
|
|
177
177
|
n4.on("input", function (msg) {
|
|
178
178
|
countOff++;
|
|
179
|
-
expect(msg).
|
|
179
|
+
expect(msg).to.have.deep.property("payload", false);
|
|
180
180
|
if (countOff === 2) {
|
|
181
181
|
n1.receive({ payload: { config: { override: "on" }, time: plan.time } });
|
|
182
182
|
}
|
|
@@ -196,20 +196,20 @@ describe("ps-strategy-best-save node", function () {
|
|
|
196
196
|
const n3 = helper.getNode("n3");
|
|
197
197
|
const n4 = helper.getNode("n4");
|
|
198
198
|
n2.on("input", function (msg) {
|
|
199
|
-
expect(msg.payload.config.outputValueForOn).
|
|
200
|
-
expect(msg.payload.config.outputValueForOff).
|
|
201
|
-
expect(msg.payload.config.outputValueForOntype).
|
|
202
|
-
expect(msg.payload.config.outputValueForOfftype).
|
|
199
|
+
expect(msg.payload.config.outputValueForOn).to.equal(1);
|
|
200
|
+
expect(msg.payload.config.outputValueForOff).to.equal(0);
|
|
201
|
+
expect(msg.payload.config.outputValueForOntype).to.equal("num");
|
|
202
|
+
expect(msg.payload.config.outputValueForOfftype).to.equal("num");
|
|
203
203
|
n1.warn.should.not.be.called;
|
|
204
204
|
setTimeout(() => {
|
|
205
205
|
done();
|
|
206
206
|
}, 100);
|
|
207
207
|
});
|
|
208
208
|
n3.on("input", function (msg) {
|
|
209
|
-
expect(msg).
|
|
209
|
+
expect(msg).to.have.deep.property("payload", 1);
|
|
210
210
|
});
|
|
211
211
|
n4.on("input", function (msg) {
|
|
212
|
-
expect(msg).
|
|
212
|
+
expect(msg).to.have.deep.property("payload", 0);
|
|
213
213
|
});
|
|
214
214
|
n1.receive({ payload: makePayload(prices, plan.time) });
|
|
215
215
|
});
|
|
@@ -226,20 +226,20 @@ describe("ps-strategy-best-save node", function () {
|
|
|
226
226
|
const n3 = helper.getNode("n3");
|
|
227
227
|
const n4 = helper.getNode("n4");
|
|
228
228
|
n2.on("input", function (msg) {
|
|
229
|
-
expect(msg.payload.config.outputValueForOn).
|
|
230
|
-
expect(msg.payload.config.outputValueForOff).
|
|
231
|
-
expect(msg.payload.config.outputValueForOntype).
|
|
232
|
-
expect(msg.payload.config.outputValueForOfftype).
|
|
229
|
+
expect(msg.payload.config.outputValueForOn).to.equal("on");
|
|
230
|
+
expect(msg.payload.config.outputValueForOff).to.equal("off");
|
|
231
|
+
expect(msg.payload.config.outputValueForOntype).to.equal("str");
|
|
232
|
+
expect(msg.payload.config.outputValueForOfftype).to.equal("str");
|
|
233
233
|
n1.warn.should.not.be.called;
|
|
234
234
|
setTimeout(() => {
|
|
235
235
|
done();
|
|
236
236
|
}, 100);
|
|
237
237
|
});
|
|
238
238
|
n3.on("input", function (msg) {
|
|
239
|
-
expect(msg).
|
|
239
|
+
expect(msg).to.have.deep.property("payload", "on");
|
|
240
240
|
});
|
|
241
241
|
n4.on("input", function (msg) {
|
|
242
|
-
expect(msg).
|
|
242
|
+
expect(msg).to.have.deep.property("payload", "off");
|
|
243
243
|
});
|
|
244
244
|
n1.receive({ payload: makePayload(prices, plan.time) });
|
|
245
245
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const cloneDeep = require("lodash.clonedeep");
|
|
2
2
|
const { DateTime } = require("luxon");
|
|
3
|
-
const expect = require("
|
|
3
|
+
const expect = require("chai").expect;
|
|
4
4
|
const helper = require("node-red-node-test-helper");
|
|
5
5
|
const fixedSchedule = require("../src/strategy-fixed-schedule.js");
|
|
6
6
|
const prices = require("./data/converted-prices.json");
|
|
@@ -24,7 +24,7 @@ describe("ps-strategy-fixed-schedule node", function () {
|
|
|
24
24
|
const flow = [{ id: "n1", type: "ps-strategy-fixed-schedule", name: "test name" }];
|
|
25
25
|
helper.load(fixedSchedule, flow, function () {
|
|
26
26
|
const n1 = helper.getNode("n1");
|
|
27
|
-
expect(n1).
|
|
27
|
+
expect(n1).to.have.property("name", "test name");
|
|
28
28
|
done();
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -36,7 +36,7 @@ describe("ps-strategy-fixed-schedule node", function () {
|
|
|
36
36
|
const n1 = helper.getNode("n1");
|
|
37
37
|
const n2 = helper.getNode("n2");
|
|
38
38
|
n2.on("input", function (msg) {
|
|
39
|
-
expect(equalPlan(expected, msg.payload)).
|
|
39
|
+
expect(equalPlan(expected, msg.payload)).to.equal(true);
|
|
40
40
|
n1.warn.should.not.be.called;
|
|
41
41
|
done();
|
|
42
42
|
});
|
|
@@ -57,7 +57,7 @@ describe("ps-strategy-fixed-schedule node", function () {
|
|
|
57
57
|
const n1 = helper.getNode("n1");
|
|
58
58
|
const n2 = helper.getNode("n2");
|
|
59
59
|
n2.on("input", function (msg) {
|
|
60
|
-
expect(equalPlan(expected, msg.payload)).
|
|
60
|
+
expect(equalPlan(expected, msg.payload)).to.equal(true);
|
|
61
61
|
n1.warn.should.not.be.called;
|
|
62
62
|
done();
|
|
63
63
|
});
|
|
@@ -85,7 +85,7 @@ describe("ps-strategy-fixed-schedule node", function () {
|
|
|
85
85
|
const n1 = helper.getNode("n1");
|
|
86
86
|
const n2 = helper.getNode("n2");
|
|
87
87
|
n2.on("input", function (msg) {
|
|
88
|
-
expect(equalSchedule(expected, msg.payload.schedule)).
|
|
88
|
+
expect(equalSchedule(expected, msg.payload.schedule)).to.equal(true);
|
|
89
89
|
done();
|
|
90
90
|
});
|
|
91
91
|
n1.receive({ payload: prices, time: prices.priceData[0].start });
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const cloneDeep = require("lodash.clonedeep");
|
|
3
3
|
const { DateTime } = require("luxon");
|
|
4
|
-
const expect = require("
|
|
4
|
+
const expect = require("chai").expect;
|
|
5
5
|
const helper = require("node-red-node-test-helper");
|
|
6
6
|
const node = require("../src/strategy-heat-capacitor.js");
|
|
7
7
|
const prices = require("./data/converted-prices.json");
|
|
8
8
|
const multiTrade = require("./data/multiple-trades.json");
|
|
9
|
+
const nanTest = require("./data/heat-capacitor-prices-NaN-test.json");
|
|
9
10
|
|
|
10
11
|
helper.init(require.resolve("node-red"));
|
|
11
12
|
|
|
@@ -24,7 +25,7 @@ describe("ps-strategy-heat-capacitor node", function () {
|
|
|
24
25
|
const flow = [{ id: "n1", type: "ps-strategy-heat-capacitor", name: "Temp. Adj." }];
|
|
25
26
|
helper.load(node, flow, function () {
|
|
26
27
|
const n1 = helper.getNode("n1");
|
|
27
|
-
expect(n1).
|
|
28
|
+
expect(n1).to.have.property("name", "Temp. Adj.");
|
|
28
29
|
done();
|
|
29
30
|
});
|
|
30
31
|
});
|
|
@@ -76,15 +77,15 @@ describe("ps-strategy-heat-capacitor node", function () {
|
|
|
76
77
|
},
|
|
77
78
|
},
|
|
78
79
|
});
|
|
79
|
-
expect(n1).
|
|
80
|
-
expect(n1).
|
|
81
|
-
expect(n1).
|
|
82
|
-
expect(n1).
|
|
83
|
-
expect(n1).
|
|
84
|
-
expect(n1).
|
|
85
|
-
expect(n1).
|
|
80
|
+
expect(n1).to.have.property("timeHeat1C", 1);
|
|
81
|
+
expect(n1).to.have.property("timeCool1C", 2);
|
|
82
|
+
expect(n1).to.have.property("boostTempHeat", 6);
|
|
83
|
+
expect(n1).to.have.property("boostTempCool", 7);
|
|
84
|
+
expect(n1).to.have.property("setpoint", 3);
|
|
85
|
+
expect(n1).to.have.property("maxTempAdjustment", 4);
|
|
86
|
+
expect(n1).to.have.property("minSavings", 5);
|
|
86
87
|
n1.receive({ payload: { config: { setpoint: 24 } } });
|
|
87
|
-
expect(n1).
|
|
88
|
+
expect(n1).to.have.property("setpoint", 24);
|
|
88
89
|
done();
|
|
89
90
|
});
|
|
90
91
|
});
|
|
@@ -98,12 +99,12 @@ describe("ps-strategy-heat-capacitor node", function () {
|
|
|
98
99
|
const n3 = helper.getNode("n3");
|
|
99
100
|
let bothReceived = false;
|
|
100
101
|
n2.on("input", function (msg) {
|
|
101
|
-
expect(msg).
|
|
102
|
+
expect(msg).to.have.deep.property("payload", 22.5);
|
|
102
103
|
n1.warn.should.not.be.called;
|
|
103
104
|
bothReceived ? done() : (bothReceived = true);
|
|
104
105
|
});
|
|
105
106
|
n3.on("input", function (msg) {
|
|
106
|
-
expect(msg).
|
|
107
|
+
expect(msg).to.have.deep.property("payload", -0.5);
|
|
107
108
|
n1.warn.should.not.be.called;
|
|
108
109
|
bothReceived ? done() : (bothReceived = true);
|
|
109
110
|
});
|
|
@@ -121,17 +122,23 @@ describe("ps-strategy-heat-capacitor node", function () {
|
|
|
121
122
|
const n1 = helper.getNode("n1");
|
|
122
123
|
const n2 = helper.getNode("n2");
|
|
123
124
|
const n3 = helper.getNode("n3");
|
|
125
|
+
const n4 = helper.getNode("n4");
|
|
126
|
+
const n5 = helper.getNode("n5");
|
|
124
127
|
let bothReceived = false;
|
|
125
128
|
n2.on("input", function (msg) {
|
|
126
|
-
expect(msg).
|
|
129
|
+
expect(msg).to.have.deep.property("payload", 24.5);
|
|
127
130
|
n1.warn.should.not.be.called;
|
|
128
131
|
bothReceived ? done() : (bothReceived = true);
|
|
129
132
|
});
|
|
130
133
|
n3.on("input", function (msg) {
|
|
131
|
-
expect(msg).
|
|
134
|
+
expect(msg).to.have.deep.property("payload", 1.5);
|
|
132
135
|
n1.warn.should.not.be.called;
|
|
133
136
|
bothReceived ? done() : (bothReceived = true);
|
|
134
137
|
});
|
|
138
|
+
n5.on("input", function (msg) {
|
|
139
|
+
expect(msg).to.have.deep.property("payload.current_setpoint", 24.5);
|
|
140
|
+
n1.warn.should.not.be.called;
|
|
141
|
+
});
|
|
135
142
|
const time = DateTime.fromISO(multiTrade.priceData[4].start).plus({ minutes: 10 });
|
|
136
143
|
multiTrade.time = time;
|
|
137
144
|
n1.receive({ payload: multiTrade });
|
|
@@ -144,10 +151,101 @@ describe("ps-strategy-heat-capacitor node", function () {
|
|
|
144
151
|
const n1 = helper.getNode("n1");
|
|
145
152
|
n1.receive({ payload: multiTrade });
|
|
146
153
|
n1.receive({ payload: prices });
|
|
147
|
-
expect(n1.priceData.length).
|
|
154
|
+
expect(n1.priceData.length).to.equal(72);
|
|
148
155
|
done();
|
|
149
156
|
});
|
|
150
157
|
});
|
|
158
|
+
|
|
159
|
+
it("should plan correctly, NaN test", function (done) {
|
|
160
|
+
const result = 0.5;
|
|
161
|
+
const flow = [
|
|
162
|
+
{
|
|
163
|
+
id: "n1",
|
|
164
|
+
type: "ps-strategy-heat-capacitor",
|
|
165
|
+
name: "Temp. Adj.",
|
|
166
|
+
timeHeat1C: 480,
|
|
167
|
+
timeCool1C: 360,
|
|
168
|
+
boostTempHeat: 2,
|
|
169
|
+
boostTempCool: 2,
|
|
170
|
+
setpoint: 20,
|
|
171
|
+
maxTempAdjustment: 1,
|
|
172
|
+
minSavings: 0.08,
|
|
173
|
+
wires: [["n2"], ["n3"], ["n4"], ["n5"]],
|
|
174
|
+
},
|
|
175
|
+
{ id: "n2", type: "helper" },
|
|
176
|
+
{ id: "n3", type: "helper" },
|
|
177
|
+
{ id: "n4", type: "helper" },
|
|
178
|
+
{ id: "n5", type: "helper" },
|
|
179
|
+
];
|
|
180
|
+
helper.load(node, flow, function () {
|
|
181
|
+
const n1 = helper.getNode("n1");
|
|
182
|
+
const n2 = helper.getNode("n2");
|
|
183
|
+
const n3 = helper.getNode("n3");
|
|
184
|
+
const n4 = helper.getNode("n4");
|
|
185
|
+
const n5 = helper.getNode("n5");
|
|
186
|
+
let bothReceived = false;
|
|
187
|
+
n2.on("input", function (msg) {
|
|
188
|
+
expect(msg).to.have.deep.property("payload", 17);
|
|
189
|
+
n1.warn.should.not.be.called;
|
|
190
|
+
bothReceived ? done() : (bothReceived = true);
|
|
191
|
+
});
|
|
192
|
+
n3.on("input", function (msg) {
|
|
193
|
+
expect(msg).to.have.deep.property("payload", -3);
|
|
194
|
+
n1.warn.should.not.be.called;
|
|
195
|
+
bothReceived ? done() : (bothReceived = true);
|
|
196
|
+
});
|
|
197
|
+
n5.on("input", function (msg) {
|
|
198
|
+
expect(msg).to.have.deep.property("payload.current_setpoint", 17);
|
|
199
|
+
n1.warn.should.not.be.called;
|
|
200
|
+
});
|
|
201
|
+
const time = DateTime.fromISO("2022-12-06T10:51:48.126+01:00");
|
|
202
|
+
nanTest.time = time;
|
|
203
|
+
n1.receive({ payload: nanTest });
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should support dynamic commands", function (done) {
|
|
208
|
+
const result = 0.5;
|
|
209
|
+
const flow = makeFlow();
|
|
210
|
+
helper.load(node, flow, function () {
|
|
211
|
+
const n1 = helper.getNode("n1");
|
|
212
|
+
const n2 = helper.getNode("n2");
|
|
213
|
+
const n3 = helper.getNode("n3");
|
|
214
|
+
const n4 = helper.getNode("n4");
|
|
215
|
+
const n5 = helper.getNode("n5");
|
|
216
|
+
const numInputs = [0, 0, 0, 0];
|
|
217
|
+
const expectedNumInputs = [2, 2, 2, 2];
|
|
218
|
+
function testNumInputs() {
|
|
219
|
+
if (numInputs.every((e, i) => e === expectedNumInputs[i])) {
|
|
220
|
+
done();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
n2.on("input", function (msg) {
|
|
224
|
+
numInputs[0]++;
|
|
225
|
+
testNumInputs();
|
|
226
|
+
});
|
|
227
|
+
n3.on("input", function (msg) {
|
|
228
|
+
numInputs[1]++;
|
|
229
|
+
testNumInputs();
|
|
230
|
+
});
|
|
231
|
+
n4.on("input", function (msg) {
|
|
232
|
+
numInputs[2]++;
|
|
233
|
+
testNumInputs();
|
|
234
|
+
});
|
|
235
|
+
n5.on("input", function (msg) {
|
|
236
|
+
numInputs[3]++;
|
|
237
|
+
testNumInputs();
|
|
238
|
+
});
|
|
239
|
+
const time = DateTime.fromISO(multiTrade.priceData[4].start).plus({ minutes: 10 });
|
|
240
|
+
const outputCommand = { payload: { commands: { sendOutput: true } } };
|
|
241
|
+
const scheduleCommand = { payload: { commands: { sendSchedule: true } } };
|
|
242
|
+
multiTrade.time = time;
|
|
243
|
+
|
|
244
|
+
n1.receive({ payload: multiTrade });
|
|
245
|
+
n1.receive(outputCommand);
|
|
246
|
+
n1.receive(scheduleCommand);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
151
249
|
});
|
|
152
250
|
|
|
153
251
|
function makeFlow() {
|
|
@@ -163,11 +261,12 @@ function makeFlow() {
|
|
|
163
261
|
setpoint: 23,
|
|
164
262
|
maxTempAdjustment: 0.5,
|
|
165
263
|
minSavings: 0.08,
|
|
166
|
-
wires: [["n2"], ["n3"], ["n4"]],
|
|
264
|
+
wires: [["n2"], ["n3"], ["n4"], ["n5"]],
|
|
167
265
|
},
|
|
168
266
|
{ id: "n2", type: "helper" },
|
|
169
267
|
{ id: "n3", type: "helper" },
|
|
170
268
|
{ id: "n4", type: "helper" },
|
|
269
|
+
{ id: "n5", type: "helper" },
|
|
171
270
|
];
|
|
172
271
|
}
|
|
173
272
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const { DateTime } = require("luxon");
|
|
3
|
-
const expect = require("
|
|
3
|
+
const expect = require("chai").expect;
|
|
4
4
|
const {
|
|
5
5
|
calculateSchedule,
|
|
6
6
|
calculateOpportunities,
|
|
@@ -17,6 +17,7 @@ describe("ps-strategy-heat-capacitor-functions", () => {
|
|
|
17
17
|
//User input
|
|
18
18
|
const timeHeat1C = 60;
|
|
19
19
|
const timeCool1C = 40;
|
|
20
|
+
const setpoint = 23;
|
|
20
21
|
const maxTempAdjustment = 1;
|
|
21
22
|
const boostTempHeat = 1;
|
|
22
23
|
const boostTempCool = 1;
|
|
@@ -37,7 +38,7 @@ describe("ps-strategy-heat-capacitor-functions", () => {
|
|
|
37
38
|
let result = calculateOpportunities(my_prices, my_buy_pattern, 1);
|
|
38
39
|
//Remove float precisions errors by rounding
|
|
39
40
|
result = result.map((x) => Math.round(x * 1000000) / 1000000);
|
|
40
|
-
expect(result).
|
|
41
|
+
expect(result).to.eql(Array(56).fill(my_prices[0]));
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
it("Can find procurement pattern", () => {
|
|
@@ -49,7 +50,7 @@ describe("ps-strategy-heat-capacitor-functions", () => {
|
|
|
49
50
|
|
|
50
51
|
const my_buy_sell = findBestBuySellPattern(buy_prices, buy_pattern.length, sell_prices, sell_pattern.length);
|
|
51
52
|
|
|
52
|
-
expect(my_buy_sell).
|
|
53
|
+
expect(my_buy_sell).to.eql([
|
|
53
54
|
[0, 141],
|
|
54
55
|
[100, 240],
|
|
55
56
|
]);
|
|
@@ -65,7 +66,7 @@ describe("ps-strategy-heat-capacitor-functions", () => {
|
|
|
65
66
|
const sell_prices = calculateOpportunities(my_prices, sell_pattern, 1);
|
|
66
67
|
const result = calculateValueDictList(my_buy_sell_indexes, buy_prices, sell_prices, start_date);
|
|
67
68
|
|
|
68
|
-
expect(result[0].sellDate).
|
|
69
|
+
expect(result[0].sellDate).to.eql(start_date.plus({ minutes: 131 }));
|
|
69
70
|
});
|
|
70
71
|
|
|
71
72
|
it("DictList test at decreasing end", () => {
|
|
@@ -79,13 +80,14 @@ describe("ps-strategy-heat-capacitor-functions", () => {
|
|
|
79
80
|
my_buy_sell,
|
|
80
81
|
buy_prices,
|
|
81
82
|
sell_prices,
|
|
83
|
+
setpoint,
|
|
82
84
|
maxTempAdjustment,
|
|
83
85
|
boostTempHeat,
|
|
84
86
|
boostTempCool,
|
|
85
87
|
buy_pattern.length,
|
|
86
88
|
sell_pattern.length
|
|
87
89
|
);
|
|
88
|
-
expect(my_schedule.temperatures[my_schedule.temperatures.length - 1]).
|
|
90
|
+
expect(my_schedule.temperatures[my_schedule.temperatures.length - 1]).to.equal(-maxTempAdjustment);
|
|
89
91
|
});
|
|
90
92
|
|
|
91
93
|
it("Check removal of low benefit buy-sell pairs", () => {
|
|
@@ -98,6 +100,6 @@ describe("ps-strategy-heat-capacitor-functions", () => {
|
|
|
98
100
|
//Should remove the sell at 1.05 and the re-buy at 1 (only 0.05 difference)
|
|
99
101
|
const compare = [my_buy_sell[0].slice(0, 2), [my_buy_sell[1][0], my_buy_sell[1][2]]];
|
|
100
102
|
|
|
101
|
-
expect(result).
|
|
103
|
+
expect(result).to.eql(compare);
|
|
102
104
|
});
|
|
103
105
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const cloneDeep = require("lodash.clonedeep");
|
|
2
2
|
const { DateTime } = require("luxon");
|
|
3
|
-
const expect = require("
|
|
3
|
+
const expect = require("chai").expect;
|
|
4
4
|
const helper = require("node-red-node-test-helper");
|
|
5
5
|
const lowestPrice = require("../src/strategy-lowest-price.js");
|
|
6
6
|
const prices = require("./data/nordpool-3-days-prices.json");
|
|
@@ -24,7 +24,7 @@ describe("ps-strategy-lowest-price with data day before", function () {
|
|
|
24
24
|
const flow = [{ id: "n1", type: "ps-strategy-lowest-price", name: "test name" }];
|
|
25
25
|
helper.load(lowestPrice, flow, function () {
|
|
26
26
|
const n1 = helper.getNode("n1");
|
|
27
|
-
expect(n1).
|
|
27
|
+
expect(n1).to.have.property("name", "test name");
|
|
28
28
|
done();
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -41,7 +41,7 @@ describe("ps-strategy-lowest-price with data day before", function () {
|
|
|
41
41
|
let count = 0;
|
|
42
42
|
n2.on("input", function (msg) {
|
|
43
43
|
if (count === 1) {
|
|
44
|
-
expect(msg.payload).
|
|
44
|
+
expect(msg.payload).to.have.deep.property("schedule", result.schedule);
|
|
45
45
|
done();
|
|
46
46
|
}
|
|
47
47
|
count++;
|
|
@@ -72,7 +72,7 @@ describe("ps-strategy-lowest-price with data day before", function () {
|
|
|
72
72
|
let count = 0;
|
|
73
73
|
n2.on("input", function (msg) {
|
|
74
74
|
if (count === 1) {
|
|
75
|
-
expect(msg.payload).
|
|
75
|
+
expect(msg.payload).to.have.deep.property("schedule", res.schedule);
|
|
76
76
|
done();
|
|
77
77
|
}
|
|
78
78
|
count++;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { DateTime } = require("luxon");
|
|
2
|
-
const expect = require("
|
|
2
|
+
const expect = require("chai").expect;
|
|
3
3
|
const { getBestContinuous, getBestX } = require("../src/strategy-lowest-price-functions");
|
|
4
4
|
const convertedPrices = require("./data/converted-prices.json");
|
|
5
5
|
const cloneDeep = require("lodash.clonedeep");
|
|
@@ -10,31 +10,31 @@ describe("strategy-lowest-price-functions", () => {
|
|
|
10
10
|
const result = cloneDeep(values).fill(false);
|
|
11
11
|
result.fill(true, 0, 6);
|
|
12
12
|
result.fill(true, 24, 31);
|
|
13
|
-
expect(getBestX(values, 13)).
|
|
13
|
+
expect(getBestX(values, 13)).to.eql(result);
|
|
14
14
|
const values1 = convertedPrices.priceData.slice(0, 12).map((p) => p.value);
|
|
15
15
|
const result1 = cloneDeep(values1).fill(false);
|
|
16
|
-
expect(getBestX(values1, 1)).
|
|
17
|
-
expect(getBestX(values1, 3)).
|
|
16
|
+
expect(getBestX(values1, 1)).to.eql(cloneDeep(result1).fill(true, 2, 3));
|
|
17
|
+
expect(getBestX(values1, 3)).to.eql(cloneDeep(result1).fill(true, 1, 4));
|
|
18
18
|
const values2 = convertedPrices.priceData.slice(24, 28).map((p) => p.value);
|
|
19
19
|
const result2 = cloneDeep(values2).fill(false);
|
|
20
|
-
expect(getBestX(values2, 1)).
|
|
21
|
-
expect(getBestX(values2, 3)).
|
|
20
|
+
expect(getBestX(values2, 1)).to.eql(cloneDeep(result2).fill(true, 3, 4));
|
|
21
|
+
expect(getBestX(values2, 3)).to.eql(cloneDeep(result2).fill(true, 1, 4));
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it("can find best x continuous", () => {
|
|
25
25
|
const values = convertedPrices.priceData.slice(0, 12).map((p) => p.value);
|
|
26
26
|
const result = cloneDeep(values).fill(false);
|
|
27
|
-
expect(getBestContinuous(values, 1)).
|
|
28
|
-
expect(getBestContinuous(values, 3)).
|
|
27
|
+
expect(getBestContinuous(values, 1)).to.eql(cloneDeep(result).fill(true, 2, 3));
|
|
28
|
+
expect(getBestContinuous(values, 3)).to.eql(cloneDeep(result).fill(true, 1, 4));
|
|
29
29
|
const values2 = convertedPrices.priceData.slice(24, 28).map((p) => p.value);
|
|
30
30
|
const result2 = cloneDeep(values2).fill(false);
|
|
31
|
-
expect(getBestContinuous(values2, 1)).
|
|
32
|
-
expect(getBestContinuous(values2, 3)).
|
|
31
|
+
expect(getBestContinuous(values2, 1)).to.eql(cloneDeep(result2).fill(true, 3, 4));
|
|
32
|
+
expect(getBestContinuous(values2, 3)).to.eql(cloneDeep(result2).fill(true, 1, 4));
|
|
33
33
|
});
|
|
34
34
|
});
|
|
35
35
|
|
|
36
36
|
function validatePeriod(period, start, end, count) {
|
|
37
|
-
expect(period.length).
|
|
38
|
-
expect(period[0]).
|
|
39
|
-
expect(period[count - 1]).
|
|
37
|
+
expect(period.length).to.eql(count);
|
|
38
|
+
expect(period[0]).to.eql(start);
|
|
39
|
+
expect(period[count - 1]).to.eql(end);
|
|
40
40
|
}
|