Finding the best energy price with API call

With the current energy prices I want to see when it's most favorable to do energy heavy stuff.
Here in Denmark we can make API call to a data hub to see energy prices at an hourly rate for current day and for the next day.

So potentially 48h in to the future if the API call I made at 00.01 o'clock.

For this I make the API call at https://api.energidataservice.dk/dataset/Elspotprices?start=now&filter={"PriceArea":"DK2"}&sort=HourDK

[{"id":"0c9e143f916d7ede","type":"debug","z":"f6979629ded1c46f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1410,"y":4560,"wires":[]},{"id":"3845925bb4cff5e5","type":"http request","z":"f6979629ded1c46f","name":"","method":"GET","ret":"obj","paytoqs":"body","url":"https://api.energidataservice.dk/dataset/Elspotprices?start=now&filter={\"PriceArea\":\"DK2\"}&sort=HourDK","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"credentials":{},"x":1250,"y":4560,"wires":[["0c9e143f916d7ede","e69e27a90f604234","af8a879cb2b7a03b","42163f5a.825ce","4792c3d34e5a365c","8591e13802f229bf","3a8202f34b4da043","d3b5cf5afb235a3a","16042cbb2499e48f","8a6a9b0be8d02f56","4e2a7ae4eff15af9","049e024c256058ba","5279adbe98f9a23d","545f83c293705e1c"]]},{"id":"a9307deb28e64897","type":"inject","z":"f6979629ded1c46f","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":1530,"y":4140,"wires":[["3845925bb4cff5e5"]]}]

As you can see in this example the array is 30 because there's 6 hours left of today, and 24 of tomorrow:

What I would like to do is find the lowest "SpotPriceDKK:" in the array, and also know at what time it is.
So that way I can see that the lowest price is in x-time.

Even better if I can filter it out to what is the lowest price in a time frame I specified.

Any ideas?

Hello ..
you can do this with some Javascript in a Function node

let lowestPrice = msg.payload.records[0];  // start with 1st element

msg.payload.records.forEach(el => {
    if (el.SpotPriceDKK < lowestPrice.SpotPriceDKK) { lowestPrice = el }
})

return { payload: lowestPrice };

you can start by setting the lowerPrice to the first element in the records array and then loop through it with the forEach method checking each element if it is lower that the already set lowestPrice

Test Flow:

[{"id":"0c9e143f916d7ede","type":"debug","z":"54efb553244c241f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":810,"y":1600,"wires":[]},{"id":"3845925bb4cff5e5","type":"http request","z":"54efb553244c241f","name":"","method":"GET","ret":"obj","paytoqs":"body","url":"https://api.energidataservice.dk/dataset/Elspotprices?start=now&filter={\"PriceArea\":\"DK2\"}&sort=HourDK","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":450,"y":1600,"wires":[["44022b394e443ada","f5fa3a19fb71df29"]]},{"id":"a9307deb28e64897","type":"inject","z":"54efb553244c241f","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":290,"y":1600,"wires":[["3845925bb4cff5e5"]]},{"id":"44022b394e443ada","type":"function","z":"54efb553244c241f","name":"find lowest price","func":"let lowestPrice = msg.payload.records[0];  // start with 1st element\n\nmsg.payload.records.forEach(el => {\n    if (el.SpotPriceDKK < lowestPrice.SpotPriceDKK) { lowestPrice = el }\n})\n\nreturn { payload: lowestPrice };","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":1600,"wires":[["0c9e143f916d7ede"]]},{"id":"f5fa3a19fb71df29","type":"debug","z":"54efb553244c241f","name":"debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":600,"y":1520,"wires":[]}]
1 Like

Once again you come to the rescue! Thanks mate!

I might be a bit dim, but is there a way to give a input to only look at element 1-16 or 3-10 etc.?

hmm .. possibly introduce in the forEach loop the index of the iterated element and check if its between those values ?

msg.payload.records.forEach( (el, index) => {
    if ( index >= 1 && index <= 16 && el.SpotPriceDKK < lowestPrice.SpotPriceDKK) { lowestPrice = el }
})
1 Like

That seems to do the job!

That way I can set a specific time to inject and make a time frame within the current day and the day after or both to see what time I must favorable.

The time is a string, and I haven't found a solution to use that to start a flow at that time. Is it possible at all? Or is it better to use the array number as an indicator for when to fire the flow?

you mean 1 minute past midnight ?
the Inject node has some scheduling capabilities

image

or you can use cron-plus node for more advanced schedules

No, I mean the time that I get attached to the lowest price -

oh i see .. in that case Cron-plus can do that .. with a specially crafted msg you can create dynamic schedules based on that string date coming in from the previous msg.

Example

[{"id":"0c9e143f916d7ede","type":"debug","z":"54efb553244c241f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":870,"y":1600,"wires":[]},{"id":"3845925bb4cff5e5","type":"http request","z":"54efb553244c241f","name":"","method":"GET","ret":"obj","paytoqs":"body","url":"https://api.energidataservice.dk/dataset/Elspotprices?start=now&filter={\"PriceArea\":\"DK2\"}&sort=HourDK","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":450,"y":1600,"wires":[["44022b394e443ada","f5fa3a19fb71df29"]]},{"id":"a9307deb28e64897","type":"inject","z":"54efb553244c241f","name":"","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":290,"y":1600,"wires":[["3845925bb4cff5e5"]]},{"id":"44022b394e443ada","type":"function","z":"54efb553244c241f","name":"find lowest price","func":"let lowestPrice = msg.payload.records[0];  // start with 1st element\n\nmsg.payload.records.forEach( (el, index) => {\n    if ( index >= 1 && index <= 16 && el.SpotPriceDKK < lowestPrice.SpotPriceDKK) { lowestPrice = el }\n})\n\nreturn { payload: lowestPrice };","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":640,"y":1600,"wires":[["0c9e143f916d7ede","9470c79839117bff"]]},{"id":"f5fa3a19fb71df29","type":"debug","z":"54efb553244c241f","name":"debug","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":600,"y":1520,"wires":[]},{"id":"fb61b81a73fe42ee","type":"cronplus","z":"54efb553244c241f","name":"","outputField":"payload","timeZone":"","persistDynamic":false,"commandResponseMsgOutput":"output1","outputs":1,"options":[],"x":580,"y":1760,"wires":[["6f2aeefec7b0268a"]]},{"id":"9470c79839117bff","type":"function","z":"54efb553244c241f","name":"create cron Schedule","func":"msg.payload = {\n    \"command\": \"add\",\n    \"name\": \"my dynamic schedule\",\n    \"expression\": msg.payload.HourUTC,\n    \"expressionType\": \"dates\",\n    \"payloadType\": \"default\"\n};\n  \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":1760,"wires":[["fb61b81a73fe42ee"]]},{"id":"6f2aeefec7b0268a","type":"debug","z":"54efb553244c241f","name":"debug 23","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":770,"y":1760,"wires":[]}]

In the node's help tab you'll find extensive details for each of the msg properties for the Cron plus node

2 Likes

:raised_hands:t2:
That works just beautifully!

Thanks again!

1 Like

Starting to get errors in the setup.
Shouldn't "msg.payload.records[0];" start with the same as " if ( index >= 5 &&"

Hello Kim,

yes you are right. I can see now that the code would fail to update to the correct lowestPrice since we introduced the hours check with index

if we set lowestPrice to records[0] and there is no other lowestPrice between the hours 5 to 18 then it would fail to update.

maybe its better to filter out the needed records based on the hours and then check to find the lowest

// records between hours
let recordsBetween = msg.payload.records.filter((el, index) => index >= 5 && index <= 18);

// start with 1st element
let lowestPrice = recordsBetween[0]

recordsBetween.forEach( el => {
    if ( el.SpotPriceDKK < lowestPrice.SpotPriceDKK) { lowestPrice = el }
})

return { payload: lowestPrice };

It seems to solve the issue :relaxed:

I tried to introduce a second range, would it be that simple?

// records between hours
let recordsBetween = msg.payload.records.filter((el, index) => index >= 2 && <= 4 && >= 11 && <= 13);

hi .. you need index infront of some of your code comparisons and maybe also to group with brackets the two separate hour ranges and use OR || ?

// records between hours
let recordsBetween = msg.payload.records.filter((el, index) => (index >= 2 && index <= 4) || (index >= 11 && index <= 13));
1 Like

Tried with the index, but that was with the other one (with the lowestPrice before index)
Didn't know about the || being OR. :clap:t2:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.